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