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