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"
2018-10-16 22:47:58 +03:00
# include "smb2proto.h"
2015-06-28 07:18:36 +03:00
# include <linux/btrfs.h>
2005-04-17 02:20:36 +04:00
2018-10-08 03:19:58 +03:00
static long cifs_ioctl_query_info ( unsigned int xid , struct file * filep ,
unsigned long p )
{
2018-10-16 22:47:58 +03:00
struct inode * inode = file_inode ( filep ) ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( inode - > i_sb ) ;
struct cifs_tcon * tcon = cifs_sb_master_tcon ( cifs_sb ) ;
struct dentry * dentry = filep - > f_path . dentry ;
unsigned char * path ;
__le16 * utf16_path = NULL , root_path ;
int rc = 0 ;
2018-10-08 03:19:58 +03:00
2018-10-16 22:47:58 +03:00
path = build_path_from_dentry ( dentry ) ;
if ( path = = NULL )
return - ENOMEM ;
cifs_dbg ( FYI , " %s %s \n " , __func__ , path ) ;
if ( ! path [ 0 ] ) {
root_path = 0 ;
utf16_path = & root_path ;
} else {
utf16_path = cifs_convert_path_to_utf16 ( path + 1 , cifs_sb ) ;
if ( ! utf16_path ) {
rc = - ENOMEM ;
goto ici_exit ;
}
}
2018-10-08 03:19:58 +03:00
if ( tcon - > ses - > server - > ops - > ioctl_query_info )
2018-10-16 22:47:58 +03:00
rc = tcon - > ses - > server - > ops - > ioctl_query_info (
xid , tcon , utf16_path ,
filep - > private_data ? 0 : 1 , p ) ;
2018-10-08 03:19:58 +03:00
else
2018-10-16 22:47:58 +03:00
rc = - EOPNOTSUPP ;
ici_exit :
if ( utf16_path ! = & root_path )
kfree ( utf16_path ) ;
kfree ( path ) ;
return rc ;
2018-10-08 03:19:58 +03:00
}
2017-04-04 10:12:04 +03:00
static long cifs_ioctl_copychunk ( unsigned int xid , struct file * dst_file ,
2015-12-03 14:59:50 +03:00
unsigned long srcfd )
{
int rc ;
struct fd src_file ;
struct inode * src_inode ;
2017-04-04 10:12:04 +03:00
cifs_dbg ( FYI , " ioctl copychunk range \n " ) ;
2015-12-03 14:59:50 +03:00
/* 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 ;
2017-02-10 13:33:51 +03:00
rc = cifs_file_copychunk_range ( xid , src_file . file , 0 , dst_file , 0 ,
src_inode - > i_size , 0 ) ;
2017-04-26 19:10:17 +03:00
if ( rc > 0 )
rc = 0 ;
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 ) ;
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 ;
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 ;
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
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 :
2017-04-04 10:12:04 +03:00
rc = cifs_ioctl_copychunk ( xid , filep , arg ) ;
2013-11-14 10:05:36 +04:00
break ;
2018-10-08 03:19:58 +03:00
case CIFS_QUERY_INFO :
rc = cifs_ioctl_query_info ( xid , filep , arg ) ;
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 :
2017-05-04 01:41:13 +03:00
if ( pSMBFile = = NULL )
break ;
2015-07-05 02:40:10 +03:00
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 :
2017-05-03 18:39:08 +03:00
if ( pSMBFile = = NULL )
break ;
2016-10-01 05:14:26 +03:00
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
}