| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *   fs/cifs/ioctl.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   vfs operations that deal with io control | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-10-14 15:31:32 -05:00
										 |  |  |  *   Copyright (C) International Business Machines  Corp., 2005,2013 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  *   Author(s): Steve French (sfrench@us.ibm.com) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This library is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  *   it under the terms of the GNU Lesser General Public License as published | 
					
						
							|  |  |  |  *   by the Free Software Foundation; either version 2.1 of the License, or | 
					
						
							|  |  |  |  *   (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See | 
					
						
							|  |  |  |  *   the GNU Lesser General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  |  *   along with this library; if not, write to the Free Software | 
					
						
							|  |  |  |  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/fs.h>
 | 
					
						
							| 
									
										
										
										
											2013-11-14 00:05:36 -06:00
										 |  |  | #include <linux/file.h>
 | 
					
						
							|  |  |  | #include <linux/mount.h>
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | #include <linux/pagemap.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include "cifspdu.h"
 | 
					
						
							|  |  |  | #include "cifsglob.h"
 | 
					
						
							|  |  |  | #include "cifsproto.h"
 | 
					
						
							|  |  |  | #include "cifs_debug.h"
 | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | #include "cifsfs.h"
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-24 21:53:17 -06:00
										 |  |  | #define CIFS_IOCTL_MAGIC	0xCF
 | 
					
						
							|  |  |  | #define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-14 00:05:36 -06:00
										 |  |  | static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, | 
					
						
							|  |  |  | 			unsigned long srcfd, u64 off, u64 len, u64 destoff) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 	struct cifsFileInfo *smb_file_target = dst_file->private_data; | 
					
						
							|  |  |  | 	struct inode *target_inode = file_inode(dst_file); | 
					
						
							|  |  |  | 	struct cifs_tcon *target_tcon; | 
					
						
							|  |  |  | 	struct fd src_file; | 
					
						
							|  |  |  | 	struct cifsFileInfo *smb_file_src; | 
					
						
							|  |  |  | 	struct inode *src_inode; | 
					
						
							|  |  |  | 	struct cifs_tcon *src_tcon; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cifs_dbg(FYI, "ioctl clone range\n"); | 
					
						
							|  |  |  | 	/* the destination must be opened for writing */ | 
					
						
							|  |  |  | 	if (!(dst_file->f_mode & FMODE_WRITE)) { | 
					
						
							|  |  |  | 		cifs_dbg(FYI, "file target not open for write\n"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* check if target volume is readonly and take reference */ | 
					
						
							|  |  |  | 	rc = mnt_want_write_file(dst_file); | 
					
						
							|  |  |  | 	if (rc) { | 
					
						
							|  |  |  | 		cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); | 
					
						
							|  |  |  | 		return rc; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	src_file = fdget(srcfd); | 
					
						
							|  |  |  | 	if (!src_file.file) { | 
					
						
							|  |  |  | 		rc = -EBADF; | 
					
						
							|  |  |  | 		goto out_drop_write; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((!src_file.file->private_data) || (!dst_file->private_data)) { | 
					
						
							|  |  |  | 		rc = -EBADF; | 
					
						
							|  |  |  | 		cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); | 
					
						
							|  |  |  | 		goto out_fput; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = -EXDEV; | 
					
						
							|  |  |  | 	smb_file_target = dst_file->private_data; | 
					
						
							|  |  |  | 	smb_file_src = src_file.file->private_data; | 
					
						
							|  |  |  | 	src_tcon = tlink_tcon(smb_file_src->tlink); | 
					
						
							|  |  |  | 	target_tcon = tlink_tcon(smb_file_target->tlink); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* check if source and target are on same tree connection */ | 
					
						
							|  |  |  | 	if (src_tcon != target_tcon) { | 
					
						
							|  |  |  | 		cifs_dbg(VFS, "file copy src and target on different volume\n"); | 
					
						
							|  |  |  | 		goto out_fput; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	src_inode = src_file.file->f_dentry->d_inode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Note: cifs case is easier than btrfs since server responsible for | 
					
						
							|  |  |  | 	 * checks for proper open modes and file type and if it wants | 
					
						
							|  |  |  | 	 * server could even support copy of range where source = target | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* so we do not deadlock racing two ioctls on same files */ | 
					
						
							|  |  |  | 	if (target_inode < src_inode) { | 
					
						
							|  |  |  | 		mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT); | 
					
						
							|  |  |  | 		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT); | 
					
						
							|  |  |  | 		mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* determine range to clone */ | 
					
						
							|  |  |  | 	rc = -EINVAL; | 
					
						
							|  |  |  | 	if (off + len > src_inode->i_size || off + len < off) | 
					
						
							|  |  |  | 		goto out_unlock; | 
					
						
							|  |  |  | 	if (len == 0) | 
					
						
							|  |  |  | 		len = src_inode->i_size - off; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cifs_dbg(FYI, "about to flush pages\n"); | 
					
						
							|  |  |  | 	/* should we flush first and last page first */ | 
					
						
							|  |  |  | 	truncate_inode_pages_range(&target_inode->i_data, destoff, | 
					
						
							|  |  |  | 				   PAGE_CACHE_ALIGN(destoff + len)-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (target_tcon->ses->server->ops->clone_range) | 
					
						
							|  |  |  | 		rc = target_tcon->ses->server->ops->clone_range(xid, | 
					
						
							|  |  |  | 			smb_file_src, smb_file_target, off, len, destoff); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* force revalidate of size and timestamps of target file now
 | 
					
						
							|  |  |  | 	   that target is updated on the server */ | 
					
						
							|  |  |  | 	CIFS_I(target_inode)->time = 0; | 
					
						
							|  |  |  | out_unlock: | 
					
						
							|  |  |  | 	/* although unlocking in the reverse order from locking is not
 | 
					
						
							|  |  |  | 	   strictly necessary here it is a little cleaner to be consistent */ | 
					
						
							|  |  |  | 	if (target_inode < src_inode) { | 
					
						
							|  |  |  | 		mutex_unlock(&src_inode->i_mutex); | 
					
						
							|  |  |  | 		mutex_unlock(&target_inode->i_mutex); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		mutex_unlock(&target_inode->i_mutex); | 
					
						
							|  |  |  | 		mutex_unlock(&src_inode->i_mutex); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | out_fput: | 
					
						
							|  |  |  | 	fdput(src_file); | 
					
						
							|  |  |  | out_drop_write: | 
					
						
							|  |  |  | 	mnt_drop_write_file(dst_file); | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-15 05:51:55 +00:00
										 |  |  | long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-23 17:07:38 -05:00
										 |  |  | 	struct inode *inode = file_inode(filep); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int rc = -ENOTTY; /* strange error - but the precedent */ | 
					
						
							| 
									
										
										
										
											2012-06-20 11:21:16 +04:00
										 |  |  | 	unsigned int xid; | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:07 -07:00
										 |  |  | 	struct cifs_sb_info *cifs_sb; | 
					
						
							| 
									
										
										
										
											2010-09-20 16:01:31 -07:00
										 |  |  | 	struct cifsFileInfo *pSMBFile = filep->private_data; | 
					
						
							| 
									
										
										
										
											2011-05-27 04:34:02 +00:00
										 |  |  | 	struct cifs_tcon *tcon; | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 	__u64	ExtAttrBits = 0; | 
					
						
							| 
									
										
										
										
											2010-11-08 07:28:32 -05:00
										 |  |  | 	__u64   caps; | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-20 11:21:16 +04:00
										 |  |  | 	xid = get_xid(); | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-04 22:12:25 -05:00
										 |  |  | 	cifs_dbg(FYI, "ioctl file %p  cmd %u  arg %lu\n", filep, command, arg); | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:07 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 	cifs_sb = CIFS_SB(inode->i_sb); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-05 18:30:44 +00:00
										 |  |  | 	switch (command) { | 
					
						
							| 
									
										
										
										
											2006-08-29 19:06:16 +01:00
										 |  |  | 		case FS_IOC_GETFLAGS: | 
					
						
							| 
									
										
										
										
											2010-11-08 07:28:32 -05:00
										 |  |  | 			if (pSMBFile == NULL) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			tcon = tlink_tcon(pSMBFile->tlink); | 
					
						
							|  |  |  | 			caps = le64_to_cpu(tcon->fsUnixInfo.Capability); | 
					
						
							| 
									
										
										
										
											2013-10-14 15:31:32 -05:00
										 |  |  | #ifdef CONFIG_CIFS_POSIX
 | 
					
						
							| 
									
										
										
										
											2007-06-05 18:30:44 +00:00
										 |  |  | 			if (CIFS_UNIX_EXTATTR_CAP & caps) { | 
					
						
							| 
									
										
										
										
											2013-10-13 22:32:30 -05:00
										 |  |  | 				__u64	ExtAttrMask = 0; | 
					
						
							| 
									
										
										
										
											2012-09-18 16:20:26 -07:00
										 |  |  | 				rc = CIFSGetExtAttr(xid, tcon, | 
					
						
							|  |  |  | 						    pSMBFile->fid.netfid, | 
					
						
							|  |  |  | 						    &ExtAttrBits, &ExtAttrMask); | 
					
						
							| 
									
										
										
										
											2007-06-05 18:30:44 +00:00
										 |  |  | 				if (rc == 0) | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 					rc = put_user(ExtAttrBits & | 
					
						
							| 
									
										
										
										
											2006-08-29 19:06:16 +01:00
										 |  |  | 						FS_FL_USER_VISIBLE, | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 						(int __user *)arg); | 
					
						
							| 
									
										
										
										
											2013-10-14 15:31:32 -05:00
										 |  |  | 				if (rc != EOPNOTSUPP) | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | #endif /* CONFIG_CIFS_POSIX */
 | 
					
						
							|  |  |  | 			rc = 0; | 
					
						
							|  |  |  | 			if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { | 
					
						
							|  |  |  | 				/* add in the compressed bit */ | 
					
						
							|  |  |  | 				ExtAttrBits = FS_COMPR_FL; | 
					
						
							|  |  |  | 				rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, | 
					
						
							|  |  |  | 					      (int __user *)arg); | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2006-08-29 19:06:16 +01:00
										 |  |  | 		case FS_IOC_SETFLAGS: | 
					
						
							| 
									
										
										
										
											2010-11-08 07:28:32 -05:00
										 |  |  | 			if (pSMBFile == NULL) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			tcon = tlink_tcon(pSMBFile->tlink); | 
					
						
							|  |  |  | 			caps = le64_to_cpu(tcon->fsUnixInfo.Capability); | 
					
						
							| 
									
										
										
										
											2013-10-14 15:31:32 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (get_user(ExtAttrBits, (int __user *)arg)) { | 
					
						
							|  |  |  | 				rc = -EFAULT; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * if (CIFS_UNIX_EXTATTR_CAP & caps) | 
					
						
							|  |  |  | 			 *	rc = CIFSSetExtAttr(xid, tcon, | 
					
						
							|  |  |  | 			 *		       pSMBFile->fid.netfid, | 
					
						
							|  |  |  | 			 *		       extAttrBits, | 
					
						
							|  |  |  | 			 *		       &ExtAttrMask); | 
					
						
							|  |  |  | 			 * if (rc != EOPNOTSUPP) | 
					
						
							|  |  |  | 			 *	break; | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Currently only flag we can set is compressed flag */ | 
					
						
							|  |  |  | 			if ((ExtAttrBits & FS_COMPR_FL) == 0) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Try to set compress flag */ | 
					
						
							|  |  |  | 			if (tcon->ses->server->ops->set_compression) { | 
					
						
							|  |  |  | 				rc = tcon->ses->server->ops->set_compression( | 
					
						
							|  |  |  | 							xid, tcon, pSMBFile); | 
					
						
							|  |  |  | 				cifs_dbg(FYI, "set compress flag rc %d\n", rc); | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-11-24 21:53:17 -06:00
										 |  |  | 		case CIFS_IOC_COPYCHUNK_FILE: | 
					
						
							| 
									
										
										
										
											2013-11-14 00:05:36 -06:00
										 |  |  | 			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2013-05-04 22:12:25 -05:00
										 |  |  | 			cifs_dbg(FYI, "unsupported ioctl\n"); | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:07 -07:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-28 22:41:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-20 11:21:16 +04:00
										 |  |  | 	free_xid(xid); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return rc; | 
					
						
							| 
									
										
										
										
											2007-06-05 18:30:44 +00:00
										 |  |  | } |