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