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
  13
  14#include "ncp_fs.h"
  15
  16static inline void assert_server_locked(struct ncp_server *server)
  17{
  18        if (server->lock == 0) {
  19                DPRINTK("ncpfs: 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                DPRINTK("ncpfs: 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                DPRINTK("ncpfs: 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                DPRINTK("ncpfs: 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                        PPRINTK("ncp_make_closed: 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                        DPRINTK(KERN_DEBUG
 398                                "ncp_obtain_nfs_info: (%s) mode=0%o, rdev=0x%x\n",
 399                                target->entryName, target->nfs.mode,
 400                                target->nfs.rdev);
 401                } else {
 402                        target->nfs.mode = 0;
 403                        target->nfs.rdev = 0;
 404                }
 405                ncp_unlock_server(server);
 406
 407        } else
 408#endif
 409        {
 410                target->nfs.mode = 0;
 411                target->nfs.rdev = 0;
 412        }
 413        return result;
 414}
 415
 416/*
 417 * Returns information for a (one-component) name relative to
 418 * the specified directory.
 419 */
 420int ncp_obtain_info(struct ncp_server *server, struct inode *dir, const char *path,
 421                        struct nw_info_struct *target)
 422{
 423        __u8  volnum = NCP_FINFO(dir)->volNumber;
 424        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 425        int result;
 426
 427        if (target == NULL) {
 428                printk(KERN_ERR "ncp_obtain_info: invalid call\n");
 429                return -EINVAL;
 430        }
 431        ncp_init_request(server);
 432        ncp_add_byte(server, 6);        /* subfunction */
 433        ncp_add_byte(server, server->name_space[volnum]);
 434        ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
 435        ncp_add_word(server, cpu_to_le16(0x8006));      /* get all */
 436        ncp_add_dword(server, RIM_ALL);
 437        ncp_add_handle_path(server, volnum, dirent, 1, path);
 438
 439        if ((result = ncp_request(server, 87)) != 0)
 440                goto out;
 441        ncp_extract_file_info(ncp_reply_data(server, 0), target);
 442        ncp_unlock_server(server);
 443        
 444        result = ncp_obtain_nfs_info(server, target);
 445        return result;
 446
 447out:
 448        ncp_unlock_server(server);
 449        return result;
 450}
 451
 452#ifdef CONFIG_NCPFS_NFS_NS
 453static int
 454ncp_obtain_DOS_dir_base(struct ncp_server *server,
 455                __u8 ns, __u8 volnum, __le32 dirent,
 456                const char *path, /* At most 1 component */
 457                __le32 *DOS_dir_base)
 458{
 459        int result;
 460
 461        ncp_init_request(server);
 462        ncp_add_byte(server, 6); /* subfunction */
 463        ncp_add_byte(server, ns);
 464        ncp_add_byte(server, ns);
 465        ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */
 466        ncp_add_dword(server, RIM_DIRECTORY);
 467        ncp_add_handle_path(server, volnum, dirent, 1, path);
 468
 469        if ((result = ncp_request(server, 87)) == 0)
 470        {
 471                if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
 472        }
 473        ncp_unlock_server(server);
 474        return result;
 475}
 476#endif /* CONFIG_NCPFS_NFS_NS */
 477
 478static inline int
 479ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
 480{
 481#if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
 482        int result;
 483        __u8 *namespace;
 484        __u16 no_namespaces;
 485
 486        ncp_init_request(server);
 487        ncp_add_byte(server, 24);       /* Subfunction: Get Name Spaces Loaded */
 488        ncp_add_word(server, 0);
 489        ncp_add_byte(server, volume);
 490
 491        if ((result = ncp_request(server, 87)) != 0) {
 492                ncp_unlock_server(server);
 493                return NW_NS_DOS; /* not result ?? */
 494        }
 495
 496        result = NW_NS_DOS;
 497        no_namespaces = ncp_reply_le16(server, 0);
 498        namespace = ncp_reply_data(server, 2);
 499
 500        while (no_namespaces > 0) {
 501                DPRINTK("get_namespaces: found %d on %d\n", *namespace, volume);
 502
 503#ifdef CONFIG_NCPFS_NFS_NS
 504                if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS)) 
 505                {
 506                        result = NW_NS_NFS;
 507                        break;
 508                }
 509#endif  /* CONFIG_NCPFS_NFS_NS */
 510#ifdef CONFIG_NCPFS_OS2_NS
 511                if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
 512                {
 513                        result = NW_NS_OS2;
 514                }
 515#endif  /* CONFIG_NCPFS_OS2_NS */
 516                namespace += 1;
 517                no_namespaces -= 1;
 518        }
 519        ncp_unlock_server(server);
 520        return result;
 521#else   /* neither OS2 nor NFS - only DOS */
 522        return NW_NS_DOS;
 523#endif  /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
 524}
 525
 526int
 527ncp_update_known_namespace(struct ncp_server *server, __u8 volume, int *ret_ns)
 528{
 529        int ns = ncp_get_known_namespace(server, volume);
 530
 531        if (ret_ns)
 532                *ret_ns = ns;
 533
 534        DPRINTK("lookup_vol: namespace[%d] = %d\n",
 535                volume, server->name_space[volume]);
 536
 537        if (server->name_space[volume] == ns)
 538                return 0;
 539        server->name_space[volume] = ns;
 540        return 1;
 541}
 542
 543static int
 544ncp_ObtainSpecificDirBase(struct ncp_server *server,
 545                __u8 nsSrc, __u8 nsDst, __u8 vol_num, __le32 dir_base,
 546                const char *path, /* At most 1 component */
 547                __le32 *dirEntNum, __le32 *DosDirNum)
 548{
 549        int result;
 550
 551        ncp_init_request(server);
 552        ncp_add_byte(server, 6); /* subfunction */
 553        ncp_add_byte(server, nsSrc);
 554        ncp_add_byte(server, nsDst);
 555        ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */
 556        ncp_add_dword(server, RIM_ALL);
 557        ncp_add_handle_path(server, vol_num, dir_base, 1, path);
 558
 559        if ((result = ncp_request(server, 87)) != 0)
 560        {
 561                ncp_unlock_server(server);
 562                return result;
 563        }
 564
 565        if (dirEntNum)
 566                *dirEntNum = ncp_reply_dword(server, 0x30);
 567        if (DosDirNum)
 568                *DosDirNum = ncp_reply_dword(server, 0x34);
 569        ncp_unlock_server(server);
 570        return 0;
 571}
 572
 573int
 574ncp_mount_subdir(struct ncp_server *server,
 575                 __u8 volNumber, __u8 srcNS, __le32 dirEntNum,
 576                 __u32* volume, __le32* newDirEnt, __le32* newDosEnt)
 577{
 578        int dstNS;
 579        int result;
 580
 581        ncp_update_known_namespace(server, volNumber, &dstNS);
 582        if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber, 
 583                                      dirEntNum, NULL, newDirEnt, newDosEnt)) != 0)
 584        {
 585                return result;
 586        }
 587        *volume = volNumber;
 588        server->m.mounted_vol[1] = 0;
 589        server->m.mounted_vol[0] = 'X';
 590        return 0;
 591}
 592
 593int 
 594ncp_get_volume_root(struct ncp_server *server,
 595                    const char *volname, __u32* volume, __le32* dirent, __le32* dosdirent)
 596{
 597        int result;
 598
 599        DPRINTK("ncp_get_volume_root: looking up vol %s\n", volname);
 600
 601        ncp_init_request(server);
 602        ncp_add_byte(server, 22);       /* Subfunction: Generate dir handle */
 603        ncp_add_byte(server, 0);        /* DOS namespace */
 604        ncp_add_byte(server, 0);        /* reserved */
 605        ncp_add_byte(server, 0);        /* reserved */
 606        ncp_add_byte(server, 0);        /* reserved */
 607
 608        ncp_add_byte(server, 0);        /* faked volume number */
 609        ncp_add_dword(server, 0);       /* faked dir_base */
 610        ncp_add_byte(server, 0xff);     /* Don't have a dir_base */
 611        ncp_add_byte(server, 1);        /* 1 path component */
 612        ncp_add_pstring(server, volname);
 613
 614        if ((result = ncp_request(server, 87)) != 0) {
 615                ncp_unlock_server(server);
 616                return result;
 617        }
 618        *dirent = *dosdirent = ncp_reply_dword(server, 4);
 619        *volume = ncp_reply_byte(server, 8);
 620        ncp_unlock_server(server);
 621        return 0;
 622}
 623
 624int
 625ncp_lookup_volume(struct ncp_server *server,
 626                  const char *volname, struct nw_info_struct *target)
 627{
 628        int result;
 629
 630        memset(target, 0, sizeof(*target));
 631        result = ncp_get_volume_root(server, volname,
 632                        &target->volNumber, &target->dirEntNum, &target->DosDirNum);
 633        if (result) {
 634                return result;
 635        }
 636        ncp_update_known_namespace(server, target->volNumber, NULL);
 637        target->nameLen = strlen(volname);
 638        memcpy(target->entryName, volname, target->nameLen+1);
 639        target->attributes = aDIR;
 640        /* set dates to Jan 1, 1986  00:00 */
 641        target->creationTime = target->modifyTime = cpu_to_le16(0x0000);
 642        target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21);
 643        target->nfs.mode = 0;
 644        return 0;
 645}
 646
 647int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server,
 648                                            struct inode *dir,
 649                                            const char *path,
 650                                            __le32 info_mask,
 651                                            const struct nw_modify_dos_info *info)
 652{
 653        __u8  volnum = NCP_FINFO(dir)->volNumber;
 654        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 655        int result;
 656
 657        ncp_init_request(server);
 658        ncp_add_byte(server, 7);        /* subfunction */
 659        ncp_add_byte(server, server->name_space[volnum]);
 660        ncp_add_byte(server, 0);        /* reserved */
 661        ncp_add_word(server, cpu_to_le16(0x8006));      /* search attribs: all */
 662
 663        ncp_add_dword(server, info_mask);
 664        ncp_add_mem(server, info, sizeof(*info));
 665        ncp_add_handle_path(server, volnum, dirent, 1, path);
 666
 667        result = ncp_request(server, 87);
 668        ncp_unlock_server(server);
 669        return result;
 670}
 671
 672int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
 673                                       struct inode *dir,
 674                                       __le32 info_mask,
 675                                       const struct nw_modify_dos_info *info)
 676{
 677        return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL,
 678                info_mask, info);
 679}
 680
 681#ifdef CONFIG_NCPFS_NFS_NS
 682int ncp_modify_nfs_info(struct ncp_server *server, __u8 volnum, __le32 dirent,
 683                               __u32 mode, __u32 rdev)
 684
 685{
 686        int result = 0;
 687
 688        ncp_init_request(server);
 689        if (server->name_space[volnum] == NW_NS_NFS) {
 690                ncp_add_byte(server, 25);       /* subfunction */
 691                ncp_add_byte(server, server->name_space[volnum]);
 692                ncp_add_byte(server, NW_NS_NFS);
 693                ncp_add_byte(server, volnum);
 694                ncp_add_dword(server, dirent);
 695                /* we must always operate on both nlinks and rdev, otherwise
 696                   rdev is not set */
 697                ncp_add_dword_lh(server, NSIBM_NFS_MODE | NSIBM_NFS_NLINKS | NSIBM_NFS_RDEV);
 698                ncp_add_dword_lh(server, mode);
 699                ncp_add_dword_lh(server, 1);    /* nlinks */
 700                ncp_add_dword_lh(server, rdev);
 701                result = ncp_request(server, 87);
 702        }
 703        ncp_unlock_server(server);
 704        return result;
 705}
 706#endif
 707
 708
 709static int
 710ncp_DeleteNSEntry(struct ncp_server *server,
 711                  __u8 have_dir_base, __u8 volnum, __le32 dirent,
 712                  const char* name, __u8 ns, __le16 attr)
 713{
 714        int result;
 715
 716        ncp_init_request(server);
 717        ncp_add_byte(server, 8);        /* subfunction */
 718        ncp_add_byte(server, ns);
 719        ncp_add_byte(server, 0);        /* reserved */
 720        ncp_add_word(server, attr);     /* search attribs: all */
 721        ncp_add_handle_path(server, volnum, dirent, have_dir_base, name);
 722
 723        result = ncp_request(server, 87);
 724        ncp_unlock_server(server);
 725        return result;
 726}
 727
 728int
 729ncp_del_file_or_subdir2(struct ncp_server *server,
 730                        struct dentry *dentry)
 731{
 732        struct inode *inode = dentry->d_inode;
 733        __u8  volnum;
 734        __le32 dirent;
 735
 736        if (!inode) {
 737                return 0xFF;    /* Any error */
 738        }
 739        volnum = NCP_FINFO(inode)->volNumber;
 740        dirent = NCP_FINFO(inode)->DosDirNum;
 741        return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, cpu_to_le16(0x8006));
 742}
 743
 744int
 745ncp_del_file_or_subdir(struct ncp_server *server,
 746                       struct inode *dir, const char *name)
 747{
 748        __u8  volnum = NCP_FINFO(dir)->volNumber;
 749        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 750        int name_space;
 751
 752        name_space = server->name_space[volnum];
 753#ifdef CONFIG_NCPFS_NFS_NS
 754        if (name_space == NW_NS_NFS)
 755        {
 756                int result;
 757 
 758                result=ncp_obtain_DOS_dir_base(server, name_space, volnum, dirent, name, &dirent);
 759                if (result) return result;
 760                name = NULL;
 761                name_space = NW_NS_DOS;
 762        }
 763#endif  /* CONFIG_NCPFS_NFS_NS */
 764        return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, name_space, cpu_to_le16(0x8006));
 765}
 766
 767static inline void ConvertToNWfromDWORD(__u16 v0, __u16 v1, __u8 ret[6])
 768{
 769        __le16 *dest = (__le16 *) ret;
 770        dest[1] = cpu_to_le16(v0);
 771        dest[2] = cpu_to_le16(v1);
 772        dest[0] = cpu_to_le16(v0 + 1);
 773        return;
 774}
 775
 776/* If both dir and name are NULL, then in target there's already a
 777   looked-up entry that wants to be opened. */
 778int ncp_open_create_file_or_subdir(struct ncp_server *server,
 779                                   struct inode *dir, const char *name,
 780                                   int open_create_mode,
 781                                   __le32 create_attributes,
 782                                   __le16 desired_acc_rights,
 783                                   struct ncp_entry_info *target)
 784{
 785        __le16 search_attribs = cpu_to_le16(0x0006);
 786        __u8  volnum;
 787        __le32 dirent;
 788        int result;
 789
 790        volnum = NCP_FINFO(dir)->volNumber;
 791        dirent = NCP_FINFO(dir)->dirEntNum;
 792
 793        if ((create_attributes & aDIR) != 0) {
 794                search_attribs |= cpu_to_le16(0x8000);
 795        }
 796        ncp_init_request(server);
 797        ncp_add_byte(server, 1);        /* subfunction */
 798        ncp_add_byte(server, server->name_space[volnum]);
 799        ncp_add_byte(server, open_create_mode);
 800        ncp_add_word(server, search_attribs);
 801        ncp_add_dword(server, RIM_ALL);
 802        ncp_add_dword(server, create_attributes);
 803        /* The desired acc rights seem to be the inherited rights mask
 804           for directories */
 805        ncp_add_word(server, desired_acc_rights);
 806        ncp_add_handle_path(server, volnum, dirent, 1, name);
 807
 808        if ((result = ncp_request(server, 87)) != 0)
 809                goto out;
 810        if (!(create_attributes & aDIR))
 811                target->opened = 1;
 812
 813        /* in target there's a new finfo to fill */
 814        ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i));
 815        target->volume = target->i.volNumber;
 816        ConvertToNWfromDWORD(ncp_reply_le16(server, 0),
 817                             ncp_reply_le16(server, 2),
 818                             target->file_handle);
 819        
 820        ncp_unlock_server(server);
 821
 822        (void)ncp_obtain_nfs_info(server, &(target->i));
 823        return 0;
 824
 825out:
 826        ncp_unlock_server(server);
 827        return result;
 828}
 829
 830int
 831ncp_initialize_search(struct ncp_server *server, struct inode *dir,
 832                        struct nw_search_sequence *target)
 833{
 834        __u8  volnum = NCP_FINFO(dir)->volNumber;
 835        __le32 dirent = NCP_FINFO(dir)->dirEntNum;
 836        int result;
 837
 838        ncp_init_request(server);
 839        ncp_add_byte(server, 2);        /* subfunction */
 840        ncp_add_byte(server, server->name_space[volnum]);
 841        ncp_add_byte(server, 0);        /* reserved */
 842        ncp_add_handle_path(server, volnum, dirent, 1, NULL);
 843
 844        result = ncp_request(server, 87);
 845        if (result)
 846                goto out;
 847        memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
 848
 849out:
 850        ncp_unlock_server(server);
 851        return result;
 852}
 853
 854int ncp_search_for_fileset(struct ncp_server *server,
 855                           struct nw_search_sequence *seq,
 856                           int* more,
 857                           int* cnt,
 858                           char* buffer,
 859                           size_t bufsize,
 860                           char** rbuf,
 861                           size_t* rsize)
 862{
 863        int result;
 864
 865        ncp_init_request(server);
 866        ncp_add_byte(server, 20);
 867        ncp_add_byte(server, server->name_space[seq->volNumber]);
 868        ncp_add_byte(server, 0);                /* datastream */
 869        ncp_add_word(server, cpu_to_le16(0x8006));
 870        ncp_add_dword(server, RIM_ALL);
 871        ncp_add_word(server, cpu_to_le16(32767));       /* max returned items */
 872        ncp_add_mem(server, seq, 9);
 873#ifdef CONFIG_NCPFS_NFS_NS
 874        if (server->name_space[seq->volNumber] == NW_NS_NFS) {
 875                ncp_add_byte(server, 0);        /* 0 byte pattern */
 876        } else 
 877#endif
 878        {
 879                ncp_add_byte(server, 2);        /* 2 byte pattern */
 880                ncp_add_byte(server, 0xff);     /* following is a wildcard */
 881                ncp_add_byte(server, '*');
 882        }
 883        result = ncp_request2(server, 87, buffer, bufsize);
 884        if (result) {
 885                ncp_unlock_server(server);
 886                return result;
 887        }
 888        if (server->ncp_reply_size < 12) {
 889                ncp_unlock_server(server);
 890                return 0xFF;
 891        }
 892        *rsize = server->ncp_reply_size - 12;
 893        ncp_unlock_server(server);
 894        buffer = buffer + sizeof(struct ncp_reply_header);
 895        *rbuf = buffer + 12;
 896        *cnt = WVAL_LH(buffer + 10);
 897        *more = BVAL(buffer + 9);
 898        memcpy(seq, buffer, 9);
 899        return 0;
 900}
 901
 902static int
 903ncp_RenameNSEntry(struct ncp_server *server,
 904                  struct inode *old_dir, const char *old_name, __le16 old_type,
 905                  struct inode *new_dir, const char *new_name)
 906{
 907        int result = -EINVAL;
 908
 909        if ((old_dir == NULL) || (old_name == NULL) ||
 910            (new_dir == NULL) || (new_name == NULL))
 911                goto out;
 912
 913        ncp_init_request(server);
 914        ncp_add_byte(server, 4);        /* subfunction */
 915        ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
 916        ncp_add_byte(server, 1);        /* rename flag */
 917        ncp_add_word(server, old_type); /* search attributes */
 918
 919        /* source Handle Path */
 920        ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
 921        ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum);
 922        ncp_add_byte(server, 1);
 923        ncp_add_byte(server, 1);        /* 1 source component */
 924
 925        /* dest Handle Path */
 926        ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber);
 927        ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum);
 928        ncp_add_byte(server, 1);
 929        ncp_add_byte(server, 1);        /* 1 destination component */
 930
 931        /* source path string */
 932        ncp_add_pstring(server, old_name);
 933        /* dest path string */
 934        ncp_add_pstring(server, new_name);
 935
 936        result = ncp_request(server, 87);
 937        ncp_unlock_server(server);
 938out:
 939        return result;
 940}
 941
 942int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
 943                                struct inode *old_dir, const char *old_name,
 944                                struct inode *new_dir, const char *new_name)
 945{
 946        int result;
 947        __le16 old_type = cpu_to_le16(0x06);
 948
 949/* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
 950        result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
 951                                           new_dir, new_name);
 952        if (result == 0xFF)     /* File Not Found, try directory */
 953        {
 954                old_type = cpu_to_le16(0x16);
 955                result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
 956                                                   new_dir, new_name);
 957        }
 958        if (result != 0x92) return result;      /* All except NO_FILES_RENAMED */
 959        result = ncp_del_file_or_subdir(server, new_dir, new_name);
 960        if (result != 0) return -EACCES;
 961        result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
 962                                           new_dir, new_name);
 963        return result;
 964}
 965        
 966
 967/* We have to transfer to/from user space */
 968int
 969ncp_read_kernel(struct ncp_server *server, const char *file_id,
 970             __u32 offset, __u16 to_read, char *target, int *bytes_read)
 971{
 972        const char *source;
 973        int result;
 974
 975        ncp_init_request(server);
 976        ncp_add_byte(server, 0);
 977        ncp_add_mem(server, file_id, 6);
 978        ncp_add_be32(server, offset);
 979        ncp_add_be16(server, to_read);
 980
 981        if ((result = ncp_request(server, 72)) != 0) {
 982                goto out;
 983        }
 984        *bytes_read = ncp_reply_be16(server, 0);
 985        source = ncp_reply_data(server, 2 + (offset & 1));
 986
 987        memcpy(target, source, *bytes_read);
 988out:
 989        ncp_unlock_server(server);
 990        return result;
 991}
 992
 993/* There is a problem... egrep and some other silly tools do:
 994        x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768);
 995        read(<ncpfs fd>, x, 32768);
 996   Now copying read result by copy_to_user causes pagefault. This pagefault
 997   could not be handled because of server was locked due to read. So we have
 998   to use temporary buffer. So ncp_unlock_server must be done before
 999   copy_to_user (and for write, copy_from_user must be done before 
1000   ncp_init_request... same applies for send raw packet ioctl). Because of
1001   file is normally read in bigger chunks, caller provides kmalloced 
1002   (vmalloced) chunk of memory with size >= to_read...
1003 */
1004int
1005ncp_read_bounce(struct ncp_server *server, const char *file_id,
1006         __u32 offset, __u16 to_read, char __user *target, int *bytes_read,
1007         void* bounce, __u32 bufsize)
1008{
1009        int result;
1010
1011        ncp_init_request(server);
1012        ncp_add_byte(server, 0);
1013        ncp_add_mem(server, file_id, 6);
1014        ncp_add_be32(server, offset);
1015        ncp_add_be16(server, to_read);
1016        result = ncp_request2(server, 72, bounce, bufsize);
1017        ncp_unlock_server(server);
1018        if (!result) {
1019                int len = get_unaligned_be16((char *)bounce +
1020                          sizeof(struct ncp_reply_header));
1021                result = -EIO;
1022                if (len <= to_read) {
1023                        char* source;
1024
1025                        source = (char*)bounce + 
1026                                 sizeof(struct ncp_reply_header) + 2 + 
1027                                 (offset & 1);
1028                        *bytes_read = len;
1029                        result = 0;
1030                        if (copy_to_user(target, source, len))
1031                                result = -EFAULT;
1032                }
1033        }
1034        return result;
1035}
1036
1037int
1038ncp_write_kernel(struct ncp_server *server, const char *file_id,
1039                 __u32 offset, __u16 to_write,
1040                 const char *source, int *bytes_written)
1041{
1042        int result;
1043
1044        ncp_init_request(server);
1045        ncp_add_byte(server, 0);
1046        ncp_add_mem(server, file_id, 6);
1047        ncp_add_be32(server, offset);
1048        ncp_add_be16(server, to_write);
1049        ncp_add_mem(server, source, to_write);
1050        
1051        if ((result = ncp_request(server, 73)) == 0)
1052                *bytes_written = to_write;
1053        ncp_unlock_server(server);
1054        return result;
1055}
1056
1057#ifdef CONFIG_NCPFS_IOCTL_LOCKING
1058int
1059ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
1060          __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
1061{
1062        int result;
1063
1064        ncp_init_request(server);
1065        ncp_add_byte(server, locktype);
1066        ncp_add_mem(server, file_id, 6);
1067        ncp_add_be32(server, offset);
1068        ncp_add_be32(server, length);
1069        ncp_add_be16(server, timeout);
1070
1071        if ((result = ncp_request(server, 0x1A)) != 0)
1072        {
1073                ncp_unlock_server(server);
1074                return result;
1075        }
1076        ncp_unlock_server(server);
1077        return 0;
1078}
1079
1080int
1081ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
1082          __u32 offset, __u32 length)
1083{
1084        int result;
1085
1086        ncp_init_request(server);
1087        ncp_add_byte(server, 0);        /* who knows... lanalyzer says that */
1088        ncp_add_mem(server, file_id, 6);
1089        ncp_add_be32(server, offset);
1090        ncp_add_be32(server, length);
1091
1092        if ((result = ncp_request(server, 0x1E)) != 0)
1093        {
1094                ncp_unlock_server(server);
1095                return result;
1096        }
1097        ncp_unlock_server(server);
1098        return 0;
1099}
1100#endif  /* CONFIG_NCPFS_IOCTL_LOCKING */
1101
1102#ifdef CONFIG_NCPFS_NLS
1103/* This are the NLS conversion routines with inspirations and code parts
1104 * from the vfat file system and hints from Petr Vandrovec.
1105 */
1106
1107int
1108ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen,
1109                const unsigned char *iname, unsigned int ilen, int cc)
1110{
1111        struct nls_table *in = server->nls_io;
1112        struct nls_table *out = server->nls_vol;
1113        unsigned char *vname_start;
1114        unsigned char *vname_end;
1115        const unsigned char *iname_end;
1116
1117        iname_end = iname + ilen;
1118        vname_start = vname;
1119        vname_end = vname + *vlen - 1;
1120
1121        while (iname < iname_end) {
1122                int chl;
1123                wchar_t ec;
1124
1125                if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
1126                        int k;
1127                        unicode_t u;
1128
1129                        k = utf8_to_utf32(iname, iname_end - iname, &u);
1130                        if (k < 0 || u > MAX_WCHAR_T)
1131                                return -EINVAL;
1132                        iname += k;
1133                        ec = u;
1134                } else {
1135                        if (*iname == NCP_ESC) {
1136                                int k;
1137
1138                                if (iname_end - iname < 5)
1139                                        goto nospec;
1140
1141                                ec = 0;
1142                                for (k = 1; k < 5; k++) {
1143                                        unsigned char nc;
1144
1145                                        nc = iname[k] - '0';
1146                                        if (nc >= 10) {
1147                                                nc -= 'A' - '0' - 10;
1148                                                if ((nc < 10) || (nc > 15)) {
1149                                                        goto nospec;
1150                                                }
1151                                        }
1152                                        ec = (ec << 4) | nc;
1153                                }
1154                                iname += 5;
1155                        } else {
1156nospec:;                        
1157                                if ( (chl = in->char2uni(iname, iname_end - iname, &ec)) < 0)
1158                                        return chl;
1159                                iname += chl;
1160                        }
1161                }
1162
1163                /* unitoupper should be here! */
1164
1165                chl = out->uni2char(ec, vname, vname_end - vname);
1166                if (chl < 0)
1167                        return chl;
1168
1169                /* this is wrong... */
1170                if (cc) {
1171                        int chi;
1172
1173                        for (chi = 0; chi < chl; chi++){
1174                                vname[chi] = ncp_toupper(out, vname[chi]);
1175                        }
1176                }
1177                vname += chl;
1178        }
1179
1180        *vname = 0;
1181        *vlen = vname - vname_start;
1182        return 0;
1183}
1184
1185int
1186ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen,
1187                const unsigned char *vname, unsigned int vlen, int cc)
1188{
1189        struct nls_table *in = server->nls_vol;
1190        struct nls_table *out = server->nls_io;
1191        const unsigned char *vname_end;
1192        unsigned char *iname_start;
1193        unsigned char *iname_end;
1194        unsigned char *vname_cc;
1195        int err;
1196
1197        vname_cc = NULL;
1198
1199        if (cc) {
1200                int i;
1201
1202                /* this is wrong! */
1203                vname_cc = kmalloc(vlen, GFP_KERNEL);
1204                if (!vname_cc)
1205                        return -ENOMEM;
1206                for (i = 0; i < vlen; i++)
1207                        vname_cc[i] = ncp_tolower(in, vname[i]);
1208                vname = vname_cc;
1209        }
1210
1211        iname_start = iname;
1212        iname_end = iname + *ilen - 1;
1213        vname_end = vname + vlen;
1214
1215        while (vname < vname_end) {
1216                wchar_t ec;
1217                int chl;
1218
1219                if ( (chl = in->char2uni(vname, vname_end - vname, &ec)) < 0) {
1220                        err = chl;
1221                        goto quit;
1222                }
1223                vname += chl;
1224
1225                /* unitolower should be here! */
1226
1227                if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
1228                        int k;
1229
1230                        k = utf32_to_utf8(ec, iname, iname_end - iname);
1231                        if (k < 0) {
1232                                err = -ENAMETOOLONG;
1233                                goto quit;
1234                        }
1235                        iname += k;
1236                } else {
1237                        if ( (chl = out->uni2char(ec, iname, iname_end - iname)) >= 0) {
1238                                iname += chl;
1239                        } else {
1240                                int k;
1241
1242                                if (iname_end - iname < 5) {
1243                                        err = -ENAMETOOLONG;
1244                                        goto quit;
1245                                }
1246                                *iname = NCP_ESC;
1247                                for (k = 4; k > 0; k--) {
1248                                        unsigned char v;
1249                                        
1250                                        v = (ec & 0xF) + '0';
1251                                        if (v > '9') {
1252                                                v += 'A' - '9' - 1;
1253                                        }
1254                                        iname[k] = v;
1255                                        ec >>= 4;
1256                                }
1257                                iname += 5;
1258                        }
1259                }
1260        }
1261
1262        *iname = 0;
1263        *ilen = iname - iname_start;
1264        err = 0;
1265quit:;
1266        if (cc)
1267                kfree(vname_cc);
1268        return err;
1269}
1270
1271#else
1272
1273int
1274ncp__io2vol(unsigned char *vname, unsigned int *vlen,
1275                const unsigned char *iname, unsigned int ilen, int cc)
1276{
1277        int i;
1278
1279        if (*vlen <= ilen)
1280                return -ENAMETOOLONG;
1281
1282        if (cc)
1283                for (i = 0; i < ilen; i++) {
1284                        *vname = toupper(*iname);
1285                        vname++;
1286                        iname++;
1287                }
1288        else {
1289                memmove(vname, iname, ilen);
1290                vname += ilen;
1291        }
1292
1293        *vlen = ilen;
1294        *vname = 0;
1295        return 0;
1296}
1297
1298int
1299ncp__vol2io(unsigned char *iname, unsigned int *ilen,
1300                const unsigned char *vname, unsigned int vlen, int cc)
1301{
1302        int i;
1303
1304        if (*ilen <= vlen)
1305                return -ENAMETOOLONG;
1306
1307        if (cc)
1308                for (i = 0; i < vlen; i++) {
1309                        *iname = tolower(*vname);
1310                        iname++;
1311                        vname++;
1312                }
1313        else {
1314                memmove(iname, vname, vlen);
1315                iname += vlen;
1316        }
1317
1318        *ilen = vlen;
1319        *iname = 0;
1320        return 0;
1321}
1322
1323#endif
1324