Linux Audio

Check our new training course

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