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