linux/fs/cifs/ioctl.c
<<
>>
Prefs
   1/*
   2 *   fs/cifs/ioctl.c
   3 *
   4 *   vfs operations that deal with io control
   5 *
   6 *   Copyright (C) International Business Machines  Corp., 2005,2013
   7 *   Author(s): Steve French (sfrench@us.ibm.com)
   8 *
   9 *   This library is free software; you can redistribute it and/or modify
  10 *   it under the terms of the GNU Lesser General Public License as published
  11 *   by the Free Software Foundation; either version 2.1 of the License, or
  12 *   (at your option) any later version.
  13 *
  14 *   This library is distributed in the hope that it will be useful,
  15 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  17 *   the GNU Lesser General Public License for more details.
  18 *
  19 *   You should have received a copy of the GNU Lesser General Public License
  20 *   along with this library; if not, write to the Free Software
  21 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22 */
  23
  24#include <linux/fs.h>
  25#include <linux/file.h>
  26#include <linux/mount.h>
  27#include <linux/mm.h>
  28#include <linux/pagemap.h>
  29#include "cifspdu.h"
  30#include "cifsglob.h"
  31#include "cifsproto.h"
  32#include "cifs_debug.h"
  33#include "cifsfs.h"
  34#include "cifs_ioctl.h"
  35#include <linux/btrfs.h>
  36
  37static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
  38                        unsigned long srcfd)
  39{
  40        int rc;
  41        struct fd src_file;
  42        struct inode *src_inode;
  43
  44        cifs_dbg(FYI, "ioctl copychunk range\n");
  45        /* the destination must be opened for writing */
  46        if (!(dst_file->f_mode & FMODE_WRITE)) {
  47                cifs_dbg(FYI, "file target not open for write\n");
  48                return -EINVAL;
  49        }
  50
  51        /* check if target volume is readonly and take reference */
  52        rc = mnt_want_write_file(dst_file);
  53        if (rc) {
  54                cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
  55                return rc;
  56        }
  57
  58        src_file = fdget(srcfd);
  59        if (!src_file.file) {
  60                rc = -EBADF;
  61                goto out_drop_write;
  62        }
  63
  64        if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
  65                rc = -EBADF;
  66                cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
  67                goto out_fput;
  68        }
  69
  70        src_inode = file_inode(src_file.file);
  71        rc = -EINVAL;
  72        if (S_ISDIR(src_inode->i_mode))
  73                goto out_fput;
  74
  75        rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0,
  76                                        src_inode->i_size, 0);
  77        if (rc > 0)
  78                rc = 0;
  79out_fput:
  80        fdput(src_file);
  81out_drop_write:
  82        mnt_drop_write_file(dst_file);
  83        return rc;
  84}
  85
  86static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
  87                                void __user *arg)
  88{
  89        int rc = 0;
  90        struct smb_mnt_fs_info *fsinf;
  91
  92        fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL);
  93        if (fsinf == NULL)
  94                return -ENOMEM;
  95
  96        fsinf->version = 1;
  97        fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
  98        fsinf->device_characteristics =
  99                        le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
 100        fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
 101        fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes);
 102        fsinf->max_path_component =
 103                le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
 104        fsinf->vol_serial_number = tcon->vol_serial_number;
 105        fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time);
 106        fsinf->share_flags = tcon->share_flags;
 107        fsinf->share_caps = le32_to_cpu(tcon->capabilities);
 108        fsinf->sector_flags = tcon->ss_flags;
 109        fsinf->optimal_sector_size = tcon->perf_sector_size;
 110        fsinf->max_bytes_chunk = tcon->max_bytes_chunk;
 111        fsinf->maximal_access = tcon->maximal_access;
 112        fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 113
 114        if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info)))
 115                rc = -EFAULT;
 116
 117        kfree(fsinf);
 118        return rc;
 119}
 120
 121long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 122{
 123        struct inode *inode = file_inode(filep);
 124        int rc = -ENOTTY; /* strange error - but the precedent */
 125        unsigned int xid;
 126        struct cifs_sb_info *cifs_sb;
 127        struct cifsFileInfo *pSMBFile = filep->private_data;
 128        struct cifs_tcon *tcon;
 129        __u64   ExtAttrBits = 0;
 130        __u64   caps;
 131
 132        xid = get_xid();
 133
 134        cifs_sb = CIFS_SB(inode->i_sb);
 135        cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
 136        switch (command) {
 137                case FS_IOC_GETFLAGS:
 138                        if (pSMBFile == NULL)
 139                                break;
 140                        tcon = tlink_tcon(pSMBFile->tlink);
 141                        caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 142#ifdef CONFIG_CIFS_POSIX
 143                        if (CIFS_UNIX_EXTATTR_CAP & caps) {
 144                                __u64   ExtAttrMask = 0;
 145                                rc = CIFSGetExtAttr(xid, tcon,
 146                                                    pSMBFile->fid.netfid,
 147                                                    &ExtAttrBits, &ExtAttrMask);
 148                                if (rc == 0)
 149                                        rc = put_user(ExtAttrBits &
 150                                                FS_FL_USER_VISIBLE,
 151                                                (int __user *)arg);
 152                                if (rc != EOPNOTSUPP)
 153                                        break;
 154                        }
 155#endif /* CONFIG_CIFS_POSIX */
 156                        rc = 0;
 157                        if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
 158                                /* add in the compressed bit */
 159                                ExtAttrBits = FS_COMPR_FL;
 160                                rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
 161                                              (int __user *)arg);
 162                        }
 163                        break;
 164                case FS_IOC_SETFLAGS:
 165                        if (pSMBFile == NULL)
 166                                break;
 167                        tcon = tlink_tcon(pSMBFile->tlink);
 168                        caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 169
 170                        if (get_user(ExtAttrBits, (int __user *)arg)) {
 171                                rc = -EFAULT;
 172                                break;
 173                        }
 174
 175                        /*
 176                         * if (CIFS_UNIX_EXTATTR_CAP & caps)
 177                         *      rc = CIFSSetExtAttr(xid, tcon,
 178                         *                     pSMBFile->fid.netfid,
 179                         *                     extAttrBits,
 180                         *                     &ExtAttrMask);
 181                         * if (rc != EOPNOTSUPP)
 182                         *      break;
 183                         */
 184
 185                        /* Currently only flag we can set is compressed flag */
 186                        if ((ExtAttrBits & FS_COMPR_FL) == 0)
 187                                break;
 188
 189                        /* Try to set compress flag */
 190                        if (tcon->ses->server->ops->set_compression) {
 191                                rc = tcon->ses->server->ops->set_compression(
 192                                                        xid, tcon, pSMBFile);
 193                                cifs_dbg(FYI, "set compress flag rc %d\n", rc);
 194                        }
 195                        break;
 196                case CIFS_IOC_COPYCHUNK_FILE:
 197                        rc = cifs_ioctl_copychunk(xid, filep, arg);
 198                        break;
 199                case CIFS_IOC_SET_INTEGRITY:
 200                        if (pSMBFile == NULL)
 201                                break;
 202                        tcon = tlink_tcon(pSMBFile->tlink);
 203                        if (tcon->ses->server->ops->set_integrity)
 204                                rc = tcon->ses->server->ops->set_integrity(xid,
 205                                                tcon, pSMBFile);
 206                        else
 207                                rc = -EOPNOTSUPP;
 208                        break;
 209                case CIFS_IOC_GET_MNT_INFO:
 210                        if (pSMBFile == NULL)
 211                                break;
 212                        tcon = tlink_tcon(pSMBFile->tlink);
 213                        rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
 214                        break;
 215                case CIFS_ENUMERATE_SNAPSHOTS:
 216                        if (pSMBFile == NULL)
 217                                break;
 218                        if (arg == 0) {
 219                                rc = -EINVAL;
 220                                goto cifs_ioc_exit;
 221                        }
 222                        tcon = tlink_tcon(pSMBFile->tlink);
 223                        if (tcon->ses->server->ops->enum_snapshots)
 224                                rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
 225                                                pSMBFile, (void __user *)arg);
 226                        else
 227                                rc = -EOPNOTSUPP;
 228                        break;
 229                default:
 230                        cifs_dbg(FYI, "unsupported ioctl\n");
 231                        break;
 232        }
 233cifs_ioc_exit:
 234        free_xid(xid);
 235        return rc;
 236}
 237