ec8f45f12e058ff7ee73869b2c1a425ce6f45f16
[linux-2.6.git] / 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/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
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
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
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
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         unload_nls(oldset_cp);
227         unload_nls(oldset_io);
228
229         return 0;
230 }
231
232 static int
233 ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
234 {
235         struct ncp_nls_ioctl user;
236         int len;
237
238         memset(&user, 0, sizeof(user));
239         if (server->nls_vol && server->nls_vol->charset) {
240                 len = strlen(server->nls_vol->charset);
241                 if (len > NCP_IOCSNAME_LEN)
242                         len = NCP_IOCSNAME_LEN;
243                 strncpy(user.codepage, server->nls_vol->charset, len);
244                 user.codepage[len] = 0;
245         }
246
247         if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
248                 strcpy(user.iocharset, "utf8");
249         else if (server->nls_io && server->nls_io->charset) {
250                 len = strlen(server->nls_io->charset);
251                 if (len > NCP_IOCSNAME_LEN)
252                         len = NCP_IOCSNAME_LEN;
253                 strncpy(user.iocharset, server->nls_io->charset, len);
254                 user.iocharset[len] = 0;
255         }
256
257         if (copy_to_user(arg, &user, sizeof(user)))
258                 return -EFAULT;
259         return 0;
260 }
261 #endif /* CONFIG_NCPFS_NLS */
262
263 static int __ncp_ioctl(struct inode *inode, struct file *filp,
264               unsigned int cmd, unsigned long arg)
265 {
266         struct ncp_server *server = NCP_SERVER(inode);
267         int result;
268         struct ncp_ioctl_request request;
269         char* bouncebuffer;
270         void __user *argp = (void __user *)arg;
271         uid_t uid = current_uid();
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                     && 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                 /* FIXME: We hack around in the server's structures
310                    here to be able to use ncp_request */
311
312                 server->has_subfunction = 0;
313                 server->current_size = request.size;
314                 memcpy(server->packet, bouncebuffer, request.size);
315
316                 result = ncp_request2(server, request.function, 
317                         bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
318                 if (result < 0)
319                         result = -EIO;
320                 else
321                         result = server->reply_size;
322                 ncp_unlock_server(server);
323                 DPRINTK("ncp_ioctl: copy %d bytes\n",
324                         result);
325                 if (result >= 0)
326                         if (copy_to_user(request.data, bouncebuffer, result))
327                                 result = -EFAULT;
328                 vfree(bouncebuffer);
329                 return result;
330
331         case NCP_IOC_CONN_LOGGED_IN:
332
333                 if (!capable(CAP_SYS_ADMIN))
334                         return -EACCES;
335                 if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
336                         return -EINVAL;
337                 if (server->root_setuped)
338                         return -EBUSY;
339                 server->root_setuped = 1;
340                 return ncp_conn_logged_in(inode->i_sb);
341
342         case NCP_IOC_GET_FS_INFO:
343                 return ncp_get_fs_info(server, filp, argp);
344
345         case NCP_IOC_GET_FS_INFO_V2:
346                 return ncp_get_fs_info_v2(server, filp, argp);
347
348 #ifdef CONFIG_COMPAT
349         case NCP_IOC_GET_FS_INFO_V2_32:
350                 return ncp_get_compat_fs_info_v2(server, filp, argp);
351 #endif
352         /* we have too many combinations of CONFIG_COMPAT,
353          * CONFIG_64BIT and CONFIG_UID16, so just handle
354          * any of the possible ioctls */
355         case NCP_IOC_GETMOUNTUID16:
356         case NCP_IOC_GETMOUNTUID32:
357         case NCP_IOC_GETMOUNTUID64:
358                 if (file_permission(filp, MAY_READ) != 0
359                         && uid != server->m.mounted_uid)
360                         return -EACCES;
361
362                 if (cmd == NCP_IOC_GETMOUNTUID16) {
363                         u16 uid;
364                         SET_UID(uid, server->m.mounted_uid);
365                         if (put_user(uid, (u16 __user *)argp))
366                                 return -EFAULT;
367                 } else if (cmd == NCP_IOC_GETMOUNTUID32) {
368                         if (put_user(server->m.mounted_uid,
369                                                 (u32 __user *)argp))
370                                 return -EFAULT;
371                 } else {
372                         if (put_user(server->m.mounted_uid,
373                                                 (u64 __user *)argp))
374                                 return -EFAULT;
375                 }
376                 return 0;
377
378         case NCP_IOC_GETROOT:
379                 {
380                         struct ncp_setroot_ioctl sr;
381
382                         if (file_permission(filp, MAY_READ) != 0
383                             && uid != server->m.mounted_uid)
384                                 return -EACCES;
385
386                         if (server->m.mounted_vol[0]) {
387                                 struct dentry* dentry = inode->i_sb->s_root;
388
389                                 if (dentry) {
390                                         struct inode* s_inode = dentry->d_inode;
391                                 
392                                         if (s_inode) {
393                                                 sr.volNumber = NCP_FINFO(s_inode)->volNumber;
394                                                 sr.dirEntNum = NCP_FINFO(s_inode)->dirEntNum;
395                                                 sr.namespace = server->name_space[sr.volNumber];
396                                         } else
397                                                 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
398                                 } else
399                                         DPRINTK("ncpfs: s_root==NULL\n");
400                         } else {
401                                 sr.volNumber = -1;
402                                 sr.namespace = 0;
403                                 sr.dirEntNum = 0;
404                         }
405                         if (copy_to_user(argp, &sr, sizeof(sr)))
406                                 return -EFAULT;
407                         return 0;
408                 }
409
410         case NCP_IOC_SETROOT:
411                 {
412                         struct ncp_setroot_ioctl sr;
413                         __u32 vnum;
414                         __le32 de;
415                         __le32 dosde;
416                         struct dentry* dentry;
417
418                         if (!capable(CAP_SYS_ADMIN))
419                         {
420                                 return -EACCES;
421                         }
422                         if (server->root_setuped) return -EBUSY;
423                         if (copy_from_user(&sr, argp, sizeof(sr)))
424                                 return -EFAULT;
425                         if (sr.volNumber < 0) {
426                                 server->m.mounted_vol[0] = 0;
427                                 vnum = NCP_NUMBER_OF_VOLUMES;
428                                 de = 0;
429                                 dosde = 0;
430                         } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
431                                 return -EINVAL;
432                         } else if (ncp_mount_subdir(server, sr.volNumber,
433                                                 sr.namespace, sr.dirEntNum,
434                                                 &vnum, &de, &dosde)) {
435                                 return -ENOENT;
436                         }
437                         
438                         dentry = inode->i_sb->s_root;
439                         server->root_setuped = 1;
440                         if (dentry) {
441                                 struct inode* s_inode = dentry->d_inode;
442                                 
443                                 if (s_inode) {
444                                         NCP_FINFO(s_inode)->volNumber = vnum;
445                                         NCP_FINFO(s_inode)->dirEntNum = de;
446                                         NCP_FINFO(s_inode)->DosDirNum = dosde;
447                                 } else
448                                         DPRINTK("ncpfs: s_root->d_inode==NULL\n");
449                         } else
450                                 DPRINTK("ncpfs: s_root==NULL\n");
451
452                         return 0;
453                 }
454
455 #ifdef CONFIG_NCPFS_PACKET_SIGNING      
456         case NCP_IOC_SIGN_INIT:
457                 if (file_permission(filp, MAY_WRITE) != 0
458                     && uid != server->m.mounted_uid)
459                         return -EACCES;
460
461                 if (argp) {
462                         if (server->sign_wanted)
463                         {
464                                 struct ncp_sign_init sign;
465
466                                 if (copy_from_user(&sign, argp, sizeof(sign)))
467                                         return -EFAULT;
468                                 memcpy(server->sign_root,sign.sign_root,8);
469                                 memcpy(server->sign_last,sign.sign_last,16);
470                                 server->sign_active = 1;
471                         }
472                         /* ignore when signatures not wanted */
473                 } else {
474                         server->sign_active = 0;
475                 }
476                 return 0;               
477                 
478         case NCP_IOC_SIGN_WANTED:
479                 if (file_permission(filp, MAY_READ) != 0
480                     && uid != server->m.mounted_uid)
481                         return -EACCES;
482                 
483                 if (put_user(server->sign_wanted, (int __user *)argp))
484                         return -EFAULT;
485                 return 0;
486
487         case NCP_IOC_SET_SIGN_WANTED:
488                 {
489                         int newstate;
490
491                         if (file_permission(filp, MAY_WRITE) != 0
492                             && uid != server->m.mounted_uid)
493                                 return -EACCES;
494
495                         /* get only low 8 bits... */
496                         if (get_user(newstate, (unsigned char __user *)argp))
497                                 return -EFAULT;
498                         if (server->sign_active) {
499                                 /* cannot turn signatures OFF when active */
500                                 if (!newstate) return -EINVAL;
501                         } else {
502                                 server->sign_wanted = newstate != 0;
503                         }
504                         return 0;
505                 }
506
507 #endif /* CONFIG_NCPFS_PACKET_SIGNING */
508
509 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
510         case NCP_IOC_LOCKUNLOCK:
511                 if (file_permission(filp, MAY_WRITE) != 0
512                     && uid != server->m.mounted_uid)
513                         return -EACCES;
514
515                 {
516                         struct ncp_lock_ioctl    rqdata;
517
518                         if (copy_from_user(&rqdata, argp, sizeof(rqdata)))
519                                 return -EFAULT;
520                         if (rqdata.origin != 0)
521                                 return -EINVAL;
522                         /* check for cmd */
523                         switch (rqdata.cmd) {
524                                 case NCP_LOCK_EX:
525                                 case NCP_LOCK_SH:
526                                                 if (rqdata.timeout == 0)
527                                                         rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
528                                                 else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
529                                                         rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
530                                                 break;
531                                 case NCP_LOCK_LOG:
532                                                 rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;      /* has no effect */
533                                 case NCP_LOCK_CLEAR:
534                                                 break;
535                                 default:
536                                                 return -EINVAL;
537                         }
538                         /* locking needs both read and write access */
539                         if ((result = ncp_make_open(inode, O_RDWR)) != 0)
540                         {
541                                 return result;
542                         }
543                         result = -EIO;
544                         if (!ncp_conn_valid(server))
545                                 goto outrel;
546                         result = -EISDIR;
547                         if (!S_ISREG(inode->i_mode))
548                                 goto outrel;
549                         if (rqdata.cmd == NCP_LOCK_CLEAR)
550                         {
551                                 result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
552                                                         NCP_FINFO(inode)->file_handle, 
553                                                         rqdata.offset,
554                                                         rqdata.length);
555                                 if (result > 0) result = 0;     /* no such lock */
556                         }
557                         else
558                         {
559                                 int lockcmd;
560
561                                 switch (rqdata.cmd)
562                                 {
563                                         case NCP_LOCK_EX:  lockcmd=1; break;
564                                         case NCP_LOCK_SH:  lockcmd=3; break;
565                                         default:           lockcmd=0; break;
566                                 }
567                                 result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
568                                                         NCP_FINFO(inode)->file_handle,
569                                                         lockcmd,
570                                                         rqdata.offset,
571                                                         rqdata.length,
572                                                         rqdata.timeout);
573                                 if (result > 0) result = -EAGAIN;
574                         }
575 outrel:                 
576                         ncp_inode_close(inode);
577                         return result;
578                 }
579 #endif  /* CONFIG_NCPFS_IOCTL_LOCKING */
580
581 #ifdef CONFIG_COMPAT
582         case NCP_IOC_GETOBJECTNAME_32:
583                 if (uid != server->m.mounted_uid)
584                         return -EACCES;
585                 {
586                         struct compat_ncp_objectname_ioctl user;
587                         size_t outl;
588
589                         if (copy_from_user(&user, argp, sizeof(user)))
590                                 return -EFAULT;
591                         user.auth_type = server->auth.auth_type;
592                         outl = user.object_name_len;
593                         user.object_name_len = server->auth.object_name_len;
594                         if (outl > user.object_name_len)
595                                 outl = user.object_name_len;
596                         if (outl) {
597                                 if (copy_to_user(compat_ptr(user.object_name),
598                                                  server->auth.object_name,
599                                                  outl)) return -EFAULT;
600                         }
601                         if (copy_to_user(argp, &user, sizeof(user)))
602                                 return -EFAULT;
603                         return 0;
604                 }
605 #endif
606
607         case NCP_IOC_GETOBJECTNAME:
608                 if (uid != server->m.mounted_uid)
609                         return -EACCES;
610                 {
611                         struct ncp_objectname_ioctl user;
612                         size_t outl;
613
614                         if (copy_from_user(&user, argp, sizeof(user)))
615                                 return -EFAULT;
616                         user.auth_type = server->auth.auth_type;
617                         outl = user.object_name_len;
618                         user.object_name_len = server->auth.object_name_len;
619                         if (outl > user.object_name_len)
620                                 outl = user.object_name_len;
621                         if (outl) {
622                                 if (copy_to_user(user.object_name,
623                                                  server->auth.object_name,
624                                                  outl)) return -EFAULT;
625                         }
626                         if (copy_to_user(argp, &user, sizeof(user)))
627                                 return -EFAULT;
628                         return 0;
629                 }
630
631 #ifdef CONFIG_COMPAT
632         case NCP_IOC_SETOBJECTNAME_32:
633 #endif
634         case NCP_IOC_SETOBJECTNAME:
635                 if (uid != server->m.mounted_uid)
636                         return -EACCES;
637                 {
638                         struct ncp_objectname_ioctl user;
639                         void* newname;
640                         void* oldname;
641                         size_t oldnamelen;
642                         void* oldprivate;
643                         size_t oldprivatelen;
644
645 #ifdef CONFIG_COMPAT
646                         if (cmd == NCP_IOC_SETOBJECTNAME_32) {
647                                 struct compat_ncp_objectname_ioctl user32;
648                                 if (copy_from_user(&user32, argp, sizeof(user32)))
649                                         return -EFAULT;
650                                 user.auth_type = user32.auth_type;
651                                 user.object_name_len = user32.object_name_len;
652                                 user.object_name = compat_ptr(user32.object_name);
653                         } else
654 #endif
655                         if (copy_from_user(&user, argp, sizeof(user)))
656                                 return -EFAULT;
657
658                         if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
659                                 return -ENOMEM;
660                         if (user.object_name_len) {
661                                 newname = memdup_user(user.object_name,
662                                                       user.object_name_len);
663                                 if (IS_ERR(newname))
664                                         return PTR_ERR(newname);
665                         } else {
666                                 newname = NULL;
667                         }
668                         /* enter critical section */
669                         /* maybe that kfree can sleep so do that this way */
670                         /* it is at least more SMP friendly (in future...) */
671                         oldname = server->auth.object_name;
672                         oldnamelen = server->auth.object_name_len;
673                         oldprivate = server->priv.data;
674                         oldprivatelen = server->priv.len;
675                         server->auth.auth_type = user.auth_type;
676                         server->auth.object_name_len = user.object_name_len;
677                         server->auth.object_name = newname;
678                         server->priv.len = 0;
679                         server->priv.data = NULL;
680                         /* leave critical section */
681                         kfree(oldprivate);
682                         kfree(oldname);
683                         return 0;
684                 }
685
686 #ifdef CONFIG_COMPAT
687         case NCP_IOC_GETPRIVATEDATA_32:
688 #endif
689         case NCP_IOC_GETPRIVATEDATA:
690                 if (uid != server->m.mounted_uid)
691                         return -EACCES;
692                 {
693                         struct ncp_privatedata_ioctl user;
694                         size_t outl;
695
696 #ifdef CONFIG_COMPAT
697                         if (cmd == NCP_IOC_GETPRIVATEDATA_32) {
698                                 struct compat_ncp_privatedata_ioctl user32;
699                                 if (copy_from_user(&user32, argp, sizeof(user32)))
700                                         return -EFAULT;
701                                 user.len = user32.len;
702                                 user.data = compat_ptr(user32.data);
703                         } else
704 #endif
705                         if (copy_from_user(&user, argp, sizeof(user)))
706                                 return -EFAULT;
707
708                         outl = user.len;
709                         user.len = server->priv.len;
710                         if (outl > user.len) outl = user.len;
711                         if (outl) {
712                                 if (copy_to_user(user.data,
713                                                  server->priv.data,
714                                                  outl)) return -EFAULT;
715                         }
716 #ifdef CONFIG_COMPAT
717                         if (cmd == NCP_IOC_GETPRIVATEDATA_32) {
718                                 struct compat_ncp_privatedata_ioctl user32;
719                                 user32.len = user.len;
720                                 user32.data = (unsigned long) user.data;
721                                 if (copy_to_user(argp, &user32, sizeof(user32)))
722                                         return -EFAULT;
723                         } else
724 #endif
725                         if (copy_to_user(argp, &user, sizeof(user)))
726                                 return -EFAULT;
727
728                         return 0;
729                 }
730
731 #ifdef CONFIG_COMPAT
732         case NCP_IOC_SETPRIVATEDATA_32:
733 #endif
734         case NCP_IOC_SETPRIVATEDATA:
735                 if (uid != server->m.mounted_uid)
736                         return -EACCES;
737                 {
738                         struct ncp_privatedata_ioctl user;
739                         void* new;
740                         void* old;
741                         size_t oldlen;
742
743 #ifdef CONFIG_COMPAT
744                         if (cmd == NCP_IOC_SETPRIVATEDATA_32) {
745                                 struct compat_ncp_privatedata_ioctl user32;
746                                 if (copy_from_user(&user32, argp, sizeof(user32)))
747                                         return -EFAULT;
748                                 user.len = user32.len;
749                                 user.data = compat_ptr(user32.data);
750                         } else
751 #endif
752                         if (copy_from_user(&user, argp, sizeof(user)))
753                                 return -EFAULT;
754
755                         if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
756                                 return -ENOMEM;
757                         if (user.len) {
758                                 new = memdup_user(user.data, user.len);
759                                 if (IS_ERR(new))
760                                         return PTR_ERR(new);
761                         } else {
762                                 new = NULL;
763                         }
764                         /* enter critical section */
765                         old = server->priv.data;
766                         oldlen = server->priv.len;
767                         server->priv.len = user.len;
768                         server->priv.data = new;
769                         /* leave critical section */
770                         kfree(old);
771                         return 0;
772                 }
773
774 #ifdef CONFIG_NCPFS_NLS
775         case NCP_IOC_SETCHARSETS:
776                 return ncp_set_charsets(server, argp);
777                 
778         case NCP_IOC_GETCHARSETS:
779                 return ncp_get_charsets(server, argp);
780
781 #endif /* CONFIG_NCPFS_NLS */
782
783         case NCP_IOC_SETDENTRYTTL:
784                 if (file_permission(filp, MAY_WRITE) != 0 &&
785                     uid != server->m.mounted_uid)
786                         return -EACCES;
787
788                 {
789                         u_int32_t user;
790
791                         if (copy_from_user(&user, argp, sizeof(user)))
792                                 return -EFAULT;
793                         /* 20 secs at most... */
794                         if (user > 20000)
795                                 return -EINVAL;
796                         user = (user * HZ) / 1000;
797                         server->dentry_ttl = user;
798                         return 0;
799                 }
800                 
801         case NCP_IOC_GETDENTRYTTL:
802                 {
803                         u_int32_t user = (server->dentry_ttl * 1000) / HZ;
804                         if (copy_to_user(argp, &user, sizeof(user)))
805                                 return -EFAULT;
806                         return 0;
807                 }
808
809         }
810         return -EINVAL;
811 }
812
813 static int ncp_ioctl_need_write(unsigned int cmd)
814 {
815         switch (cmd) {
816         case NCP_IOC_GET_FS_INFO:
817         case NCP_IOC_GET_FS_INFO_V2:
818         case NCP_IOC_NCPREQUEST:
819         case NCP_IOC_SETDENTRYTTL:
820         case NCP_IOC_SIGN_INIT:
821         case NCP_IOC_LOCKUNLOCK:
822         case NCP_IOC_SET_SIGN_WANTED:
823                 return 1;
824         case NCP_IOC_GETOBJECTNAME:
825         case NCP_IOC_SETOBJECTNAME:
826         case NCP_IOC_GETPRIVATEDATA:
827         case NCP_IOC_SETPRIVATEDATA:
828         case NCP_IOC_SETCHARSETS:
829         case NCP_IOC_GETCHARSETS:
830         case NCP_IOC_CONN_LOGGED_IN:
831         case NCP_IOC_GETDENTRYTTL:
832         case NCP_IOC_GETMOUNTUID2:
833         case NCP_IOC_SIGN_WANTED:
834         case NCP_IOC_GETROOT:
835         case NCP_IOC_SETROOT:
836                 return 0;
837         default:
838                 /* unknown IOCTL command, assume write */
839                 return 1;
840         }
841 }
842
843 int ncp_ioctl(struct inode *inode, struct file *filp,
844               unsigned int cmd, unsigned long arg)
845 {
846         int ret;
847
848         if (ncp_ioctl_need_write(cmd)) {
849                 /*
850                  * inside the ioctl(), any failures which
851                  * are because of file_permission() are
852                  * -EACCESS, so it seems consistent to keep
853                  *  that here.
854                  */
855                 if (mnt_want_write(filp->f_path.mnt))
856                         return -EACCES;
857         }
858         ret = __ncp_ioctl(inode, filp, cmd, arg);
859         if (ncp_ioctl_need_write(cmd))
860                 mnt_drop_write(filp->f_path.mnt);
861         return ret;
862 }
863
864 #ifdef CONFIG_COMPAT
865 long ncp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
866 {
867         struct inode *inode = file->f_path.dentry->d_inode;
868         int ret;
869
870         lock_kernel();
871         arg = (unsigned long) compat_ptr(arg);
872         ret = ncp_ioctl(inode, file, cmd, arg);
873         unlock_kernel();
874         return ret;
875 }
876 #endif