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