at v3.16 228 lines 6.7 kB view raw
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 = file_inode(src_file.file); 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}