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
  35#define CIFS_IOCTL_MAGIC        0xCF
  36#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
  37
  38static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
  39                        unsigned long srcfd, u64 off, u64 len, u64 destoff)
  40{
  41        int rc;
  42        struct cifsFileInfo *smb_file_target = dst_file->private_data;
  43        struct inode *target_inode = file_inode(dst_file);
  44        struct cifs_tcon *target_tcon;
  45        struct fd src_file;
  46        struct cifsFileInfo *smb_file_src;
  47        struct inode *src_inode;
  48        struct cifs_tcon *src_tcon;
  49
  50        cifs_dbg(FYI, "ioctl clone range\n");
  51        /* the destination must be opened for writing */
  52        if (!(dst_file->f_mode & FMODE_WRITE)) {
  53                cifs_dbg(FYI, "file target not open for write\n");
  54                return -EINVAL;
  55        }
  56
  57        /* check if target volume is readonly and take reference */
  58        rc = mnt_want_write_file(dst_file);
  59        if (rc) {
  60                cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
  61                return rc;
  62        }
  63
  64        src_file = fdget(srcfd);
  65        if (!src_file.file) {
  66                rc = -EBADF;
  67                goto out_drop_write;
  68        }
  69
  70        if ((!src_file.file->private_data) || (!dst_file->private_data)) {
  71                rc = -EBADF;
  72                cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
  73                goto out_fput;
  74        }
  75
  76        rc = -EXDEV;
  77        smb_file_target = dst_file->private_data;
  78        smb_file_src = src_file.file->private_data;
  79        src_tcon = tlink_tcon(smb_file_src->tlink);
  80        target_tcon = tlink_tcon(smb_file_target->tlink);
  81
  82        /* check if source and target are on same tree connection */
  83        if (src_tcon != target_tcon) {
  84                cifs_dbg(VFS, "file copy src and target on different volume\n");
  85                goto out_fput;
  86        }
  87
  88        src_inode = src_file.file->f_dentry->d_inode;
  89
  90        /*
  91         * Note: cifs case is easier than btrfs since server responsible for
  92         * checks for proper open modes and file type and if it wants
  93         * server could even support copy of range where source = target
  94         */
  95
  96        /* so we do not deadlock racing two ioctls on same files */
  97        if (target_inode < src_inode) {
  98                mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT);
  99                mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD);
 100        } else {
 101                mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT);
 102                mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD);
 103        }
 104
 105        /* determine range to clone */
 106        rc = -EINVAL;
 107        if (off + len > src_inode->i_size || off + len < off)
 108                goto out_unlock;
 109        if (len == 0)
 110                len = src_inode->i_size - off;
 111
 112        cifs_dbg(FYI, "about to flush pages\n");
 113        /* should we flush first and last page first */
 114        truncate_inode_pages_range(&target_inode->i_data, destoff,
 115                                   PAGE_CACHE_ALIGN(destoff + len)-1);
 116
 117        if (target_tcon->ses->server->ops->clone_range)
 118                rc = target_tcon->ses->server->ops->clone_range(xid,
 119                        smb_file_src, smb_file_target, off, len, destoff);
 120
 121        /* force revalidate of size and timestamps of target file now
 122           that target is updated on the server */
 123        CIFS_I(target_inode)->time = 0;
 124out_unlock:
 125        /* although unlocking in the reverse order from locking is not
 126           strictly necessary here it is a little cleaner to be consistent */
 127        if (target_inode < src_inode) {
 128                mutex_unlock(&src_inode->i_mutex);
 129                mutex_unlock(&target_inode->i_mutex);
 130        } else {
 131                mutex_unlock(&target_inode->i_mutex);
 132                mutex_unlock(&src_inode->i_mutex);
 133        }
 134out_fput:
 135        fdput(src_file);
 136out_drop_write:
 137        mnt_drop_write_file(dst_file);
 138        return rc;
 139}
 140
 141long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 142{
 143        struct inode *inode = file_inode(filep);
 144        int rc = -ENOTTY; /* strange error - but the precedent */
 145        unsigned int xid;
 146        struct cifs_sb_info *cifs_sb;
 147        struct cifsFileInfo *pSMBFile = filep->private_data;
 148        struct cifs_tcon *tcon;
 149        __u64   ExtAttrBits = 0;
 150        __u64   caps;
 151
 152        xid = get_xid();
 153
 154        cifs_dbg(FYI, "ioctl file %p  cmd %u  arg %lu\n", filep, command, arg);
 155
 156        cifs_sb = CIFS_SB(inode->i_sb);
 157
 158        switch (command) {
 159                case FS_IOC_GETFLAGS:
 160                        if (pSMBFile == NULL)
 161                                break;
 162                        tcon = tlink_tcon(pSMBFile->tlink);
 163                        caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 164#ifdef CONFIG_CIFS_POSIX
 165                        if (CIFS_UNIX_EXTATTR_CAP & caps) {
 166                                __u64   ExtAttrMask = 0;
 167                                rc = CIFSGetExtAttr(xid, tcon,
 168                                                    pSMBFile->fid.netfid,
 169                                                    &ExtAttrBits, &ExtAttrMask);
 170                                if (rc == 0)
 171                                        rc = put_user(ExtAttrBits &
 172                                                FS_FL_USER_VISIBLE,
 173                                                (int __user *)arg);
 174                                if (rc != EOPNOTSUPP)
 175                                        break;
 176                        }
 177#endif /* CONFIG_CIFS_POSIX */
 178                        rc = 0;
 179                        if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
 180                                /* add in the compressed bit */
 181                                ExtAttrBits = FS_COMPR_FL;
 182                                rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
 183                                              (int __user *)arg);
 184                        }
 185                        break;
 186                case FS_IOC_SETFLAGS:
 187                        if (pSMBFile == NULL)
 188                                break;
 189                        tcon = tlink_tcon(pSMBFile->tlink);
 190                        caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
 191
 192                        if (get_user(ExtAttrBits, (int __user *)arg)) {
 193                                rc = -EFAULT;
 194                                break;
 195                        }
 196
 197                        /*
 198                         * if (CIFS_UNIX_EXTATTR_CAP & caps)
 199                         *      rc = CIFSSetExtAttr(xid, tcon,
 200                         *                     pSMBFile->fid.netfid,
 201                         *                     extAttrBits,
 202                         *                     &ExtAttrMask);
 203                         * if (rc != EOPNOTSUPP)
 204                         *      break;
 205                         */
 206
 207                        /* Currently only flag we can set is compressed flag */
 208                        if ((ExtAttrBits & FS_COMPR_FL) == 0)
 209                                break;
 210
 211                        /* Try to set compress flag */
 212                        if (tcon->ses->server->ops->set_compression) {
 213                                rc = tcon->ses->server->ops->set_compression(
 214                                                        xid, tcon, pSMBFile);
 215                                cifs_dbg(FYI, "set compress flag rc %d\n", rc);
 216                        }
 217                        break;
 218                case CIFS_IOC_COPYCHUNK_FILE:
 219                        rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
 220                        break;
 221                default:
 222                        cifs_dbg(FYI, "unsupported ioctl\n");
 223                        break;
 224        }
 225
 226        free_xid(xid);
 227        return rc;
 228}
 229