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