at v4.16 236 lines 6.6 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 long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, 38 unsigned long srcfd) 39{ 40 int rc; 41 struct fd src_file; 42 struct inode *src_inode; 43 44 cifs_dbg(FYI, "ioctl copychunk range\n"); 45 /* the destination must be opened for writing */ 46 if (!(dst_file->f_mode & FMODE_WRITE)) { 47 cifs_dbg(FYI, "file target not open for write\n"); 48 return -EINVAL; 49 } 50 51 /* check if target volume is readonly and take reference */ 52 rc = mnt_want_write_file(dst_file); 53 if (rc) { 54 cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); 55 return rc; 56 } 57 58 src_file = fdget(srcfd); 59 if (!src_file.file) { 60 rc = -EBADF; 61 goto out_drop_write; 62 } 63 64 if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) { 65 rc = -EBADF; 66 cifs_dbg(VFS, "src file seems to be from a different filesystem type\n"); 67 goto out_fput; 68 } 69 70 src_inode = file_inode(src_file.file); 71 rc = -EINVAL; 72 if (S_ISDIR(src_inode->i_mode)) 73 goto out_fput; 74 75 rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0, 76 src_inode->i_size, 0); 77 if (rc > 0) 78 rc = 0; 79out_fput: 80 fdput(src_file); 81out_drop_write: 82 mnt_drop_write_file(dst_file); 83 return rc; 84} 85 86static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, 87 void __user *arg) 88{ 89 int rc = 0; 90 struct smb_mnt_fs_info *fsinf; 91 92 fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL); 93 if (fsinf == NULL) 94 return -ENOMEM; 95 96 fsinf->version = 1; 97 fsinf->protocol_id = tcon->ses->server->vals->protocol_id; 98 fsinf->device_characteristics = 99 le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics); 100 fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); 101 fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes); 102 fsinf->max_path_component = 103 le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); 104 fsinf->vol_serial_number = tcon->vol_serial_number; 105 fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time); 106 fsinf->share_flags = tcon->share_flags; 107 fsinf->share_caps = le32_to_cpu(tcon->capabilities); 108 fsinf->sector_flags = tcon->ss_flags; 109 fsinf->optimal_sector_size = tcon->perf_sector_size; 110 fsinf->max_bytes_chunk = tcon->max_bytes_chunk; 111 fsinf->maximal_access = tcon->maximal_access; 112 fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability); 113 114 if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info))) 115 rc = -EFAULT; 116 117 kfree(fsinf); 118 return rc; 119} 120 121long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) 122{ 123 struct inode *inode = file_inode(filep); 124 int rc = -ENOTTY; /* strange error - but the precedent */ 125 unsigned int xid; 126 struct cifs_sb_info *cifs_sb; 127 struct cifsFileInfo *pSMBFile = filep->private_data; 128 struct cifs_tcon *tcon; 129 __u64 ExtAttrBits = 0; 130 __u64 caps; 131 132 xid = get_xid(); 133 134 cifs_sb = CIFS_SB(inode->i_sb); 135 cifs_dbg(FYI, "cifs ioctl 0x%x\n", command); 136 switch (command) { 137 case FS_IOC_GETFLAGS: 138 if (pSMBFile == NULL) 139 break; 140 tcon = tlink_tcon(pSMBFile->tlink); 141 caps = le64_to_cpu(tcon->fsUnixInfo.Capability); 142#ifdef CONFIG_CIFS_POSIX 143 if (CIFS_UNIX_EXTATTR_CAP & caps) { 144 __u64 ExtAttrMask = 0; 145 rc = CIFSGetExtAttr(xid, tcon, 146 pSMBFile->fid.netfid, 147 &ExtAttrBits, &ExtAttrMask); 148 if (rc == 0) 149 rc = put_user(ExtAttrBits & 150 FS_FL_USER_VISIBLE, 151 (int __user *)arg); 152 if (rc != EOPNOTSUPP) 153 break; 154 } 155#endif /* CONFIG_CIFS_POSIX */ 156 rc = 0; 157 if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { 158 /* add in the compressed bit */ 159 ExtAttrBits = FS_COMPR_FL; 160 rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, 161 (int __user *)arg); 162 } 163 break; 164 case FS_IOC_SETFLAGS: 165 if (pSMBFile == NULL) 166 break; 167 tcon = tlink_tcon(pSMBFile->tlink); 168 caps = le64_to_cpu(tcon->fsUnixInfo.Capability); 169 170 if (get_user(ExtAttrBits, (int __user *)arg)) { 171 rc = -EFAULT; 172 break; 173 } 174 175 /* 176 * if (CIFS_UNIX_EXTATTR_CAP & caps) 177 * rc = CIFSSetExtAttr(xid, tcon, 178 * pSMBFile->fid.netfid, 179 * extAttrBits, 180 * &ExtAttrMask); 181 * if (rc != EOPNOTSUPP) 182 * break; 183 */ 184 185 /* Currently only flag we can set is compressed flag */ 186 if ((ExtAttrBits & FS_COMPR_FL) == 0) 187 break; 188 189 /* Try to set compress flag */ 190 if (tcon->ses->server->ops->set_compression) { 191 rc = tcon->ses->server->ops->set_compression( 192 xid, tcon, pSMBFile); 193 cifs_dbg(FYI, "set compress flag rc %d\n", rc); 194 } 195 break; 196 case CIFS_IOC_COPYCHUNK_FILE: 197 rc = cifs_ioctl_copychunk(xid, filep, arg); 198 break; 199 case CIFS_IOC_SET_INTEGRITY: 200 if (pSMBFile == NULL) 201 break; 202 tcon = tlink_tcon(pSMBFile->tlink); 203 if (tcon->ses->server->ops->set_integrity) 204 rc = tcon->ses->server->ops->set_integrity(xid, 205 tcon, pSMBFile); 206 else 207 rc = -EOPNOTSUPP; 208 break; 209 case CIFS_IOC_GET_MNT_INFO: 210 if (pSMBFile == NULL) 211 break; 212 tcon = tlink_tcon(pSMBFile->tlink); 213 rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); 214 break; 215 case CIFS_ENUMERATE_SNAPSHOTS: 216 if (pSMBFile == NULL) 217 break; 218 if (arg == 0) { 219 rc = -EINVAL; 220 goto cifs_ioc_exit; 221 } 222 tcon = tlink_tcon(pSMBFile->tlink); 223 if (tcon->ses->server->ops->enum_snapshots) 224 rc = tcon->ses->server->ops->enum_snapshots(xid, tcon, 225 pSMBFile, (void __user *)arg); 226 else 227 rc = -EOPNOTSUPP; 228 break; 229 default: 230 cifs_dbg(FYI, "unsupported ioctl\n"); 231 break; 232 } 233cifs_ioc_exit: 234 free_xid(xid); 235 return rc; 236}