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