at v3.19 217 lines 6.3 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 rc = -EINVAL; 90 if (S_ISDIR(src_inode->i_mode)) 91 goto out_fput; 92 93 /* 94 * Note: cifs case is easier than btrfs since server responsible for 95 * checks for proper open modes and file type and if it wants 96 * server could even support copy of range where source = target 97 */ 98 lock_two_nondirectories(target_inode, src_inode); 99 100 /* determine range to clone */ 101 rc = -EINVAL; 102 if (off + len > src_inode->i_size || off + len < off) 103 goto out_unlock; 104 if (len == 0) 105 len = src_inode->i_size - off; 106 107 cifs_dbg(FYI, "about to flush pages\n"); 108 /* should we flush first and last page first */ 109 truncate_inode_pages_range(&target_inode->i_data, destoff, 110 PAGE_CACHE_ALIGN(destoff + len)-1); 111 112 if (target_tcon->ses->server->ops->clone_range) 113 rc = target_tcon->ses->server->ops->clone_range(xid, 114 smb_file_src, smb_file_target, off, len, destoff); 115 116 /* force revalidate of size and timestamps of target file now 117 that target is updated on the server */ 118 CIFS_I(target_inode)->time = 0; 119out_unlock: 120 /* although unlocking in the reverse order from locking is not 121 strictly necessary here it is a little cleaner to be consistent */ 122 unlock_two_nondirectories(src_inode, target_inode); 123out_fput: 124 fdput(src_file); 125out_drop_write: 126 mnt_drop_write_file(dst_file); 127 return rc; 128} 129 130long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) 131{ 132 struct inode *inode = file_inode(filep); 133 int rc = -ENOTTY; /* strange error - but the precedent */ 134 unsigned int xid; 135 struct cifs_sb_info *cifs_sb; 136 struct cifsFileInfo *pSMBFile = filep->private_data; 137 struct cifs_tcon *tcon; 138 __u64 ExtAttrBits = 0; 139 __u64 caps; 140 141 xid = get_xid(); 142 143 cifs_dbg(FYI, "ioctl file %p cmd %u arg %lu\n", filep, command, arg); 144 145 cifs_sb = CIFS_SB(inode->i_sb); 146 147 switch (command) { 148 case FS_IOC_GETFLAGS: 149 if (pSMBFile == NULL) 150 break; 151 tcon = tlink_tcon(pSMBFile->tlink); 152 caps = le64_to_cpu(tcon->fsUnixInfo.Capability); 153#ifdef CONFIG_CIFS_POSIX 154 if (CIFS_UNIX_EXTATTR_CAP & caps) { 155 __u64 ExtAttrMask = 0; 156 rc = CIFSGetExtAttr(xid, tcon, 157 pSMBFile->fid.netfid, 158 &ExtAttrBits, &ExtAttrMask); 159 if (rc == 0) 160 rc = put_user(ExtAttrBits & 161 FS_FL_USER_VISIBLE, 162 (int __user *)arg); 163 if (rc != EOPNOTSUPP) 164 break; 165 } 166#endif /* CONFIG_CIFS_POSIX */ 167 rc = 0; 168 if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { 169 /* add in the compressed bit */ 170 ExtAttrBits = FS_COMPR_FL; 171 rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, 172 (int __user *)arg); 173 } 174 break; 175 case FS_IOC_SETFLAGS: 176 if (pSMBFile == NULL) 177 break; 178 tcon = tlink_tcon(pSMBFile->tlink); 179 caps = le64_to_cpu(tcon->fsUnixInfo.Capability); 180 181 if (get_user(ExtAttrBits, (int __user *)arg)) { 182 rc = -EFAULT; 183 break; 184 } 185 186 /* 187 * if (CIFS_UNIX_EXTATTR_CAP & caps) 188 * rc = CIFSSetExtAttr(xid, tcon, 189 * pSMBFile->fid.netfid, 190 * extAttrBits, 191 * &ExtAttrMask); 192 * if (rc != EOPNOTSUPP) 193 * break; 194 */ 195 196 /* Currently only flag we can set is compressed flag */ 197 if ((ExtAttrBits & FS_COMPR_FL) == 0) 198 break; 199 200 /* Try to set compress flag */ 201 if (tcon->ses->server->ops->set_compression) { 202 rc = tcon->ses->server->ops->set_compression( 203 xid, tcon, pSMBFile); 204 cifs_dbg(FYI, "set compress flag rc %d\n", rc); 205 } 206 break; 207 case CIFS_IOC_COPYCHUNK_FILE: 208 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); 209 break; 210 default: 211 cifs_dbg(FYI, "unsupported ioctl\n"); 212 break; 213 } 214 215 free_xid(xid); 216 return rc; 217}