• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  ioctl.c
3  *
4  *  Copyright (C) 1995, 1996 by Volker Lendecke
5  *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
6  *  Modified 1998, 1999 Wolfram Pienkoss for NLS
7  *
8  */
9 
10 #include <linux/capability.h>
11 #include <linux/compat.h>
12 #include <linux/errno.h>
13 #include <linux/fs.h>
14 #include <linux/ioctl.h>
15 #include <linux/time.h>
16 #include <linux/mm.h>
17 #include <linux/mount.h>
18 #include <linux/highuid.h>
19 #include <linux/smp_lock.h>
20 #include <linux/vmalloc.h>
21 #include <linux/sched.h>
22 
23 #include <linux/ncp_fs.h>
24 
25 #include <asm/uaccess.h>
26 
27 #include "ncplib_kernel.h"
28 
29 /* maximum limit for ncp_objectname_ioctl */
30 #define NCP_OBJECT_NAME_MAX_LEN	4096
31 /* maximum limit for ncp_privatedata_ioctl */
32 #define NCP_PRIVATE_DATA_MAX_LEN 8192
33 /* maximum negotiable packet size */
34 #define NCP_PACKET_SIZE_INTERNAL 65536
35 
36 static int
ncp_get_fs_info(struct ncp_server * server,struct file * file,struct ncp_fs_info __user * arg)37 ncp_get_fs_info(struct ncp_server * server, struct file *file,
38 		struct ncp_fs_info __user *arg)
39 {
40 	struct inode *inode = file->f_path.dentry->d_inode;
41 	struct ncp_fs_info info;
42 
43 	if (file_permission(file, MAY_WRITE) != 0
44 	    && current_uid() != server->m.mounted_uid)
45 		return -EACCES;
46 
47 	if (copy_from_user(&info, arg, sizeof(info)))
48 		return -EFAULT;
49 
50 	if (info.version != NCP_GET_FS_INFO_VERSION) {
51 		DPRINTK("info.version invalid: %d\n", info.version);
52 		return -EINVAL;
53 	}
54 	/* TODO: info.addr = server->m.serv_addr; */
55 	SET_UID(info.mounted_uid, server->m.mounted_uid);
56 	info.connection		= server->connection;
57 	info.buffer_size	= server->buffer_size;
58 	info.volume_number	= NCP_FINFO(inode)->volNumber;
59 	info.directory_id	= NCP_FINFO(inode)->DosDirNum;
60 
61 	if (copy_to_user(arg, &info, sizeof(info)))
62 		return -EFAULT;
63 	return 0;
64 }
65 
66 static int
ncp_get_fs_info_v2(struct ncp_server * server,struct file * file,struct ncp_fs_info_v2 __user * arg)67 ncp_get_fs_info_v2(struct ncp_server * server, struct file *file,
68 		   struct ncp_fs_info_v2 __user * arg)
69 {
70 	struct inode *inode = file->f_path.dentry->d_inode;
71 	struct ncp_fs_info_v2 info2;
72 
73 	if (file_permission(file, MAY_WRITE) != 0
74 	    && current_uid() != server->m.mounted_uid)
75 		return -EACCES;
76 
77 	if (copy_from_user(&info2, arg, sizeof(info2)))
78 		return -EFAULT;
79 
80 	if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
81 		DPRINTK("info.version invalid: %d\n", info2.version);
82 		return -EINVAL;
83 	}
84 	info2.mounted_uid   = server->m.mounted_uid;
85 	info2.connection    = server->connection;
86 	info2.buffer_size   = server->buffer_size;
87 	info2.volume_number = NCP_FINFO(inode)->volNumber;
88 	info2.directory_id  = NCP_FINFO(inode)->DosDirNum;
89 	info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
90 
91 	if (copy_to_user(arg, &info2, sizeof(info2)))
92 		return -EFAULT;
93 	return 0;
94 }
95 
96 #ifdef CONFIG_COMPAT
97 struct compat_ncp_objectname_ioctl
98 {
99 	s32		auth_type;
100 	u32		object_name_len;
101 	compat_caddr_t	object_name;	/* a userspace data, in most cases user name */
102 };
103 
104 struct compat_ncp_fs_info_v2 {
105 	s32 version;
106 	u32 mounted_uid;
107 	u32 connection;
108 	u32 buffer_size;
109 
110 	u32 volume_number;
111 	u32 directory_id;
112 
113 	u32 dummy1;
114 	u32 dummy2;
115 	u32 dummy3;
116 };
117 
118 struct compat_ncp_ioctl_request {
119 	u32 function;
120 	u32 size;
121 	compat_caddr_t data;
122 };
123 
124 struct compat_ncp_privatedata_ioctl
125 {
126 	u32		len;
127 	compat_caddr_t	data;		/* ~1000 for NDS */
128 };
129 
130 #define NCP_IOC_GET_FS_INFO_V2_32	_IOWR('n', 4, struct compat_ncp_fs_info_v2)
131 #define NCP_IOC_NCPREQUEST_32		_IOR('n', 1, struct compat_ncp_ioctl_request)
132 #define NCP_IOC_GETOBJECTNAME_32	_IOWR('n', 9, struct compat_ncp_objectname_ioctl)
133 #define NCP_IOC_SETOBJECTNAME_32	_IOR('n', 9, struct compat_ncp_objectname_ioctl)
134 #define NCP_IOC_GETPRIVATEDATA_32	_IOWR('n', 10, struct compat_ncp_privatedata_ioctl)
135 #define NCP_IOC_SETPRIVATEDATA_32	_IOR('n', 10, struct compat_ncp_privatedata_ioctl)
136 
137 static int
ncp_get_compat_fs_info_v2(struct ncp_server * server,struct file * file,struct compat_ncp_fs_info_v2 __user * arg)138 ncp_get_compat_fs_info_v2(struct ncp_server * server, struct file *file,
139 		   struct compat_ncp_fs_info_v2 __user * arg)
140 {
141 	struct inode *inode = file->f_path.dentry->d_inode;
142 	struct compat_ncp_fs_info_v2 info2;
143 
144 	if (file_permission(file, MAY_WRITE) != 0
145 	    && current_uid() != server->m.mounted_uid)
146 		return -EACCES;
147 
148 	if (copy_from_user(&info2, arg, sizeof(info2)))
149 		return -EFAULT;
150 
151 	if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
152 		DPRINTK("info.version invalid: %d\n", info2.version);
153 		return -EINVAL;
154 	}
155 	info2.mounted_uid   = server->m.mounted_uid;
156 	info2.connection    = server->connection;
157 	info2.buffer_size   = server->buffer_size;
158 	info2.volume_number = NCP_FINFO(inode)->volNumber;
159 	info2.directory_id  = NCP_FINFO(inode)->DosDirNum;
160 	info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
161 
162 	if (copy_to_user(arg, &info2, sizeof(info2)))
163 		return -EFAULT;
164 	return 0;
165 }
166 #endif
167 
168 #define NCP_IOC_GETMOUNTUID16		_IOW('n', 2, u16)
169 #define NCP_IOC_GETMOUNTUID32		_IOW('n', 2, u32)
170 #define NCP_IOC_GETMOUNTUID64		_IOW('n', 2, u64)
171 
172 #ifdef CONFIG_NCPFS_NLS
173 /* Here we are select the iocharset and the codepage for NLS.
174  * Thanks Petr Vandrovec for idea and many hints.
175  */
176 static int
ncp_set_charsets(struct ncp_server * server,struct ncp_nls_ioctl __user * arg)177 ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
178 {
179 	struct ncp_nls_ioctl user;
180 	struct nls_table *codepage;
181 	struct nls_table *iocharset;
182 	struct nls_table *oldset_io;
183 	struct nls_table *oldset_cp;
184 
185 	if (!capable(CAP_SYS_ADMIN))
186 		return -EACCES;
187 	if (server->root_setuped)
188 		return -EBUSY;
189 
190 	if (copy_from_user(&user, arg, sizeof(user)))
191 		return -EFAULT;
192 
193 	codepage = NULL;
194 	user.codepage[NCP_IOCSNAME_LEN] = 0;
195 	if (!user.codepage[0] || !strcmp(user.codepage, "default"))
196 		codepage = load_nls_default();
197 	else {
198 		codepage = load_nls(user.codepage);
199 		if (!codepage) {
200 			return -EBADRQC;
201 		}
202 	}
203 
204 	iocharset = NULL;
205 	user.iocharset[NCP_IOCSNAME_LEN] = 0;
206 	if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) {
207 		iocharset = load_nls_default();
208 		NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
209 	} else if (!strcmp(user.iocharset, "utf8")) {
210 		iocharset = load_nls_default();
211 		NCP_SET_FLAG(server, NCP_FLAG_UTF8);
212 	} else {
213 		iocharset = load_nls(user.iocharset);
214 		if (!iocharset) {
215 			unload_nls(codepage);
216 			return -EBADRQC;
217 		}
218 		NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
219 	}
220 
221 	oldset_cp = server->nls_vol;
222 	server->nls_vol = codepage;
223 	oldset_io = server->nls_io;
224 	server->nls_io = iocharset;
225 
226 	if (oldset_cp)
227 		unload_nls(oldset_cp);
228 	if (oldset_io)
229 		unload_nls(oldset_io);
230 
231 	return 0;
232 }
233 
234 static int
ncp_get_charsets(struct ncp_server * server,struct ncp_nls_ioctl __user * arg)235 ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
236 {
237 	struct ncp_nls_ioctl user;
238 	int len;
239 
240 	memset(&user, 0, sizeof(user));
241 	if (server->nls_vol && server->nls_vol->charset) {
242 		len = strlen(server->nls_vol->charset);
243 		if (len > NCP_IOCSNAME_LEN)
244 			len = NCP_IOCSNAME_LEN;
245 		strncpy(user.codepage, server->nls_vol->charset, len);
246 		user.codepage[len] = 0;
247 	}
248 
249 	if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
250 		strcpy(user.iocharset, "utf8");
251 	else if (server->nls_io && server->nls_io->charset) {
252 		len = strlen(server->nls_io->charset);
253 		if (len > NCP_IOCSNAME_LEN)
254 			len = NCP_IOCSNAME_LEN;
255 		strncpy(user.iocharset,	server->nls_io->charset, len);
256 		user.iocharset[len] = 0;
257 	}
258 
259 	if (copy_to_user(arg, &user, sizeof(user)))
260 		return -EFAULT;
261 	return 0;
262 }
263 #endif /* CONFIG_NCPFS_NLS */
264 
__ncp_ioctl(struct inode * inode,struct file * filp,unsigned int cmd,unsigned long arg)265 static int __ncp_ioctl(struct inode *inode, struct file *filp,
266 	      unsigned int cmd, unsigned long arg)
267 {
268 	struct ncp_server *server = NCP_SERVER(inode);
269 	int result;
270 	struct ncp_ioctl_request request;
271 	char* bouncebuffer;
272 	void __user *argp = (void __user *)arg;
273 	uid_t uid = current_uid();
274 
275 	switch (cmd) {
276 #ifdef CONFIG_COMPAT
277 	case NCP_IOC_NCPREQUEST_32:
278 #endif
279 	case NCP_IOC_NCPREQUEST:
280 		if (file_permission(filp, MAY_WRITE) != 0
281 		    && uid != server->m.mounted_uid)
282 			return -EACCES;
283 
284 #ifdef CONFIG_COMPAT
285 		if (cmd == NCP_IOC_NCPREQUEST_32) {
286 			struct compat_ncp_ioctl_request request32;
287 			if (copy_from_user(&request32, argp, sizeof(request32)))
288 				return -EFAULT;
289 			request.function = request32.function;
290 			request.size = request32.size;
291 			request.data = compat_ptr(request32.data);
292 		} else
293 #endif
294 		if (copy_from_user(&request, argp, sizeof(request)))
295 			return -EFAULT;
296 
297 		if ((request.function > 255)
298 		    || (request.size >
299 		  NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
300 			return -EINVAL;
301 		}
302 		bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL);
303 		if (!bouncebuffer)
304 			return -ENOMEM;
305 		if (copy_from_user(bouncebuffer, request.data, request.size)) {
306 			vfree(bouncebuffer);
307 			return -EFAULT;
308 		}
309 		ncp_lock_server(server);
310 
311 		/* FIXME: We hack around in the server's structures
312 		   here to be able to use ncp_request */
313 
314 		server->has_subfunction = 0;
315 		server->current_size = request.size;
316 		memcpy(server->packet, bouncebuffer, request.size);
317 
318 		result = ncp_request2(server, request.function,
319 			bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
320 		if (result < 0)
321 			result = -EIO;
322 		else
323 			result = server->reply_size;
324 		ncp_unlock_server(server);
325 		DPRINTK("ncp_ioctl: copy %d bytes\n",
326 			result);
327 		if (result >= 0)
328 			if (copy_to_user(request.data, bouncebuffer, result))
329 				result = -EFAULT;
330 		vfree(bouncebuffer);
331 		return result;
332 
333 	case NCP_IOC_CONN_LOGGED_IN:
334 
335 		if (!capable(CAP_SYS_ADMIN))
336 			return -EACCES;
337 		if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
338 			return -EINVAL;
339 		if (server->root_setuped)
340 			return -EBUSY;
341 		server->root_setuped = 1;
342 		return ncp_conn_logged_in(inode->i_sb);
343 
344 	case NCP_IOC_GET_FS_INFO:
345 		return ncp_get_fs_info(server, filp, argp);
346 
347 	case NCP_IOC_GET_FS_INFO_V2:
348 		return ncp_get_fs_info_v2(server, filp, argp);
349 
350 #ifdef CONFIG_COMPAT
351 	case NCP_IOC_GET_FS_INFO_V2_32:
352 		return ncp_get_compat_fs_info_v2(server, filp, argp);
353 #endif
354 	/* we have too many combinations of CONFIG_COMPAT,
355 	 * CONFIG_64BIT and CONFIG_UID16, so just handle
356 	 * any of the possible ioctls */
357 	case NCP_IOC_GETMOUNTUID16:
358 	case NCP_IOC_GETMOUNTUID32:
359 	case NCP_IOC_GETMOUNTUID64:
360 		if (file_permission(filp, MAY_READ) != 0
361 			&& uid != server->m.mounted_uid)
362 			return -EACCES;
363 
364 		if (cmd == NCP_IOC_GETMOUNTUID16) {
365 			u16 uid;
366 			SET_UID(uid, server->m.mounted_uid);
367 			if (put_user(uid, (u16 __user *)argp))
368 				return -EFAULT;
369 		} else if (cmd == NCP_IOC_GETMOUNTUID32) {
370 			if (put_user(server->m.mounted_uid,
371 						(u32 __user *)argp))
372 				return -EFAULT;
373 		} else {
374 			if (put_user(server->m.mounted_uid,
375 						(u64 __user *)argp))
376 				return -EFAULT;
377 		}
378 		return 0;
379 
380 	case NCP_IOC_GETROOT:
381 		{
382 			struct ncp_setroot_ioctl sr;
383 
384 			if (file_permission(filp, MAY_READ) != 0
385 			    && uid != server->m.mounted_uid)
386 				return -EACCES;
387 
388 			if (server->m.mounted_vol[0]) {
389 				struct dentry* dentry = inode->i_sb->s_root;
390 
391 				if (dentry) {
392 					struct inode* s_inode = dentry->d_inode;
393 
394 					if (s_inode) {
395 						sr.volNumber = NCP_FINFO(s_inode)->volNumber;
396 						sr.dirEntNum = NCP_FINFO(s_inode)->dirEntNum;
397 						sr.namespace = server->name_space[sr.volNumber];
398 					} else
399 						DPRINTK("ncpfs: s_root->d_inode==NULL\n");
400 				} else
401 					DPRINTK("ncpfs: s_root==NULL\n");
402 			} else {
403 				sr.volNumber = -1;
404 				sr.namespace = 0;
405 				sr.dirEntNum = 0;
406 			}
407 			if (copy_to_user(argp, &sr, sizeof(sr)))
408 				return -EFAULT;
409 			return 0;
410 		}
411 
412 	case NCP_IOC_SETROOT:
413 		{
414 			struct ncp_setroot_ioctl sr;
415 			__u32 vnum;
416 			__le32 de;
417 			__le32 dosde;
418 			struct dentry* dentry;
419 
420 			if (!capable(CAP_SYS_ADMIN))
421 			{
422 				return -EACCES;
423 			}
424 			if (server->root_setuped) return -EBUSY;
425 			if (copy_from_user(&sr, argp, sizeof(sr)))
426 				return -EFAULT;
427 			if (sr.volNumber < 0) {
428 				server->m.mounted_vol[0] = 0;
429 				vnum = NCP_NUMBER_OF_VOLUMES;
430 				de = 0;
431 				dosde = 0;
432 			} else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
433 				return -EINVAL;
434 			} else if (ncp_mount_subdir(server, sr.volNumber,
435 						sr.namespace, sr.dirEntNum,
436 						&vnum, &de, &dosde)) {
437 				return -ENOENT;
438 			}
439 
440 			dentry = inode->i_sb->s_root;
441 			server->root_setuped = 1;
442 			if (dentry) {
443 				struct inode* s_inode = dentry->d_inode;
444 
445 				if (inode) {
446 					NCP_FINFO(s_inode)->volNumber = vnum;
447 					NCP_FINFO(s_inode)->dirEntNum = de;
448 					NCP_FINFO(s_inode)->DosDirNum = dosde;
449 				} else
450 					DPRINTK("ncpfs: s_root->d_inode==NULL\n");
451 			} else
452 				DPRINTK("ncpfs: s_root==NULL\n");
453 
454 			return 0;
455 		}
456 
457 #ifdef CONFIG_NCPFS_PACKET_SIGNING
458 	case NCP_IOC_SIGN_INIT:
459 		if (file_permission(filp, MAY_WRITE) != 0
460 		    && uid != server->m.mounted_uid)
461 			return -EACCES;
462 
463 		if (argp) {
464 			if (server->sign_wanted)
465 			{
466 				struct ncp_sign_init sign;
467 
468 				if (copy_from_user(&sign, argp, sizeof(sign)))
469 					return -EFAULT;
470 				memcpy(server->sign_root,sign.sign_root,8);
471 				memcpy(server->sign_last,sign.sign_last,16);
472 				server->sign_active = 1;
473 			}
474 			/* ignore when signatures not wanted */
475 		} else {
476 			server->sign_active = 0;
477 		}
478 		return 0;
479 
480         case NCP_IOC_SIGN_WANTED:
481 		if (file_permission(filp, MAY_READ) != 0
482 		    && uid != server->m.mounted_uid)
483 			return -EACCES;
484 
485                 if (put_user(server->sign_wanted, (int __user *)argp))
486 			return -EFAULT;
487                 return 0;
488 
489 	case NCP_IOC_SET_SIGN_WANTED:
490 		{
491 			int newstate;
492 
493 			if (file_permission(filp, MAY_WRITE) != 0
494 			    && uid != server->m.mounted_uid)
495 				return -EACCES;
496 
497 			/* get only low 8 bits... */
498 			if (get_user(newstate, (unsigned char __user *)argp))
499 				return -EFAULT;
500 			if (server->sign_active) {
501 				/* cannot turn signatures OFF when active */
502 				if (!newstate) return -EINVAL;
503 			} else {
504 				server->sign_wanted = newstate != 0;
505 			}
506 			return 0;
507 		}
508 
509 #endif /* CONFIG_NCPFS_PACKET_SIGNING */
510 
511 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
512 	case NCP_IOC_LOCKUNLOCK:
513 		if (file_permission(filp, MAY_WRITE) != 0
514 		    && uid != server->m.mounted_uid)
515 			return -EACCES;
516 
517 		{
518 			struct ncp_lock_ioctl	 rqdata;
519 
520 			if (copy_from_user(&rqdata, argp, sizeof(rqdata)))
521 				return -EFAULT;
522 			if (rqdata.origin != 0)
523 				return -EINVAL;
524 			/* check for cmd */
525 			switch (rqdata.cmd) {
526 				case NCP_LOCK_EX:
527 				case NCP_LOCK_SH:
528 						if (rqdata.timeout == 0)
529 							rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
530 						else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
531 							rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
532 						break;
533 				case NCP_LOCK_LOG:
534 						rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;	/* has no effect */
535 				case NCP_LOCK_CLEAR:
536 						break;
537 				default:
538 						return -EINVAL;
539 			}
540 			/* locking needs both read and write access */
541 			if ((result = ncp_make_open(inode, O_RDWR)) != 0)
542 			{
543 				return result;
544 			}
545 			result = -EIO;
546 			if (!ncp_conn_valid(server))
547 				goto outrel;
548 			result = -EISDIR;
549 			if (!S_ISREG(inode->i_mode))
550 				goto outrel;
551 			if (rqdata.cmd == NCP_LOCK_CLEAR)
552 			{
553 				result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
554 							NCP_FINFO(inode)->file_handle,
555 							rqdata.offset,
556 							rqdata.length);
557 				if (result > 0) result = 0;	/* no such lock */
558 			}
559 			else
560 			{
561 				int lockcmd;
562 
563 				switch (rqdata.cmd)
564 				{
565 					case NCP_LOCK_EX:  lockcmd=1; break;
566 					case NCP_LOCK_SH:  lockcmd=3; break;
567 					default:	   lockcmd=0; break;
568 				}
569 				result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
570 							NCP_FINFO(inode)->file_handle,
571 							lockcmd,
572 							rqdata.offset,
573 							rqdata.length,
574 							rqdata.timeout);
575 				if (result > 0) result = -EAGAIN;
576 			}
577 outrel:
578 			ncp_inode_close(inode);
579 			return result;
580 		}
581 #endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
582 
583 #ifdef CONFIG_COMPAT
584 	case NCP_IOC_GETOBJECTNAME_32:
585 		if (uid != server->m.mounted_uid)
586 			return -EACCES;
587 		{
588 			struct compat_ncp_objectname_ioctl user;
589 			size_t outl;
590 
591 			if (copy_from_user(&user, argp, sizeof(user)))
592 				return -EFAULT;
593 			user.auth_type = server->auth.auth_type;
594 			outl = user.object_name_len;
595 			user.object_name_len = server->auth.object_name_len;
596 			if (outl > user.object_name_len)
597 				outl = user.object_name_len;
598 			if (outl) {
599 				if (copy_to_user(compat_ptr(user.object_name),
600 						 server->auth.object_name,
601 						 outl)) return -EFAULT;
602 			}
603 			if (copy_to_user(argp, &user, sizeof(user)))
604 				return -EFAULT;
605 			return 0;
606 		}
607 #endif
608 
609 	case NCP_IOC_GETOBJECTNAME:
610 		if (uid != server->m.mounted_uid)
611 			return -EACCES;
612 		{
613 			struct ncp_objectname_ioctl user;
614 			size_t outl;
615 
616 			if (copy_from_user(&user, argp, sizeof(user)))
617 				return -EFAULT;
618 			user.auth_type = server->auth.auth_type;
619 			outl = user.object_name_len;
620 			user.object_name_len = server->auth.object_name_len;
621 			if (outl > user.object_name_len)
622 				outl = user.object_name_len;
623 			if (outl) {
624 				if (copy_to_user(user.object_name,
625 						 server->auth.object_name,
626 						 outl)) return -EFAULT;
627 			}
628 			if (copy_to_user(argp, &user, sizeof(user)))
629 				return -EFAULT;
630 			return 0;
631 		}
632 
633 #ifdef CONFIG_COMPAT
634 	case NCP_IOC_SETOBJECTNAME_32:
635 #endif
636 	case NCP_IOC_SETOBJECTNAME:
637 		if (uid != server->m.mounted_uid)
638 			return -EACCES;
639 		{
640 			struct ncp_objectname_ioctl user;
641 			void* newname;
642 			void* oldname;
643 			size_t oldnamelen;
644 			void* oldprivate;
645 			size_t oldprivatelen;
646 
647 #ifdef CONFIG_COMPAT
648 			if (cmd == NCP_IOC_SETOBJECTNAME_32) {
649 				struct compat_ncp_objectname_ioctl user32;
650 				if (copy_from_user(&user32, argp, sizeof(user32)))
651 					return -EFAULT;
652 				user.auth_type = user32.auth_type;
653 				user.object_name_len = user32.object_name_len;
654 				user.object_name = compat_ptr(user32.object_name);
655 			} else
656 #endif
657 			if (copy_from_user(&user, argp, sizeof(user)))
658 				return -EFAULT;
659 
660 			if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
661 				return -ENOMEM;
662 			if (user.object_name_len) {
663 				newname = kmalloc(user.object_name_len, GFP_USER);
664 				if (!newname)
665 					return -ENOMEM;
666 				if (copy_from_user(newname, user.object_name, user.object_name_len)) {
667 					kfree(newname);
668 					return -EFAULT;
669 				}
670 			} else {
671 				newname = NULL;
672 			}
673 			/* enter critical section */
674 			/* maybe that kfree can sleep so do that this way */
675 			/* it is at least more SMP friendly (in future...) */
676 			oldname = server->auth.object_name;
677 			oldnamelen = server->auth.object_name_len;
678 			oldprivate = server->priv.data;
679 			oldprivatelen = server->priv.len;
680 			server->auth.auth_type = user.auth_type;
681 			server->auth.object_name_len = user.object_name_len;
682 			server->auth.object_name = newname;
683 			server->priv.len = 0;
684 			server->priv.data = NULL;
685 			/* leave critical section */
686 			kfree(oldprivate);
687 			kfree(oldname);
688 			return 0;
689 		}
690 
691 #ifdef CONFIG_COMPAT
692 	case NCP_IOC_GETPRIVATEDATA_32:
693 #endif
694 	case NCP_IOC_GETPRIVATEDATA:
695 		if (uid != server->m.mounted_uid)
696 			return -EACCES;
697 		{
698 			struct ncp_privatedata_ioctl user;
699 			size_t outl;
700 
701 #ifdef CONFIG_COMPAT
702 			if (cmd == NCP_IOC_GETPRIVATEDATA_32) {
703 				struct compat_ncp_privatedata_ioctl user32;
704 				if (copy_from_user(&user32, argp, sizeof(user32)))
705 					return -EFAULT;
706 				user.len = user32.len;
707 				user.data = compat_ptr(user32.data);
708 			} else
709 #endif
710 			if (copy_from_user(&user, argp, sizeof(user)))
711 				return -EFAULT;
712 
713 			outl = user.len;
714 			user.len = server->priv.len;
715 			if (outl > user.len) outl = user.len;
716 			if (outl) {
717 				if (copy_to_user(user.data,
718 						 server->priv.data,
719 						 outl)) return -EFAULT;
720 			}
721 #ifdef CONFIG_COMPAT
722 			if (cmd == NCP_IOC_GETPRIVATEDATA_32) {
723 				struct compat_ncp_privatedata_ioctl user32;
724 				user32.len = user.len;
725 				user32.data = (unsigned long) user.data;
726 				if (copy_to_user(argp, &user32, sizeof(user32)))
727 					return -EFAULT;
728 			} else
729 #endif
730 			if (copy_to_user(argp, &user, sizeof(user)))
731 				return -EFAULT;
732 
733 			return 0;
734 		}
735 
736 #ifdef CONFIG_COMPAT
737 	case NCP_IOC_SETPRIVATEDATA_32:
738 #endif
739 	case NCP_IOC_SETPRIVATEDATA:
740 		if (uid != server->m.mounted_uid)
741 			return -EACCES;
742 		{
743 			struct ncp_privatedata_ioctl user;
744 			void* new;
745 			void* old;
746 			size_t oldlen;
747 
748 #ifdef CONFIG_COMPAT
749 			if (cmd == NCP_IOC_SETPRIVATEDATA_32) {
750 				struct compat_ncp_privatedata_ioctl user32;
751 				if (copy_from_user(&user32, argp, sizeof(user32)))
752 					return -EFAULT;
753 				user.len = user32.len;
754 				user.data = compat_ptr(user32.data);
755 			} else
756 #endif
757 			if (copy_from_user(&user, argp, sizeof(user)))
758 				return -EFAULT;
759 
760 			if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
761 				return -ENOMEM;
762 			if (user.len) {
763 				new = kmalloc(user.len, GFP_USER);
764 				if (!new)
765 					return -ENOMEM;
766 				if (copy_from_user(new, user.data, user.len)) {
767 					kfree(new);
768 					return -EFAULT;
769 				}
770 			} else {
771 				new = NULL;
772 			}
773 			/* enter critical section */
774 			old = server->priv.data;
775 			oldlen = server->priv.len;
776 			server->priv.len = user.len;
777 			server->priv.data = new;
778 			/* leave critical section */
779 			kfree(old);
780 			return 0;
781 		}
782 
783 #ifdef CONFIG_NCPFS_NLS
784 	case NCP_IOC_SETCHARSETS:
785 		return ncp_set_charsets(server, argp);
786 
787 	case NCP_IOC_GETCHARSETS:
788 		return ncp_get_charsets(server, argp);
789 
790 #endif /* CONFIG_NCPFS_NLS */
791 
792 	case NCP_IOC_SETDENTRYTTL:
793 		if (file_permission(filp, MAY_WRITE) != 0 &&
794 		    uid != server->m.mounted_uid)
795 			return -EACCES;
796 
797 		{
798 			u_int32_t user;
799 
800 			if (copy_from_user(&user, argp, sizeof(user)))
801 				return -EFAULT;
802 			/* 20 secs at most... */
803 			if (user > 20000)
804 				return -EINVAL;
805 			user = (user * HZ) / 1000;
806 			server->dentry_ttl = user;
807 			return 0;
808 		}
809 
810 	case NCP_IOC_GETDENTRYTTL:
811 		{
812 			u_int32_t user = (server->dentry_ttl * 1000) / HZ;
813 			if (copy_to_user(argp, &user, sizeof(user)))
814 				return -EFAULT;
815 			return 0;
816 		}
817 
818 	}
819 	return -EINVAL;
820 }
821 
ncp_ioctl_need_write(unsigned int cmd)822 static int ncp_ioctl_need_write(unsigned int cmd)
823 {
824 	switch (cmd) {
825 	case NCP_IOC_GET_FS_INFO:
826 	case NCP_IOC_GET_FS_INFO_V2:
827 	case NCP_IOC_NCPREQUEST:
828 	case NCP_IOC_SETDENTRYTTL:
829 	case NCP_IOC_SIGN_INIT:
830 	case NCP_IOC_LOCKUNLOCK:
831 	case NCP_IOC_SET_SIGN_WANTED:
832 		return 1;
833 	case NCP_IOC_GETOBJECTNAME:
834 	case NCP_IOC_SETOBJECTNAME:
835 	case NCP_IOC_GETPRIVATEDATA:
836 	case NCP_IOC_SETPRIVATEDATA:
837 	case NCP_IOC_SETCHARSETS:
838 	case NCP_IOC_GETCHARSETS:
839 	case NCP_IOC_CONN_LOGGED_IN:
840 	case NCP_IOC_GETDENTRYTTL:
841 	case NCP_IOC_GETMOUNTUID2:
842 	case NCP_IOC_SIGN_WANTED:
843 	case NCP_IOC_GETROOT:
844 	case NCP_IOC_SETROOT:
845 		return 0;
846 	default:
847 		/* unkown IOCTL command, assume write */
848 		return 1;
849 	}
850 }
851 
ncp_ioctl(struct inode * inode,struct file * filp,unsigned int cmd,unsigned long arg)852 int ncp_ioctl(struct inode *inode, struct file *filp,
853 	      unsigned int cmd, unsigned long arg)
854 {
855 	int ret;
856 
857 	if (ncp_ioctl_need_write(cmd)) {
858 		/*
859 		 * inside the ioctl(), any failures which
860 		 * are because of file_permission() are
861 		 * -EACCESS, so it seems consistent to keep
862 		 *  that here.
863 		 */
864 		if (mnt_want_write(filp->f_path.mnt))
865 			return -EACCES;
866 	}
867 	ret = __ncp_ioctl(inode, filp, cmd, arg);
868 	if (ncp_ioctl_need_write(cmd))
869 		mnt_drop_write(filp->f_path.mnt);
870 	return ret;
871 }
872 
873 #ifdef CONFIG_COMPAT
ncp_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)874 long ncp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
875 {
876 	struct inode *inode = file->f_path.dentry->d_inode;
877 	int ret;
878 
879 	lock_kernel();
880 	arg = (unsigned long) compat_ptr(arg);
881 	ret = ncp_ioctl(inode, file, cmd, arg);
882 	unlock_kernel();
883 	return ret;
884 }
885 #endif
886