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