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
10 #include <linux/config.h>
12 #include <asm/uaccess.h>
13 #include <linux/errno.h>
15 #include <linux/ioctl.h>
16 #include <linux/sched.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
);
37 struct ncp_ioctl_request request
;
41 case NCP_IOC_NCPREQUEST
:
43 if ((permission(inode
, MAY_WRITE
) != 0)
44 && (current
->uid
!= server
->m
.mounted_uid
)) {
47 if (copy_from_user(&request
, (struct ncp_ioctl_request
*) arg
,
51 if ((request
.function
> 255)
53 NCP_PACKET_SIZE
- sizeof(struct ncp_request_header
))) {
56 bouncebuffer
= vmalloc(NCP_PACKET_SIZE_INTERNAL
);
59 if (copy_from_user(bouncebuffer
, request
.data
, request
.size
)) {
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
);
77 result
= server
->reply_size
;
78 ncp_unlock_server(server
);
79 DPRINTK("ncp_ioctl: copy %d bytes\n",
82 if (copy_to_user(request
.data
, bouncebuffer
, result
))
87 case NCP_IOC_CONN_LOGGED_IN
:
89 if ((permission(inode
, MAY_WRITE
) != 0)
90 && (current
->uid
!= server
->m
.mounted_uid
)) {
93 if (!(server
->m
.int_flags
& NCP_IMOUNT_LOGGEDIN_POSSIBLE
))
95 if (server
->root_setuped
)
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
)) {
108 if (copy_from_user(&info
, (struct ncp_fs_info
*) arg
,
112 if (info
.version
!= NCP_GET_FS_INFO_VERSION
) {
113 DPRINTK("info.version invalid: %d\n", info
.version
);
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
;
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
)) {
136 if (copy_from_user(&info2
, (struct ncp_fs_info_v2
*) arg
,
140 if (info2
.version
!= NCP_GET_FS_INFO_VERSION_V2
) {
141 DPRINTK("info.version invalid: %d\n", info2
.version
);
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
;
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
))
165 if (put_user(tmp
, (unsigned long*) arg
))
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
))
180 if (server
->m
.mounted_vol
[0]) {
181 struct dentry
* dentry
= inode
->i_sb
->s_root
;
184 struct inode
* inode
= dentry
->d_inode
;
187 sr
.volNumber
= NCP_FINFO(inode
)->volNumber
;
188 sr
.dirEntNum
= NCP_FINFO(inode
)->dirEntNum
;
189 sr
.namespace = server
->name_space
[sr
.volNumber
];
191 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
193 DPRINTK("ncpfs: s_root==NULL\n");
199 if (copy_to_user((struct ncp_setroot_ioctl
*)arg
,
201 sizeof(sr
))) return -EFAULT
;
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
))
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;
224 } else if (sr
.volNumber
>= NCP_NUMBER_OF_VOLUMES
) {
227 if (ncp_mount_subdir(server
, &i
, sr
.volNumber
,
228 sr
.namespace, sr
.dirEntNum
))
231 dentry
= inode
->i_sb
->s_root
;
232 server
->root_setuped
= 1;
234 struct inode
* inode
= dentry
->d_inode
;
237 NCP_FINFO(inode
)->volNumber
= i
.volNumber
;
238 NCP_FINFO(inode
)->dirEntNum
= i
.dirEntNum
;
239 NCP_FINFO(inode
)->DosDirNum
= i
.DosDirNum
;
241 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
243 DPRINTK("ncpfs: s_root==NULL\n");
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
))
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 */
269 server
->sign_active
= 0;
273 case NCP_IOC_SIGN_WANTED
:
274 if ( (permission(inode
, MAY_READ
) != 0)
275 && (current
->uid
!= server
->m
.mounted_uid
))
280 if (put_user(server
->sign_wanted
, (int*) arg
))
283 case NCP_IOC_SET_SIGN_WANTED
:
287 if ( (permission(inode
, MAY_WRITE
) != 0)
288 && (current
->uid
!= server
->m
.mounted_uid
))
292 /* get only low 8 bits... */
293 if (get_user(newstate
, (unsigned char *) arg
))
295 if (server
->sign_active
) {
296 /* cannot turn signatures OFF when active */
297 if (!newstate
) return -EINVAL
;
299 server
->sign_wanted
= newstate
!= 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
))
314 struct ncp_lock_ioctl rqdata
;
317 if (copy_from_user(&rqdata
, (struct ncp_lock_ioctl
*)arg
,
318 sizeof(rqdata
))) return -EFAULT
;
319 if (rqdata
.origin
!= 0)
322 switch (rqdata
.cmd
) {
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
;
331 rqdata
.timeout
= NCP_LOCK_DEFAULT_TIMEOUT
; /* has no effect */
337 /* locking needs both read and write access */
338 if ((result
= ncp_make_open(inode
, O_RDWR
)) != 0)
343 if (!ncp_conn_valid(server
))
346 if (!S_ISREG(inode
->i_mode
))
348 if (rqdata
.cmd
== NCP_LOCK_CLEAR
)
350 result
= ncp_ClearPhysicalRecord(NCP_SERVER(inode
),
351 NCP_FINFO(inode
)->file_handle
,
354 if (result
> 0) result
= 0; /* no such lock */
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
,
372 if (result
> 0) result
= -EAGAIN
;
375 ncp_inode_close(inode
);
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
) {
386 struct ncp_objectname_ioctl user
;
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
;
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
,
404 sizeof(user
))) return -EFAULT
;
407 case NCP_IOC_SETOBJECTNAME
:
408 if (current
->uid
!= server
->m
.mounted_uid
) {
412 struct ncp_objectname_ioctl user
;
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
)
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
);
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
);
451 case NCP_IOC_GETPRIVATEDATA
:
452 if (current
->uid
!= server
->m
.mounted_uid
) {
456 struct ncp_privatedata_ioctl user
;
459 if (copy_from_user(&user
,
460 (struct ncp_privatedata_ioctl
*)arg
,
461 sizeof(user
))) return -EFAULT
;
463 user
.len
= server
->priv
.len
;
464 if (outl
> user
.len
) outl
= user
.len
;
466 if (copy_to_user(user
.data
,
468 outl
)) return -EFAULT
;
470 if (copy_to_user((struct ncp_privatedata_ioctl
*)arg
,
472 sizeof(user
))) return -EFAULT
;
475 case NCP_IOC_SETPRIVATEDATA
:
476 if (current
->uid
!= server
->m
.mounted_uid
) {
480 struct ncp_privatedata_ioctl user
;
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
)
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
);
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
);
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
))
519 if (server
->root_setuped
)
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
,
534 user
.codepage
[NCP_IOCSNAME_LEN
] = 0;
535 if (!user
.codepage
[0] ||
536 !strcmp(user
.codepage
, "default"))
537 codepage
= load_nls_default();
539 codepage
= load_nls(user
.codepage
);
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
);
552 if (!strcmp(user
.iocharset
, "utf8")) {
553 iocharset
= load_nls_default();
554 NCP_SET_FLAG(server
, NCP_FLAG_UTF8
);
556 iocharset
= load_nls(user
.iocharset
);
558 unload_nls(codepage
);
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
;
571 unload_nls(oldset_cp
);
573 unload_nls(oldset_io
);
578 case NCP_IOC_GETCHARSETS
: /* not tested */
580 struct ncp_nls_ioctl user
;
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");
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
,
611 #endif /* CONFIG_NCPFS_NLS */
612 case NCP_IOC_SETDENTRYTTL
:
613 if ((permission(inode
, MAY_WRITE
) != 0) &&
614 (current
->uid
!= server
->m
.mounted_uid
))
619 if (copy_from_user(&user
, (u_int32_t
*)arg
, sizeof(user
)))
621 /* 20 secs at most... */
624 user
= (user
* HZ
) / 1000;
625 server
->dentry_ttl
= user
;
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
)))
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
)) {
646 if (put_user(NEW_TO_OLD_UID(server
->m
.mounted_uid
), (__kernel_uid_t
*) arg
))