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-07-05 02:40:10 +03:00
# include "cifs_ioctl.h"
2015-06-28 07:18:36 +03:00
# include <linux/btrfs.h>
2005-04-17 02:20:36 +04:00
2015-12-03 14:59:50 +03:00
static int cifs_file_clone_range ( unsigned int xid , struct file * src_file ,
struct file * dst_file )
2013-11-14 10:05:36 +04:00
{
2015-12-03 14:59:50 +03:00
struct inode * src_inode = file_inode ( src_file ) ;
2013-11-14 10:05:36 +04:00
struct inode * target_inode = file_inode ( dst_file ) ;
struct cifsFileInfo * smb_file_src ;
2015-12-03 14:59:50 +03:00
struct cifsFileInfo * smb_file_target ;
2013-11-14 10:05:36 +04:00
struct cifs_tcon * src_tcon ;
2015-12-03 14:59:50 +03:00
struct cifs_tcon * target_tcon ;
int rc ;
2013-11-14 10:05:36 +04:00
cifs_dbg ( FYI , " ioctl clone range \n " ) ;
2015-12-03 14:59:50 +03:00
if ( ! src_file - > private_data | | ! dst_file - > private_data ) {
2013-11-14 10:05:36 +04:00
rc = - EBADF ;
cifs_dbg ( VFS , " missing cifsFileInfo on copy range src file \n " ) ;
2015-12-03 14:59:50 +03:00
goto out ;
2013-11-14 10:05:36 +04:00
}
rc = - EXDEV ;
smb_file_target = dst_file - > private_data ;
2015-12-03 14:59:50 +03:00
smb_file_src = src_file - > private_data ;
2013-11-14 10:05:36 +04:00
src_tcon = tlink_tcon ( smb_file_src - > tlink ) ;
target_tcon = tlink_tcon ( smb_file_target - > tlink ) ;
2015-12-03 14:59:50 +03:00
if ( src_tcon - > ses ! = target_tcon - > ses ) {
2015-11-09 17:59:45 +03:00
cifs_dbg ( VFS , " source and target of copy not on same server \n " ) ;
2015-12-03 14:59:50 +03:00
goto out ;
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
cifs_dbg ( FYI , " about to flush pages \n " ) ;
/* should we flush first and last page first */
2015-12-03 14:59:50 +03:00
truncate_inode_pages ( & target_inode - > i_data , 0 ) ;
2013-11-14 10:05:36 +04:00
2015-12-03 14:59:50 +03:00
if ( target_tcon - > ses - > server - > ops - > clone_range )
2013-11-14 10:05:36 +04:00
rc = target_tcon - > ses - > server - > ops - > clone_range ( xid ,
2015-12-03 14:59:50 +03:00
smb_file_src , smb_file_target , 0 , src_inode - > i_size , 0 ) ;
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 ;
/* 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 ) ;
2015-12-03 14:59:50 +03:00
out :
return rc ;
}
static long cifs_ioctl_clone ( unsigned int xid , struct file * dst_file ,
unsigned long srcfd )
{
int rc ;
struct fd src_file ;
struct inode * src_inode ;
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 - > f_op - > unlocked_ioctl ! = cifs_ioctl ) {
rc = - EBADF ;
cifs_dbg ( VFS , " src file seems to be from a different filesystem type \n " ) ;
goto out_fput ;
}
src_inode = file_inode ( src_file . file ) ;
rc = - EINVAL ;
if ( S_ISDIR ( src_inode - > i_mode ) )
goto out_fput ;
rc = cifs_file_clone_range ( xid , src_file . file , dst_file ) ;
2013-11-14 10:05:36 +04:00
out_fput :
fdput ( src_file ) ;
out_drop_write :
mnt_drop_write_file ( dst_file ) ;
return rc ;
}
2015-07-05 02:40:10 +03:00
static long smb_mnt_get_fsinfo ( unsigned int xid , struct cifs_tcon * tcon ,
void __user * arg )
{
int rc = 0 ;
struct smb_mnt_fs_info * fsinf ;
fsinf = kzalloc ( sizeof ( struct smb_mnt_fs_info ) , GFP_KERNEL ) ;
if ( fsinf = = NULL )
return - ENOMEM ;
fsinf - > version = 1 ;
fsinf - > protocol_id = tcon - > ses - > server - > vals - > protocol_id ;
fsinf - > device_characteristics =
le32_to_cpu ( tcon - > fsDevInfo . DeviceCharacteristics ) ;
fsinf - > device_type = le32_to_cpu ( tcon - > fsDevInfo . DeviceType ) ;
fsinf - > fs_attributes = le32_to_cpu ( tcon - > fsAttrInfo . Attributes ) ;
fsinf - > max_path_component =
le32_to_cpu ( tcon - > fsAttrInfo . MaxPathNameComponentLength ) ;
# ifdef CONFIG_CIFS_SMB2
fsinf - > vol_serial_number = tcon - > vol_serial_number ;
fsinf - > vol_create_time = le64_to_cpu ( tcon - > vol_create_time ) ;
fsinf - > share_flags = tcon - > share_flags ;
fsinf - > share_caps = le32_to_cpu ( tcon - > capabilities ) ;
fsinf - > sector_flags = tcon - > ss_flags ;
fsinf - > optimal_sector_size = tcon - > perf_sector_size ;
fsinf - > max_bytes_chunk = tcon - > max_bytes_chunk ;
fsinf - > maximal_access = tcon - > maximal_access ;
# endif /* SMB2 */
fsinf - > cifs_posix_caps = le64_to_cpu ( tcon - > fsUnixInfo . Capability ) ;
if ( copy_to_user ( arg , fsinf , sizeof ( struct smb_mnt_fs_info ) ) )
rc = - EFAULT ;
kfree ( fsinf ) ;
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
cifs_sb = CIFS_SB ( inode - > i_sb ) ;
2016-11-17 02:17:15 +03:00
cifs_dbg ( FYI , " cifs ioctl 0x%x \n " , command ) ;
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-12-03 14:59:50 +03:00
rc = cifs_ioctl_clone ( xid , filep , arg ) ;
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 ;
2015-07-05 02:40:10 +03:00
case CIFS_IOC_GET_MNT_INFO :
tcon = tlink_tcon ( pSMBFile - > tlink ) ;
rc = smb_mnt_get_fsinfo ( xid , tcon , ( void __user * ) arg ) ;
break ;
2016-10-01 05:14:26 +03:00
case CIFS_ENUMERATE_SNAPSHOTS :
if ( arg = = 0 ) {
rc = - EINVAL ;
goto cifs_ioc_exit ;
}
tcon = tlink_tcon ( pSMBFile - > tlink ) ;
if ( tcon - > ses - > server - > ops - > enum_snapshots )
rc = tcon - > ses - > server - > ops - > enum_snapshots ( xid , tcon ,
pSMBFile , ( void __user * ) arg ) ;
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
}
2016-10-01 05:14:26 +03:00
cifs_ioc_exit :
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
}