- pre4:
[davej-history.git] / fs / ncpfs / ioctl.c
blob4df91abe3a3523149a025d4df933e92a5e92d800
1 /*
2 * ioctl.c
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
8 */
10 #include <linux/config.h>
12 #include <asm/uaccess.h>
13 #include <linux/errno.h>
14 #include <linux/fs.h>
15 #include <linux/ioctl.h>
16 #include <linux/sched.h>
17 #include <linux/mm.h>
18 #include <linux/highuid.h>
19 #include <linux/vmalloc.h>
21 #include <linux/ncp_fs.h>
23 #include "ncplib_kernel.h"
25 /* maximum limit for ncp_objectname_ioctl */
26 #define NCP_OBJECT_NAME_MAX_LEN 4096
27 /* maximum limit for ncp_privatedata_ioctl */
28 #define NCP_PRIVATE_DATA_MAX_LEN 8192
29 /* maximum negotiable packet size */
30 #define NCP_PACKET_SIZE_INTERNAL 65536
32 int ncp_ioctl(struct inode *inode, struct file *filp,
33 unsigned int cmd, unsigned long arg)
35 struct ncp_server *server = NCP_SERVER(inode);
36 int result;
37 struct ncp_ioctl_request request;
38 char* bouncebuffer;
40 switch (cmd) {
41 case NCP_IOC_NCPREQUEST:
43 if ((permission(inode, MAY_WRITE) != 0)
44 && (current->uid != server->m.mounted_uid)) {
45 return -EACCES;
47 if (copy_from_user(&request, (struct ncp_ioctl_request *) arg,
48 sizeof(request)))
49 return -EFAULT;
51 if ((request.function > 255)
52 || (request.size >
53 NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
54 return -EINVAL;
56 bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL);
57 if (!bouncebuffer)
58 return -ENOMEM;
59 if (copy_from_user(bouncebuffer, request.data, request.size)) {
60 vfree(bouncebuffer);
61 return -EFAULT;
63 ncp_lock_server(server);
65 /* FIXME: We hack around in the server's structures
66 here to be able to use ncp_request */
68 server->has_subfunction = 0;
69 server->current_size = request.size;
70 memcpy(server->packet, bouncebuffer, request.size);
72 result = ncp_request2(server, request.function,
73 bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
74 if (result < 0)
75 result = -EIO;
76 else
77 result = server->reply_size;
78 ncp_unlock_server(server);
79 DPRINTK("ncp_ioctl: copy %d bytes\n",
80 result);
81 if (result >= 0)
82 if (copy_to_user(request.data, bouncebuffer, result))
83 result = -EFAULT;
84 vfree(bouncebuffer);
85 return result;
87 case NCP_IOC_CONN_LOGGED_IN:
89 if ((permission(inode, MAY_WRITE) != 0)
90 && (current->uid != server->m.mounted_uid)) {
91 return -EACCES;
93 if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
94 return -EINVAL;
95 if (server->root_setuped)
96 return -EBUSY;
97 server->root_setuped = 1;
98 return ncp_conn_logged_in(inode->i_sb);
100 case NCP_IOC_GET_FS_INFO:
102 struct ncp_fs_info info;
104 if ((permission(inode, MAY_WRITE) != 0)
105 && (current->uid != server->m.mounted_uid)) {
106 return -EACCES;
108 if (copy_from_user(&info, (struct ncp_fs_info *) arg,
109 sizeof(info)))
110 return -EFAULT;
112 if (info.version != NCP_GET_FS_INFO_VERSION) {
113 DPRINTK("info.version invalid: %d\n", info.version);
114 return -EINVAL;
116 /* TODO: info.addr = server->m.serv_addr; */
117 info.mounted_uid = NEW_TO_OLD_UID(server->m.mounted_uid);
118 info.connection = server->connection;
119 info.buffer_size = server->buffer_size;
120 info.volume_number = NCP_FINFO(inode)->volNumber;
121 info.directory_id = NCP_FINFO(inode)->DosDirNum;
123 if (copy_to_user((struct ncp_fs_info *) arg, &info,
124 sizeof(info))) return -EFAULT;
125 return 0;
128 case NCP_IOC_GET_FS_INFO_V2:
130 struct ncp_fs_info_v2 info2;
132 if ((permission(inode, MAY_WRITE) != 0)
133 && (current->uid != server->m.mounted_uid)) {
134 return -EACCES;
136 if (copy_from_user(&info2, (struct ncp_fs_info_v2 *) arg,
137 sizeof(info2)))
138 return -EFAULT;
140 if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
141 DPRINTK("info.version invalid: %d\n", info2.version);
142 return -EINVAL;
144 info2.mounted_uid = server->m.mounted_uid;
145 info2.connection = server->connection;
146 info2.buffer_size = server->buffer_size;
147 info2.volume_number = NCP_FINFO(inode)->volNumber;
148 info2.directory_id = NCP_FINFO(inode)->DosDirNum;
149 info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
151 if (copy_to_user((struct ncp_fs_info_v2 *) arg, &info2,
152 sizeof(info2))) return -EFAULT;
153 return 0;
156 case NCP_IOC_GETMOUNTUID2:
158 unsigned long tmp = server->m.mounted_uid;
160 if ( (permission(inode, MAY_READ) != 0)
161 && (current->uid != server->m.mounted_uid))
163 return -EACCES;
165 if (put_user(tmp, (unsigned long*) arg))
166 return -EFAULT;
167 return 0;
170 #ifdef CONFIG_NCPFS_MOUNT_SUBDIR
171 case NCP_IOC_GETROOT:
173 struct ncp_setroot_ioctl sr;
175 if ( (permission(inode, MAY_READ) != 0)
176 && (current->uid != server->m.mounted_uid))
178 return -EACCES;
180 if (server->m.mounted_vol[0]) {
181 struct dentry* dentry = inode->i_sb->s_root;
183 if (dentry) {
184 struct inode* inode = dentry->d_inode;
186 if (inode) {
187 sr.volNumber = NCP_FINFO(inode)->volNumber;
188 sr.dirEntNum = NCP_FINFO(inode)->dirEntNum;
189 sr.namespace = server->name_space[sr.volNumber];
190 } else
191 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
192 } else
193 DPRINTK("ncpfs: s_root==NULL\n");
194 } else {
195 sr.volNumber = -1;
196 sr.namespace = 0;
197 sr.dirEntNum = 0;
199 if (copy_to_user((struct ncp_setroot_ioctl*)arg,
200 &sr,
201 sizeof(sr))) return -EFAULT;
202 return 0;
204 case NCP_IOC_SETROOT:
206 struct ncp_setroot_ioctl sr;
207 struct nw_info_struct i;
208 struct dentry* dentry;
210 if ( (permission(inode, MAY_WRITE) != 0)
211 && (current->uid != server->m.mounted_uid))
213 return -EACCES;
215 if (server->root_setuped) return -EBUSY;
216 if (copy_from_user(&sr,
217 (struct ncp_setroot_ioctl*)arg,
218 sizeof(sr))) return -EFAULT;
219 if (sr.volNumber < 0) {
220 server->m.mounted_vol[0] = 0;
221 i.volNumber = NCP_NUMBER_OF_VOLUMES + 1;
222 i.dirEntNum = 0;
223 i.DosDirNum = 0;
224 } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
225 return -EINVAL;
226 } else
227 if (ncp_mount_subdir(server, &i, sr.volNumber,
228 sr.namespace, sr.dirEntNum))
229 return -ENOENT;
231 dentry = inode->i_sb->s_root;
232 server->root_setuped = 1;
233 if (dentry) {
234 struct inode* inode = dentry->d_inode;
236 if (inode) {
237 NCP_FINFO(inode)->volNumber = i.volNumber;
238 NCP_FINFO(inode)->dirEntNum = i.dirEntNum;
239 NCP_FINFO(inode)->DosDirNum = i.DosDirNum;
240 } else
241 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
242 } else
243 DPRINTK("ncpfs: s_root==NULL\n");
245 return 0;
247 #endif /* CONFIG_NCPFS_MOUNT_SUBDIR */
249 #ifdef CONFIG_NCPFS_PACKET_SIGNING
250 case NCP_IOC_SIGN_INIT:
251 if ((permission(inode, MAY_WRITE) != 0)
252 && (current->uid != server->m.mounted_uid))
254 return -EACCES;
256 if (arg) {
257 if (server->sign_wanted)
259 struct ncp_sign_init sign;
261 if (copy_from_user(&sign, (struct ncp_sign_init *) arg,
262 sizeof(sign))) return -EFAULT;
263 memcpy(server->sign_root,sign.sign_root,8);
264 memcpy(server->sign_last,sign.sign_last,16);
265 server->sign_active = 1;
267 /* ignore when signatures not wanted */
268 } else {
269 server->sign_active = 0;
271 return 0;
273 case NCP_IOC_SIGN_WANTED:
274 if ( (permission(inode, MAY_READ) != 0)
275 && (current->uid != server->m.mounted_uid))
277 return -EACCES;
280 if (put_user(server->sign_wanted, (int*) arg))
281 return -EFAULT;
282 return 0;
283 case NCP_IOC_SET_SIGN_WANTED:
285 int newstate;
287 if ( (permission(inode, MAY_WRITE) != 0)
288 && (current->uid != server->m.mounted_uid))
290 return -EACCES;
292 /* get only low 8 bits... */
293 if (get_user(newstate, (unsigned char *) arg))
294 return -EFAULT;
295 if (server->sign_active) {
296 /* cannot turn signatures OFF when active */
297 if (!newstate) return -EINVAL;
298 } else {
299 server->sign_wanted = newstate != 0;
301 return 0;
304 #endif /* CONFIG_NCPFS_PACKET_SIGNING */
306 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
307 case NCP_IOC_LOCKUNLOCK:
308 if ( (permission(inode, MAY_WRITE) != 0)
309 && (current->uid != server->m.mounted_uid))
311 return -EACCES;
314 struct ncp_lock_ioctl rqdata;
315 int result;
317 if (copy_from_user(&rqdata, (struct ncp_lock_ioctl*)arg,
318 sizeof(rqdata))) return -EFAULT;
319 if (rqdata.origin != 0)
320 return -EINVAL;
321 /* check for cmd */
322 switch (rqdata.cmd) {
323 case NCP_LOCK_EX:
324 case NCP_LOCK_SH:
325 if (rqdata.timeout == 0)
326 rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
327 else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
328 rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
329 break;
330 case NCP_LOCK_LOG:
331 rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */
332 case NCP_LOCK_CLEAR:
333 break;
334 default:
335 return -EINVAL;
337 /* locking needs both read and write access */
338 if ((result = ncp_make_open(inode, O_RDWR)) != 0)
340 return result;
342 result = -EIO;
343 if (!ncp_conn_valid(server))
344 goto outrel;
345 result = -EISDIR;
346 if (!S_ISREG(inode->i_mode))
347 goto outrel;
348 if (rqdata.cmd == NCP_LOCK_CLEAR)
350 result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
351 NCP_FINFO(inode)->file_handle,
352 rqdata.offset,
353 rqdata.length);
354 if (result > 0) result = 0; /* no such lock */
356 else
358 int lockcmd;
360 switch (rqdata.cmd)
362 case NCP_LOCK_EX: lockcmd=1; break;
363 case NCP_LOCK_SH: lockcmd=3; break;
364 default: lockcmd=0; break;
366 result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
367 NCP_FINFO(inode)->file_handle,
368 lockcmd,
369 rqdata.offset,
370 rqdata.length,
371 rqdata.timeout);
372 if (result > 0) result = -EAGAIN;
374 outrel:
375 ncp_inode_close(inode);
376 return result;
378 #endif /* CONFIG_NCPFS_IOCTL_LOCKING */
380 #ifdef CONFIG_NCPFS_NDS_DOMAINS
381 case NCP_IOC_GETOBJECTNAME:
382 if (current->uid != server->m.mounted_uid) {
383 return -EACCES;
386 struct ncp_objectname_ioctl user;
387 int outl;
389 if (copy_from_user(&user,
390 (struct ncp_objectname_ioctl*)arg,
391 sizeof(user))) return -EFAULT;
392 user.auth_type = server->auth.auth_type;
393 outl = user.object_name_len;
394 user.object_name_len = server->auth.object_name_len;
395 if (outl > user.object_name_len)
396 outl = user.object_name_len;
397 if (outl) {
398 if (copy_to_user(user.object_name,
399 server->auth.object_name,
400 outl)) return -EFAULT;
402 if (copy_to_user((struct ncp_objectname_ioctl*)arg,
403 &user,
404 sizeof(user))) return -EFAULT;
405 return 0;
407 case NCP_IOC_SETOBJECTNAME:
408 if (current->uid != server->m.mounted_uid) {
409 return -EACCES;
412 struct ncp_objectname_ioctl user;
413 void* newname;
414 void* oldname;
415 size_t oldnamelen;
416 void* oldprivate;
417 size_t oldprivatelen;
419 if (copy_from_user(&user,
420 (struct ncp_objectname_ioctl*)arg,
421 sizeof(user))) return -EFAULT;
422 if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
423 return -ENOMEM;
424 if (user.object_name_len) {
425 newname = ncp_kmalloc(user.object_name_len, GFP_USER);
426 if (!newname) return -ENOMEM;
427 if (copy_from_user(newname, user.object_name, sizeof(user))) {
428 ncp_kfree_s(newname, user.object_name_len);
429 return -EFAULT;
431 } else {
432 newname = NULL;
434 /* enter critical section */
435 /* maybe that kfree can sleep so do that this way */
436 /* it is at least more SMP friendly (in future...) */
437 oldname = server->auth.object_name;
438 oldnamelen = server->auth.object_name_len;
439 oldprivate = server->priv.data;
440 oldprivatelen = server->priv.len;
441 server->auth.auth_type = user.auth_type;
442 server->auth.object_name_len = user.object_name_len;
443 server->auth.object_name = user.object_name;
444 server->priv.len = 0;
445 server->priv.data = NULL;
446 /* leave critical section */
447 if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
448 if (oldname) ncp_kfree_s(oldname, oldnamelen);
449 return 0;
451 case NCP_IOC_GETPRIVATEDATA:
452 if (current->uid != server->m.mounted_uid) {
453 return -EACCES;
456 struct ncp_privatedata_ioctl user;
457 int outl;
459 if (copy_from_user(&user,
460 (struct ncp_privatedata_ioctl*)arg,
461 sizeof(user))) return -EFAULT;
462 outl = user.len;
463 user.len = server->priv.len;
464 if (outl > user.len) outl = user.len;
465 if (outl) {
466 if (copy_to_user(user.data,
467 server->priv.data,
468 outl)) return -EFAULT;
470 if (copy_to_user((struct ncp_privatedata_ioctl*)arg,
471 &user,
472 sizeof(user))) return -EFAULT;
473 return 0;
475 case NCP_IOC_SETPRIVATEDATA:
476 if (current->uid != server->m.mounted_uid) {
477 return -EACCES;
480 struct ncp_privatedata_ioctl user;
481 void* new;
482 void* old;
483 size_t oldlen;
485 if (copy_from_user(&user,
486 (struct ncp_privatedata_ioctl*)arg,
487 sizeof(user))) return -EFAULT;
488 if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
489 return -ENOMEM;
490 if (user.len) {
491 new = ncp_kmalloc(user.len, GFP_USER);
492 if (!new) return -ENOMEM;
493 if (copy_from_user(new, user.data, user.len)) {
494 ncp_kfree_s(new, user.len);
495 return -EFAULT;
497 } else {
498 new = NULL;
500 /* enter critical section */
501 old = server->priv.data;
502 oldlen = server->priv.len;
503 server->priv.len = user.len;
504 server->priv.data = new;
505 /* leave critical section */
506 if (old) ncp_kfree_s(old, oldlen);
507 return 0;
509 #endif /* CONFIG_NCPFS_NDS_DOMAINS */
511 #ifdef CONFIG_NCPFS_NLS
512 /* Here we are select the iocharset and the codepage for NLS.
513 * Thanks Petr Vandrovec for idea and many hints.
515 case NCP_IOC_SETCHARSETS:
516 if ((permission(inode, MAY_WRITE) != 0) &&
517 (current->uid != server->m.mounted_uid))
518 return -EACCES;
519 if (server->root_setuped)
520 return -EBUSY;
523 struct ncp_nls_ioctl user;
524 struct nls_table *codepage;
525 struct nls_table *iocharset;
526 struct nls_table *oldset_io;
527 struct nls_table *oldset_cp;
529 if (copy_from_user(&user, (struct ncp_nls_ioctl*)arg,
530 sizeof(user)))
531 return -EFAULT;
533 codepage = NULL;
534 user.codepage[NCP_IOCSNAME_LEN] = 0;
535 if (!user.codepage[0] ||
536 !strcmp(user.codepage, "default"))
537 codepage = load_nls_default();
538 else {
539 codepage = load_nls(user.codepage);
540 if (!codepage) {
541 return -EBADRQC;
545 iocharset = NULL;
546 user.iocharset[NCP_IOCSNAME_LEN] = 0;
547 if (!user.iocharset[0] ||
548 !strcmp(user.iocharset, "default")) {
549 iocharset = load_nls_default();
550 NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
551 } else {
552 if (!strcmp(user.iocharset, "utf8")) {
553 iocharset = load_nls_default();
554 NCP_SET_FLAG(server, NCP_FLAG_UTF8);
555 } else {
556 iocharset = load_nls(user.iocharset);
557 if (!iocharset) {
558 unload_nls(codepage);
559 return -EBADRQC;
561 NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
565 oldset_cp = server->nls_vol;
566 server->nls_vol = codepage;
567 oldset_io = server->nls_io;
568 server->nls_io = iocharset;
570 if (oldset_cp)
571 unload_nls(oldset_cp);
572 if (oldset_io)
573 unload_nls(oldset_io);
575 return 0;
578 case NCP_IOC_GETCHARSETS: /* not tested */
580 struct ncp_nls_ioctl user;
581 int len;
583 memset(&user, 0, sizeof(user));
584 if (server->nls_vol && server->nls_vol->charset) {
585 len = strlen(server->nls_vol->charset);
586 if (len > NCP_IOCSNAME_LEN)
587 len = NCP_IOCSNAME_LEN;
588 strncpy(user.codepage,
589 server->nls_vol->charset, len);
590 user.codepage[len] = 0;
593 if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
594 strcpy(user.iocharset, "utf8");
595 else
596 if (server->nls_io && server->nls_io->charset) {
597 len = strlen(server->nls_io->charset);
598 if (len > NCP_IOCSNAME_LEN)
599 len = NCP_IOCSNAME_LEN;
600 strncpy(user.iocharset,
601 server->nls_io->charset, len);
602 user.iocharset[len] = 0;
605 if (copy_to_user((struct ncp_nls_ioctl*)arg, &user,
606 sizeof(user)))
607 return -EFAULT;
609 return 0;
611 #endif /* CONFIG_NCPFS_NLS */
612 case NCP_IOC_SETDENTRYTTL:
613 if ((permission(inode, MAY_WRITE) != 0) &&
614 (current->uid != server->m.mounted_uid))
615 return -EACCES;
617 u_int32_t user;
619 if (copy_from_user(&user, (u_int32_t*)arg, sizeof(user)))
620 return -EFAULT;
621 /* 20 secs at most... */
622 if (user > 20000)
623 return -EINVAL;
624 user = (user * HZ) / 1000;
625 server->dentry_ttl = user;
626 return 0;
629 case NCP_IOC_GETDENTRYTTL:
631 u_int32_t user = (server->dentry_ttl * 1000) / HZ;
632 if (copy_to_user((u_int32_t*)arg, &user, sizeof(user)))
633 return -EFAULT;
634 return 0;
638 /* #ifdef CONFIG_UID16 */
639 /* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2,
640 so we have this out of switch */
641 if (cmd == NCP_IOC_GETMOUNTUID) {
642 if ((permission(inode, MAY_READ) != 0)
643 && (current->uid != server->m.mounted_uid)) {
644 return -EACCES;
646 if (put_user(NEW_TO_OLD_UID(server->m.mounted_uid), (__kernel_uid_t *) arg))
647 return -EFAULT;
648 return 0;
650 /* #endif */
651 return -EINVAL;