aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ncpfs/ioctl.c
diff options
context:
space:
mode:
authorPetr Vandrovec <petr@vandrovec.name>2010-09-26 19:47:33 -0400
committerArnd Bergmann <arnd@arndb.de>2010-10-04 15:10:52 -0400
commit2e54eb96e2c801f33d95b5dade15212ac4d6c4a5 (patch)
treec2996f29aa49881e93c7a37fad41e4961f6b8578 /fs/ncpfs/ioctl.c
parent60056794127a25d641465b706e8828186f7a2e1f (diff)
BKL: Remove BKL from ncpfs
Dozen of changes in ncpfs to provide some locking other than BKL. In readdir cache unlock and mark complete first page as last operation, so it can be used for synchronization, as code intended. When updating dentry name on case insensitive filesystems do at least some basic locking... Hold i_mutex when updating inode fields. Push some ncp_conn_is_valid down to ncp_request. Connection can become invalid at any moment, and fewer error code paths to test the better. Use i_size_{read,write} to modify file size. Set inode's backing_dev_info as ncpfs has its own special bdi. In ioctl unbreak ioctls invoked on filesystem mounted 'ro' - tests are for inode writeable or owner match, but were turned to filesystem writeable and inode writeable or owner match. Also collect all permission checks in single place. Add some locking, and remove comments saying that it would be cool to add some locks to the code. Constify some pointers. Signed-off-by: Petr Vandrovec <petr@vandrovec.name> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'fs/ncpfs/ioctl.c')
-rw-r--r--fs/ncpfs/ioctl.c470
1 files changed, 255 insertions, 215 deletions
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index 84a8cfc4e38e..c2a1f9a155c3 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -35,16 +35,11 @@
35#define NCP_PACKET_SIZE_INTERNAL 65536 35#define NCP_PACKET_SIZE_INTERNAL 65536
36 36
37static int 37static int
38ncp_get_fs_info(struct ncp_server * server, struct file *file, 38ncp_get_fs_info(struct ncp_server * server, struct inode *inode,
39 struct ncp_fs_info __user *arg) 39 struct ncp_fs_info __user *arg)
40{ 40{
41 struct inode *inode = file->f_path.dentry->d_inode;
42 struct ncp_fs_info info; 41 struct ncp_fs_info info;
43 42
44 if (file_permission(file, MAY_WRITE) != 0
45 && current_uid() != server->m.mounted_uid)
46 return -EACCES;
47
48 if (copy_from_user(&info, arg, sizeof(info))) 43 if (copy_from_user(&info, arg, sizeof(info)))
49 return -EFAULT; 44 return -EFAULT;
50 45
@@ -65,16 +60,11 @@ ncp_get_fs_info(struct ncp_server * server, struct file *file,
65} 60}
66 61
67static int 62static int
68ncp_get_fs_info_v2(struct ncp_server * server, struct file *file, 63ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode,
69 struct ncp_fs_info_v2 __user * arg) 64 struct ncp_fs_info_v2 __user * arg)
70{ 65{
71 struct inode *inode = file->f_path.dentry->d_inode;
72 struct ncp_fs_info_v2 info2; 66 struct ncp_fs_info_v2 info2;
73 67
74 if (file_permission(file, MAY_WRITE) != 0
75 && current_uid() != server->m.mounted_uid)
76 return -EACCES;
77
78 if (copy_from_user(&info2, arg, sizeof(info2))) 68 if (copy_from_user(&info2, arg, sizeof(info2)))
79 return -EFAULT; 69 return -EFAULT;
80 70
@@ -136,16 +126,11 @@ struct compat_ncp_privatedata_ioctl
136#define NCP_IOC_SETPRIVATEDATA_32 _IOR('n', 10, struct compat_ncp_privatedata_ioctl) 126#define NCP_IOC_SETPRIVATEDATA_32 _IOR('n', 10, struct compat_ncp_privatedata_ioctl)
137 127
138static int 128static int
139ncp_get_compat_fs_info_v2(struct ncp_server * server, struct file *file, 129ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode,
140 struct compat_ncp_fs_info_v2 __user * arg) 130 struct compat_ncp_fs_info_v2 __user * arg)
141{ 131{
142 struct inode *inode = file->f_path.dentry->d_inode;
143 struct compat_ncp_fs_info_v2 info2; 132 struct compat_ncp_fs_info_v2 info2;
144 133
145 if (file_permission(file, MAY_WRITE) != 0
146 && current_uid() != server->m.mounted_uid)
147 return -EACCES;
148
149 if (copy_from_user(&info2, arg, sizeof(info2))) 134 if (copy_from_user(&info2, arg, sizeof(info2)))
150 return -EFAULT; 135 return -EFAULT;
151 136
@@ -182,11 +167,8 @@ ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
182 struct nls_table *iocharset; 167 struct nls_table *iocharset;
183 struct nls_table *oldset_io; 168 struct nls_table *oldset_io;
184 struct nls_table *oldset_cp; 169 struct nls_table *oldset_cp;
185 170 int utf8;
186 if (!capable(CAP_SYS_ADMIN)) 171 int err;
187 return -EACCES;
188 if (server->root_setuped)
189 return -EBUSY;
190 172
191 if (copy_from_user(&user, arg, sizeof(user))) 173 if (copy_from_user(&user, arg, sizeof(user)))
192 return -EFAULT; 174 return -EFAULT;
@@ -206,28 +188,40 @@ ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
206 user.iocharset[NCP_IOCSNAME_LEN] = 0; 188 user.iocharset[NCP_IOCSNAME_LEN] = 0;
207 if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) { 189 if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) {
208 iocharset = load_nls_default(); 190 iocharset = load_nls_default();
209 NCP_CLR_FLAG(server, NCP_FLAG_UTF8); 191 utf8 = 0;
210 } else if (!strcmp(user.iocharset, "utf8")) { 192 } else if (!strcmp(user.iocharset, "utf8")) {
211 iocharset = load_nls_default(); 193 iocharset = load_nls_default();
212 NCP_SET_FLAG(server, NCP_FLAG_UTF8); 194 utf8 = 1;
213 } else { 195 } else {
214 iocharset = load_nls(user.iocharset); 196 iocharset = load_nls(user.iocharset);
215 if (!iocharset) { 197 if (!iocharset) {
216 unload_nls(codepage); 198 unload_nls(codepage);
217 return -EBADRQC; 199 return -EBADRQC;
218 } 200 }
219 NCP_CLR_FLAG(server, NCP_FLAG_UTF8); 201 utf8 = 0;
220 } 202 }
221 203
222 oldset_cp = server->nls_vol; 204 mutex_lock(&server->root_setup_lock);
223 server->nls_vol = codepage; 205 if (server->root_setuped) {
224 oldset_io = server->nls_io; 206 oldset_cp = codepage;
225 server->nls_io = iocharset; 207 oldset_io = iocharset;
226 208 err = -EBUSY;
209 } else {
210 if (utf8)
211 NCP_SET_FLAG(server, NCP_FLAG_UTF8);
212 else
213 NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
214 oldset_cp = server->nls_vol;
215 server->nls_vol = codepage;
216 oldset_io = server->nls_io;
217 server->nls_io = iocharset;
218 err = 0;
219 }
220 mutex_unlock(&server->root_setup_lock);
227 unload_nls(oldset_cp); 221 unload_nls(oldset_cp);
228 unload_nls(oldset_io); 222 unload_nls(oldset_io);
229 223
230 return 0; 224 return err;
231} 225}
232 226
233static int 227static int
@@ -237,6 +231,7 @@ ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
237 int len; 231 int len;
238 232
239 memset(&user, 0, sizeof(user)); 233 memset(&user, 0, sizeof(user));
234 mutex_lock(&server->root_setup_lock);
240 if (server->nls_vol && server->nls_vol->charset) { 235 if (server->nls_vol && server->nls_vol->charset) {
241 len = strlen(server->nls_vol->charset); 236 len = strlen(server->nls_vol->charset);
242 if (len > NCP_IOCSNAME_LEN) 237 if (len > NCP_IOCSNAME_LEN)
@@ -254,6 +249,7 @@ ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
254 strncpy(user.iocharset, server->nls_io->charset, len); 249 strncpy(user.iocharset, server->nls_io->charset, len);
255 user.iocharset[len] = 0; 250 user.iocharset[len] = 0;
256 } 251 }
252 mutex_unlock(&server->root_setup_lock);
257 253
258 if (copy_to_user(arg, &user, sizeof(user))) 254 if (copy_to_user(arg, &user, sizeof(user)))
259 return -EFAULT; 255 return -EFAULT;
@@ -261,25 +257,19 @@ ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
261} 257}
262#endif /* CONFIG_NCPFS_NLS */ 258#endif /* CONFIG_NCPFS_NLS */
263 259
264static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 260static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg)
265{ 261{
266 struct inode *inode = filp->f_dentry->d_inode;
267 struct ncp_server *server = NCP_SERVER(inode); 262 struct ncp_server *server = NCP_SERVER(inode);
268 int result; 263 int result;
269 struct ncp_ioctl_request request; 264 struct ncp_ioctl_request request;
270 char* bouncebuffer; 265 char* bouncebuffer;
271 void __user *argp = (void __user *)arg; 266 void __user *argp = (void __user *)arg;
272 uid_t uid = current_uid();
273 267
274 switch (cmd) { 268 switch (cmd) {
275#ifdef CONFIG_COMPAT 269#ifdef CONFIG_COMPAT
276 case NCP_IOC_NCPREQUEST_32: 270 case NCP_IOC_NCPREQUEST_32:
277#endif 271#endif
278 case NCP_IOC_NCPREQUEST: 272 case NCP_IOC_NCPREQUEST:
279 if (file_permission(filp, MAY_WRITE) != 0
280 && uid != server->m.mounted_uid)
281 return -EACCES;
282
283#ifdef CONFIG_COMPAT 273#ifdef CONFIG_COMPAT
284 if (cmd == NCP_IOC_NCPREQUEST_32) { 274 if (cmd == NCP_IOC_NCPREQUEST_32) {
285 struct compat_ncp_ioctl_request request32; 275 struct compat_ncp_ioctl_request request32;
@@ -314,7 +304,7 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
314 server->current_size = request.size; 304 server->current_size = request.size;
315 memcpy(server->packet, bouncebuffer, request.size); 305 memcpy(server->packet, bouncebuffer, request.size);
316 306
317 result = ncp_request2(server, request.function, 307 result = ncp_request2(server, request.function,
318 bouncebuffer, NCP_PACKET_SIZE_INTERNAL); 308 bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
319 if (result < 0) 309 if (result < 0)
320 result = -EIO; 310 result = -EIO;
@@ -331,69 +321,69 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
331 321
332 case NCP_IOC_CONN_LOGGED_IN: 322 case NCP_IOC_CONN_LOGGED_IN:
333 323
334 if (!capable(CAP_SYS_ADMIN))
335 return -EACCES;
336 if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE)) 324 if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
337 return -EINVAL; 325 return -EINVAL;
326 mutex_lock(&server->root_setup_lock);
338 if (server->root_setuped) 327 if (server->root_setuped)
339 return -EBUSY; 328 result = -EBUSY;
340 server->root_setuped = 1; 329 else {
341 return ncp_conn_logged_in(inode->i_sb); 330 result = ncp_conn_logged_in(inode->i_sb);
331 if (result == 0)
332 server->root_setuped = 1;
333 }
334 mutex_unlock(&server->root_setup_lock);
335 return result;
342 336
343 case NCP_IOC_GET_FS_INFO: 337 case NCP_IOC_GET_FS_INFO:
344 return ncp_get_fs_info(server, filp, argp); 338 return ncp_get_fs_info(server, inode, argp);
345 339
346 case NCP_IOC_GET_FS_INFO_V2: 340 case NCP_IOC_GET_FS_INFO_V2:
347 return ncp_get_fs_info_v2(server, filp, argp); 341 return ncp_get_fs_info_v2(server, inode, argp);
348 342
349#ifdef CONFIG_COMPAT 343#ifdef CONFIG_COMPAT
350 case NCP_IOC_GET_FS_INFO_V2_32: 344 case NCP_IOC_GET_FS_INFO_V2_32:
351 return ncp_get_compat_fs_info_v2(server, filp, argp); 345 return ncp_get_compat_fs_info_v2(server, inode, argp);
352#endif 346#endif
353 /* we have too many combinations of CONFIG_COMPAT, 347 /* we have too many combinations of CONFIG_COMPAT,
354 * CONFIG_64BIT and CONFIG_UID16, so just handle 348 * CONFIG_64BIT and CONFIG_UID16, so just handle
355 * any of the possible ioctls */ 349 * any of the possible ioctls */
356 case NCP_IOC_GETMOUNTUID16: 350 case NCP_IOC_GETMOUNTUID16:
357 case NCP_IOC_GETMOUNTUID32: 351 {
358 case NCP_IOC_GETMOUNTUID64:
359 if (file_permission(filp, MAY_READ) != 0
360 && uid != server->m.mounted_uid)
361 return -EACCES;
362
363 if (cmd == NCP_IOC_GETMOUNTUID16) {
364 u16 uid; 352 u16 uid;
353
365 SET_UID(uid, server->m.mounted_uid); 354 SET_UID(uid, server->m.mounted_uid);
366 if (put_user(uid, (u16 __user *)argp)) 355 if (put_user(uid, (u16 __user *)argp))
367 return -EFAULT; 356 return -EFAULT;
368 } else if (cmd == NCP_IOC_GETMOUNTUID32) { 357 return 0;
369 if (put_user(server->m.mounted_uid,
370 (u32 __user *)argp))
371 return -EFAULT;
372 } else {
373 if (put_user(server->m.mounted_uid,
374 (u64 __user *)argp))
375 return -EFAULT;
376 } 358 }
359 case NCP_IOC_GETMOUNTUID32:
360 if (put_user(server->m.mounted_uid,
361 (u32 __user *)argp))
362 return -EFAULT;
363 return 0;
364 case NCP_IOC_GETMOUNTUID64:
365 if (put_user(server->m.mounted_uid,
366 (u64 __user *)argp))
367 return -EFAULT;
377 return 0; 368 return 0;
378 369
379 case NCP_IOC_GETROOT: 370 case NCP_IOC_GETROOT:
380 { 371 {
381 struct ncp_setroot_ioctl sr; 372 struct ncp_setroot_ioctl sr;
382 373
383 if (file_permission(filp, MAY_READ) != 0 374 result = -EACCES;
384 && uid != server->m.mounted_uid) 375 mutex_lock(&server->root_setup_lock);
385 return -EACCES;
386
387 if (server->m.mounted_vol[0]) { 376 if (server->m.mounted_vol[0]) {
388 struct dentry* dentry = inode->i_sb->s_root; 377 struct dentry* dentry = inode->i_sb->s_root;
389 378
390 if (dentry) { 379 if (dentry) {
391 struct inode* s_inode = dentry->d_inode; 380 struct inode* s_inode = dentry->d_inode;
392 381
393 if (s_inode) { 382 if (s_inode) {
394 sr.volNumber = NCP_FINFO(s_inode)->volNumber; 383 sr.volNumber = NCP_FINFO(s_inode)->volNumber;
395 sr.dirEntNum = NCP_FINFO(s_inode)->dirEntNum; 384 sr.dirEntNum = NCP_FINFO(s_inode)->dirEntNum;
396 sr.namespace = server->name_space[sr.volNumber]; 385 sr.namespace = server->name_space[sr.volNumber];
386 result = 0;
397 } else 387 } else
398 DPRINTK("ncpfs: s_root->d_inode==NULL\n"); 388 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
399 } else 389 } else
@@ -402,10 +392,12 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
402 sr.volNumber = -1; 392 sr.volNumber = -1;
403 sr.namespace = 0; 393 sr.namespace = 0;
404 sr.dirEntNum = 0; 394 sr.dirEntNum = 0;
395 result = 0;
405 } 396 }
406 if (copy_to_user(argp, &sr, sizeof(sr))) 397 mutex_unlock(&server->root_setup_lock);
407 return -EFAULT; 398 if (!result && copy_to_user(argp, &sr, sizeof(sr)))
408 return 0; 399 result = -EFAULT;
400 return result;
409 } 401 }
410 402
411 case NCP_IOC_SETROOT: 403 case NCP_IOC_SETROOT:
@@ -416,103 +408,114 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
416 __le32 dosde; 408 __le32 dosde;
417 struct dentry* dentry; 409 struct dentry* dentry;
418 410
419 if (!capable(CAP_SYS_ADMIN))
420 {
421 return -EACCES;
422 }
423 if (server->root_setuped) return -EBUSY;
424 if (copy_from_user(&sr, argp, sizeof(sr))) 411 if (copy_from_user(&sr, argp, sizeof(sr)))
425 return -EFAULT; 412 return -EFAULT;
426 if (sr.volNumber < 0) { 413 mutex_lock(&server->root_setup_lock);
427 server->m.mounted_vol[0] = 0; 414 if (server->root_setuped)
428 vnum = NCP_NUMBER_OF_VOLUMES; 415 result = -EBUSY;
429 de = 0; 416 else {
430 dosde = 0; 417 if (sr.volNumber < 0) {
431 } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) { 418 server->m.mounted_vol[0] = 0;
432 return -EINVAL; 419 vnum = NCP_NUMBER_OF_VOLUMES;
433 } else if (ncp_mount_subdir(server, sr.volNumber, 420 de = 0;
434 sr.namespace, sr.dirEntNum, 421 dosde = 0;
435 &vnum, &de, &dosde)) { 422 result = 0;
436 return -ENOENT; 423 } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
437 } 424 result = -EINVAL;
438 425 } else if (ncp_mount_subdir(server, sr.volNumber,
439 dentry = inode->i_sb->s_root; 426 sr.namespace, sr.dirEntNum,
440 server->root_setuped = 1; 427 &vnum, &de, &dosde)) {
441 if (dentry) { 428 result = -ENOENT;
442 struct inode* s_inode = dentry->d_inode;
443
444 if (s_inode) {
445 NCP_FINFO(s_inode)->volNumber = vnum;
446 NCP_FINFO(s_inode)->dirEntNum = de;
447 NCP_FINFO(s_inode)->DosDirNum = dosde;
448 } else 429 } else
449 DPRINTK("ncpfs: s_root->d_inode==NULL\n"); 430 result = 0;
450 } else 431
451 DPRINTK("ncpfs: s_root==NULL\n"); 432 if (result == 0) {
433 dentry = inode->i_sb->s_root;
434 if (dentry) {
435 struct inode* s_inode = dentry->d_inode;
436
437 if (s_inode) {
438 NCP_FINFO(s_inode)->volNumber = vnum;
439 NCP_FINFO(s_inode)->dirEntNum = de;
440 NCP_FINFO(s_inode)->DosDirNum = dosde;
441 server->root_setuped = 1;
442 } else {
443 DPRINTK("ncpfs: s_root->d_inode==NULL\n");
444 result = -EIO;
445 }
446 } else {
447 DPRINTK("ncpfs: s_root==NULL\n");
448 result = -EIO;
449 }
450 }
451 result = 0;
452 }
453 mutex_unlock(&server->root_setup_lock);
452 454
453 return 0; 455 return result;
454 } 456 }
455 457
456#ifdef CONFIG_NCPFS_PACKET_SIGNING 458#ifdef CONFIG_NCPFS_PACKET_SIGNING
457 case NCP_IOC_SIGN_INIT: 459 case NCP_IOC_SIGN_INIT:
458 if (file_permission(filp, MAY_WRITE) != 0 460 {
459 && uid != server->m.mounted_uid) 461 struct ncp_sign_init sign;
460 return -EACCES;
461
462 if (argp) {
463 if (server->sign_wanted)
464 {
465 struct ncp_sign_init sign;
466 462
463 if (argp)
467 if (copy_from_user(&sign, argp, sizeof(sign))) 464 if (copy_from_user(&sign, argp, sizeof(sign)))
468 return -EFAULT; 465 return -EFAULT;
469 memcpy(server->sign_root,sign.sign_root,8); 466 ncp_lock_server(server);
470 memcpy(server->sign_last,sign.sign_last,16); 467 mutex_lock(&server->rcv.creq_mutex);
471 server->sign_active = 1; 468 if (argp) {
469 if (server->sign_wanted) {
470 memcpy(server->sign_root,sign.sign_root,8);
471 memcpy(server->sign_last,sign.sign_last,16);
472 server->sign_active = 1;
473 }
474 /* ignore when signatures not wanted */
475 } else {
476 server->sign_active = 0;
472 } 477 }
473 /* ignore when signatures not wanted */ 478 mutex_unlock(&server->rcv.creq_mutex);
474 } else { 479 ncp_unlock_server(server);
475 server->sign_active = 0; 480 return 0;
476 } 481 }
477 return 0; 482
478
479 case NCP_IOC_SIGN_WANTED: 483 case NCP_IOC_SIGN_WANTED:
480 if (file_permission(filp, MAY_READ) != 0 484 {
481 && uid != server->m.mounted_uid) 485 int state;
482 return -EACCES; 486
483 487 ncp_lock_server(server);
484 if (put_user(server->sign_wanted, (int __user *)argp)) 488 state = server->sign_wanted;
485 return -EFAULT; 489 ncp_unlock_server(server);
486 return 0; 490 if (put_user(state, (int __user *)argp))
491 return -EFAULT;
492 return 0;
493 }
487 494
488 case NCP_IOC_SET_SIGN_WANTED: 495 case NCP_IOC_SET_SIGN_WANTED:
489 { 496 {
490 int newstate; 497 int newstate;
491 498
492 if (file_permission(filp, MAY_WRITE) != 0
493 && uid != server->m.mounted_uid)
494 return -EACCES;
495
496 /* get only low 8 bits... */ 499 /* get only low 8 bits... */
497 if (get_user(newstate, (unsigned char __user *)argp)) 500 if (get_user(newstate, (unsigned char __user *)argp))
498 return -EFAULT; 501 return -EFAULT;
502 result = 0;
503 ncp_lock_server(server);
499 if (server->sign_active) { 504 if (server->sign_active) {
500 /* cannot turn signatures OFF when active */ 505 /* cannot turn signatures OFF when active */
501 if (!newstate) return -EINVAL; 506 if (!newstate)
507 result = -EINVAL;
502 } else { 508 } else {
503 server->sign_wanted = newstate != 0; 509 server->sign_wanted = newstate != 0;
504 } 510 }
505 return 0; 511 ncp_unlock_server(server);
512 return result;
506 } 513 }
507 514
508#endif /* CONFIG_NCPFS_PACKET_SIGNING */ 515#endif /* CONFIG_NCPFS_PACKET_SIGNING */
509 516
510#ifdef CONFIG_NCPFS_IOCTL_LOCKING 517#ifdef CONFIG_NCPFS_IOCTL_LOCKING
511 case NCP_IOC_LOCKUNLOCK: 518 case NCP_IOC_LOCKUNLOCK:
512 if (file_permission(filp, MAY_WRITE) != 0
513 && uid != server->m.mounted_uid)
514 return -EACCES;
515
516 { 519 {
517 struct ncp_lock_ioctl rqdata; 520 struct ncp_lock_ioctl rqdata;
518 521
@@ -541,16 +544,13 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
541 { 544 {
542 return result; 545 return result;
543 } 546 }
544 result = -EIO;
545 if (!ncp_conn_valid(server))
546 goto outrel;
547 result = -EISDIR; 547 result = -EISDIR;
548 if (!S_ISREG(inode->i_mode)) 548 if (!S_ISREG(inode->i_mode))
549 goto outrel; 549 goto outrel;
550 if (rqdata.cmd == NCP_LOCK_CLEAR) 550 if (rqdata.cmd == NCP_LOCK_CLEAR)
551 { 551 {
552 result = ncp_ClearPhysicalRecord(NCP_SERVER(inode), 552 result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
553 NCP_FINFO(inode)->file_handle, 553 NCP_FINFO(inode)->file_handle,
554 rqdata.offset, 554 rqdata.offset,
555 rqdata.length); 555 rqdata.length);
556 if (result > 0) result = 0; /* no such lock */ 556 if (result > 0) result = 0; /* no such lock */
@@ -573,7 +573,7 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
573 rqdata.timeout); 573 rqdata.timeout);
574 if (result > 0) result = -EAGAIN; 574 if (result > 0) result = -EAGAIN;
575 } 575 }
576outrel: 576outrel:
577 ncp_inode_close(inode); 577 ncp_inode_close(inode);
578 return result; 578 return result;
579 } 579 }
@@ -581,60 +581,62 @@ outrel:
581 581
582#ifdef CONFIG_COMPAT 582#ifdef CONFIG_COMPAT
583 case NCP_IOC_GETOBJECTNAME_32: 583 case NCP_IOC_GETOBJECTNAME_32:
584 if (uid != server->m.mounted_uid)
585 return -EACCES;
586 { 584 {
587 struct compat_ncp_objectname_ioctl user; 585 struct compat_ncp_objectname_ioctl user;
588 size_t outl; 586 size_t outl;
589 587
590 if (copy_from_user(&user, argp, sizeof(user))) 588 if (copy_from_user(&user, argp, sizeof(user)))
591 return -EFAULT; 589 return -EFAULT;
590 down_read(&server->auth_rwsem);
592 user.auth_type = server->auth.auth_type; 591 user.auth_type = server->auth.auth_type;
593 outl = user.object_name_len; 592 outl = user.object_name_len;
594 user.object_name_len = server->auth.object_name_len; 593 user.object_name_len = server->auth.object_name_len;
595 if (outl > user.object_name_len) 594 if (outl > user.object_name_len)
596 outl = user.object_name_len; 595 outl = user.object_name_len;
596 result = 0;
597 if (outl) { 597 if (outl) {
598 if (copy_to_user(compat_ptr(user.object_name), 598 if (copy_to_user(compat_ptr(user.object_name),
599 server->auth.object_name, 599 server->auth.object_name,
600 outl)) return -EFAULT; 600 outl))
601 result = -EFAULT;
601 } 602 }
602 if (copy_to_user(argp, &user, sizeof(user))) 603 up_read(&server->auth_rwsem);
603 return -EFAULT; 604 if (!result && copy_to_user(argp, &user, sizeof(user)))
604 return 0; 605 result = -EFAULT;
606 return result;
605 } 607 }
606#endif 608#endif
607 609
608 case NCP_IOC_GETOBJECTNAME: 610 case NCP_IOC_GETOBJECTNAME:
609 if (uid != server->m.mounted_uid)
610 return -EACCES;
611 { 611 {
612 struct ncp_objectname_ioctl user; 612 struct ncp_objectname_ioctl user;
613 size_t outl; 613 size_t outl;
614 614
615 if (copy_from_user(&user, argp, sizeof(user))) 615 if (copy_from_user(&user, argp, sizeof(user)))
616 return -EFAULT; 616 return -EFAULT;
617 down_read(&server->auth_rwsem);
617 user.auth_type = server->auth.auth_type; 618 user.auth_type = server->auth.auth_type;
618 outl = user.object_name_len; 619 outl = user.object_name_len;
619 user.object_name_len = server->auth.object_name_len; 620 user.object_name_len = server->auth.object_name_len;
620 if (outl > user.object_name_len) 621 if (outl > user.object_name_len)
621 outl = user.object_name_len; 622 outl = user.object_name_len;
623 result = 0;
622 if (outl) { 624 if (outl) {
623 if (copy_to_user(user.object_name, 625 if (copy_to_user(user.object_name,
624 server->auth.object_name, 626 server->auth.object_name,
625 outl)) return -EFAULT; 627 outl))
628 result = -EFAULT;
626 } 629 }
627 if (copy_to_user(argp, &user, sizeof(user))) 630 up_read(&server->auth_rwsem);
628 return -EFAULT; 631 if (!result && copy_to_user(argp, &user, sizeof(user)))
629 return 0; 632 result = -EFAULT;
633 return result;
630 } 634 }
631 635
632#ifdef CONFIG_COMPAT 636#ifdef CONFIG_COMPAT
633 case NCP_IOC_SETOBJECTNAME_32: 637 case NCP_IOC_SETOBJECTNAME_32:
634#endif 638#endif
635 case NCP_IOC_SETOBJECTNAME: 639 case NCP_IOC_SETOBJECTNAME:
636 if (uid != server->m.mounted_uid)
637 return -EACCES;
638 { 640 {
639 struct ncp_objectname_ioctl user; 641 struct ncp_objectname_ioctl user;
640 void* newname; 642 void* newname;
@@ -666,9 +668,7 @@ outrel:
666 } else { 668 } else {
667 newname = NULL; 669 newname = NULL;
668 } 670 }
669 /* enter critical section */ 671 down_write(&server->auth_rwsem);
670 /* maybe that kfree can sleep so do that this way */
671 /* it is at least more SMP friendly (in future...) */
672 oldname = server->auth.object_name; 672 oldname = server->auth.object_name;
673 oldnamelen = server->auth.object_name_len; 673 oldnamelen = server->auth.object_name_len;
674 oldprivate = server->priv.data; 674 oldprivate = server->priv.data;
@@ -678,7 +678,7 @@ outrel:
678 server->auth.object_name = newname; 678 server->auth.object_name = newname;
679 server->priv.len = 0; 679 server->priv.len = 0;
680 server->priv.data = NULL; 680 server->priv.data = NULL;
681 /* leave critical section */ 681 up_write(&server->auth_rwsem);
682 kfree(oldprivate); 682 kfree(oldprivate);
683 kfree(oldname); 683 kfree(oldname);
684 return 0; 684 return 0;
@@ -688,8 +688,6 @@ outrel:
688 case NCP_IOC_GETPRIVATEDATA_32: 688 case NCP_IOC_GETPRIVATEDATA_32:
689#endif 689#endif
690 case NCP_IOC_GETPRIVATEDATA: 690 case NCP_IOC_GETPRIVATEDATA:
691 if (uid != server->m.mounted_uid)
692 return -EACCES;
693 { 691 {
694 struct ncp_privatedata_ioctl user; 692 struct ncp_privatedata_ioctl user;
695 size_t outl; 693 size_t outl;
@@ -706,14 +704,20 @@ outrel:
706 if (copy_from_user(&user, argp, sizeof(user))) 704 if (copy_from_user(&user, argp, sizeof(user)))
707 return -EFAULT; 705 return -EFAULT;
708 706
707 down_read(&server->auth_rwsem);
709 outl = user.len; 708 outl = user.len;
710 user.len = server->priv.len; 709 user.len = server->priv.len;
711 if (outl > user.len) outl = user.len; 710 if (outl > user.len) outl = user.len;
711 result = 0;
712 if (outl) { 712 if (outl) {
713 if (copy_to_user(user.data, 713 if (copy_to_user(user.data,
714 server->priv.data, 714 server->priv.data,
715 outl)) return -EFAULT; 715 outl))
716 result = -EFAULT;
716 } 717 }
718 up_read(&server->auth_rwsem);
719 if (result)
720 return result;
717#ifdef CONFIG_COMPAT 721#ifdef CONFIG_COMPAT
718 if (cmd == NCP_IOC_GETPRIVATEDATA_32) { 722 if (cmd == NCP_IOC_GETPRIVATEDATA_32) {
719 struct compat_ncp_privatedata_ioctl user32; 723 struct compat_ncp_privatedata_ioctl user32;
@@ -733,8 +737,6 @@ outrel:
733 case NCP_IOC_SETPRIVATEDATA_32: 737 case NCP_IOC_SETPRIVATEDATA_32:
734#endif 738#endif
735 case NCP_IOC_SETPRIVATEDATA: 739 case NCP_IOC_SETPRIVATEDATA:
736 if (uid != server->m.mounted_uid)
737 return -EACCES;
738 { 740 {
739 struct ncp_privatedata_ioctl user; 741 struct ncp_privatedata_ioctl user;
740 void* new; 742 void* new;
@@ -762,12 +764,12 @@ outrel:
762 } else { 764 } else {
763 new = NULL; 765 new = NULL;
764 } 766 }
765 /* enter critical section */ 767 down_write(&server->auth_rwsem);
766 old = server->priv.data; 768 old = server->priv.data;
767 oldlen = server->priv.len; 769 oldlen = server->priv.len;
768 server->priv.len = user.len; 770 server->priv.len = user.len;
769 server->priv.data = new; 771 server->priv.data = new;
770 /* leave critical section */ 772 up_write(&server->auth_rwsem);
771 kfree(old); 773 kfree(old);
772 return 0; 774 return 0;
773 } 775 }
@@ -775,17 +777,13 @@ outrel:
775#ifdef CONFIG_NCPFS_NLS 777#ifdef CONFIG_NCPFS_NLS
776 case NCP_IOC_SETCHARSETS: 778 case NCP_IOC_SETCHARSETS:
777 return ncp_set_charsets(server, argp); 779 return ncp_set_charsets(server, argp);
778 780
779 case NCP_IOC_GETCHARSETS: 781 case NCP_IOC_GETCHARSETS:
780 return ncp_get_charsets(server, argp); 782 return ncp_get_charsets(server, argp);
781 783
782#endif /* CONFIG_NCPFS_NLS */ 784#endif /* CONFIG_NCPFS_NLS */
783 785
784 case NCP_IOC_SETDENTRYTTL: 786 case NCP_IOC_SETDENTRYTTL:
785 if (file_permission(filp, MAY_WRITE) != 0 &&
786 uid != server->m.mounted_uid)
787 return -EACCES;
788
789 { 787 {
790 u_int32_t user; 788 u_int32_t user;
791 789
@@ -795,13 +793,13 @@ outrel:
795 if (user > 20000) 793 if (user > 20000)
796 return -EINVAL; 794 return -EINVAL;
797 user = (user * HZ) / 1000; 795 user = (user * HZ) / 1000;
798 server->dentry_ttl = user; 796 atomic_set(&server->dentry_ttl, user);
799 return 0; 797 return 0;
800 } 798 }
801 799
802 case NCP_IOC_GETDENTRYTTL: 800 case NCP_IOC_GETDENTRYTTL:
803 { 801 {
804 u_int32_t user = (server->dentry_ttl * 1000) / HZ; 802 u_int32_t user = (atomic_read(&server->dentry_ttl) * 1000) / HZ;
805 if (copy_to_user(argp, &user, sizeof(user))) 803 if (copy_to_user(argp, &user, sizeof(user)))
806 return -EFAULT; 804 return -EFAULT;
807 return 0; 805 return 0;
@@ -811,59 +809,103 @@ outrel:
811 return -EINVAL; 809 return -EINVAL;
812} 810}
813 811
814static int ncp_ioctl_need_write(unsigned int cmd) 812long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
815{ 813{
814 struct inode *inode = filp->f_dentry->d_inode;
815 struct ncp_server *server = NCP_SERVER(inode);
816 uid_t uid = current_uid();
817 int need_drop_write = 0;
818 long ret;
819
816 switch (cmd) { 820 switch (cmd) {
817 case NCP_IOC_GET_FS_INFO:
818 case NCP_IOC_GET_FS_INFO_V2:
819 case NCP_IOC_NCPREQUEST:
820 case NCP_IOC_SETDENTRYTTL:
821 case NCP_IOC_SIGN_INIT:
822 case NCP_IOC_LOCKUNLOCK:
823 case NCP_IOC_SET_SIGN_WANTED:
824 return 1;
825 case NCP_IOC_GETOBJECTNAME:
826 case NCP_IOC_SETOBJECTNAME:
827 case NCP_IOC_GETPRIVATEDATA:
828 case NCP_IOC_SETPRIVATEDATA:
829 case NCP_IOC_SETCHARSETS: 821 case NCP_IOC_SETCHARSETS:
830 case NCP_IOC_GETCHARSETS:
831 case NCP_IOC_CONN_LOGGED_IN: 822 case NCP_IOC_CONN_LOGGED_IN:
832 case NCP_IOC_GETDENTRYTTL:
833 case NCP_IOC_GETMOUNTUID2:
834 case NCP_IOC_SIGN_WANTED:
835 case NCP_IOC_GETROOT:
836 case NCP_IOC_SETROOT: 823 case NCP_IOC_SETROOT:
837 return 0; 824 if (!capable(CAP_SYS_ADMIN)) {
838 default: 825 ret = -EACCES;
839 /* unknown IOCTL command, assume write */ 826 goto out;
840 return 1; 827 }
828 break;
841 } 829 }
842} 830 if (server->m.mounted_uid != uid) {
843 831 switch (cmd) {
844long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
845{
846 long ret;
847
848 lock_kernel();
849 if (ncp_ioctl_need_write(cmd)) {
850 /* 832 /*
851 * inside the ioctl(), any failures which 833 * Only mount owner can issue these ioctls. Information
852 * are because of file_permission() are 834 * necessary to authenticate to other NDS servers are
853 * -EACCESS, so it seems consistent to keep 835 * stored here.
854 * that here.
855 */ 836 */
856 if (mnt_want_write(filp->f_path.mnt)) { 837 case NCP_IOC_GETOBJECTNAME:
838 case NCP_IOC_SETOBJECTNAME:
839 case NCP_IOC_GETPRIVATEDATA:
840 case NCP_IOC_SETPRIVATEDATA:
841#ifdef CONFIG_COMPAT
842 case NCP_IOC_GETOBJECTNAME_32:
843 case NCP_IOC_SETOBJECTNAME_32:
844 case NCP_IOC_GETPRIVATEDATA_32:
845 case NCP_IOC_SETPRIVATEDATA_32:
846#endif
857 ret = -EACCES; 847 ret = -EACCES;
858 goto out; 848 goto out;
849 /*
850 * These require write access on the inode if user id
851 * does not match. Note that they do not write to the
852 * file... But old code did mnt_want_write, so I keep
853 * it as is. Of course not for mountpoint owner, as
854 * that breaks read-only mounts altogether as ncpmount
855 * needs working NCP_IOC_NCPREQUEST and
856 * NCP_IOC_GET_FS_INFO. Some of these codes (setdentryttl,
857 * signinit, setsignwanted) should be probably restricted
858 * to owner only, or even more to CAP_SYS_ADMIN).
859 */
860 case NCP_IOC_GET_FS_INFO:
861 case NCP_IOC_GET_FS_INFO_V2:
862 case NCP_IOC_NCPREQUEST:
863 case NCP_IOC_SETDENTRYTTL:
864 case NCP_IOC_SIGN_INIT:
865 case NCP_IOC_LOCKUNLOCK:
866 case NCP_IOC_SET_SIGN_WANTED:
867#ifdef CONFIG_COMPAT
868 case NCP_IOC_GET_FS_INFO_V2_32:
869 case NCP_IOC_NCPREQUEST_32:
870#endif
871 ret = mnt_want_write_file(filp);
872 if (ret)
873 goto out;
874 need_drop_write = 1;
875 ret = inode_permission(inode, MAY_WRITE);
876 if (ret)
877 goto outDropWrite;
878 break;
879 /*
880 * Read access required.
881 */
882 case NCP_IOC_GETMOUNTUID16:
883 case NCP_IOC_GETMOUNTUID32:
884 case NCP_IOC_GETMOUNTUID64:
885 case NCP_IOC_GETROOT:
886 case NCP_IOC_SIGN_WANTED:
887 ret = inode_permission(inode, MAY_READ);
888 if (ret)
889 goto out;
890 break;
891 /*
892 * Anybody can read these.
893 */
894 case NCP_IOC_GETCHARSETS:
895 case NCP_IOC_GETDENTRYTTL:
896 default:
897 /* Three codes below are protected by CAP_SYS_ADMIN above. */
898 case NCP_IOC_SETCHARSETS:
899 case NCP_IOC_CONN_LOGGED_IN:
900 case NCP_IOC_SETROOT:
901 break;
859 } 902 }
860 } 903 }
861 ret = __ncp_ioctl(filp, cmd, arg); 904 ret = __ncp_ioctl(inode, cmd, arg);
862 if (ncp_ioctl_need_write(cmd)) 905outDropWrite:
906 if (need_drop_write)
863 mnt_drop_write(filp->f_path.mnt); 907 mnt_drop_write(filp->f_path.mnt);
864
865out: 908out:
866 unlock_kernel();
867 return ret; 909 return ret;
868} 910}
869 911
@@ -872,10 +914,8 @@ long ncp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
872{ 914{
873 long ret; 915 long ret;
874 916
875 lock_kernel();
876 arg = (unsigned long) compat_ptr(arg); 917 arg = (unsigned long) compat_ptr(arg);
877 ret = ncp_ioctl(file, cmd, arg); 918 ret = ncp_ioctl(file, cmd, arg);
878 unlock_kernel();
879 return ret; 919 return ret;
880} 920}
881#endif 921#endif