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>
20 #include <linux/ncp_fs.h>
22 #include "ncplib_kernel.h"
24 /* maximum limit for ncp_objectname_ioctl */
25 #define NCP_OBJECT_NAME_MAX_LEN 4096
26 /* maximum limit for ncp_privatedata_ioctl */
27 #define NCP_PRIVATE_DATA_MAX_LEN 8192
29 int ncp_ioctl(struct inode
*inode
, struct file
*filp
,
30 unsigned int cmd
, unsigned long arg
)
32 struct ncp_server
*server
= NCP_SERVER(inode
);
34 struct ncp_ioctl_request request
;
38 case NCP_IOC_NCPREQUEST
:
40 if ((permission(inode
, MAY_WRITE
) != 0)
41 && (current
->uid
!= server
->m
.mounted_uid
)) {
44 if (copy_from_user(&request
, (struct ncp_ioctl_request
*) arg
,
48 if ((request
.function
> 255)
50 NCP_PACKET_SIZE
- sizeof(struct ncp_request_header
))) {
53 bouncebuffer
= kmalloc(NCP_PACKET_SIZE
, GFP_NFS
);
56 if (copy_from_user(bouncebuffer
, request
.data
, request
.size
)) {
60 ncp_lock_server(server
);
62 /* FIXME: We hack around in the server's structures
63 here to be able to use ncp_request */
65 server
->has_subfunction
= 0;
66 server
->current_size
= request
.size
;
67 memcpy(server
->packet
, bouncebuffer
, request
.size
);
69 result
= ncp_request2(server
, request
.function
,
70 bouncebuffer
, NCP_PACKET_SIZE
);
74 result
= server
->reply_size
;
75 ncp_unlock_server(server
);
76 DPRINTK("ncp_ioctl: copy %d bytes\n",
79 if (copy_to_user(request
.data
, bouncebuffer
, result
))
84 case NCP_IOC_CONN_LOGGED_IN
:
86 if ((permission(inode
, MAY_WRITE
) != 0)
87 && (current
->uid
!= server
->m
.mounted_uid
)) {
90 if (!(server
->m
.int_flags
& NCP_IMOUNT_LOGGEDIN_POSSIBLE
))
92 if (server
->root_setuped
)
94 server
->root_setuped
= 1;
95 return ncp_conn_logged_in(inode
->i_sb
);
97 case NCP_IOC_GET_FS_INFO
:
99 struct ncp_fs_info info
;
101 if ((permission(inode
, MAY_WRITE
) != 0)
102 && (current
->uid
!= server
->m
.mounted_uid
)) {
105 if (copy_from_user(&info
, (struct ncp_fs_info
*) arg
,
109 if (info
.version
!= NCP_GET_FS_INFO_VERSION
) {
110 DPRINTK("info.version invalid: %d\n", info
.version
);
113 /* TODO: info.addr = server->m.serv_addr; */
114 info
.mounted_uid
= NEW_TO_OLD_UID(server
->m
.mounted_uid
);
115 info
.connection
= server
->connection
;
116 info
.buffer_size
= server
->buffer_size
;
117 info
.volume_number
= NCP_FINFO(inode
)->volNumber
;
118 info
.directory_id
= NCP_FINFO(inode
)->DosDirNum
;
120 if (copy_to_user((struct ncp_fs_info
*) arg
, &info
,
121 sizeof(info
))) return -EFAULT
;
125 case NCP_IOC_GET_FS_INFO_V2
:
127 struct ncp_fs_info_v2 info2
;
129 if ((permission(inode
, MAY_WRITE
) != 0)
130 && (current
->uid
!= server
->m
.mounted_uid
)) {
133 if (copy_from_user(&info2
, (struct ncp_fs_info_v2
*) arg
,
137 if (info2
.version
!= NCP_GET_FS_INFO_VERSION_V2
) {
138 DPRINTK("info.version invalid: %d\n", info2
.version
);
141 info2
.mounted_uid
= server
->m
.mounted_uid
;
142 info2
.connection
= server
->connection
;
143 info2
.buffer_size
= server
->buffer_size
;
144 info2
.volume_number
= NCP_FINFO(inode
)->volNumber
;
145 info2
.directory_id
= NCP_FINFO(inode
)->DosDirNum
;
146 info2
.dummy1
= info2
.dummy2
= info2
.dummy3
= 0;
148 if (copy_to_user((struct ncp_fs_info_v2
*) arg
, &info2
,
149 sizeof(info2
))) return -EFAULT
;
153 case NCP_IOC_GETMOUNTUID2
:
155 unsigned long tmp
= server
->m
.mounted_uid
;
157 if ( (permission(inode
, MAY_READ
) != 0)
158 && (current
->uid
!= server
->m
.mounted_uid
))
162 if (put_user(tmp
, (unsigned long*) arg
))
167 #ifdef CONFIG_NCPFS_MOUNT_SUBDIR
168 case NCP_IOC_GETROOT
:
170 struct ncp_setroot_ioctl sr
;
172 if ( (permission(inode
, MAY_READ
) != 0)
173 && (current
->uid
!= server
->m
.mounted_uid
))
177 if (server
->m
.mounted_vol
[0]) {
178 struct dentry
* dentry
= inode
->i_sb
->s_root
;
181 struct inode
* inode
= dentry
->d_inode
;
184 sr
.volNumber
= NCP_FINFO(inode
)->volNumber
;
185 sr
.dirEntNum
= NCP_FINFO(inode
)->dirEntNum
;
186 sr
.namespace = server
->name_space
[sr
.volNumber
];
188 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
190 DPRINTK("ncpfs: s_root==NULL\n");
196 if (copy_to_user((struct ncp_setroot_ioctl
*)arg
,
198 sizeof(sr
))) return -EFAULT
;
201 case NCP_IOC_SETROOT
:
203 struct ncp_setroot_ioctl sr
;
204 struct nw_info_struct i
;
205 struct dentry
* dentry
;
207 if ( (permission(inode
, MAY_WRITE
) != 0)
208 && (current
->uid
!= server
->m
.mounted_uid
))
212 if (server
->root_setuped
) return -EBUSY
;
213 if (copy_from_user(&sr
,
214 (struct ncp_setroot_ioctl
*)arg
,
215 sizeof(sr
))) return -EFAULT
;
216 if (sr
.volNumber
< 0) {
217 server
->m
.mounted_vol
[0] = 0;
218 i
.volNumber
= NCP_NUMBER_OF_VOLUMES
+ 1;
221 } else if (sr
.volNumber
>= NCP_NUMBER_OF_VOLUMES
) {
224 if (ncp_mount_subdir(server
, &i
, sr
.volNumber
,
225 sr
.namespace, sr
.dirEntNum
))
228 dentry
= inode
->i_sb
->s_root
;
229 server
->root_setuped
= 1;
231 struct inode
* inode
= dentry
->d_inode
;
234 NCP_FINFO(inode
)->volNumber
= i
.volNumber
;
235 NCP_FINFO(inode
)->dirEntNum
= i
.dirEntNum
;
236 NCP_FINFO(inode
)->DosDirNum
= i
.DosDirNum
;
238 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
240 DPRINTK("ncpfs: s_root==NULL\n");
244 #endif /* CONFIG_NCPFS_MOUNT_SUBDIR */
246 #ifdef CONFIG_NCPFS_PACKET_SIGNING
247 case NCP_IOC_SIGN_INIT
:
248 if ((permission(inode
, MAY_WRITE
) != 0)
249 && (current
->uid
!= server
->m
.mounted_uid
))
254 if (server
->sign_wanted
)
256 struct ncp_sign_init sign
;
258 if (copy_from_user(&sign
, (struct ncp_sign_init
*) arg
,
259 sizeof(sign
))) return -EFAULT
;
260 memcpy(server
->sign_root
,sign
.sign_root
,8);
261 memcpy(server
->sign_last
,sign
.sign_last
,16);
262 server
->sign_active
= 1;
264 /* ignore when signatures not wanted */
266 server
->sign_active
= 0;
270 case NCP_IOC_SIGN_WANTED
:
271 if ( (permission(inode
, MAY_READ
) != 0)
272 && (current
->uid
!= server
->m
.mounted_uid
))
277 if (put_user(server
->sign_wanted
, (int*) arg
))
280 case NCP_IOC_SET_SIGN_WANTED
:
284 if ( (permission(inode
, MAY_WRITE
) != 0)
285 && (current
->uid
!= server
->m
.mounted_uid
))
289 /* get only low 8 bits... */
290 get_user_ret(newstate
, (unsigned char*)arg
, -EFAULT
);
291 if (server
->sign_active
) {
292 /* cannot turn signatures OFF when active */
293 if (!newstate
) return -EINVAL
;
295 server
->sign_wanted
= newstate
!= 0;
300 #endif /* CONFIG_NCPFS_PACKET_SIGNING */
302 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
303 case NCP_IOC_LOCKUNLOCK
:
304 if ( (permission(inode
, MAY_WRITE
) != 0)
305 && (current
->uid
!= server
->m
.mounted_uid
))
310 struct ncp_lock_ioctl rqdata
;
313 if (copy_from_user(&rqdata
, (struct ncp_lock_ioctl
*)arg
,
314 sizeof(rqdata
))) return -EFAULT
;
315 if (rqdata
.origin
!= 0)
318 switch (rqdata
.cmd
) {
321 if (rqdata
.timeout
== 0)
322 rqdata
.timeout
= NCP_LOCK_DEFAULT_TIMEOUT
;
323 else if (rqdata
.timeout
> NCP_LOCK_MAX_TIMEOUT
)
324 rqdata
.timeout
= NCP_LOCK_MAX_TIMEOUT
;
327 rqdata
.timeout
= NCP_LOCK_DEFAULT_TIMEOUT
; /* has no effect */
333 /* locking needs both read and write access */
334 if ((result
= ncp_make_open(inode
, O_RDWR
)) != 0)
339 if (!ncp_conn_valid(server
))
342 if (!S_ISREG(inode
->i_mode
))
344 if (rqdata
.cmd
== NCP_LOCK_CLEAR
)
346 result
= ncp_ClearPhysicalRecord(NCP_SERVER(inode
),
347 NCP_FINFO(inode
)->file_handle
,
350 if (result
> 0) result
= 0; /* no such lock */
358 case NCP_LOCK_EX
: lockcmd
=1; break;
359 case NCP_LOCK_SH
: lockcmd
=3; break;
360 default: lockcmd
=0; break;
362 result
= ncp_LogPhysicalRecord(NCP_SERVER(inode
),
363 NCP_FINFO(inode
)->file_handle
,
368 if (result
> 0) result
= -EAGAIN
;
371 ncp_inode_close(inode
);
374 #endif /* CONFIG_NCPFS_IOCTL_LOCKING */
376 #ifdef CONFIG_NCPFS_NDS_DOMAINS
377 case NCP_IOC_GETOBJECTNAME
:
378 if (current
->uid
!= server
->m
.mounted_uid
) {
382 struct ncp_objectname_ioctl user
;
385 if (copy_from_user(&user
,
386 (struct ncp_objectname_ioctl
*)arg
,
387 sizeof(user
))) return -EFAULT
;
388 user
.auth_type
= server
->auth
.auth_type
;
389 outl
= user
.object_name_len
;
390 user
.object_name_len
= server
->auth
.object_name_len
;
391 if (outl
> user
.object_name_len
)
392 outl
= user
.object_name_len
;
394 if (copy_to_user(user
.object_name
,
395 server
->auth
.object_name
,
396 outl
)) return -EFAULT
;
398 if (copy_to_user((struct ncp_objectname_ioctl
*)arg
,
400 sizeof(user
))) return -EFAULT
;
403 case NCP_IOC_SETOBJECTNAME
:
404 if (current
->uid
!= server
->m
.mounted_uid
) {
408 struct ncp_objectname_ioctl user
;
413 size_t oldprivatelen
;
415 if (copy_from_user(&user
,
416 (struct ncp_objectname_ioctl
*)arg
,
417 sizeof(user
))) return -EFAULT
;
418 if (user
.object_name_len
> NCP_OBJECT_NAME_MAX_LEN
)
420 if (user
.object_name_len
) {
421 newname
= ncp_kmalloc(user
.object_name_len
, GFP_USER
);
422 if (!newname
) return -ENOMEM
;
423 if (copy_from_user(newname
, user
.object_name
, sizeof(user
))) {
424 ncp_kfree_s(newname
, user
.object_name_len
);
430 /* enter critical section */
431 /* maybe that kfree can sleep so do that this way */
432 /* it is at least more SMP friendly (in future...) */
433 oldname
= server
->auth
.object_name
;
434 oldnamelen
= server
->auth
.object_name_len
;
435 oldprivate
= server
->priv
.data
;
436 oldprivatelen
= server
->priv
.len
;
437 server
->auth
.auth_type
= user
.auth_type
;
438 server
->auth
.object_name_len
= user
.object_name_len
;
439 server
->auth
.object_name
= user
.object_name
;
440 server
->priv
.len
= 0;
441 server
->priv
.data
= NULL
;
442 /* leave critical section */
443 if (oldprivate
) ncp_kfree_s(oldprivate
, oldprivatelen
);
444 if (oldname
) ncp_kfree_s(oldname
, oldnamelen
);
447 case NCP_IOC_GETPRIVATEDATA
:
448 if (current
->uid
!= server
->m
.mounted_uid
) {
452 struct ncp_privatedata_ioctl user
;
455 if (copy_from_user(&user
,
456 (struct ncp_privatedata_ioctl
*)arg
,
457 sizeof(user
))) return -EFAULT
;
459 user
.len
= server
->priv
.len
;
460 if (outl
> user
.len
) outl
= user
.len
;
462 if (copy_to_user(user
.data
,
464 outl
)) return -EFAULT
;
466 if (copy_to_user((struct ncp_privatedata_ioctl
*)arg
,
468 sizeof(user
))) return -EFAULT
;
471 case NCP_IOC_SETPRIVATEDATA
:
472 if (current
->uid
!= server
->m
.mounted_uid
) {
476 struct ncp_privatedata_ioctl user
;
481 if (copy_from_user(&user
,
482 (struct ncp_privatedata_ioctl
*)arg
,
483 sizeof(user
))) return -EFAULT
;
484 if (user
.len
> NCP_PRIVATE_DATA_MAX_LEN
)
487 new = ncp_kmalloc(user
.len
, GFP_USER
);
488 if (!new) return -ENOMEM
;
489 if (copy_from_user(new, user
.data
, user
.len
)) {
490 ncp_kfree_s(new, user
.len
);
496 /* enter critical section */
497 old
= server
->priv
.data
;
498 oldlen
= server
->priv
.len
;
499 server
->priv
.len
= user
.len
;
500 server
->priv
.data
= new;
501 /* leave critical section */
502 if (old
) ncp_kfree_s(old
, oldlen
);
505 #endif /* CONFIG_NCPFS_NDS_DOMAINS */
507 #ifdef CONFIG_NCPFS_NLS
508 /* Here we are select the iocharset and the codepage for NLS.
509 * Thanks Petr Vandrovec for idea and many hints.
511 case NCP_IOC_SETCHARSETS
:
512 if ((permission(inode
, MAY_WRITE
) != 0) &&
513 (current
->uid
!= server
->m
.mounted_uid
))
515 if (server
->root_setuped
)
519 struct ncp_nls_ioctl user
;
520 struct nls_table
*codepage
;
521 struct nls_table
*iocharset
;
522 struct nls_table
*oldset_io
;
523 struct nls_table
*oldset_cp
;
525 if (copy_from_user(&user
, (struct ncp_nls_ioctl
*)arg
,
530 user
.codepage
[NCP_IOCSNAME_LEN
] = 0;
531 if (!user
.codepage
[0] ||
532 !strcmp(user
.codepage
, "default"))
533 codepage
= load_nls_default();
535 codepage
= load_nls(user
.codepage
);
542 user
.iocharset
[NCP_IOCSNAME_LEN
] = 0;
543 if (!user
.iocharset
[0] ||
544 !strcmp(user
.iocharset
, "default")) {
545 iocharset
= load_nls_default();
546 NCP_CLR_FLAG(server
, NCP_FLAG_UTF8
);
548 if (!strcmp(user
.iocharset
, "utf8")) {
549 iocharset
= load_nls_default();
550 NCP_SET_FLAG(server
, NCP_FLAG_UTF8
);
552 iocharset
= load_nls(user
.iocharset
);
554 unload_nls(codepage
);
557 NCP_CLR_FLAG(server
, NCP_FLAG_UTF8
);
561 oldset_cp
= server
->nls_vol
;
562 server
->nls_vol
= codepage
;
563 oldset_io
= server
->nls_io
;
564 server
->nls_io
= iocharset
;
567 unload_nls(oldset_cp
);
569 unload_nls(oldset_io
);
574 case NCP_IOC_GETCHARSETS
: /* not tested */
576 struct ncp_nls_ioctl user
;
579 memset(&user
, 0, sizeof(user
));
580 if (server
->nls_vol
&& server
->nls_vol
->charset
) {
581 len
= strlen(server
->nls_vol
->charset
);
582 if (len
> NCP_IOCSNAME_LEN
)
583 len
= NCP_IOCSNAME_LEN
;
584 strncpy(user
.codepage
,
585 server
->nls_vol
->charset
, len
);
586 user
.codepage
[len
] = 0;
589 if (NCP_IS_FLAG(server
, NCP_FLAG_UTF8
))
590 strcpy(user
.iocharset
, "utf8");
592 if (server
->nls_io
&& server
->nls_io
->charset
) {
593 len
= strlen(server
->nls_io
->charset
);
594 if (len
> NCP_IOCSNAME_LEN
)
595 len
= NCP_IOCSNAME_LEN
;
596 strncpy(user
.iocharset
,
597 server
->nls_io
->charset
, len
);
598 user
.iocharset
[len
] = 0;
601 if (copy_to_user((struct ncp_nls_ioctl
*)arg
, &user
,
607 #endif /* CONFIG_NCPFS_NLS */
608 case NCP_IOC_SETDENTRYTTL
:
609 if ((permission(inode
, MAY_WRITE
) != 0) &&
610 (current
->uid
!= server
->m
.mounted_uid
))
615 if (copy_from_user(&user
, (u_int32_t
*)arg
, sizeof(user
)))
617 /* 20 secs at most... */
620 user
= (user
* HZ
) / 1000;
621 server
->dentry_ttl
= user
;
625 case NCP_IOC_GETDENTRYTTL
:
627 u_int32_t user
= (server
->dentry_ttl
* 1000) / HZ
;
628 if (copy_to_user((u_int32_t
*)arg
, &user
, sizeof(user
)))
634 /* #ifdef CONFIG_UID16 */
635 /* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2,
636 so we have this out of switch */
637 if (cmd
== NCP_IOC_GETMOUNTUID
) {
638 if ((permission(inode
, MAY_READ
) != 0)
639 && (current
->uid
!= server
->m
.mounted_uid
)) {
642 if (put_user(NEW_TO_OLD_UID(server
->m
.mounted_uid
), (__kernel_uid_t
*) arg
))