linux/fs/ncpfs/ncplib_kernel.c
<<
>>
Prefs
   1/*
   2 *  ncplib_kernel.c
   3 *
   4 *  Copyright (C) 1995, 1996 by Volker Lendecke
   5 *  Modified for big endian by J.F. Chadima and David S. Miller
   6 *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
   7 *  Modified 1999 Wolfram Pienkoss for NLS
   8 *  Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
   9 *
  10 */
  11
  12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13
  14#include "ncp_fs.h"
  15
  16static inline void assert_server_locked(struct ncp_server *server)
  17{
  18        if (server->lock == 0) {
  19                ncp_dbg(1, "server not locked!\n");
  20        }
  21}
  22
  23static void ncp_add_byte(struct ncp_server *server, __u8 x)
  24{
  25        assert_server_locked(server);
  26        *(__u8 *) (&(server->packet[server->current_size])) = x;
  27        server->current_size += 1;
  28        return;
  29}
  30
  31static void ncp_add_word(struct ncp_server *server, __le16 x)
  32{
  33        assert_server_locked(server);
  34        put_unaligned(x, (__le16 *) (&(server->packet[server->current_size])));
  35        server->current_size += 2;
  36        return;
  37}
  38
  39static void ncp_add_be16(struct ncp_server *server, __u16 x)
  40{
  41        assert_server_locked(server);
  42        put_unaligned(cpu_to_be16(x), (__be16 *) (&(server->packet[server->current_size])));
  43        server->current_size += 2;
  44}
  45
  46static void ncp_add_dword(struct ncp_server *server, __le32 x)
  47{
  48        assert_server_locked(server);
  49        put_unaligned(x, (__le32 *) (&(server->packet[server->current_size])));
  50        server->current_size += 4;
  51        return;
  52}
  53
  54static void ncp_add_be32(struct ncp_server *server, __u32 x)
  55{
  56        assert_server_locked(server);
  57        put_unaligned(cpu_to_be32(x), (__be32 *)(&(server->packet[server->current_size])));
  58        server->current_size += 4;
  59}
  60
  61static inline void ncp_add_dword_lh(struct ncp_server *server, __u32 x) {
  62        ncp_add_dword(server, cpu_to_le32(x));
  63}
  64
  65static void ncp_add_mem(struct ncp_server *server, const void *source, int size)
  66{
  67        assert_server_locked(server);
  68        memcpy(&(server->packet[server->current_size]), source, size);
  69        server->current_size += size;
  70        return;
  71}
  72
  73static void ncp_add_pstring(struct ncp_server *server, const char *s)
  74{
  75        int len = strlen(s);
  76        assert_server_locked(server);
  77        if (len > 255) {
  78                ncp_dbg(1, "string too long: %s\n", s);
  79                len = 255;
  80        }
  81        ncp_add_byte(server, len);
  82        ncp_add_mem(server, s, len);
  83        return;
  84}
  85
  86static inline void ncp_init_request(struct ncp_server *server)
  87{
  88        ncp_lock_server(server);
  89
  90        server->current_size = sizeof(struct ncp_request_header);
  91        server->has_subfunction = 0;
  92}
  93
  94static inline void ncp_init_request_s(struct ncp_server *server, int subfunction)
  95{
  96        ncp_lock_server(server);
  97        
  98        server->current_size = sizeof(struct ncp_request_header) + 2;
  99        ncp_add_byte(server, subfunction);
 100
 101        server->has_subfunction = 1;
 102}
 103
 104static inline char *
 105ncp_reply_data(struct ncp_server *server, int offset)
 106{
 107        return &(server->packet[sizeof(struct ncp_reply_header) + offset]);
 108}
 109
 110static inline u8 BVAL(const void *data)
 111{
 112        return *(const u8 *)data;
 113}
 114
 115static u8 ncp_reply_byte(struct ncp_server *server, int offset)
 116{
 117        return *(const u8 *)ncp_reply_data(server, offset);
 118}
 119
 120static inline u16 WVAL_LH(const void *data)
 121{
 122        return get_unaligned_le16(data);
 123}
 124
 125static u16
 126ncp_reply_le16(struct ncp_server *server, int offset)
 127{
 128        return get_unaligned_le16(ncp_reply_data(server, offset));
 129}
 130
 131static u16
 132ncp_reply_be16(struct ncp_server *server, int offset)
 133{
 134        return get_unaligned_be16(ncp_reply_data(server, offset));
 135}
 136
 137static inline u32 DVAL_LH(const void *data)
 138{
 139        return get_unaligned_le32(data);
 140}
 141
 142static __le32
 143ncp_reply_dword(struct ncp_server *server, int offset)
 144{
 145        return get_unaligned((__le32 *)ncp_reply_data(server, offset));
 146}
 147
 148static inline __u32 ncp_reply_dword_lh(struct ncp_server* server, int offset) {
 149        return le32_to_cpu(ncp_reply_dword(server, offset));
 150}
 151
 152int
 153ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target)
 154{
 155        int result;
 156
 157        ncp_init_request(server);
 158        ncp_add_be16(server, size);
 159
 160        if ((result = ncp_request(server, 33)) != 0) {
 161                ncp_unlock_server(server);
 162                return result;
 163        }
 164        *target = min_t(unsigned int, ncp_reply_be16(server, 0), size);
 165
 166        ncp_unlock_server(server);
 167        return 0;
 168}
 169
 170
 171/* options: 
 172 *      bit 0   ipx checksum
 173 *      bit 1   packet signing
 174 */
 175int
 176ncp_negotiate_size_and_options(struct ncp_server *server, 
 177        int size, int options, int *ret_size, int *ret_options) {
 178        int result;
 179
 180        /* there is minimum */
 181        if (size < NCP_BLOCK_SIZE) size = NCP_BLOCK_SIZE;
 182
 183        ncp_init_request(server);
 184        ncp_add_be16(server, size);
 185        ncp_add_byte(server, options);
 186        
 187        if ((result = ncp_request(server, 0x61)) != 0)
 188        {
 189                ncp_unlock_server(server);
 190                return result;
 191        }
 192
 193        /* NCP over UDP returns 0 (!!!) */
 194        result = ncp_reply_be16(server, 0);
 195        if (result >= NCP_BLOCK_SIZE)
 196                size = min(result, size);
 197        *ret_size = size;
 198        *ret_options = ncp_reply_byte(server, 4);
 199
 200        ncp_unlock_server(server);
 201        return 0;
 202}
 203
 204int ncp_get_volume_info_with_number(struct ncp_server* server,
 205                             int n, struct ncp_volume_info* target) {
 206        int result;
 207        int len;
 208
 209        ncp_init_request_s(server, 44);
 210        ncp_add_byte(server, n);
 211
 212        if ((result = ncp_request(server, 22)) != 0) {
 213                goto out;
 214        }
 215        target->total_blocks = ncp_reply_dword_lh(server, 0);
 216        target->free_blocks = ncp_reply_dword_lh(server, 4);
 217        target->purgeable_blocks = ncp_reply_dword_lh(server, 8);
 218        target->not_yet_purgeable_blocks = ncp_reply_dword_lh(server, 12);
 219        target->total_dir_entries = ncp_reply_dword_lh(server, 16);
 220        target->available_dir_entries = ncp_reply_dword_lh(server, 20);
 221        target->sectors_per_block = ncp_reply_byte(server, 28);
 222
 223        memset(&(target->volume_name), 0, sizeof(target->volume_name));
 224
 225        result = -EIO;
 226        len = ncp_reply_byte(server, 29);
 227        if (len > NCP_VOLNAME_LEN) {
 228                ncp_dbg(1, "volume name too long: %d\n", len);
 229                goto out;
 230        }
 231        memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
 232        result = 0;
 233out:
 234        ncp_unlock_server(server);
 235        return result;
 236}
 237
 238int ncp_get_directory_info(struct ncp_server* server, __u8 n, 
 239                           struct ncp_volume_info* target) {
 240        int result;
 241        int len;
 242
 243        ncp_init_request_s(server, 45);
 244        ncp_add_byte(server, n);
 245
 246        if ((result = ncp_request(server, 22)) != 0) {
 247                goto out;
 248        }
 249        target->total_blocks = ncp_reply_dword_lh(server, 0);
 250        target->free_blocks = ncp_reply_dword_lh(server, 4);
 251        target->purgeable_blocks = 0;
 252        target->not_yet_purgeable_blocks = 0;
 253        target->total_dir_entries = ncp_reply_dword_lh(server, 8);
 254        target->available_dir_entries = ncp_reply_dword_lh(server, 12);
 255        target->sectors_per_block = ncp_reply_byte(server, 20);
 256
 257        memset(&(target->volume_name), 0, sizeof(target->volume_name));
 258
 259        result = -EIO;
 260        len = ncp_reply_byte(server, 21);
 261        if (len > NCP_VOLNAME_LEN) {
 262                ncp_dbg(1, "volume name too long: %d\n", len);
 263                goto out;
 264        }
 265        memcpy(&(target->volume_name), ncp_reply_data(server, 22), len);
 266        result = 0;
 267out:
 268        ncp_unlock_server(server);
 269        return result;
 270}
 271
 272int
 273ncp_close_file(struct ncp_server *server, const char *file_id)
 274{
 275        int result;
 276
 277        ncp_init_request(server);
 278        ncp_add_byte(server, 0);
 279        ncp_add_mem(server, file_id, 6);
 280
 281        result = ncp_request(server, 66);
 282        ncp_unlock_server(server);
 283        return result;
 284}
 285
 286int
 287ncp_make_closed(struct inode *inode)
 288{
 289        int err;
 290
 291        err = 0;
 292        mutex_lock(&NCP_FINFO(inode)->open_mutex);
 293        if (atomic_read(&NCP_FINFO(inode)->opened) == 1) {
 294                atomic_set(&NCP_FINFO(inode)->opened, 0);
 295                err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle);
 296
 297                if (!err)
 298                        ncp_vdbg("volnum=%d, dirent=%u, error=%d\n",
 299                                 NCP_FINFO(inode)->volNumber,
 300                                 NCP_FINFO(inode)->dirEntNum, err);
 301        }
 302        mutex_unlock(&NCP_FINFO(inode)->open_mutex);
 303        return err;
 304}
 305
 306static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num,
 307                                __le32 dir_base, int have_dir_base, 
 308                                const char *path)
 309{
 310        ncp_add_byte(server, vol_num);
 311        ncp_add_dword(server, dir_base);
 312        if (have_dir_base != 0) {
 313                ncp_add_byte(server, 1);        /* dir_base */
 314        } else {
 315                ncp_add_byte(server, 0xff);     /* no handle */
 316        }
 317        if (path != NULL) {
 318                ncp_add_byte(server, 1);        /* 1 component */
 319                ncp_add_pstring(server, path);
 320        } else {
 321                ncp_add_byte(server, 0);
 322        }
 323}
 324
 325int ncp_dirhandle_alloc(struct ncp_server* server, __u8 volnum, __le32 dirent,
 326                        __u8* dirhandle) {
 327        int result;
 328
 329        ncp_init_request(server);
 330        ncp_add_byte(server, 12);               /* subfunction */
 331        ncp_add_byte(server, NW_NS_DOS);
 332        ncp_add_byte(server, 0);
 333        ncp_add_word(server, 0);
 334        ncp_add_handle_path(server, volnum, dirent, 1, NULL);
 335        if ((result = ncp_request(server, 87)) == 0) {
 336                *dirhandle = ncp_reply_byte(server, 0);
 337        }
 338        ncp_unlock_server(server);
 339        return result;
 340}
 341
 342int ncp_dirhandle_free(struct ncp_server* server, __u8 dirhandle) {
 343        int result;
 344        
 345        ncp_init_request_s(server, 20);
 346        ncp_add_byte(server, dirhandle);
 347        result = ncp_request(server, 22);
 348        ncp_unlock_server(server);
 349        return result;
 350}
 351
 352void ncp_extract_file_info(const void *structure, struct nw_info_struct *target)
 353{
 354        const __u8 *name_len;
 355        const int info_struct_size = offsetof(struct nw_info_struct, nameLen);
 356
 357        memcpy(target, structure, info_struct_size);
 358        name_len = structure + info_struct_size;
 359        target->nameLen = *name_len;
 360        memcpy(target->entryName, name_len + 1, *name_len);
 361        target->entryName[*name_len] = '\0';
 362        target->volNumber = le32_to_cpu(target->volNumber);
 363        return;
 364}
 365
 366#ifdef CONFIG_NCPFS_NFS_NS
 367static inline void ncp_extract_nfs_info(const unsigned char *structure,
 368                                 struct nw_nfs_info *target)
 369{
 370        target->mode = DVAL_LH(structure);
 371        target->rdev = DVAL_LH(structure + 8);
 372}
 373#endif
 374
 375int ncp_obtain_nfs_info(struct ncp_server *server,
 376                        struct nw_info_struct *target)
 377
 378{
 379        int result = 0;
 380#ifdef CONFIG_NCPFS_NFS_NS
 381        __u32 volnum = target->volNumber;
 382
 383        if (ncp_is_nfs_extras(server, volnum)) {
 384                ncp_init_request(server);
 385                ncp_add_byte(server, 19);       /* subfunction */
 386                ncp_add_byte(server, server->name_space[volnum]);
 387                ncp_add_byte(server, NW_NS_NFS);
 388                ncp_add_byte(server, 0);
 389                ncp_add_byte(server, volnum);
 390                ncp_add_dword(server, target->dirEntNum);
 391                /* We must retrieve both nlinks and rdev, otherwise some server versions
 392                   report zeroes instead of valid data */
 393                ncp_add_dword_lh(server, NSIBM_NFS_MODE | NSIBM_NFS_NLINKS | NSIBM_NFS_RDEV);
 394
 395                if ((result = ncp_request(server, 87)) == 0) {
 396                        ncp_extract_nfs_info(ncp_reply_data(server, 0), &target->nfs);
 397                        ncp_dbg(1, "(%s) mode=0%o, rdev=0x%x\n",
 398                                target->entryName, target->nfs.mode,
 399                                target->nfs.rdev);
 400                } else {
 401                        target->nfs.mode = 0;
 402                        target->nfs.rdev = 0;
 403                }
 404                ncp_unlock_server(server);
 405
 406        } else
 407#endif
 408        {
 409                target->nfs.mode = 0;
 410                target->nfs.rdev = 0;
 411        }
 412        return result;
 413}
 414
 415/*
 416 * Returns information for a (one-component) name relative to
 417 * the specified directory.
 418 */
 419int ncp_obtain_info(struct ncp_server *server, struct inode *dir, const char *path,
 420                        struct nw_info_struct *target)
 421{
 422        __u8  volnum = NCP_FINFO(dir)->volNumber;
 423        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 424        int result;
 425
 426        if (target == NULL) {
 427                pr_err("%s: invalid call\n", __func__);
 428                return -EINVAL;
 429        }
 430        ncp_init_request(server);
 431        ncp_add_byte(server, 6);        /* subfunction */
 432        ncp_add_byte(server, server->name_space[volnum]);
 433        ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
 434        ncp_add_word(server, cpu_to_le16(0x8006));      /* get all */
 435        ncp_add_dword(server, RIM_ALL);
 436        ncp_add_handle_path(server, volnum, dirent, 1, path);
 437
 438        if ((result = ncp_request(server, 87)) != 0)
 439                goto out;
 440        ncp_extract_file_info(ncp_reply_data(server, 0), target);
 441        ncp_unlock_server(server);
 442        
 443        result = ncp_obtain_nfs_info(server, target);
 444        return result;
 445
 446out:
 447        ncp_unlock_server(server);
 448        return result;
 449}
 450
 451#ifdef CONFIG_NCPFS_NFS_NS
 452static int
 453ncp_obtain_DOS_dir_base(struct ncp_server *server,
 454                __u8 ns, __u8 volnum, __le32 dirent,
 455                const char *path, /* At most 1 component */
 456                __le32 *DOS_dir_base)
 457{
 458        int result;
 459
 460        ncp_init_request(server);
 461        ncp_add_byte(server, 6); /* subfunction */
 462        ncp_add_byte(server, ns);
 463        ncp_add_byte(server, ns);
 464        ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */
 465        ncp_add_dword(server, RIM_DIRECTORY);
 466        ncp_add_handle_path(server, volnum, dirent, 1, path);
 467
 468        if ((result = ncp_request(server, 87)) == 0)
 469        {
 470                if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
 471        }
 472        ncp_unlock_server(server);
 473        return result;
 474}
 475#endif /* CONFIG_NCPFS_NFS_NS */
 476
 477static inline int
 478ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
 479{
 480#if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
 481        int result;
 482        __u8 *namespace;
 483        __u16 no_namespaces;
 484
 485        ncp_init_request(server);
 486        ncp_add_byte(server, 24);       /* Subfunction: Get Name Spaces Loaded */
 487        ncp_add_word(server, 0);
 488        ncp_add_byte(server, volume);
 489
 490        if ((result = ncp_request(server, 87)) != 0) {
 491                ncp_unlock_server(server);
 492                return NW_NS_DOS; /* not result ?? */
 493        }
 494
 495        result = NW_NS_DOS;
 496        no_namespaces = ncp_reply_le16(server, 0);
 497        namespace = ncp_reply_data(server, 2);
 498
 499        while (no_namespaces > 0) {
 500                ncp_dbg(1, "found %d on %d\n", *namespace, volume);
 501
 502#ifdef CONFIG_NCPFS_NFS_NS
 503                if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS)) 
 504                {
 505                        result = NW_NS_NFS;
 506                        break;
 507                }
 508#endif  /* CONFIG_NCPFS_NFS_NS */
 509#ifdef CONFIG_NCPFS_OS2_NS
 510                if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
 511                {
 512                        result = NW_NS_OS2;
 513                }
 514#endif  /* CONFIG_NCPFS_OS2_NS */
 515                namespace += 1;
 516                no_namespaces -= 1;
 517        }
 518        ncp_unlock_server(server);
 519        return result;
 520#else   /* neither OS2 nor NFS - only DOS */
 521        return NW_NS_DOS;
 522#endif  /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
 523}
 524
 525int
 526ncp_update_known_namespace(struct ncp_server *server, __u8 volume, int *ret_ns)
 527{
 528        int ns = ncp_get_known_namespace(server, volume);
 529
 530        if (ret_ns)
 531                *ret_ns = ns;
 532
 533        ncp_dbg(1, "namespace[%d] = %d\n", volume, server->name_space[volume]);
 534
 535        if (server->name_space[volume] == ns)
 536                return 0;
 537        server->name_space[volume] = ns;
 538        return 1;
 539}
 540
 541static int
 542ncp_ObtainSpecificDirBase(struct ncp_server *server,
 543                __u8 nsSrc, __u8 nsDst, __u8 vol_num, __le32 dir_base,
 544                const char *path, /* At most 1 component */
 545                __le32 *dirEntNum, __le32 *DosDirNum)
 546{
 547        int result;
 548
 549        ncp_init_request(server);
 550        ncp_add_byte(server, 6); /* subfunction */
 551        ncp_add_byte(server, nsSrc);
 552        ncp_add_byte(server, nsDst);
 553        ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */
 554        ncp_add_dword(server, RIM_ALL);
 555        ncp_add_handle_path(server, vol_num, dir_base, 1, path);
 556
 557        if ((result = ncp_request(server, 87)) != 0)
 558        {
 559                ncp_unlock_server(server);
 560                return result;
 561        }
 562
 563        if (dirEntNum)
 564                *dirEntNum = ncp_reply_dword(server, 0x30);
 565        if (DosDirNum)
 566                *DosDirNum = ncp_reply_dword(server, 0x34);
 567        ncp_unlock_server(server);
 568        return 0;
 569}
 570
 571int
 572ncp_mount_subdir(struct ncp_server *server,
 573                 __u8 volNumber, __u8 srcNS, __le32 dirEntNum,
 574                 __u32* volume, __le32* newDirEnt, __le32* newDosEnt)
 575{
 576        int dstNS;
 577        int result;
 578
 579        ncp_update_known_namespace(server, volNumber, &dstNS);
 580        if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber, 
 581                                      dirEntNum, NULL, newDirEnt, newDosEnt)) != 0)
 582        {
 583                return result;
 584        }
 585        *volume = volNumber;
 586        server->m.mounted_vol[1] = 0;
 587        server->m.mounted_vol[0] = 'X';
 588        return 0;
 589}
 590
 591int 
 592ncp_get_volume_root(struct ncp_server *server,
 593                    const char *volname, __u32* volume, __le32* dirent, __le32* dosdirent)
 594{
 595        int result;
 596
 597        ncp_dbg(1, "looking up vol %s\n", volname);
 598
 599        ncp_init_request(server);
 600        ncp_add_byte(server, 22);       /* Subfunction: Generate dir handle */
 601        ncp_add_byte(server, 0);        /* DOS namespace */
 602        ncp_add_byte(server, 0);        /* reserved */
 603        ncp_add_byte(server, 0);        /* reserved */
 604        ncp_add_byte(server, 0);        /* reserved */
 605
 606        ncp_add_byte(server, 0);        /* faked volume number */
 607        ncp_add_dword(server, 0);       /* faked dir_base */
 608        ncp_add_byte(server, 0xff);     /* Don't have a dir_base */
 609        ncp_add_byte(server, 1);        /* 1 path component */
 610        ncp_add_pstring(server, volname);
 611
 612        if ((result = ncp_request(server, 87)) != 0) {
 613                ncp_unlock_server(server);
 614                return result;
 615        }
 616        *dirent = *dosdirent = ncp_reply_dword(server, 4);
 617        *volume = ncp_reply_byte(server, 8);
 618        ncp_unlock_server(server);
 619        return 0;
 620}
 621
 622int
 623ncp_lookup_volume(struct ncp_server *server,
 624                  const char *volname, struct nw_info_struct *target)
 625{
 626        int result;
 627
 628        memset(target, 0, sizeof(*target));
 629        result = ncp_get_volume_root(server, volname,
 630                        &target->volNumber, &target->dirEntNum, &target->DosDirNum);
 631        if (result) {
 632                return result;
 633        }
 634        ncp_update_known_namespace(server, target->volNumber, NULL);
 635        target->nameLen = strlen(volname);
 636        memcpy(target->entryName, volname, target->nameLen+1);
 637        target->attributes = aDIR;
 638        /* set dates to Jan 1, 1986  00:00 */
 639        target->creationTime = target->modifyTime = cpu_to_le16(0x0000);
 640        target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21);
 641        target->nfs.mode = 0;
 642        return 0;
 643}
 644
 645int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server,
 646                                            struct inode *dir,
 647                                            const char *path,
 648                                            __le32 info_mask,
 649                                            const struct nw_modify_dos_info *info)
 650{
 651        __u8  volnum = NCP_FINFO(dir)->volNumber;
 652        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 653        int result;
 654
 655        ncp_init_request(server);
 656        ncp_add_byte(server, 7);        /* subfunction */
 657        ncp_add_byte(server, server->name_space[volnum]);
 658        ncp_add_byte(server, 0);        /* reserved */
 659        ncp_add_word(server, cpu_to_le16(0x8006));      /* search attribs: all */
 660
 661        ncp_add_dword(server, info_mask);
 662        ncp_add_mem(server, info, sizeof(*info));
 663        ncp_add_handle_path(server, volnum, dirent, 1, path);
 664
 665        result = ncp_request(server, 87);
 666        ncp_unlock_server(server);
 667        return result;
 668}
 669
 670int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
 671                                       struct inode *dir,
 672                                       __le32 info_mask,
 673                                       const struct nw_modify_dos_info *info)
 674{
 675        return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL,
 676                info_mask, info);
 677}
 678
 679#ifdef CONFIG_NCPFS_NFS_NS
 680int ncp_modify_nfs_info(struct ncp_server *server, __u8 volnum, __le32 dirent,
 681                               __u32 mode, __u32 rdev)
 682
 683{
 684        int result = 0;
 685
 686        ncp_init_request(server);
 687        if (server->name_space[volnum] == NW_NS_NFS) {
 688                ncp_add_byte(server, 25);       /* subfunction */
 689                ncp_add_byte(server, server->name_space[volnum]);
 690                ncp_add_byte(server, NW_NS_NFS);
 691                ncp_add_byte(server, volnum);
 692                ncp_add_dword(server, dirent);
 693                /* we must always operate on both nlinks and rdev, otherwise
 694                   rdev is not set */
 695                ncp_add_dword_lh(server, NSIBM_NFS_MODE | NSIBM_NFS_NLINKS | NSIBM_NFS_RDEV);
 696                ncp_add_dword_lh(server, mode);
 697                ncp_add_dword_lh(server, 1);    /* nlinks */
 698                ncp_add_dword_lh(server, rdev);
 699                result = ncp_request(server, 87);
 700        }
 701        ncp_unlock_server(server);
 702        return result;
 703}
 704#endif
 705
 706
 707static int
 708ncp_DeleteNSEntry(struct ncp_server *server,
 709                  __u8 have_dir_base, __u8 volnum, __le32 dirent,
 710                  const char* name, __u8 ns, __le16 attr)
 711{
 712        int result;
 713
 714        ncp_init_request(server);
 715        ncp_add_byte(server, 8);        /* subfunction */
 716        ncp_add_byte(server, ns);
 717        ncp_add_byte(server, 0);        /* reserved */
 718        ncp_add_word(server, attr);     /* search attribs: all */
 719        ncp_add_handle_path(server, volnum, dirent, have_dir_base, name);
 720
 721        result = ncp_request(server, 87);
 722        ncp_unlock_server(server);
 723        return result;
 724}
 725
 726int
 727ncp_del_file_or_subdir2(struct ncp_server *server,
 728                        struct dentry *dentry)
 729{
 730        struct inode *inode = d_inode(dentry);
 731        __u8  volnum;
 732        __le32 dirent;
 733
 734        if (!inode) {
 735                return 0xFF;    /* Any error */
 736        }
 737        volnum = NCP_FINFO(inode)->volNumber;
 738        dirent = NCP_FINFO(inode)->DosDirNum;
 739        return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, cpu_to_le16(0x8006));
 740}
 741
 742int
 743ncp_del_file_or_subdir(struct ncp_server *server,
 744                       struct inode *dir, const char *name)
 745{
 746        __u8  volnum = NCP_FINFO(dir)->volNumber;
 747        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 748        int name_space;
 749
 750        name_space = server->name_space[volnum];
 751#ifdef CONFIG_NCPFS_NFS_NS
 752        if (name_space == NW_NS_NFS)
 753        {
 754                int result;
 755 
 756                result=ncp_obtain_DOS_dir_base(server, name_space, volnum, dirent, name, &dirent);
 757                if (result) return result;
 758                name = NULL;
 759                name_space = NW_NS_DOS;
 760        }
 761#endif  /* CONFIG_NCPFS_NFS_NS */
 762        return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, name_space, cpu_to_le16(0x8006));
 763}
 764
 765static inline void ConvertToNWfromDWORD(__u16 v0, __u16 v1, __u8 ret[6])
 766{
 767        __le16 *dest = (__le16 *) ret;
 768        dest[1] = cpu_to_le16(v0);
 769        dest[2] = cpu_to_le16(v1);
 770        dest[0] = cpu_to_le16(v0 + 1);
 771        return;
 772}
 773
 774/* If both dir and name are NULL, then in target there's already a
 775   looked-up entry that wants to be opened. */
 776int ncp_open_create_file_or_subdir(struct ncp_server *server,
 777                                   struct inode *dir, const char *name,
 778                                   int open_create_mode,
 779                                   __le32 create_attributes,
 780                                   __le16 desired_acc_rights,
 781                                   struct ncp_entry_info *target)
 782{
 783        __le16 search_attribs = cpu_to_le16(0x0006);
 784        __u8  volnum;
 785        __le32 dirent;
 786        int result;
 787
 788        volnum = NCP_FINFO(dir)->volNumber;
 789        dirent = NCP_FINFO(dir)->dirEntNum;
 790
 791        if ((create_attributes & aDIR) != 0) {
 792                search_attribs |= cpu_to_le16(0x8000);
 793        }
 794        ncp_init_request(server);
 795        ncp_add_byte(server, 1);        /* subfunction */
 796        ncp_add_byte(server, server->name_space[volnum]);
 797        ncp_add_byte(server, open_create_mode);
 798        ncp_add_word(server, search_attribs);
 799        ncp_add_dword(server, RIM_ALL);
 800        ncp_add_dword(server, create_attributes);
 801        /* The desired acc rights seem to be the inherited rights mask
 802           for directories */
 803        ncp_add_word(server, desired_acc_rights);
 804        ncp_add_handle_path(server, volnum, dirent, 1, name);
 805
 806        if ((result = ncp_request(server, 87)) != 0)
 807                goto out;
 808        if (!(create_attributes & aDIR))
 809                target->opened = 1;
 810
 811        /* in target there's a new finfo to fill */
 812        ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i));
 813        target->volume = target->i.volNumber;
 814        ConvertToNWfromDWORD(ncp_reply_le16(server, 0),
 815                             ncp_reply_le16(server, 2),
 816                             target->file_handle);
 817        
 818        ncp_unlock_server(server);
 819
 820        (void)ncp_obtain_nfs_info(server, &(target->i));
 821        return 0;
 822
 823out:
 824        ncp_unlock_server(server);
 825        return result;
 826}
 827
 828int
 829ncp_initialize_search(struct ncp_server *server, struct inode *dir,
 830                        struct nw_search_sequence *target)
 831{
 832        __u8  volnum = NCP_FINFO(dir)->volNumber;
 833        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 834        int result;
 835
 836        ncp_init_request(server);
 837        ncp_add_byte(server, 2);        /* subfunction */
 838        ncp_add_byte(server, server->name_space[volnum]);
 839        ncp_add_byte(server, 0);        /* reserved */
 840        ncp_add_handle_path(server, volnum, dirent, 1, NULL);
 841
 842        result = ncp_request(server, 87);
 843        if (result)
 844                goto out;
 845        memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
 846
 847out:
 848        ncp_unlock_server(server);
 849        return result;
 850}
 851
 852int ncp_search_for_fileset(struct ncp_server *server,
 853                           struct nw_search_sequence *seq,
 854                           int* more,
 855                           int* cnt,
 856                           char* buffer,
 857                           size_t bufsize,
 858                           char** rbuf,
 859                           size_t* rsize)
 860{
 861        int result;
 862
 863        ncp_init_request(server);
 864        ncp_add_byte(server, 20);
 865        ncp_add_byte(server, server->name_space[seq->volNumber]);
 866        ncp_add_byte(server, 0);                /* datastream */
 867        ncp_add_word(server, cpu_to_le16(0x8006));
 868        ncp_add_dword(server, RIM_ALL);
 869        ncp_add_word(server, cpu_to_le16(32767));       /* max returned items */
 870        ncp_add_mem(server, seq, 9);
 871#ifdef CONFIG_NCPFS_NFS_NS
 872        if (server->name_space[seq->volNumber] == NW_NS_NFS) {
 873                ncp_add_byte(server, 0);        /* 0 byte pattern */
 874        } else 
 875#endif
 876        {
 877                ncp_add_byte(server, 2);        /* 2 byte pattern */
 878                ncp_add_byte(server, 0xff);     /* following is a wildcard */
 879                ncp_add_byte(server, '*');
 880        }
 881        result = ncp_request2(server, 87, buffer, bufsize);
 882        if (result) {
 883                ncp_unlock_server(server);
 884                return result;
 885        }
 886        if (server->ncp_reply_size < 12) {
 887                ncp_unlock_server(server);
 888                return 0xFF;
 889        }
 890        *rsize = server->ncp_reply_size - 12;
 891        ncp_unlock_server(server);
 892        buffer = buffer + sizeof(struct ncp_reply_header);
 893        *rbuf = buffer + 12;
 894        *cnt = WVAL_LH(buffer + 10);
 895        *more = BVAL(buffer + 9);
 896        memcpy(seq, buffer, 9);
 897        return 0;
 898}
 899
 900static int
 901ncp_RenameNSEntry(struct ncp_server *server,
 902                  struct inode *old_dir, const char *old_name, __le16 old_type,
 903                  struct inode *new_dir, const char *new_name)
 904{
 905        int result = -EINVAL;
 906
 907        if ((old_dir == NULL) || (old_name == NULL) ||
 908            (new_dir == NULL) || (new_name == NULL))
 909                goto out;
 910
 911        ncp_init_request(server);
 912        ncp_add_byte(server, 4);        /* subfunction */
 913        ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
 914        ncp_add_byte(server, 1);        /* rename flag */
 915        ncp_add_word(server, old_type); /* search attributes */
 916
 917        /* source Handle Path */
 918        ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
 919        ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum);
 920        ncp_add_byte(server, 1);
 921        ncp_add_byte(server, 1);        /* 1 source component */
 922
 923        /* dest Handle Path */
 924        ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber);
 925        ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum);
 926        ncp_add_byte(server, 1);
 927        ncp_add_byte(server, 1);        /* 1 destination component */
 928
 929        /* source path string */
 930        ncp_add_pstring(server, old_name);
 931        /* dest path string */
 932        ncp_add_pstring(server, new_name);
 933
 934        result = ncp_request(server, 87);
 935        ncp_unlock_server(server);
 936out:
 937        return result;
 938}
 939
 940int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
 941                                struct inode *old_dir, const char *old_name,
 942                                struct inode *new_dir, const char *new_name)
 943{
 944        int result;
 945        __le16 old_type = cpu_to_le16(0x06);
 946
 947/* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
 948        result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
 949                                           new_dir, new_name);
 950        if (result == 0xFF)     /* File Not Found, try directory */
 951        {
 952                old_type = cpu_to_le16(0x16);
 953                result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
 954                                                   new_dir, new_name);
 955        }
 956        if (result != 0x92) return result;      /* All except NO_FILES_RENAMED */
 957        result = ncp_del_file_or_subdir(server, new_dir, new_name);
 958        if (result != 0) return -EACCES;
 959        result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
 960                                           new_dir, new_name);
 961        return result;
 962}
 963        
 964
 965/* We have to transfer to/from user space */
 966int
 967ncp_read_kernel(struct ncp_server *server, const char *file_id,
 968             __u32 offset, __u16 to_read, char *target, int *bytes_read)
 969{
 970        const char *source;
 971        int result;
 972
 973        ncp_init_request(server);
 974        ncp_add_byte(server, 0);
 975        ncp_add_mem(server, file_id, 6);
 976        ncp_add_be32(server, offset);
 977        ncp_add_be16(server, to_read);
 978
 979        if ((result = ncp_request(server, 72)) != 0) {
 980                goto out;
 981        }
 982        *bytes_read = ncp_reply_be16(server, 0);
 983        source = ncp_reply_data(server, 2 + (offset & 1));
 984
 985        memcpy(target, source, *bytes_read);
 986out:
 987        ncp_unlock_server(server);
 988        return result;
 989}
 990
 991/* There is a problem... egrep and some other silly tools do:
 992        x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768);
 993        read(<ncpfs fd>, x, 32768);
 994   Now copying read result by copy_to_user causes pagefault. This pagefault
 995   could not be handled because of server was locked due to read. So we have
 996   to use temporary buffer. So ncp_unlock_server must be done before
 997   copy_to_user (and for write, copy_from_user must be done before 
 998   ncp_init_request... same applies for send raw packet ioctl). Because of
 999   file is normally read in bigger chunks, caller provides kmalloced 
1000   (vmalloced) chunk of memory with size >= to_read...
1001 */
1002int
1003ncp_read_bounce(struct ncp_server *server, const char *file_id,
1004         __u32 offset, __u16 to_read, struct iov_iter *to,
1005         int *bytes_read, void *bounce, __u32 bufsize)
1006{
1007        int result;
1008
1009        ncp_init_request(server);
1010        ncp_add_byte(server, 0);
1011        ncp_add_mem(server, file_id, 6);
1012        ncp_add_be32(server, offset);
1013        ncp_add_be16(server, to_read);
1014        result = ncp_request2(server, 72, bounce, bufsize);
1015        ncp_unlock_server(server);
1016        if (!result) {
1017                int len = get_unaligned_be16((char *)bounce +
1018                          sizeof(struct ncp_reply_header));
1019                result = -EIO;
1020                if (len <= to_read) {
1021                        char* source;
1022
1023                        source = (char*)bounce + 
1024                                 sizeof(struct ncp_reply_header) + 2 + 
1025                                 (offset & 1);
1026                        *bytes_read = len;
1027                        result = 0;
1028                        if (copy_to_iter(source, len, to) != len)
1029                                result = -EFAULT;
1030                }
1031        }
1032        return result;
1033}
1034
1035int
1036ncp_write_kernel(struct ncp_server *server, const char *file_id,
1037                 __u32 offset, __u16 to_write,
1038                 const char *source, int *bytes_written)
1039{
1040        int result;
1041
1042        ncp_init_request(server);
1043        ncp_add_byte(server, 0);
1044        ncp_add_mem(server, file_id, 6);
1045        ncp_add_be32(server, offset);
1046        ncp_add_be16(server, to_write);
1047        ncp_add_mem(server, source, to_write);
1048        
1049        if ((result = ncp_request(server, 73)) == 0)
1050                *bytes_written = to_write;
1051        ncp_unlock_server(server);
1052        return result;
1053}
1054
1055#ifdef CONFIG_NCPFS_IOCTL_LOCKING
1056int
1057ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
1058          __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
1059{
1060        int result;
1061
1062        ncp_init_request(server);
1063        ncp_add_byte(server, locktype);
1064        ncp_add_mem(server, file_id, 6);
1065        ncp_add_be32(server, offset);
1066        ncp_add_be32(server, length);
1067        ncp_add_be16(server, timeout);
1068
1069        if ((result = ncp_request(server, 0x1A)) != 0)
1070        {
1071                ncp_unlock_server(server);
1072                return result;
1073        }
1074        ncp_unlock_server(server);
1075        return 0;
1076}
1077
1078int
1079ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
1080          __u32 offset, __u32 length)
1081{
1082        int result;
1083
1084        ncp_init_request(server);
1085        ncp_add_byte(server, 0);        /* who knows... lanalyzer says that */
1086        ncp_add_mem(server, file_id, 6);
1087        ncp_add_be32(server, offset);
1088        ncp_add_be32(server, length);
1089
1090        if ((result = ncp_request(server, 0x1E)) != 0)
1091        {
1092                ncp_unlock_server(server);
1093                return result;
1094        }
1095        ncp_unlock_server(server);
1096        return 0;
1097}
1098#endif  /* CONFIG_NCPFS_IOCTL_LOCKING */
1099
1100#ifdef CONFIG_NCPFS_NLS
1101/* This are the NLS conversion routines with inspirations and code parts
1102 * from the vfat file system and hints from Petr Vandrovec.
1103 */
1104
1105int
1106ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen,
1107                const unsigned char *iname, unsigned int ilen, int cc)
1108{
1109        struct nls_table *in = server->nls_io;
1110        struct nls_table *out = server->nls_vol;
1111        unsigned char *vname_start;
1112        unsigned char *vname_end;
1113        const unsigned char *iname_end;
1114
1115        iname_end = iname + ilen;
1116        vname_start = vname;
1117        vname_end = vname + *vlen - 1;
1118
1119        while (iname < iname_end) {
1120                int chl;
1121                wchar_t ec;
1122
1123                if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
1124                        int k;
1125                        unicode_t u;
1126
1127                        k = utf8_to_utf32(iname, iname_end - iname, &u);
1128                        if (k < 0 || u > MAX_WCHAR_T)
1129                                return -EINVAL;
1130                        iname += k;
1131                        ec = u;
1132                } else {
1133                        if (*iname == NCP_ESC) {
1134                                int k;
1135
1136                                if (iname_end - iname < 5)
1137                                        goto nospec;
1138
1139                                ec = 0;
1140                                for (k = 1; k < 5; k++) {
1141                                        unsigned char nc;
1142
1143                                        nc = iname[k] - '0';
1144                                        if (nc >= 10) {
1145                                                nc -= 'A' - '0' - 10;
1146                                                if ((nc < 10) || (nc > 15)) {
1147                                                        goto nospec;
1148                                                }
1149                                        }
1150                                        ec = (ec << 4) | nc;
1151                                }
1152                                iname += 5;
1153                        } else {
1154nospec:;                        
1155                                if ( (chl = in->char2uni(iname, iname_end - iname, &ec)) < 0)
1156                                        return chl;
1157                                iname += chl;
1158                        }
1159                }
1160
1161                /* unitoupper should be here! */
1162
1163                chl = out->uni2char(ec, vname, vname_end - vname);
1164                if (chl < 0)
1165                        return chl;
1166
1167                /* this is wrong... */
1168                if (cc) {
1169                        int chi;
1170
1171                        for (chi = 0; chi < chl; chi++){
1172                                vname[chi] = ncp_toupper(out, vname[chi]);
1173                        }
1174                }
1175                vname += chl;
1176        }
1177
1178        *vname = 0;
1179        *vlen = vname - vname_start;
1180        return 0;
1181}
1182
1183int
1184ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen,
1185                const unsigned char *vname, unsigned int vlen, int cc)
1186{
1187        struct nls_table *in = server->nls_vol;
1188        struct nls_table *out = server->nls_io;
1189        const unsigned char *vname_end;
1190        unsigned char *iname_start;
1191        unsigned char *iname_end;
1192        unsigned char *vname_cc;
1193        int err;
1194
1195        vname_cc = NULL;
1196
1197        if (cc) {
1198                int i;
1199
1200                /* this is wrong! */
1201                vname_cc = kmalloc(vlen, GFP_KERNEL);
1202                if (!vname_cc)
1203                        return -ENOMEM;
1204                for (i = 0; i < vlen; i++)
1205                        vname_cc[i] = ncp_tolower(in, vname[i]);
1206                vname = vname_cc;
1207        }
1208
1209        iname_start = iname;
1210        iname_end = iname + *ilen - 1;
1211        vname_end = vname + vlen;
1212
1213        while (vname < vname_end) {
1214                wchar_t ec;
1215                int chl;
1216
1217                if ( (chl = in->char2uni(vname, vname_end - vname, &ec)) < 0) {
1218                        err = chl;
1219                        goto quit;
1220                }
1221                vname += chl;
1222
1223                /* unitolower should be here! */
1224
1225                if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
1226                        int k;
1227
1228                        k = utf32_to_utf8(ec, iname, iname_end - iname);
1229                        if (k < 0) {
1230                                err = -ENAMETOOLONG;
1231                                goto quit;
1232                        }
1233                        iname += k;
1234                } else {
1235                        if ( (chl = out->uni2char(ec, iname, iname_end - iname)) >= 0) {
1236                                iname += chl;
1237                        } else {
1238                                int k;
1239
1240                                if (iname_end - iname < 5) {
1241                                        err = -ENAMETOOLONG;
1242                                        goto quit;
1243                                }
1244                                *iname = NCP_ESC;
1245                                for (k = 4; k > 0; k--) {
1246                                        unsigned char v;
1247                                        
1248                                        v = (ec & 0xF) + '0';
1249                                        if (v > '9') {
1250                                                v += 'A' - '9' - 1;
1251                                        }
1252                                        iname[k] = v;
1253                                        ec >>= 4;
1254                                }
1255                                iname += 5;
1256                        }
1257                }
1258        }
1259
1260        *iname = 0;
1261        *ilen = iname - iname_start;
1262        err = 0;
1263quit:;
1264        if (cc)
1265                kfree(vname_cc);
1266        return err;
1267}
1268
1269#else
1270
1271int
1272ncp__io2vol(unsigned char *vname, unsigned int *vlen,
1273                const unsigned char *iname, unsigned int ilen, int cc)
1274{
1275        int i;
1276
1277        if (*vlen <= ilen)
1278                return -ENAMETOOLONG;
1279
1280        if (cc)
1281                for (i = 0; i < ilen; i++) {
1282                        *vname = toupper(*iname);
1283                        vname++;
1284                        iname++;
1285                }
1286        else {
1287                memmove(vname, iname, ilen);
1288                vname += ilen;
1289        }
1290
1291        *vlen = ilen;
1292        *vname = 0;
1293        return 0;
1294}
1295
1296int
1297ncp__vol2io(unsigned char *iname, unsigned int *ilen,
1298                const unsigned char *vname, unsigned int vlen, int cc)
1299{
1300        int i;
1301
1302        if (*ilen <= vlen)
1303                return -ENAMETOOLONG;
1304
1305        if (cc)
1306                for (i = 0; i < vlen; i++) {
1307                        *iname = tolower(*vname);
1308                        iname++;
1309                        vname++;
1310                }
1311        else {
1312                memmove(iname, vname, vlen);
1313                iname += vlen;
1314        }
1315
1316        *ilen = vlen;
1317        *iname = 0;
1318        return 0;
1319}
1320
1321#endif
1322