at v4.14 1322 lines 33 kB view raw
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 source = ncp_reply_data(server, 2 + (offset & 1)); 985 986 memcpy(target, source, *bytes_read); 987out: 988 ncp_unlock_server(server); 989 return result; 990} 991 992/* There is a problem... egrep and some other silly tools do: 993 x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768); 994 read(<ncpfs fd>, x, 32768); 995 Now copying read result by copy_to_user causes pagefault. This pagefault 996 could not be handled because of server was locked due to read. So we have 997 to use temporary buffer. So ncp_unlock_server must be done before 998 copy_to_user (and for write, copy_from_user must be done before 999 ncp_init_request... same applies for send raw packet ioctl). Because of 1000 file is normally read in bigger chunks, caller provides kmalloced 1001 (vmalloced) chunk of memory with size >= to_read... 1002 */ 1003int 1004ncp_read_bounce(struct ncp_server *server, const char *file_id, 1005 __u32 offset, __u16 to_read, struct iov_iter *to, 1006 int *bytes_read, void *bounce, __u32 bufsize) 1007{ 1008 int result; 1009 1010 ncp_init_request(server); 1011 ncp_add_byte(server, 0); 1012 ncp_add_mem(server, file_id, 6); 1013 ncp_add_be32(server, offset); 1014 ncp_add_be16(server, to_read); 1015 result = ncp_request2(server, 72, bounce, bufsize); 1016 ncp_unlock_server(server); 1017 if (!result) { 1018 int len = get_unaligned_be16((char *)bounce + 1019 sizeof(struct ncp_reply_header)); 1020 result = -EIO; 1021 if (len <= to_read) { 1022 char* source; 1023 1024 source = (char*)bounce + 1025 sizeof(struct ncp_reply_header) + 2 + 1026 (offset & 1); 1027 *bytes_read = len; 1028 result = 0; 1029 if (copy_to_iter(source, len, to) != len) 1030 result = -EFAULT; 1031 } 1032 } 1033 return result; 1034} 1035 1036int 1037ncp_write_kernel(struct ncp_server *server, const char *file_id, 1038 __u32 offset, __u16 to_write, 1039 const char *source, int *bytes_written) 1040{ 1041 int result; 1042 1043 ncp_init_request(server); 1044 ncp_add_byte(server, 0); 1045 ncp_add_mem(server, file_id, 6); 1046 ncp_add_be32(server, offset); 1047 ncp_add_be16(server, to_write); 1048 ncp_add_mem(server, source, to_write); 1049 1050 if ((result = ncp_request(server, 73)) == 0) 1051 *bytes_written = to_write; 1052 ncp_unlock_server(server); 1053 return result; 1054} 1055 1056#ifdef CONFIG_NCPFS_IOCTL_LOCKING 1057int 1058ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id, 1059 __u8 locktype, __u32 offset, __u32 length, __u16 timeout) 1060{ 1061 int result; 1062 1063 ncp_init_request(server); 1064 ncp_add_byte(server, locktype); 1065 ncp_add_mem(server, file_id, 6); 1066 ncp_add_be32(server, offset); 1067 ncp_add_be32(server, length); 1068 ncp_add_be16(server, timeout); 1069 1070 if ((result = ncp_request(server, 0x1A)) != 0) 1071 { 1072 ncp_unlock_server(server); 1073 return result; 1074 } 1075 ncp_unlock_server(server); 1076 return 0; 1077} 1078 1079int 1080ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id, 1081 __u32 offset, __u32 length) 1082{ 1083 int result; 1084 1085 ncp_init_request(server); 1086 ncp_add_byte(server, 0); /* who knows... lanalyzer says that */ 1087 ncp_add_mem(server, file_id, 6); 1088 ncp_add_be32(server, offset); 1089 ncp_add_be32(server, length); 1090 1091 if ((result = ncp_request(server, 0x1E)) != 0) 1092 { 1093 ncp_unlock_server(server); 1094 return result; 1095 } 1096 ncp_unlock_server(server); 1097 return 0; 1098} 1099#endif /* CONFIG_NCPFS_IOCTL_LOCKING */ 1100 1101#ifdef CONFIG_NCPFS_NLS 1102/* This are the NLS conversion routines with inspirations and code parts 1103 * from the vfat file system and hints from Petr Vandrovec. 1104 */ 1105 1106int 1107ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen, 1108 const unsigned char *iname, unsigned int ilen, int cc) 1109{ 1110 struct nls_table *in = server->nls_io; 1111 struct nls_table *out = server->nls_vol; 1112 unsigned char *vname_start; 1113 unsigned char *vname_end; 1114 const unsigned char *iname_end; 1115 1116 iname_end = iname + ilen; 1117 vname_start = vname; 1118 vname_end = vname + *vlen - 1; 1119 1120 while (iname < iname_end) { 1121 int chl; 1122 wchar_t ec; 1123 1124 if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) { 1125 int k; 1126 unicode_t u; 1127 1128 k = utf8_to_utf32(iname, iname_end - iname, &u); 1129 if (k < 0 || u > MAX_WCHAR_T) 1130 return -EINVAL; 1131 iname += k; 1132 ec = u; 1133 } else { 1134 if (*iname == NCP_ESC) { 1135 int k; 1136 1137 if (iname_end - iname < 5) 1138 goto nospec; 1139 1140 ec = 0; 1141 for (k = 1; k < 5; k++) { 1142 unsigned char nc; 1143 1144 nc = iname[k] - '0'; 1145 if (nc >= 10) { 1146 nc -= 'A' - '0' - 10; 1147 if ((nc < 10) || (nc > 15)) { 1148 goto nospec; 1149 } 1150 } 1151 ec = (ec << 4) | nc; 1152 } 1153 iname += 5; 1154 } else { 1155nospec:; 1156 if ( (chl = in->char2uni(iname, iname_end - iname, &ec)) < 0) 1157 return chl; 1158 iname += chl; 1159 } 1160 } 1161 1162 /* unitoupper should be here! */ 1163 1164 chl = out->uni2char(ec, vname, vname_end - vname); 1165 if (chl < 0) 1166 return chl; 1167 1168 /* this is wrong... */ 1169 if (cc) { 1170 int chi; 1171 1172 for (chi = 0; chi < chl; chi++){ 1173 vname[chi] = ncp_toupper(out, vname[chi]); 1174 } 1175 } 1176 vname += chl; 1177 } 1178 1179 *vname = 0; 1180 *vlen = vname - vname_start; 1181 return 0; 1182} 1183 1184int 1185ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen, 1186 const unsigned char *vname, unsigned int vlen, int cc) 1187{ 1188 struct nls_table *in = server->nls_vol; 1189 struct nls_table *out = server->nls_io; 1190 const unsigned char *vname_end; 1191 unsigned char *iname_start; 1192 unsigned char *iname_end; 1193 unsigned char *vname_cc; 1194 int err; 1195 1196 vname_cc = NULL; 1197 1198 if (cc) { 1199 int i; 1200 1201 /* this is wrong! */ 1202 vname_cc = kmalloc(vlen, GFP_KERNEL); 1203 if (!vname_cc) 1204 return -ENOMEM; 1205 for (i = 0; i < vlen; i++) 1206 vname_cc[i] = ncp_tolower(in, vname[i]); 1207 vname = vname_cc; 1208 } 1209 1210 iname_start = iname; 1211 iname_end = iname + *ilen - 1; 1212 vname_end = vname + vlen; 1213 1214 while (vname < vname_end) { 1215 wchar_t ec; 1216 int chl; 1217 1218 if ( (chl = in->char2uni(vname, vname_end - vname, &ec)) < 0) { 1219 err = chl; 1220 goto quit; 1221 } 1222 vname += chl; 1223 1224 /* unitolower should be here! */ 1225 1226 if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) { 1227 int k; 1228 1229 k = utf32_to_utf8(ec, iname, iname_end - iname); 1230 if (k < 0) { 1231 err = -ENAMETOOLONG; 1232 goto quit; 1233 } 1234 iname += k; 1235 } else { 1236 if ( (chl = out->uni2char(ec, iname, iname_end - iname)) >= 0) { 1237 iname += chl; 1238 } else { 1239 int k; 1240 1241 if (iname_end - iname < 5) { 1242 err = -ENAMETOOLONG; 1243 goto quit; 1244 } 1245 *iname = NCP_ESC; 1246 for (k = 4; k > 0; k--) { 1247 unsigned char v; 1248 1249 v = (ec & 0xF) + '0'; 1250 if (v > '9') { 1251 v += 'A' - '9' - 1; 1252 } 1253 iname[k] = v; 1254 ec >>= 4; 1255 } 1256 iname += 5; 1257 } 1258 } 1259 } 1260 1261 *iname = 0; 1262 *ilen = iname - iname_start; 1263 err = 0; 1264quit:; 1265 if (cc) 1266 kfree(vname_cc); 1267 return err; 1268} 1269 1270#else 1271 1272int 1273ncp__io2vol(unsigned char *vname, unsigned int *vlen, 1274 const unsigned char *iname, unsigned int ilen, int cc) 1275{ 1276 int i; 1277 1278 if (*vlen <= ilen) 1279 return -ENAMETOOLONG; 1280 1281 if (cc) 1282 for (i = 0; i < ilen; i++) { 1283 *vname = toupper(*iname); 1284 vname++; 1285 iname++; 1286 } 1287 else { 1288 memmove(vname, iname, ilen); 1289 vname += ilen; 1290 } 1291 1292 *vlen = ilen; 1293 *vname = 0; 1294 return 0; 1295} 1296 1297int 1298ncp__vol2io(unsigned char *iname, unsigned int *ilen, 1299 const unsigned char *vname, unsigned int vlen, int cc) 1300{ 1301 int i; 1302 1303 if (*ilen <= vlen) 1304 return -ENAMETOOLONG; 1305 1306 if (cc) 1307 for (i = 0; i < vlen; i++) { 1308 *iname = tolower(*vname); 1309 iname++; 1310 vname++; 1311 } 1312 else { 1313 memmove(iname, vname, vlen); 1314 iname += vlen; 1315 } 1316 1317 *ilen = vlen; 1318 *iname = 0; 1319 return 0; 1320} 1321 1322#endif