at v4.6 277 lines 7.9 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#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}