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