2011-12-29 17:06:33 +04:00
/*
* fs / cifs / smb2inode . c
*
* Copyright ( C ) International Business Machines Corp . , 2002 , 2011
* Etersoft , 2012
* Author ( s ) : Pavel Shilovsky ( pshilovsky @ samba . org ) ,
* 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
*/
# include <linux/fs.h>
# include <linux/stat.h>
# include <linux/slab.h>
# include <linux/pagemap.h>
# include <asm/div64.h>
# include "cifsfs.h"
# include "cifspdu.h"
# include "cifsglob.h"
# include "cifsproto.h"
# include "cifs_debug.h"
# include "cifs_fs_sb.h"
# include "cifs_unicode.h"
# include "fscache.h"
# include "smb2glob.h"
# include "smb2pdu.h"
# include "smb2proto.h"
2018-09-03 06:33:41 +03:00
static int
smb2_compound_op ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_sb_info * cifs_sb , const char * full_path ,
__u32 desired_access , __u32 create_disposition ,
2018-09-03 06:33:45 +03:00
__u32 create_options , void * ptr , int command )
2018-09-03 06:33:41 +03:00
{
int rc ;
__le16 * utf16_path = NULL ;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
struct cifs_ses * ses = tcon - > ses ;
struct TCP_Server_Info * server = ses - > server ;
int num_rqst = 0 ;
struct smb_rqst rqst [ 3 ] ;
int resp_buftype [ 3 ] ;
struct kvec rsp_iov [ 3 ] ;
struct kvec open_iov [ SMB2_CREATE_IOV_SIZE ] ;
struct kvec qi_iov [ 1 ] ;
2018-09-03 06:33:45 +03:00
struct kvec si_iov [ 2 ] ; /* 1 + potential padding. */
2018-09-03 06:33:41 +03:00
struct kvec close_iov [ 1 ] ;
2018-09-03 06:33:45 +03:00
struct smb2_query_info_rsp * qi_rsp = NULL ;
2018-09-03 06:33:41 +03:00
int flags = 0 ;
2018-09-03 06:33:45 +03:00
__u8 delete_pending [ 8 ] = { 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
unsigned int size [ 1 ] ;
void * data [ 1 ] ;
2018-09-03 06:33:41 +03:00
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
memset ( rqst , 0 , sizeof ( rqst ) ) ;
resp_buftype [ 0 ] = resp_buftype [ 1 ] = resp_buftype [ 2 ] = CIFS_NO_BUFFER ;
memset ( rsp_iov , 0 , sizeof ( rsp_iov ) ) ;
/* Open */
utf16_path = cifs_convert_path_to_utf16 ( full_path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
oparms . tcon = tcon ;
oparms . desired_access = desired_access ;
oparms . disposition = create_disposition ;
oparms . create_options = create_options ;
oparms . fid = & fid ;
oparms . reconnect = false ;
memset ( & open_iov , 0 , sizeof ( open_iov ) ) ;
rqst [ num_rqst ] . rq_iov = open_iov ;
rqst [ num_rqst ] . rq_nvec = SMB2_CREATE_IOV_SIZE ;
rc = SMB2_open_init ( tcon , & rqst [ num_rqst ] , & oplock , & oparms ,
utf16_path ) ;
kfree ( utf16_path ) ;
if ( rc )
goto finished ;
smb2_set_next_command ( server , & rqst [ num_rqst + + ] ) ;
/* Operation */
switch ( command ) {
case SMB2_OP_QUERY_INFO :
memset ( & qi_iov , 0 , sizeof ( qi_iov ) ) ;
rqst [ num_rqst ] . rq_iov = qi_iov ;
rqst [ num_rqst ] . rq_nvec = 1 ;
rc = SMB2_query_info_init ( tcon , & rqst [ num_rqst ] , COMPOUND_FID ,
COMPOUND_FID , FILE_ALL_INFORMATION ,
SMB2_O_INFO_FILE , 0 ,
sizeof ( struct smb2_file_all_info ) +
PATH_MAX * 2 ) ;
smb2_set_next_command ( server , & rqst [ num_rqst ] ) ;
smb2_set_related ( & rqst [ num_rqst + + ] ) ;
break ;
2018-09-03 06:33:43 +03:00
case SMB2_OP_DELETE :
break ;
2018-09-03 06:33:42 +03:00
case SMB2_OP_MKDIR :
/*
* Directories are created through parameters in the
* SMB2_open ( ) call .
*/
break ;
2018-09-03 06:33:45 +03:00
case SMB2_OP_RMDIR :
memset ( & si_iov , 0 , sizeof ( si_iov ) ) ;
rqst [ num_rqst ] . rq_iov = si_iov ;
rqst [ num_rqst ] . rq_nvec = 1 ;
size [ 0 ] = 8 ;
data [ 0 ] = & delete_pending [ 0 ] ;
rc = SMB2_set_info_init ( tcon , & rqst [ num_rqst ] , COMPOUND_FID ,
COMPOUND_FID , current - > tgid ,
FILE_DISPOSITION_INFORMATION ,
SMB2_O_INFO_FILE , 0 , data , size ) ;
smb2_set_next_command ( server , & rqst [ num_rqst ] ) ;
smb2_set_related ( & rqst [ num_rqst + + ] ) ;
break ;
2018-09-03 06:33:41 +03:00
default :
cifs_dbg ( VFS , " Invalid command \n " ) ;
rc = - EINVAL ;
}
if ( rc )
goto finished ;
/* Close */
memset ( & close_iov , 0 , sizeof ( close_iov ) ) ;
rqst [ num_rqst ] . rq_iov = close_iov ;
rqst [ num_rqst ] . rq_nvec = 1 ;
rc = SMB2_close_init ( tcon , & rqst [ num_rqst ] , COMPOUND_FID ,
COMPOUND_FID ) ;
smb2_set_related ( & rqst [ num_rqst + + ] ) ;
if ( rc )
goto finished ;
rc = compound_send_recv ( xid , ses , flags , num_rqst , rqst ,
resp_buftype , rsp_iov ) ;
finished :
SMB2_open_free ( & rqst [ 0 ] ) ;
switch ( command ) {
case SMB2_OP_QUERY_INFO :
if ( rc = = 0 ) {
2018-09-03 06:33:45 +03:00
qi_rsp = ( struct smb2_query_info_rsp * )
rsp_iov [ 1 ] . iov_base ;
2018-09-03 06:33:41 +03:00
rc = smb2_validate_and_copy_iov (
2018-09-03 06:33:45 +03:00
le16_to_cpu ( qi_rsp - > OutputBufferOffset ) ,
le32_to_cpu ( qi_rsp - > OutputBufferLength ) ,
2018-09-03 06:33:41 +03:00
& rsp_iov [ 1 ] , sizeof ( struct smb2_file_all_info ) ,
2018-09-03 06:33:45 +03:00
ptr ) ;
2018-09-03 06:33:41 +03:00
}
if ( rqst [ 1 ] . rq_iov )
SMB2_query_info_free ( & rqst [ 1 ] ) ;
if ( rqst [ 2 ] . rq_iov )
SMB2_close_free ( & rqst [ 2 ] ) ;
break ;
2018-09-03 06:33:43 +03:00
case SMB2_OP_DELETE :
2018-09-03 06:33:42 +03:00
case SMB2_OP_MKDIR :
if ( rqst [ 1 ] . rq_iov )
SMB2_close_free ( & rqst [ 1 ] ) ;
break ;
2018-09-03 06:33:45 +03:00
case SMB2_OP_RMDIR :
if ( rqst [ 1 ] . rq_iov )
SMB2_set_info_free ( & rqst [ 1 ] ) ;
if ( rqst [ 2 ] . rq_iov )
SMB2_close_free ( & rqst [ 2 ] ) ;
break ;
2018-09-03 06:33:41 +03:00
}
free_rsp_buf ( resp_buftype [ 0 ] , rsp_iov [ 0 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 1 ] , rsp_iov [ 1 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 2 ] , rsp_iov [ 2 ] . iov_base ) ;
return rc ;
}
2011-12-29 17:06:33 +04:00
static int
smb2_open_op_close ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_sb_info * cifs_sb , const char * full_path ,
__u32 desired_access , __u32 create_disposition ,
2013-07-05 12:21:26 +04:00
__u32 create_options , void * data , int command )
2011-12-29 17:06:33 +04:00
{
int rc , tmprc = 0 ;
2018-04-26 06:19:09 +03:00
__le16 * utf16_path = NULL ;
2012-09-19 03:20:33 +04:00
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
2013-07-09 18:20:30 +04:00
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
2018-04-26 06:19:09 +03:00
bool use_cached_root_handle = false ;
2011-12-29 17:06:33 +04:00
2018-04-26 06:19:09 +03:00
if ( ( strcmp ( full_path , " " ) = = 0 ) & & ( create_options = = 0 ) & &
( desired_access = = FILE_READ_ATTRIBUTES ) & &
( create_disposition = = FILE_OPEN ) & &
( tcon - > nohandlecache = = false ) ) {
rc = open_shroot ( xid , tcon , & fid ) ;
if ( rc = = 0 )
use_cached_root_handle = true ;
}
if ( use_cached_root_handle = = false ) {
utf16_path = cifs_convert_path_to_utf16 ( full_path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
2011-12-29 17:06:33 +04:00
2018-04-26 06:19:09 +03:00
oparms . tcon = tcon ;
oparms . desired_access = desired_access ;
oparms . disposition = create_disposition ;
oparms . create_options = create_options ;
oparms . fid = & fid ;
oparms . reconnect = false ;
2013-07-09 18:20:30 +04:00
2018-06-08 06:21:18 +03:00
rc = SMB2_open ( xid , & oparms , utf16_path , & oplock , NULL , NULL ,
NULL ) ;
2018-04-26 06:19:09 +03:00
if ( rc ) {
kfree ( utf16_path ) ;
return rc ;
}
2011-12-29 17:06:33 +04:00
}
switch ( command ) {
2012-09-19 03:20:31 +04:00
case SMB2_OP_RENAME :
2013-07-09 18:20:30 +04:00
tmprc = SMB2_rename ( xid , tcon , fid . persistent_fid ,
fid . volatile_fid , ( __le16 * ) data ) ;
2012-09-19 03:20:31 +04:00
break ;
2012-09-19 03:20:31 +04:00
case SMB2_OP_HARDLINK :
2013-07-09 18:20:30 +04:00
tmprc = SMB2_set_hardlink ( xid , tcon , fid . persistent_fid ,
fid . volatile_fid , ( __le16 * ) data ) ;
2012-09-19 03:20:31 +04:00
break ;
2012-09-19 03:20:32 +04:00
case SMB2_OP_SET_EOF :
2013-07-09 18:20:30 +04:00
tmprc = SMB2_set_eof ( xid , tcon , fid . persistent_fid ,
fid . volatile_fid , current - > tgid ,
2014-07-20 06:44:58 +04:00
( __le64 * ) data , false ) ;
2012-09-19 03:20:32 +04:00
break ;
2012-09-19 03:20:32 +04:00
case SMB2_OP_SET_INFO :
2018-09-03 06:33:44 +03:00
tmprc = SMB2_set_basic_info ( xid , tcon , fid . persistent_fid ,
fid . volatile_fid ,
( FILE_BASIC_INFO * ) data ) ;
2012-09-19 03:20:32 +04:00
break ;
2011-12-29 17:06:33 +04:00
default :
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Invalid command \n " ) ;
2011-12-29 17:06:33 +04:00
break ;
}
2018-07-31 01:48:22 +03:00
if ( use_cached_root_handle )
close_shroot ( & tcon - > crfid ) ;
else
2018-04-26 06:19:09 +03:00
rc = SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
2011-12-29 17:06:33 +04:00
if ( tmprc )
rc = tmprc ;
kfree ( utf16_path ) ;
return rc ;
}
2012-09-19 03:20:26 +04:00
void
2011-12-29 17:06:33 +04:00
move_smb2_info_to_cifs ( FILE_ALL_INFO * dst , struct smb2_file_all_info * src )
{
memcpy ( dst , src , ( size_t ) ( & src - > CurrentByteOffset ) - ( size_t ) src ) ;
dst - > CurrentByteOffset = src - > CurrentByteOffset ;
dst - > Mode = src - > Mode ;
dst - > AlignmentRequirement = src - > AlignmentRequirement ;
dst - > IndexNumber1 = 0 ; /* we don't use it */
}
int
smb2_query_path_info ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_sb_info * cifs_sb , const char * full_path ,
2013-10-23 17:49:47 +04:00
FILE_ALL_INFO * data , bool * adjust_tz , bool * symlink )
2011-12-29 17:06:33 +04:00
{
int rc ;
struct smb2_file_all_info * smb2_data ;
* adjust_tz = false ;
2013-10-23 17:49:47 +04:00
* symlink = false ;
2011-12-29 17:06:33 +04:00
2014-08-22 13:32:11 +04:00
smb2_data = kzalloc ( sizeof ( struct smb2_file_all_info ) + PATH_MAX * 2 ,
2011-12-29 17:06:33 +04:00
GFP_KERNEL ) ;
if ( smb2_data = = NULL )
return - ENOMEM ;
2018-09-03 06:33:41 +03:00
rc = smb2_compound_op ( xid , tcon , cifs_sb , full_path ,
FILE_READ_ATTRIBUTES , FILE_OPEN , 0 ,
smb2_data , SMB2_OP_QUERY_INFO ) ;
2013-10-23 17:49:47 +04:00
if ( rc = = - EOPNOTSUPP ) {
* symlink = true ;
/* Failed on a symbolic link - query a reparse point info */
2018-09-03 06:33:41 +03:00
rc = smb2_compound_op ( xid , tcon , cifs_sb , full_path ,
FILE_READ_ATTRIBUTES , FILE_OPEN ,
OPEN_REPARSE_POINT , smb2_data ,
SMB2_OP_QUERY_INFO ) ;
2013-10-23 17:49:47 +04:00
}
2011-12-29 17:06:33 +04:00
if ( rc )
goto out ;
move_smb2_info_to_cifs ( data , smb2_data ) ;
out :
kfree ( smb2_data ) ;
return rc ;
}
2011-07-19 12:56:37 +04:00
int
smb2_mkdir ( const unsigned int xid , struct cifs_tcon * tcon , const char * name ,
struct cifs_sb_info * cifs_sb )
{
2018-09-03 06:33:42 +03:00
return smb2_compound_op ( xid , tcon , cifs_sb , name ,
FILE_WRITE_ATTRIBUTES , FILE_CREATE ,
CREATE_NOT_FILE , NULL , SMB2_OP_MKDIR ) ;
2011-07-19 12:56:37 +04:00
}
void
smb2_mkdir_setinfo ( struct inode * inode , const char * name ,
struct cifs_sb_info * cifs_sb , struct cifs_tcon * tcon ,
const unsigned int xid )
{
FILE_BASIC_INFO data ;
struct cifsInodeInfo * cifs_i ;
u32 dosattrs ;
int tmprc ;
memset ( & data , 0 , sizeof ( data ) ) ;
cifs_i = CIFS_I ( inode ) ;
dosattrs = cifs_i - > cifsAttrs | ATTR_READONLY ;
data . Attributes = cpu_to_le32 ( dosattrs ) ;
tmprc = smb2_open_op_close ( xid , tcon , cifs_sb , name ,
2013-07-05 12:21:26 +04:00
FILE_WRITE_ATTRIBUTES , FILE_CREATE ,
2011-07-19 12:56:37 +04:00
CREATE_NOT_FILE , & data , SMB2_OP_SET_INFO ) ;
if ( tmprc = = 0 )
cifs_i - > cifsAttrs = dosattrs ;
}
2012-07-10 16:14:38 +04:00
int
smb2_rmdir ( const unsigned int xid , struct cifs_tcon * tcon , const char * name ,
struct cifs_sb_info * cifs_sb )
{
2018-09-03 06:33:45 +03:00
return smb2_compound_op ( xid , tcon , cifs_sb , name , DELETE , FILE_OPEN ,
CREATE_NOT_FILE ,
NULL , SMB2_OP_RMDIR ) ;
2012-07-10 16:14:38 +04:00
}
2012-09-19 03:20:25 +04:00
int
smb2_unlink ( const unsigned int xid , struct cifs_tcon * tcon , const char * name ,
struct cifs_sb_info * cifs_sb )
{
2018-09-03 06:33:43 +03:00
return smb2_compound_op ( xid , tcon , cifs_sb , name , DELETE , FILE_OPEN ,
CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT ,
NULL , SMB2_OP_DELETE ) ;
2012-09-19 03:20:25 +04:00
}
2012-09-19 03:20:31 +04:00
2012-09-19 03:20:31 +04:00
static int
smb2_set_path_attr ( const unsigned int xid , struct cifs_tcon * tcon ,
const char * from_name , const char * to_name ,
struct cifs_sb_info * cifs_sb , __u32 access , int command )
2012-09-19 03:20:31 +04:00
{
__le16 * smb2_to_name = NULL ;
int rc ;
smb2_to_name = cifs_convert_path_to_utf16 ( to_name , cifs_sb ) ;
if ( smb2_to_name = = NULL ) {
rc = - ENOMEM ;
goto smb2_rename_path ;
}
2012-09-19 03:20:31 +04:00
rc = smb2_open_op_close ( xid , tcon , cifs_sb , from_name , access ,
2013-07-05 12:21:26 +04:00
FILE_OPEN , 0 , smb2_to_name , command ) ;
2012-09-19 03:20:31 +04:00
smb2_rename_path :
kfree ( smb2_to_name ) ;
return rc ;
}
2012-09-19 03:20:31 +04:00
int
smb2_rename_path ( const unsigned int xid , struct cifs_tcon * tcon ,
const char * from_name , const char * to_name ,
struct cifs_sb_info * cifs_sb )
{
return smb2_set_path_attr ( xid , tcon , from_name , to_name , cifs_sb ,
DELETE , SMB2_OP_RENAME ) ;
}
int
smb2_create_hardlink ( const unsigned int xid , struct cifs_tcon * tcon ,
const char * from_name , const char * to_name ,
struct cifs_sb_info * cifs_sb )
{
return smb2_set_path_attr ( xid , tcon , from_name , to_name , cifs_sb ,
FILE_READ_ATTRIBUTES , SMB2_OP_HARDLINK ) ;
}
2012-09-19 03:20:32 +04:00
int
smb2_set_path_size ( const unsigned int xid , struct cifs_tcon * tcon ,
const char * full_path , __u64 size ,
struct cifs_sb_info * cifs_sb , bool set_alloc )
{
__le64 eof = cpu_to_le64 ( size ) ;
return smb2_open_op_close ( xid , tcon , cifs_sb , full_path ,
2013-07-05 12:21:26 +04:00
FILE_WRITE_DATA , FILE_OPEN , 0 , & eof ,
2012-09-19 03:20:32 +04:00
SMB2_OP_SET_EOF ) ;
}
2012-09-19 03:20:32 +04:00
int
smb2_set_file_info ( struct inode * inode , const char * full_path ,
FILE_BASIC_INFO * buf , const unsigned int xid )
{
struct cifs_sb_info * cifs_sb = CIFS_SB ( inode - > i_sb ) ;
struct tcon_link * tlink ;
int rc ;
2016-09-26 22:23:08 +03:00
if ( ( buf - > CreationTime = = 0 ) & & ( buf - > LastAccessTime = = 0 ) & &
2018-08-03 04:28:18 +03:00
( buf - > LastWriteTime = = 0 ) & & ( buf - > ChangeTime = = 0 ) & &
2016-09-26 22:23:08 +03:00
( buf - > Attributes = = 0 ) )
return 0 ; /* would be a no op, no sense sending this */
2012-09-19 03:20:32 +04:00
tlink = cifs_sb_tlink ( cifs_sb ) ;
if ( IS_ERR ( tlink ) )
return PTR_ERR ( tlink ) ;
2016-09-26 22:23:08 +03:00
2012-09-19 03:20:32 +04:00
rc = smb2_open_op_close ( xid , tlink_tcon ( tlink ) , cifs_sb , full_path ,
2013-07-05 12:21:26 +04:00
FILE_WRITE_ATTRIBUTES , FILE_OPEN , 0 , buf ,
2012-09-19 03:20:32 +04:00
SMB2_OP_SET_INFO ) ;
cifs_put_tlink ( tlink ) ;
return rc ;
}