2005-04-17 02:20:36 +04:00
/*
* fs / cifs / ioctl . c
*
* vfs operations that deal with io control
*
2013-10-15 00:31:32 +04:00
* Copyright ( C ) International Business Machines Corp . , 2005 , 2013
2005-04-17 02:20:36 +04: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-29 09:41:04 +04:00
2005-04-17 02:20:36 +04:00
# include <linux/fs.h>
2013-11-14 10:05:36 +04:00
# include <linux/file.h>
# include <linux/mount.h>
# include <linux/mm.h>
# include <linux/pagemap.h>
2005-04-17 02:20:36 +04:00
# include "cifspdu.h"
# include "cifsglob.h"
# include "cifsproto.h"
# include "cifs_debug.h"
2005-04-29 09:41:04 +04:00
# include "cifsfs.h"
2015-06-28 07:18:36 +03:00
# include <linux/btrfs.h>
2005-04-17 02:20:36 +04:00
2013-11-25 07:53:17 +04:00
# define CIFS_IOCTL_MAGIC 0xCF
# define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
2015-06-24 11:17:02 +03:00
# define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4)
2013-11-25 07:53:17 +04:00
2013-11-14 10:05:36 +04:00
static long cifs_ioctl_clone ( unsigned int xid , struct file * dst_file ,
2015-06-28 07:18:36 +03:00
unsigned long srcfd , u64 off , u64 len , u64 destoff ,
bool dup_extents )
2013-11-14 10:05:36 +04:00
{
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 ;
}
2013-12-11 07:02:27 +04:00
src_inode = file_inode ( src_file . file ) ;
2015-01-19 07:37:32 +03:00
rc = - EINVAL ;
if ( S_ISDIR ( src_inode - > i_mode ) )
goto out_fput ;
2013-11-14 10:05:36 +04:00
/*
* 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
*/
2015-01-19 07:37:32 +03:00
lock_two_nondirectories ( target_inode , src_inode ) ;
2013-11-14 10:05:36 +04:00
/* 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 ) ;
2015-06-28 07:18:36 +03:00
if ( dup_extents & & target_tcon - > ses - > server - > ops - > duplicate_extents )
rc = target_tcon - > ses - > server - > ops - > duplicate_extents ( xid ,
smb_file_src , smb_file_target , off , len , destoff ) ;
else if ( ! dup_extents & & target_tcon - > ses - > server - > ops - > clone_range )
2013-11-14 10:05:36 +04:00
rc = target_tcon - > ses - > server - > ops - > clone_range ( xid ,
smb_file_src , smb_file_target , off , len , destoff ) ;
2015-06-28 07:18:36 +03:00
else
rc = - EOPNOTSUPP ;
2013-11-14 10:05:36 +04:00
/* 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 */
2015-01-19 07:37:32 +03:00
unlock_two_nondirectories ( src_inode , target_inode ) ;
2013-11-14 10:05:36 +04:00
out_fput :
fdput ( src_file ) ;
out_drop_write :
mnt_drop_write_file ( dst_file ) ;
return rc ;
}
2008-05-15 09:51:55 +04:00
long cifs_ioctl ( struct file * filep , unsigned int command , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2013-01-24 02:07:38 +04:00
struct inode * inode = file_inode ( filep ) ;
2005-04-17 02:20:36 +04:00
int rc = - ENOTTY ; /* strange error - but the precedent */
2012-06-20 11:21:16 +04:00
unsigned int xid ;
2005-04-29 09:41:07 +04:00
struct cifs_sb_info * cifs_sb ;
2010-09-21 03:01:31 +04:00
struct cifsFileInfo * pSMBFile = filep - > private_data ;
2011-05-27 08:34:02 +04:00
struct cifs_tcon * tcon ;
2005-04-29 09:41:04 +04:00
__u64 ExtAttrBits = 0 ;
2010-11-08 15:28:32 +03:00
__u64 caps ;
2005-04-29 09:41:04 +04:00
2012-06-20 11:21:16 +04:00
xid = get_xid ( ) ;
2005-04-29 09:41:04 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " ioctl file %p cmd %u arg %lu \n " , filep , command , arg ) ;
2005-04-29 09:41:07 +04:00
2005-04-29 09:41:04 +04:00
cifs_sb = CIFS_SB ( inode - > i_sb ) ;
2007-06-05 22:30:44 +04:00
switch ( command ) {
2006-08-29 22:06:16 +04:00
case FS_IOC_GETFLAGS :
2010-11-08 15:28:32 +03:00
if ( pSMBFile = = NULL )
break ;
tcon = tlink_tcon ( pSMBFile - > tlink ) ;
caps = le64_to_cpu ( tcon - > fsUnixInfo . Capability ) ;
2013-10-15 00:31:32 +04:00
# ifdef CONFIG_CIFS_POSIX
2007-06-05 22:30:44 +04:00
if ( CIFS_UNIX_EXTATTR_CAP & caps ) {
2013-10-14 07:32:30 +04:00
__u64 ExtAttrMask = 0 ;
2012-09-19 03:20:26 +04:00
rc = CIFSGetExtAttr ( xid , tcon ,
pSMBFile - > fid . netfid ,
& ExtAttrBits , & ExtAttrMask ) ;
2007-06-05 22:30:44 +04:00
if ( rc = = 0 )
2005-04-29 09:41:04 +04:00
rc = put_user ( ExtAttrBits &
2006-08-29 22:06:16 +04:00
FS_FL_USER_VISIBLE ,
2005-04-29 09:41:04 +04:00
( int __user * ) arg ) ;
2013-10-15 00:31:32 +04: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-29 09:41:04 +04:00
}
break ;
2006-08-29 22:06:16 +04:00
case FS_IOC_SETFLAGS :
2010-11-08 15:28:32 +03:00
if ( pSMBFile = = NULL )
break ;
tcon = tlink_tcon ( pSMBFile - > tlink ) ;
caps = le64_to_cpu ( tcon - > fsUnixInfo . Capability ) ;
2013-10-15 00:31:32 +04: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-29 09:41:04 +04:00
}
break ;
2013-11-25 07:53:17 +04:00
case CIFS_IOC_COPYCHUNK_FILE :
2015-06-28 07:18:36 +03:00
rc = cifs_ioctl_clone ( xid , filep , arg , 0 , 0 , 0 , false ) ;
break ;
case BTRFS_IOC_CLONE :
rc = cifs_ioctl_clone ( xid , filep , arg , 0 , 0 , 0 , true ) ;
2013-11-14 10:05:36 +04:00
break ;
2015-06-24 11:17:02 +03:00
case CIFS_IOC_SET_INTEGRITY :
if ( pSMBFile = = NULL )
break ;
tcon = tlink_tcon ( pSMBFile - > tlink ) ;
if ( tcon - > ses - > server - > ops - > set_integrity )
rc = tcon - > ses - > server - > ops - > set_integrity ( xid ,
tcon , pSMBFile ) ;
else
rc = - EOPNOTSUPP ;
break ;
2005-04-17 02:20:36 +04:00
default :
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " unsupported ioctl \n " ) ;
2005-04-29 09:41:07 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2005-04-29 09:41:04 +04:00
2012-06-20 11:21:16 +04:00
free_xid ( xid ) ;
2005-04-17 02:20:36 +04:00
return rc ;
2007-06-05 22:30:44 +04:00
}