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/config.h>
11
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>
20
21 #include <linux/ncp_fs.h>
22
23 #include "ncplib_kernel.h"
24
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
31
ncp_ioctl(struct inode * inode,struct file * filp,unsigned int cmd,unsigned long arg)32 int ncp_ioctl(struct inode *inode, struct file *filp,
33 unsigned int cmd, unsigned long arg)
34 {
35 struct ncp_server *server = NCP_SERVER(inode);
36 int result;
37 struct ncp_ioctl_request request;
38 char* bouncebuffer;
39
40 switch (cmd) {
41 case NCP_IOC_NCPREQUEST:
42
43 if ((permission(inode, MAY_WRITE) != 0)
44 && (current->uid != server->m.mounted_uid)) {
45 return -EACCES;
46 }
47 if (copy_from_user(&request, (struct ncp_ioctl_request *) arg,
48 sizeof(request)))
49 return -EFAULT;
50
51 if ((request.function > 255)
52 || (request.size >
53 NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
54 return -EINVAL;
55 }
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;
62 }
63 ncp_lock_server(server);
64
65 /* FIXME: We hack around in the server's structures
66 here to be able to use ncp_request */
67
68 server->has_subfunction = 0;
69 server->current_size = request.size;
70 memcpy(server->packet, bouncebuffer, request.size);
71
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;
86
87 case NCP_IOC_CONN_LOGGED_IN:
88
89 if (!capable(CAP_SYS_ADMIN))
90 return -EACCES;
91 if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
92 return -EINVAL;
93 if (server->root_setuped)
94 return -EBUSY;
95 server->root_setuped = 1;
96 return ncp_conn_logged_in(inode->i_sb);
97
98 case NCP_IOC_GET_FS_INFO:
99 {
100 struct ncp_fs_info info;
101
102 if ((permission(inode, MAY_WRITE) != 0)
103 && (current->uid != server->m.mounted_uid)) {
104 return -EACCES;
105 }
106 if (copy_from_user(&info, (struct ncp_fs_info *) arg,
107 sizeof(info)))
108 return -EFAULT;
109
110 if (info.version != NCP_GET_FS_INFO_VERSION) {
111 DPRINTK("info.version invalid: %d\n", info.version);
112 return -EINVAL;
113 }
114 /* TODO: info.addr = server->m.serv_addr; */
115 info.mounted_uid = NEW_TO_OLD_UID(server->m.mounted_uid);
116 info.connection = server->connection;
117 info.buffer_size = server->buffer_size;
118 info.volume_number = NCP_FINFO(inode)->volNumber;
119 info.directory_id = NCP_FINFO(inode)->DosDirNum;
120
121 if (copy_to_user((struct ncp_fs_info *) arg, &info,
122 sizeof(info))) return -EFAULT;
123 return 0;
124 }
125
126 case NCP_IOC_GET_FS_INFO_V2:
127 {
128 struct ncp_fs_info_v2 info2;
129
130 if ((permission(inode, MAY_WRITE) != 0)
131 && (current->uid != server->m.mounted_uid)) {
132 return -EACCES;
133 }
134 if (copy_from_user(&info2, (struct ncp_fs_info_v2 *) arg,
135 sizeof(info2)))
136 return -EFAULT;
137
138 if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
139 DPRINTK("info.version invalid: %d\n", info2.version);
140 return -EINVAL;
141 }
142 info2.mounted_uid = server->m.mounted_uid;
143 info2.connection = server->connection;
144 info2.buffer_size = server->buffer_size;
145 info2.volume_number = NCP_FINFO(inode)->volNumber;
146 info2.directory_id = NCP_FINFO(inode)->DosDirNum;
147 info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
148
149 if (copy_to_user((struct ncp_fs_info_v2 *) arg, &info2,
150 sizeof(info2))) return -EFAULT;
151 return 0;
152 }
153
154 case NCP_IOC_GETMOUNTUID2:
155 {
156 unsigned long tmp = server->m.mounted_uid;
157
158 if ( (permission(inode, MAY_READ) != 0)
159 && (current->uid != server->m.mounted_uid))
160 {
161 return -EACCES;
162 }
163 if (put_user(tmp, (unsigned long*) arg))
164 return -EFAULT;
165 return 0;
166 }
167
168 case NCP_IOC_GETROOT:
169 {
170 struct ncp_setroot_ioctl sr;
171
172 if ( (permission(inode, MAY_READ) != 0)
173 && (current->uid != server->m.mounted_uid))
174 {
175 return -EACCES;
176 }
177 if (server->m.mounted_vol[0]) {
178 struct dentry* dentry = inode->i_sb->s_root;
179
180 if (dentry) {
181 struct inode* inode = dentry->d_inode;
182
183 if (inode) {
184 sr.volNumber = NCP_FINFO(inode)->volNumber;
185 sr.dirEntNum = NCP_FINFO(inode)->dirEntNum;
186 sr.namespace = server->name_space[sr.volNumber];
187 } else
188 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
189 } else
190 DPRINTK("ncpfs: s_root==NULL\n");
191 } else {
192 sr.volNumber = -1;
193 sr.namespace = 0;
194 sr.dirEntNum = 0;
195 }
196 if (copy_to_user((struct ncp_setroot_ioctl*)arg,
197 &sr,
198 sizeof(sr))) return -EFAULT;
199 return 0;
200 }
201 case NCP_IOC_SETROOT:
202 {
203 struct ncp_setroot_ioctl sr;
204 struct nw_info_struct i;
205 struct dentry* dentry;
206
207 if (!capable(CAP_SYS_ADMIN))
208 {
209 return -EACCES;
210 }
211 if (server->root_setuped) return -EBUSY;
212 if (copy_from_user(&sr,
213 (struct ncp_setroot_ioctl*)arg,
214 sizeof(sr))) return -EFAULT;
215 if (sr.volNumber < 0) {
216 server->m.mounted_vol[0] = 0;
217 i.volNumber = NCP_NUMBER_OF_VOLUMES + 1;
218 i.dirEntNum = 0;
219 i.DosDirNum = 0;
220 } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
221 return -EINVAL;
222 } else
223 if (ncp_mount_subdir(server, &i, sr.volNumber,
224 sr.namespace, sr.dirEntNum))
225 return -ENOENT;
226
227 dentry = inode->i_sb->s_root;
228 server->root_setuped = 1;
229 if (dentry) {
230 struct inode* inode = dentry->d_inode;
231
232 if (inode) {
233 NCP_FINFO(inode)->volNumber = i.volNumber;
234 NCP_FINFO(inode)->dirEntNum = i.dirEntNum;
235 NCP_FINFO(inode)->DosDirNum = i.DosDirNum;
236 } else
237 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
238 } else
239 DPRINTK("ncpfs: s_root==NULL\n");
240
241 return 0;
242 }
243
244 #ifdef CONFIG_NCPFS_PACKET_SIGNING
245 case NCP_IOC_SIGN_INIT:
246 if ((permission(inode, MAY_WRITE) != 0)
247 && (current->uid != server->m.mounted_uid))
248 {
249 return -EACCES;
250 }
251 if (arg) {
252 if (server->sign_wanted)
253 {
254 struct ncp_sign_init sign;
255
256 if (copy_from_user(&sign, (struct ncp_sign_init *) arg,
257 sizeof(sign))) return -EFAULT;
258 memcpy(server->sign_root,sign.sign_root,8);
259 memcpy(server->sign_last,sign.sign_last,16);
260 server->sign_active = 1;
261 }
262 /* ignore when signatures not wanted */
263 } else {
264 server->sign_active = 0;
265 }
266 return 0;
267
268 case NCP_IOC_SIGN_WANTED:
269 if ( (permission(inode, MAY_READ) != 0)
270 && (current->uid != server->m.mounted_uid))
271 {
272 return -EACCES;
273 }
274
275 if (put_user(server->sign_wanted, (int*) arg))
276 return -EFAULT;
277 return 0;
278 case NCP_IOC_SET_SIGN_WANTED:
279 {
280 int newstate;
281
282 if ( (permission(inode, MAY_WRITE) != 0)
283 && (current->uid != server->m.mounted_uid))
284 {
285 return -EACCES;
286 }
287 /* get only low 8 bits... */
288 if (get_user(newstate, (unsigned char *) arg))
289 return -EFAULT;
290 if (server->sign_active) {
291 /* cannot turn signatures OFF when active */
292 if (!newstate) return -EINVAL;
293 } else {
294 server->sign_wanted = newstate != 0;
295 }
296 return 0;
297 }
298
299 #endif /* CONFIG_NCPFS_PACKET_SIGNING */
300
301 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
302 case NCP_IOC_LOCKUNLOCK:
303 if ( (permission(inode, MAY_WRITE) != 0)
304 && (current->uid != server->m.mounted_uid))
305 {
306 return -EACCES;
307 }
308 {
309 struct ncp_lock_ioctl rqdata;
310 int result;
311
312 if (copy_from_user(&rqdata, (struct ncp_lock_ioctl*)arg,
313 sizeof(rqdata))) return -EFAULT;
314 if (rqdata.origin != 0)
315 return -EINVAL;
316 /* check for cmd */
317 switch (rqdata.cmd) {
318 case NCP_LOCK_EX:
319 case NCP_LOCK_SH:
320 if (rqdata.timeout == 0)
321 rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
322 else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
323 rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
324 break;
325 case NCP_LOCK_LOG:
326 rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */
327 case NCP_LOCK_CLEAR:
328 break;
329 default:
330 return -EINVAL;
331 }
332 /* locking needs both read and write access */
333 if ((result = ncp_make_open(inode, O_RDWR)) != 0)
334 {
335 return result;
336 }
337 result = -EIO;
338 if (!ncp_conn_valid(server))
339 goto outrel;
340 result = -EISDIR;
341 if (!S_ISREG(inode->i_mode))
342 goto outrel;
343 if (rqdata.cmd == NCP_LOCK_CLEAR)
344 {
345 result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
346 NCP_FINFO(inode)->file_handle,
347 rqdata.offset,
348 rqdata.length);
349 if (result > 0) result = 0; /* no such lock */
350 }
351 else
352 {
353 int lockcmd;
354
355 switch (rqdata.cmd)
356 {
357 case NCP_LOCK_EX: lockcmd=1; break;
358 case NCP_LOCK_SH: lockcmd=3; break;
359 default: lockcmd=0; break;
360 }
361 result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
362 NCP_FINFO(inode)->file_handle,
363 lockcmd,
364 rqdata.offset,
365 rqdata.length,
366 rqdata.timeout);
367 if (result > 0) result = -EAGAIN;
368 }
369 outrel:
370 ncp_inode_close(inode);
371 return result;
372 }
373 #endif /* CONFIG_NCPFS_IOCTL_LOCKING */
374
375 case NCP_IOC_GETOBJECTNAME:
376 if (current->uid != server->m.mounted_uid) {
377 return -EACCES;
378 }
379 {
380 struct ncp_objectname_ioctl user;
381 size_t outl;
382
383 if (copy_from_user(&user,
384 (struct ncp_objectname_ioctl*)arg,
385 sizeof(user))) return -EFAULT;
386 user.auth_type = server->auth.auth_type;
387 outl = user.object_name_len;
388 user.object_name_len = server->auth.object_name_len;
389 if (outl > user.object_name_len)
390 outl = user.object_name_len;
391 if (outl) {
392 if (copy_to_user(user.object_name,
393 server->auth.object_name,
394 outl)) return -EFAULT;
395 }
396 if (copy_to_user((struct ncp_objectname_ioctl*)arg,
397 &user,
398 sizeof(user))) return -EFAULT;
399 return 0;
400 }
401 case NCP_IOC_SETOBJECTNAME:
402 if (current->uid != server->m.mounted_uid) {
403 return -EACCES;
404 }
405 {
406 struct ncp_objectname_ioctl user;
407 void* newname;
408 void* oldname;
409 size_t oldnamelen;
410 void* oldprivate;
411 size_t oldprivatelen;
412
413 if (copy_from_user(&user,
414 (struct ncp_objectname_ioctl*)arg,
415 sizeof(user))) return -EFAULT;
416 if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
417 return -ENOMEM;
418 if (user.object_name_len) {
419 newname = ncp_kmalloc(user.object_name_len, GFP_USER);
420 if (!newname) return -ENOMEM;
421 if (copy_from_user(newname, user.object_name, user.object_name_len)) {
422 ncp_kfree_s(newname, user.object_name_len);
423 return -EFAULT;
424 }
425 } else {
426 newname = NULL;
427 }
428 /* enter critical section */
429 /* maybe that kfree can sleep so do that this way */
430 /* it is at least more SMP friendly (in future...) */
431 oldname = server->auth.object_name;
432 oldnamelen = server->auth.object_name_len;
433 oldprivate = server->priv.data;
434 oldprivatelen = server->priv.len;
435 server->auth.auth_type = user.auth_type;
436 server->auth.object_name_len = user.object_name_len;
437 server->auth.object_name = newname;
438 server->priv.len = 0;
439 server->priv.data = NULL;
440 /* leave critical section */
441 if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
442 if (oldname) ncp_kfree_s(oldname, oldnamelen);
443 return 0;
444 }
445 case NCP_IOC_GETPRIVATEDATA:
446 if (current->uid != server->m.mounted_uid) {
447 return -EACCES;
448 }
449 {
450 struct ncp_privatedata_ioctl user;
451 size_t outl;
452
453 if (copy_from_user(&user,
454 (struct ncp_privatedata_ioctl*)arg,
455 sizeof(user))) return -EFAULT;
456 outl = user.len;
457 user.len = server->priv.len;
458 if (outl > user.len) outl = user.len;
459 if (outl) {
460 if (copy_to_user(user.data,
461 server->priv.data,
462 outl)) return -EFAULT;
463 }
464 if (copy_to_user((struct ncp_privatedata_ioctl*)arg,
465 &user,
466 sizeof(user))) return -EFAULT;
467 return 0;
468 }
469 case NCP_IOC_SETPRIVATEDATA:
470 if (current->uid != server->m.mounted_uid) {
471 return -EACCES;
472 }
473 {
474 struct ncp_privatedata_ioctl user;
475 void* new;
476 void* old;
477 size_t oldlen;
478
479 if (copy_from_user(&user,
480 (struct ncp_privatedata_ioctl*)arg,
481 sizeof(user))) return -EFAULT;
482 if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
483 return -ENOMEM;
484 if (user.len) {
485 new = ncp_kmalloc(user.len, GFP_USER);
486 if (!new) return -ENOMEM;
487 if (copy_from_user(new, user.data, user.len)) {
488 ncp_kfree_s(new, user.len);
489 return -EFAULT;
490 }
491 } else {
492 new = NULL;
493 }
494 /* enter critical section */
495 old = server->priv.data;
496 oldlen = server->priv.len;
497 server->priv.len = user.len;
498 server->priv.data = new;
499 /* leave critical section */
500 if (old) ncp_kfree_s(old, oldlen);
501 return 0;
502 }
503
504 #ifdef CONFIG_NCPFS_NLS
505 /* Here we are select the iocharset and the codepage for NLS.
506 * Thanks Petr Vandrovec for idea and many hints.
507 */
508 case NCP_IOC_SETCHARSETS:
509 if (!capable(CAP_SYS_ADMIN))
510 return -EACCES;
511 if (server->root_setuped)
512 return -EBUSY;
513
514 {
515 struct ncp_nls_ioctl user;
516 struct nls_table *codepage;
517 struct nls_table *iocharset;
518 struct nls_table *oldset_io;
519 struct nls_table *oldset_cp;
520
521 if (copy_from_user(&user, (struct ncp_nls_ioctl*)arg,
522 sizeof(user)))
523 return -EFAULT;
524
525 codepage = NULL;
526 user.codepage[NCP_IOCSNAME_LEN] = 0;
527 if (!user.codepage[0] ||
528 !strcmp(user.codepage, "default"))
529 codepage = load_nls_default();
530 else {
531 codepage = load_nls(user.codepage);
532 if (!codepage) {
533 return -EBADRQC;
534 }
535 }
536
537 iocharset = NULL;
538 user.iocharset[NCP_IOCSNAME_LEN] = 0;
539 if (!user.iocharset[0] ||
540 !strcmp(user.iocharset, "default")) {
541 iocharset = load_nls_default();
542 NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
543 } else {
544 if (!strcmp(user.iocharset, "utf8")) {
545 iocharset = load_nls_default();
546 NCP_SET_FLAG(server, NCP_FLAG_UTF8);
547 } else {
548 iocharset = load_nls(user.iocharset);
549 if (!iocharset) {
550 unload_nls(codepage);
551 return -EBADRQC;
552 }
553 NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
554 }
555 }
556
557 oldset_cp = server->nls_vol;
558 server->nls_vol = codepage;
559 oldset_io = server->nls_io;
560 server->nls_io = iocharset;
561
562 if (oldset_cp)
563 unload_nls(oldset_cp);
564 if (oldset_io)
565 unload_nls(oldset_io);
566
567 return 0;
568 }
569
570 case NCP_IOC_GETCHARSETS: /* not tested */
571 {
572 struct ncp_nls_ioctl user;
573 int len;
574
575 memset(&user, 0, sizeof(user));
576 if (server->nls_vol && server->nls_vol->charset) {
577 len = strlen(server->nls_vol->charset);
578 if (len > NCP_IOCSNAME_LEN)
579 len = NCP_IOCSNAME_LEN;
580 strncpy(user.codepage,
581 server->nls_vol->charset, len);
582 user.codepage[len] = 0;
583 }
584
585 if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
586 strcpy(user.iocharset, "utf8");
587 else
588 if (server->nls_io && server->nls_io->charset) {
589 len = strlen(server->nls_io->charset);
590 if (len > NCP_IOCSNAME_LEN)
591 len = NCP_IOCSNAME_LEN;
592 strncpy(user.iocharset,
593 server->nls_io->charset, len);
594 user.iocharset[len] = 0;
595 }
596
597 if (copy_to_user((struct ncp_nls_ioctl*)arg, &user,
598 sizeof(user)))
599 return -EFAULT;
600
601 return 0;
602 }
603 #endif /* CONFIG_NCPFS_NLS */
604 case NCP_IOC_SETDENTRYTTL:
605 if ((permission(inode, MAY_WRITE) != 0) &&
606 (current->uid != server->m.mounted_uid))
607 return -EACCES;
608 {
609 u_int32_t user;
610
611 if (copy_from_user(&user, (u_int32_t*)arg, sizeof(user)))
612 return -EFAULT;
613 /* 20 secs at most... */
614 if (user > 20000)
615 return -EINVAL;
616 user = (user * HZ) / 1000;
617 server->dentry_ttl = user;
618 return 0;
619 }
620
621 case NCP_IOC_GETDENTRYTTL:
622 {
623 u_int32_t user = (server->dentry_ttl * 1000) / HZ;
624 if (copy_to_user((u_int32_t*)arg, &user, sizeof(user)))
625 return -EFAULT;
626 return 0;
627 }
628
629 }
630 /* #ifdef CONFIG_UID16 */
631 /* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2,
632 so we have this out of switch */
633 if (cmd == NCP_IOC_GETMOUNTUID) {
634 if ((permission(inode, MAY_READ) != 0)
635 && (current->uid != server->m.mounted_uid)) {
636 return -EACCES;
637 }
638 if (put_user(NEW_TO_OLD_UID(server->m.mounted_uid), (__kernel_uid_t *) arg))
639 return -EFAULT;
640 return 0;
641 }
642 /* #endif */
643 return -EINVAL;
644 }
645