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 int cifs_file_clone_range(unsigned int xid, struct file *src_file,
  38                          struct file *dst_file)
  39{
  40        struct inode *src_inode = file_inode(src_file);
  41        struct inode *target_inode = file_inode(dst_file);
  42        struct cifsFileInfo *smb_file_src;
  43        struct cifsFileInfo *smb_file_target;
  44        struct cifs_tcon *src_tcon;
  45        struct cifs_tcon *target_tcon;
  46        int rc;
  47
  48        cifs_dbg(FYI, "ioctl clone range\n");
  49
  50        if (!src_file->private_data || !dst_file->private_data) {
  51                rc = -EBADF;
  52                cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
  53                goto out;
  54        }
  55
  56        rc = -EXDEV;
  57        smb_file_target = dst_file->private_data;
  58        smb_file_src = src_file->private_data;
  59        src_tcon = tlink_tcon(smb_file_src->tlink);
  60        target_tcon = tlink_tcon(smb_file_target->tlink);
  61
  62        if (src_tcon->ses != target_tcon->ses) {
  63                cifs_dbg(VFS, "source and target of copy not on same server\n");
  64                goto out;
  65        }
  66
  67        /*
  68         * Note: cifs case is easier than btrfs since server responsible for
  69         * checks for proper open modes and file type and if it wants
  70         * server could even support copy of range where source = target
  71         */
  72        lock_two_nondirectories(target_inode, src_inode);
  73
  74        cifs_dbg(FYI, "about to flush pages\n");
  75        /* should we flush first and last page first */
  76        truncate_inode_pages(&target_inode->i_data, 0);
  77
  78        if (target_tcon->ses->server->ops->clone_range)
  79                rc = target_tcon->ses->server->ops->clone_range(xid,
  80                        smb_file_src, smb_file_target, 0, src_inode->i_size, 0);
  81        else
  82                rc = -EOPNOTSUPP;
  83
  84        /* force revalidate of size and timestamps of target file now
  85           that target is updated on the server */
  86        CIFS_I(target_inode)->time = 0;
  87        /* although unlocking in the reverse order from locking is not
  88           strictly necessary here it is a little cleaner to be consistent */
  89        unlock_two_nondirectories(src_inode, target_inode);
  90out:
  91        return rc;
  92}
  93
  94static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
  95                        unsigned long srcfd)
  96{
  97        int rc;
  98        struct fd src_file;
  99        struct inode *src_inode;
 100
 101        cifs_dbg(FYI, "ioctl clone range\n");
 102        /* the destination must be opened for writing */
 103        if (!(dst_file->f_mode & FMODE_WRITE)) {
 104                cifs_dbg(FYI, "file target not open for write\n");
 105                return -EINVAL;
 106        }
 107
 108        /* check if target volume is readonly and take reference */
 109        rc = mnt_want_write_file(dst_file);
 110        if (rc) {
 111                cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
 112                return rc;
 113        }
 114
 115        src_file = fdget(srcfd);
 116        if (!src_file.file) {
 117                rc = -EBADF;
 118                goto out_drop_write;
 119        }
 120
 121        if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
 122                rc = -EBADF;
 123                cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
 124                goto out_fput;
 125        }
 126
 127        src_inode = file_inode(src_file.file);
 128        rc = -EINVAL;
 129        if (S_ISDIR(src_inode->i_mode))
 130                goto out_fput;
 131
 132        rc = cifs_file_clone_range(xid, src_file.file, dst_file);
 133
 134out_fput:
 135        fdput(src_file);
 136out_drop_write:
 137        mnt_drop_write_file(dst_file);
 138        return rc;
 139}
 140
 141static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
 142                                void __user *arg)
 143{
 144        int rc = 0;
 145        struct smb_mnt_fs_info *fsinf;
 146
 147        fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL);
 148        if (fsinf == NULL)
 149                return -ENOMEM;
 150
 151        fsinf->version = 1;
 152        fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
 153        fsinf->device_characteristics =
 154                        le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
 155        fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
 156        fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes);
 157        fsinf->max_path_component =
 158                le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
 159#ifdef CONFIG_CIFS_SMB2
 160        fsinf->vol_serial_number = tcon->vol_serial_number;
 161        fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time);
 162        fsinf->share_flags = tcon->share_flags;
 163        fsinf->share_caps = le32_to_cpu(tcon->capabilities);
 164        fsinf->sector_flags = tcon->ss_flags;
 165        fsinf->optimal_sector_size = tcon->perf_sector_size;
 166        fsinf->max_bytes_chunk = tcon->max_bytes_chunk;
 167        fsinf->maximal_access = tcon->maximal_access;
 168#endif /* SMB2 */
 169        fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 170
 171        if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info)))
 172                rc = -EFAULT;
 173
 174        kfree(fsinf);
 175        return rc;
 176}
 177
 178long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 179{
 180        struct inode *inode = file_inode(filep);
 181        int rc = -ENOTTY; /* strange error - but the precedent */
 182        unsigned int xid;
 183        struct cifs_sb_info *cifs_sb;
 184        struct cifsFileInfo *pSMBFile = filep->private_data;
 185        struct cifs_tcon *tcon;
 186        __u64   ExtAttrBits = 0;
 187        __u64   caps;
 188
 189        xid = get_xid();
 190
 191        cifs_sb = CIFS_SB(inode->i_sb);
 192
 193        switch (command) {
 194                case FS_IOC_GETFLAGS:
 195                        if (pSMBFile == NULL)
 196                                break;
 197                        tcon = tlink_tcon(pSMBFile->tlink);
 198                        caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 199#ifdef CONFIG_CIFS_POSIX
 200                        if (CIFS_UNIX_EXTATTR_CAP & caps) {
 201                                __u64   ExtAttrMask = 0;
 202                                rc = CIFSGetExtAttr(xid, tcon,
 203                                                    pSMBFile->fid.netfid,
 204                                                    &ExtAttrBits, &ExtAttrMask);
 205                                if (rc == 0)
 206                                        rc = put_user(ExtAttrBits &
 207                                                FS_FL_USER_VISIBLE,
 208                                                (int __user *)arg);
 209                                if (rc != EOPNOTSUPP)
 210                                        break;
 211                        }
 212#endif /* CONFIG_CIFS_POSIX */
 213                        rc = 0;
 214                        if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
 215                                /* add in the compressed bit */
 216                                ExtAttrBits = FS_COMPR_FL;
 217                                rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
 218                                              (int __user *)arg);
 219                        }
 220                        break;
 221                case FS_IOC_SETFLAGS:
 222                        if (pSMBFile == NULL)
 223                                break;
 224                        tcon = tlink_tcon(pSMBFile->tlink);
 225                        caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 226
 227                        if (get_user(ExtAttrBits, (int __user *)arg)) {
 228                                rc = -EFAULT;
 229                                break;
 230                        }
 231
 232                        /*
 233                         * if (CIFS_UNIX_EXTATTR_CAP & caps)
 234                         *      rc = CIFSSetExtAttr(xid, tcon,
 235                         *                     pSMBFile->fid.netfid,
 236                         *                     extAttrBits,
 237                         *                     &ExtAttrMask);
 238                         * if (rc != EOPNOTSUPP)
 239                         *      break;
 240                         */
 241
 242                        /* Currently only flag we can set is compressed flag */
 243                        if ((ExtAttrBits & FS_COMPR_FL) == 0)
 244                                break;
 245
 246                        /* Try to set compress flag */
 247                        if (tcon->ses->server->ops->set_compression) {
 248                                rc = tcon->ses->server->ops->set_compression(
 249                                                        xid, tcon, pSMBFile);
 250                                cifs_dbg(FYI, "set compress flag rc %d\n", rc);
 251                        }
 252                        break;
 253                case CIFS_IOC_COPYCHUNK_FILE:
 254                        rc = cifs_ioctl_clone(xid, filep, arg);
 255                        break;
 256                case CIFS_IOC_SET_INTEGRITY:
 257                        if (pSMBFile == NULL)
 258                                break;
 259                        tcon = tlink_tcon(pSMBFile->tlink);
 260                        if (tcon->ses->server->ops->set_integrity)
 261                                rc = tcon->ses->server->ops->set_integrity(xid,
 262                                                tcon, pSMBFile);
 263                        else
 264                                rc = -EOPNOTSUPP;
 265                        break;
 266                case CIFS_IOC_GET_MNT_INFO:
 267                        tcon = tlink_tcon(pSMBFile->tlink);
 268                        rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
 269                        break;
 270                default:
 271                        cifs_dbg(FYI, "unsupported ioctl\n");
 272                        break;
 273        }
 274
 275        free_xid(xid);
 276        return rc;
 277}
 278