at v4.12 922 lines 23 kB view raw
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/capability.h> 11#include <linux/compat.h> 12#include <linux/errno.h> 13#include <linux/fs.h> 14#include <linux/ioctl.h> 15#include <linux/time.h> 16#include <linux/mm.h> 17#include <linux/mount.h> 18#include <linux/slab.h> 19#include <linux/highuid.h> 20#include <linux/vmalloc.h> 21#include <linux/sched.h> 22#include <linux/cred.h> 23 24#include <linux/uaccess.h> 25 26#include "ncp_fs.h" 27 28/* maximum limit for ncp_objectname_ioctl */ 29#define NCP_OBJECT_NAME_MAX_LEN 4096 30/* maximum limit for ncp_privatedata_ioctl */ 31#define NCP_PRIVATE_DATA_MAX_LEN 8192 32/* maximum negotiable packet size */ 33#define NCP_PACKET_SIZE_INTERNAL 65536 34 35static int 36ncp_get_fs_info(struct ncp_server * server, struct inode *inode, 37 struct ncp_fs_info __user *arg) 38{ 39 struct ncp_fs_info info; 40 41 if (copy_from_user(&info, arg, sizeof(info))) 42 return -EFAULT; 43 44 if (info.version != NCP_GET_FS_INFO_VERSION) { 45 ncp_dbg(1, "info.version invalid: %d\n", info.version); 46 return -EINVAL; 47 } 48 /* TODO: info.addr = server->m.serv_addr; */ 49 SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid)); 50 info.connection = server->connection; 51 info.buffer_size = server->buffer_size; 52 info.volume_number = NCP_FINFO(inode)->volNumber; 53 info.directory_id = NCP_FINFO(inode)->DosDirNum; 54 55 if (copy_to_user(arg, &info, sizeof(info))) 56 return -EFAULT; 57 return 0; 58} 59 60static int 61ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode, 62 struct ncp_fs_info_v2 __user * arg) 63{ 64 struct ncp_fs_info_v2 info2; 65 66 if (copy_from_user(&info2, arg, sizeof(info2))) 67 return -EFAULT; 68 69 if (info2.version != NCP_GET_FS_INFO_VERSION_V2) { 70 ncp_dbg(1, "info.version invalid: %d\n", info2.version); 71 return -EINVAL; 72 } 73 info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); 74 info2.connection = server->connection; 75 info2.buffer_size = server->buffer_size; 76 info2.volume_number = NCP_FINFO(inode)->volNumber; 77 info2.directory_id = NCP_FINFO(inode)->DosDirNum; 78 info2.dummy1 = info2.dummy2 = info2.dummy3 = 0; 79 80 if (copy_to_user(arg, &info2, sizeof(info2))) 81 return -EFAULT; 82 return 0; 83} 84 85#ifdef CONFIG_COMPAT 86struct compat_ncp_objectname_ioctl 87{ 88 s32 auth_type; 89 u32 object_name_len; 90 compat_caddr_t object_name; /* a userspace data, in most cases user name */ 91}; 92 93struct compat_ncp_fs_info_v2 { 94 s32 version; 95 u32 mounted_uid; 96 u32 connection; 97 u32 buffer_size; 98 99 u32 volume_number; 100 u32 directory_id; 101 102 u32 dummy1; 103 u32 dummy2; 104 u32 dummy3; 105}; 106 107struct compat_ncp_ioctl_request { 108 u32 function; 109 u32 size; 110 compat_caddr_t data; 111}; 112 113struct compat_ncp_privatedata_ioctl 114{ 115 u32 len; 116 compat_caddr_t data; /* ~1000 for NDS */ 117}; 118 119#define NCP_IOC_GET_FS_INFO_V2_32 _IOWR('n', 4, struct compat_ncp_fs_info_v2) 120#define NCP_IOC_NCPREQUEST_32 _IOR('n', 1, struct compat_ncp_ioctl_request) 121#define NCP_IOC_GETOBJECTNAME_32 _IOWR('n', 9, struct compat_ncp_objectname_ioctl) 122#define NCP_IOC_SETOBJECTNAME_32 _IOR('n', 9, struct compat_ncp_objectname_ioctl) 123#define NCP_IOC_GETPRIVATEDATA_32 _IOWR('n', 10, struct compat_ncp_privatedata_ioctl) 124#define NCP_IOC_SETPRIVATEDATA_32 _IOR('n', 10, struct compat_ncp_privatedata_ioctl) 125 126static int 127ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode, 128 struct compat_ncp_fs_info_v2 __user * arg) 129{ 130 struct compat_ncp_fs_info_v2 info2; 131 132 if (copy_from_user(&info2, arg, sizeof(info2))) 133 return -EFAULT; 134 135 if (info2.version != NCP_GET_FS_INFO_VERSION_V2) { 136 ncp_dbg(1, "info.version invalid: %d\n", info2.version); 137 return -EINVAL; 138 } 139 info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); 140 info2.connection = server->connection; 141 info2.buffer_size = server->buffer_size; 142 info2.volume_number = NCP_FINFO(inode)->volNumber; 143 info2.directory_id = NCP_FINFO(inode)->DosDirNum; 144 info2.dummy1 = info2.dummy2 = info2.dummy3 = 0; 145 146 if (copy_to_user(arg, &info2, sizeof(info2))) 147 return -EFAULT; 148 return 0; 149} 150#endif 151 152#define NCP_IOC_GETMOUNTUID16 _IOW('n', 2, u16) 153#define NCP_IOC_GETMOUNTUID32 _IOW('n', 2, u32) 154#define NCP_IOC_GETMOUNTUID64 _IOW('n', 2, u64) 155 156#ifdef CONFIG_NCPFS_NLS 157/* Here we are select the iocharset and the codepage for NLS. 158 * Thanks Petr Vandrovec for idea and many hints. 159 */ 160static int 161ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) 162{ 163 struct ncp_nls_ioctl user; 164 struct nls_table *codepage; 165 struct nls_table *iocharset; 166 struct nls_table *oldset_io; 167 struct nls_table *oldset_cp; 168 int utf8; 169 int err; 170 171 if (copy_from_user(&user, arg, sizeof(user))) 172 return -EFAULT; 173 174 codepage = NULL; 175 user.codepage[NCP_IOCSNAME_LEN] = 0; 176 if (!user.codepage[0] || !strcmp(user.codepage, "default")) 177 codepage = load_nls_default(); 178 else { 179 codepage = load_nls(user.codepage); 180 if (!codepage) { 181 return -EBADRQC; 182 } 183 } 184 185 iocharset = NULL; 186 user.iocharset[NCP_IOCSNAME_LEN] = 0; 187 if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) { 188 iocharset = load_nls_default(); 189 utf8 = 0; 190 } else if (!strcmp(user.iocharset, "utf8")) { 191 iocharset = load_nls_default(); 192 utf8 = 1; 193 } else { 194 iocharset = load_nls(user.iocharset); 195 if (!iocharset) { 196 unload_nls(codepage); 197 return -EBADRQC; 198 } 199 utf8 = 0; 200 } 201 202 mutex_lock(&server->root_setup_lock); 203 if (server->root_setuped) { 204 oldset_cp = codepage; 205 oldset_io = iocharset; 206 err = -EBUSY; 207 } else { 208 if (utf8) 209 NCP_SET_FLAG(server, NCP_FLAG_UTF8); 210 else 211 NCP_CLR_FLAG(server, NCP_FLAG_UTF8); 212 oldset_cp = server->nls_vol; 213 server->nls_vol = codepage; 214 oldset_io = server->nls_io; 215 server->nls_io = iocharset; 216 err = 0; 217 } 218 mutex_unlock(&server->root_setup_lock); 219 unload_nls(oldset_cp); 220 unload_nls(oldset_io); 221 222 return err; 223} 224 225static int 226ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) 227{ 228 struct ncp_nls_ioctl user; 229 int len; 230 231 memset(&user, 0, sizeof(user)); 232 mutex_lock(&server->root_setup_lock); 233 if (server->nls_vol && server->nls_vol->charset) { 234 len = strlen(server->nls_vol->charset); 235 if (len > NCP_IOCSNAME_LEN) 236 len = NCP_IOCSNAME_LEN; 237 strncpy(user.codepage, server->nls_vol->charset, len); 238 user.codepage[len] = 0; 239 } 240 241 if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) 242 strcpy(user.iocharset, "utf8"); 243 else if (server->nls_io && server->nls_io->charset) { 244 len = strlen(server->nls_io->charset); 245 if (len > NCP_IOCSNAME_LEN) 246 len = NCP_IOCSNAME_LEN; 247 strncpy(user.iocharset, server->nls_io->charset, len); 248 user.iocharset[len] = 0; 249 } 250 mutex_unlock(&server->root_setup_lock); 251 252 if (copy_to_user(arg, &user, sizeof(user))) 253 return -EFAULT; 254 return 0; 255} 256#endif /* CONFIG_NCPFS_NLS */ 257 258static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg) 259{ 260 struct ncp_server *server = NCP_SERVER(inode); 261 int result; 262 struct ncp_ioctl_request request; 263 char* bouncebuffer; 264 void __user *argp = (void __user *)arg; 265 266 switch (cmd) { 267#ifdef CONFIG_COMPAT 268 case NCP_IOC_NCPREQUEST_32: 269#endif 270 case NCP_IOC_NCPREQUEST: 271#ifdef CONFIG_COMPAT 272 if (cmd == NCP_IOC_NCPREQUEST_32) { 273 struct compat_ncp_ioctl_request request32; 274 if (copy_from_user(&request32, argp, sizeof(request32))) 275 return -EFAULT; 276 request.function = request32.function; 277 request.size = request32.size; 278 request.data = compat_ptr(request32.data); 279 } else 280#endif 281 if (copy_from_user(&request, argp, sizeof(request))) 282 return -EFAULT; 283 284 if ((request.function > 255) 285 || (request.size > 286 NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) { 287 return -EINVAL; 288 } 289 bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL); 290 if (!bouncebuffer) 291 return -ENOMEM; 292 if (copy_from_user(bouncebuffer, request.data, request.size)) { 293 vfree(bouncebuffer); 294 return -EFAULT; 295 } 296 ncp_lock_server(server); 297 298 /* FIXME: We hack around in the server's structures 299 here to be able to use ncp_request */ 300 301 server->has_subfunction = 0; 302 server->current_size = request.size; 303 memcpy(server->packet, bouncebuffer, request.size); 304 305 result = ncp_request2(server, request.function, 306 bouncebuffer, NCP_PACKET_SIZE_INTERNAL); 307 if (result < 0) 308 result = -EIO; 309 else 310 result = server->reply_size; 311 ncp_unlock_server(server); 312 ncp_dbg(1, "copy %d bytes\n", result); 313 if (result >= 0) 314 if (copy_to_user(request.data, bouncebuffer, result)) 315 result = -EFAULT; 316 vfree(bouncebuffer); 317 return result; 318 319 case NCP_IOC_CONN_LOGGED_IN: 320 321 if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE)) 322 return -EINVAL; 323 mutex_lock(&server->root_setup_lock); 324 if (server->root_setuped) 325 result = -EBUSY; 326 else { 327 result = ncp_conn_logged_in(inode->i_sb); 328 if (result == 0) 329 server->root_setuped = 1; 330 } 331 mutex_unlock(&server->root_setup_lock); 332 return result; 333 334 case NCP_IOC_GET_FS_INFO: 335 return ncp_get_fs_info(server, inode, argp); 336 337 case NCP_IOC_GET_FS_INFO_V2: 338 return ncp_get_fs_info_v2(server, inode, argp); 339 340#ifdef CONFIG_COMPAT 341 case NCP_IOC_GET_FS_INFO_V2_32: 342 return ncp_get_compat_fs_info_v2(server, inode, argp); 343#endif 344 /* we have too many combinations of CONFIG_COMPAT, 345 * CONFIG_64BIT and CONFIG_UID16, so just handle 346 * any of the possible ioctls */ 347 case NCP_IOC_GETMOUNTUID16: 348 { 349 u16 uid; 350 351 SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid)); 352 if (put_user(uid, (u16 __user *)argp)) 353 return -EFAULT; 354 return 0; 355 } 356 case NCP_IOC_GETMOUNTUID32: 357 { 358 uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); 359 if (put_user(uid, (u32 __user *)argp)) 360 return -EFAULT; 361 return 0; 362 } 363 case NCP_IOC_GETMOUNTUID64: 364 { 365 uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); 366 if (put_user(uid, (u64 __user *)argp)) 367 return -EFAULT; 368 return 0; 369 } 370 case NCP_IOC_GETROOT: 371 { 372 struct ncp_setroot_ioctl sr; 373 374 result = -EACCES; 375 mutex_lock(&server->root_setup_lock); 376 if (server->m.mounted_vol[0]) { 377 struct dentry* dentry = inode->i_sb->s_root; 378 379 if (dentry) { 380 struct inode* s_inode = d_inode(dentry); 381 382 if (s_inode) { 383 sr.volNumber = NCP_FINFO(s_inode)->volNumber; 384 sr.dirEntNum = NCP_FINFO(s_inode)->dirEntNum; 385 sr.namespace = server->name_space[sr.volNumber]; 386 result = 0; 387 } else 388 ncp_dbg(1, "d_inode(s_root)==NULL\n"); 389 } else 390 ncp_dbg(1, "s_root==NULL\n"); 391 } else { 392 sr.volNumber = -1; 393 sr.namespace = 0; 394 sr.dirEntNum = 0; 395 result = 0; 396 } 397 mutex_unlock(&server->root_setup_lock); 398 if (!result && copy_to_user(argp, &sr, sizeof(sr))) 399 result = -EFAULT; 400 return result; 401 } 402 403 case NCP_IOC_SETROOT: 404 { 405 struct ncp_setroot_ioctl sr; 406 __u32 vnum; 407 __le32 de; 408 __le32 dosde; 409 struct dentry* dentry; 410 411 if (copy_from_user(&sr, argp, sizeof(sr))) 412 return -EFAULT; 413 mutex_lock(&server->root_setup_lock); 414 if (server->root_setuped) 415 result = -EBUSY; 416 else { 417 if (sr.volNumber < 0) { 418 server->m.mounted_vol[0] = 0; 419 vnum = NCP_NUMBER_OF_VOLUMES; 420 de = 0; 421 dosde = 0; 422 result = 0; 423 } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) { 424 result = -EINVAL; 425 } else if (ncp_mount_subdir(server, sr.volNumber, 426 sr.namespace, sr.dirEntNum, 427 &vnum, &de, &dosde)) { 428 result = -ENOENT; 429 } else 430 result = 0; 431 432 if (result == 0) { 433 dentry = inode->i_sb->s_root; 434 if (dentry) { 435 struct inode* s_inode = d_inode(dentry); 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 ncp_dbg(1, "d_inode(s_root)==NULL\n"); 444 result = -EIO; 445 } 446 } else { 447 ncp_dbg(1, "s_root==NULL\n"); 448 result = -EIO; 449 } 450 } 451 } 452 mutex_unlock(&server->root_setup_lock); 453 454 return result; 455 } 456 457#ifdef CONFIG_NCPFS_PACKET_SIGNING 458 case NCP_IOC_SIGN_INIT: 459 { 460 struct ncp_sign_init sign; 461 462 if (argp) 463 if (copy_from_user(&sign, argp, sizeof(sign))) 464 return -EFAULT; 465 ncp_lock_server(server); 466 mutex_lock(&server->rcv.creq_mutex); 467 if (argp) { 468 if (server->sign_wanted) { 469 memcpy(server->sign_root,sign.sign_root,8); 470 memcpy(server->sign_last,sign.sign_last,16); 471 server->sign_active = 1; 472 } 473 /* ignore when signatures not wanted */ 474 } else { 475 server->sign_active = 0; 476 } 477 mutex_unlock(&server->rcv.creq_mutex); 478 ncp_unlock_server(server); 479 return 0; 480 } 481 482 case NCP_IOC_SIGN_WANTED: 483 { 484 int state; 485 486 ncp_lock_server(server); 487 state = server->sign_wanted; 488 ncp_unlock_server(server); 489 if (put_user(state, (int __user *)argp)) 490 return -EFAULT; 491 return 0; 492 } 493 494 case NCP_IOC_SET_SIGN_WANTED: 495 { 496 int newstate; 497 498 /* get only low 8 bits... */ 499 if (get_user(newstate, (unsigned char __user *)argp)) 500 return -EFAULT; 501 result = 0; 502 ncp_lock_server(server); 503 if (server->sign_active) { 504 /* cannot turn signatures OFF when active */ 505 if (!newstate) 506 result = -EINVAL; 507 } else { 508 server->sign_wanted = newstate != 0; 509 } 510 ncp_unlock_server(server); 511 return result; 512 } 513 514#endif /* CONFIG_NCPFS_PACKET_SIGNING */ 515 516#ifdef CONFIG_NCPFS_IOCTL_LOCKING 517 case NCP_IOC_LOCKUNLOCK: 518 { 519 struct ncp_lock_ioctl rqdata; 520 521 if (copy_from_user(&rqdata, argp, sizeof(rqdata))) 522 return -EFAULT; 523 if (rqdata.origin != 0) 524 return -EINVAL; 525 /* check for cmd */ 526 switch (rqdata.cmd) { 527 case NCP_LOCK_EX: 528 case NCP_LOCK_SH: 529 if (rqdata.timeout < 0) 530 return -EINVAL; 531 if (rqdata.timeout == 0) 532 rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; 533 else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT) 534 rqdata.timeout = NCP_LOCK_MAX_TIMEOUT; 535 break; 536 case NCP_LOCK_LOG: 537 rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */ 538 case NCP_LOCK_CLEAR: 539 break; 540 default: 541 return -EINVAL; 542 } 543 /* locking needs both read and write access */ 544 if ((result = ncp_make_open(inode, O_RDWR)) != 0) 545 { 546 return result; 547 } 548 result = -EISDIR; 549 if (!S_ISREG(inode->i_mode)) 550 goto outrel; 551 if (rqdata.cmd == NCP_LOCK_CLEAR) 552 { 553 result = ncp_ClearPhysicalRecord(NCP_SERVER(inode), 554 NCP_FINFO(inode)->file_handle, 555 rqdata.offset, 556 rqdata.length); 557 if (result > 0) result = 0; /* no such lock */ 558 } 559 else 560 { 561 int lockcmd; 562 563 switch (rqdata.cmd) 564 { 565 case NCP_LOCK_EX: lockcmd=1; break; 566 case NCP_LOCK_SH: lockcmd=3; break; 567 default: lockcmd=0; break; 568 } 569 result = ncp_LogPhysicalRecord(NCP_SERVER(inode), 570 NCP_FINFO(inode)->file_handle, 571 lockcmd, 572 rqdata.offset, 573 rqdata.length, 574 rqdata.timeout); 575 if (result > 0) result = -EAGAIN; 576 } 577outrel: 578 ncp_inode_close(inode); 579 return result; 580 } 581#endif /* CONFIG_NCPFS_IOCTL_LOCKING */ 582 583#ifdef CONFIG_COMPAT 584 case NCP_IOC_GETOBJECTNAME_32: 585 { 586 struct compat_ncp_objectname_ioctl user; 587 size_t outl; 588 589 if (copy_from_user(&user, argp, sizeof(user))) 590 return -EFAULT; 591 down_read(&server->auth_rwsem); 592 user.auth_type = server->auth.auth_type; 593 outl = user.object_name_len; 594 user.object_name_len = server->auth.object_name_len; 595 if (outl > user.object_name_len) 596 outl = user.object_name_len; 597 result = 0; 598 if (outl) { 599 if (copy_to_user(compat_ptr(user.object_name), 600 server->auth.object_name, 601 outl)) 602 result = -EFAULT; 603 } 604 up_read(&server->auth_rwsem); 605 if (!result && copy_to_user(argp, &user, sizeof(user))) 606 result = -EFAULT; 607 return result; 608 } 609#endif 610 611 case NCP_IOC_GETOBJECTNAME: 612 { 613 struct ncp_objectname_ioctl user; 614 size_t outl; 615 616 if (copy_from_user(&user, argp, sizeof(user))) 617 return -EFAULT; 618 down_read(&server->auth_rwsem); 619 user.auth_type = server->auth.auth_type; 620 outl = user.object_name_len; 621 user.object_name_len = server->auth.object_name_len; 622 if (outl > user.object_name_len) 623 outl = user.object_name_len; 624 result = 0; 625 if (outl) { 626 if (copy_to_user(user.object_name, 627 server->auth.object_name, 628 outl)) 629 result = -EFAULT; 630 } 631 up_read(&server->auth_rwsem); 632 if (!result && copy_to_user(argp, &user, sizeof(user))) 633 result = -EFAULT; 634 return result; 635 } 636 637#ifdef CONFIG_COMPAT 638 case NCP_IOC_SETOBJECTNAME_32: 639#endif 640 case NCP_IOC_SETOBJECTNAME: 641 { 642 struct ncp_objectname_ioctl user; 643 void* newname; 644 void* oldname; 645 size_t oldnamelen; 646 void* oldprivate; 647 size_t oldprivatelen; 648 649#ifdef CONFIG_COMPAT 650 if (cmd == NCP_IOC_SETOBJECTNAME_32) { 651 struct compat_ncp_objectname_ioctl user32; 652 if (copy_from_user(&user32, argp, sizeof(user32))) 653 return -EFAULT; 654 user.auth_type = user32.auth_type; 655 user.object_name_len = user32.object_name_len; 656 user.object_name = compat_ptr(user32.object_name); 657 } else 658#endif 659 if (copy_from_user(&user, argp, sizeof(user))) 660 return -EFAULT; 661 662 if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN) 663 return -ENOMEM; 664 if (user.object_name_len) { 665 newname = memdup_user(user.object_name, 666 user.object_name_len); 667 if (IS_ERR(newname)) 668 return PTR_ERR(newname); 669 } else { 670 newname = NULL; 671 } 672 down_write(&server->auth_rwsem); 673 oldname = server->auth.object_name; 674 oldnamelen = server->auth.object_name_len; 675 oldprivate = server->priv.data; 676 oldprivatelen = server->priv.len; 677 server->auth.auth_type = user.auth_type; 678 server->auth.object_name_len = user.object_name_len; 679 server->auth.object_name = newname; 680 server->priv.len = 0; 681 server->priv.data = NULL; 682 up_write(&server->auth_rwsem); 683 kfree(oldprivate); 684 kfree(oldname); 685 return 0; 686 } 687 688#ifdef CONFIG_COMPAT 689 case NCP_IOC_GETPRIVATEDATA_32: 690#endif 691 case NCP_IOC_GETPRIVATEDATA: 692 { 693 struct ncp_privatedata_ioctl user; 694 size_t outl; 695 696#ifdef CONFIG_COMPAT 697 if (cmd == NCP_IOC_GETPRIVATEDATA_32) { 698 struct compat_ncp_privatedata_ioctl user32; 699 if (copy_from_user(&user32, argp, sizeof(user32))) 700 return -EFAULT; 701 user.len = user32.len; 702 user.data = compat_ptr(user32.data); 703 } else 704#endif 705 if (copy_from_user(&user, argp, sizeof(user))) 706 return -EFAULT; 707 708 down_read(&server->auth_rwsem); 709 outl = user.len; 710 user.len = server->priv.len; 711 if (outl > user.len) outl = user.len; 712 result = 0; 713 if (outl) { 714 if (copy_to_user(user.data, 715 server->priv.data, 716 outl)) 717 result = -EFAULT; 718 } 719 up_read(&server->auth_rwsem); 720 if (result) 721 return result; 722#ifdef CONFIG_COMPAT 723 if (cmd == NCP_IOC_GETPRIVATEDATA_32) { 724 struct compat_ncp_privatedata_ioctl user32; 725 user32.len = user.len; 726 user32.data = (unsigned long) user.data; 727 if (copy_to_user(argp, &user32, sizeof(user32))) 728 return -EFAULT; 729 } else 730#endif 731 if (copy_to_user(argp, &user, sizeof(user))) 732 return -EFAULT; 733 734 return 0; 735 } 736 737#ifdef CONFIG_COMPAT 738 case NCP_IOC_SETPRIVATEDATA_32: 739#endif 740 case NCP_IOC_SETPRIVATEDATA: 741 { 742 struct ncp_privatedata_ioctl user; 743 void* new; 744 void* old; 745 size_t oldlen; 746 747#ifdef CONFIG_COMPAT 748 if (cmd == NCP_IOC_SETPRIVATEDATA_32) { 749 struct compat_ncp_privatedata_ioctl user32; 750 if (copy_from_user(&user32, argp, sizeof(user32))) 751 return -EFAULT; 752 user.len = user32.len; 753 user.data = compat_ptr(user32.data); 754 } else 755#endif 756 if (copy_from_user(&user, argp, sizeof(user))) 757 return -EFAULT; 758 759 if (user.len > NCP_PRIVATE_DATA_MAX_LEN) 760 return -ENOMEM; 761 if (user.len) { 762 new = memdup_user(user.data, user.len); 763 if (IS_ERR(new)) 764 return PTR_ERR(new); 765 } else { 766 new = NULL; 767 } 768 down_write(&server->auth_rwsem); 769 old = server->priv.data; 770 oldlen = server->priv.len; 771 server->priv.len = user.len; 772 server->priv.data = new; 773 up_write(&server->auth_rwsem); 774 kfree(old); 775 return 0; 776 } 777 778#ifdef CONFIG_NCPFS_NLS 779 case NCP_IOC_SETCHARSETS: 780 return ncp_set_charsets(server, argp); 781 782 case NCP_IOC_GETCHARSETS: 783 return ncp_get_charsets(server, argp); 784 785#endif /* CONFIG_NCPFS_NLS */ 786 787 case NCP_IOC_SETDENTRYTTL: 788 { 789 u_int32_t user; 790 791 if (copy_from_user(&user, argp, sizeof(user))) 792 return -EFAULT; 793 /* 20 secs at most... */ 794 if (user > 20000) 795 return -EINVAL; 796 user = (user * HZ) / 1000; 797 atomic_set(&server->dentry_ttl, user); 798 return 0; 799 } 800 801 case NCP_IOC_GETDENTRYTTL: 802 { 803 u_int32_t user = (atomic_read(&server->dentry_ttl) * 1000) / HZ; 804 if (copy_to_user(argp, &user, sizeof(user))) 805 return -EFAULT; 806 return 0; 807 } 808 809 } 810 return -EINVAL; 811} 812 813long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 814{ 815 struct inode *inode = file_inode(filp); 816 struct ncp_server *server = NCP_SERVER(inode); 817 kuid_t uid = current_uid(); 818 int need_drop_write = 0; 819 long ret; 820 821 switch (cmd) { 822 case NCP_IOC_SETCHARSETS: 823 case NCP_IOC_CONN_LOGGED_IN: 824 case NCP_IOC_SETROOT: 825 if (!capable(CAP_SYS_ADMIN)) { 826 ret = -EPERM; 827 goto out; 828 } 829 break; 830 } 831 if (!uid_eq(server->m.mounted_uid, uid)) { 832 switch (cmd) { 833 /* 834 * Only mount owner can issue these ioctls. Information 835 * necessary to authenticate to other NDS servers are 836 * stored here. 837 */ 838 case NCP_IOC_GETOBJECTNAME: 839 case NCP_IOC_SETOBJECTNAME: 840 case NCP_IOC_GETPRIVATEDATA: 841 case NCP_IOC_SETPRIVATEDATA: 842#ifdef CONFIG_COMPAT 843 case NCP_IOC_GETOBJECTNAME_32: 844 case NCP_IOC_SETOBJECTNAME_32: 845 case NCP_IOC_GETPRIVATEDATA_32: 846 case NCP_IOC_SETPRIVATEDATA_32: 847#endif 848 ret = -EACCES; 849 goto out; 850 /* 851 * These require write access on the inode if user id 852 * does not match. Note that they do not write to the 853 * file... But old code did mnt_want_write, so I keep 854 * it as is. Of course not for mountpoint owner, as 855 * that breaks read-only mounts altogether as ncpmount 856 * needs working NCP_IOC_NCPREQUEST and 857 * NCP_IOC_GET_FS_INFO. Some of these codes (setdentryttl, 858 * signinit, setsignwanted) should be probably restricted 859 * to owner only, or even more to CAP_SYS_ADMIN). 860 */ 861 case NCP_IOC_GET_FS_INFO: 862 case NCP_IOC_GET_FS_INFO_V2: 863 case NCP_IOC_NCPREQUEST: 864 case NCP_IOC_SETDENTRYTTL: 865 case NCP_IOC_SIGN_INIT: 866 case NCP_IOC_LOCKUNLOCK: 867 case NCP_IOC_SET_SIGN_WANTED: 868#ifdef CONFIG_COMPAT 869 case NCP_IOC_GET_FS_INFO_V2_32: 870 case NCP_IOC_NCPREQUEST_32: 871#endif 872 ret = mnt_want_write_file(filp); 873 if (ret) 874 goto out; 875 need_drop_write = 1; 876 ret = inode_permission(inode, MAY_WRITE); 877 if (ret) 878 goto outDropWrite; 879 break; 880 /* 881 * Read access required. 882 */ 883 case NCP_IOC_GETMOUNTUID16: 884 case NCP_IOC_GETMOUNTUID32: 885 case NCP_IOC_GETMOUNTUID64: 886 case NCP_IOC_GETROOT: 887 case NCP_IOC_SIGN_WANTED: 888 ret = inode_permission(inode, MAY_READ); 889 if (ret) 890 goto out; 891 break; 892 /* 893 * Anybody can read these. 894 */ 895 case NCP_IOC_GETCHARSETS: 896 case NCP_IOC_GETDENTRYTTL: 897 default: 898 /* Three codes below are protected by CAP_SYS_ADMIN above. */ 899 case NCP_IOC_SETCHARSETS: 900 case NCP_IOC_CONN_LOGGED_IN: 901 case NCP_IOC_SETROOT: 902 break; 903 } 904 } 905 ret = __ncp_ioctl(inode, cmd, arg); 906outDropWrite: 907 if (need_drop_write) 908 mnt_drop_write_file(filp); 909out: 910 return ret; 911} 912 913#ifdef CONFIG_COMPAT 914long ncp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 915{ 916 long ret; 917 918 arg = (unsigned long) compat_ptr(arg); 919 ret = ncp_ioctl(file, cmd, arg); 920 return ret; 921} 922#endif