2012-09-19 03:20:26 +04:00
/*
* fs / cifs / smb2file . c
*
* Copyright ( C ) International Business Machines Corp . , 2002 , 2011
* Author ( s ) : Steve French ( sfrench @ us . ibm . com ) ,
* Pavel Shilovsky ( ( pshilovsky @ samba . org ) 2012
*
* 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 "smb2proto.h"
int
2013-07-05 12:00:30 +04:00
smb2_open_file ( const unsigned int xid , struct cifs_open_parms * oparms ,
__u32 * oplock , FILE_ALL_INFO * buf )
2012-09-19 03:20:26 +04:00
{
int rc ;
__le16 * smb2_path ;
struct smb2_file_all_info * smb2_data = NULL ;
2018-07-05 16:10:02 +03:00
__u8 smb2_oplock ;
2013-07-05 12:00:30 +04:00
struct cifs_fid * fid = oparms - > fid ;
2015-11-03 19:08:53 +03:00
struct network_resiliency_req nr_ioctl_req ;
2012-09-19 03:20:26 +04:00
2013-07-05 12:00:30 +04:00
smb2_path = cifs_convert_path_to_utf16 ( oparms - > path , oparms - > cifs_sb ) ;
2012-09-19 03:20:26 +04:00
if ( smb2_path = = NULL ) {
rc = - ENOMEM ;
goto out ;
}
2014-08-22 13:32:11 +04:00
smb2_data = kzalloc ( sizeof ( struct smb2_file_all_info ) + PATH_MAX * 2 ,
2012-09-19 03:20:26 +04:00
GFP_KERNEL ) ;
if ( smb2_data = = NULL ) {
rc = - ENOMEM ;
goto out ;
}
2013-07-05 12:00:30 +04:00
oparms - > desired_access | = FILE_READ_ATTRIBUTES ;
2018-07-05 16:10:02 +03:00
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH ;
2012-09-19 17:22:44 +04:00
2018-07-05 16:10:02 +03:00
rc = SMB2_open ( xid , oparms , smb2_path , & smb2_oplock , smb2_data , NULL ,
2020-03-02 19:53:22 +03:00
NULL , NULL ) ;
2012-09-19 03:20:26 +04:00
if ( rc )
goto out ;
2015-11-03 19:08:53 +03:00
2019-12-18 06:04:51 +03:00
if ( oparms - > tcon - > use_resilient ) {
2019-03-30 00:31:07 +03:00
/* default timeout is 0, servers pick default (120 seconds) */
nr_ioctl_req . Timeout =
cpu_to_le32 ( oparms - > tcon - > handle_timeout ) ;
2015-11-03 19:08:53 +03:00
nr_ioctl_req . Reserved = 0 ;
rc = SMB2_ioctl ( xid , oparms - > tcon , fid - > persistent_fid ,
2017-02-28 17:08:41 +03:00
fid - > volatile_fid , FSCTL_LMR_REQUEST_RESILIENCY ,
2018-01-24 15:46:11 +03:00
true /* is_fsctl */ ,
2015-11-03 19:08:53 +03:00
( char * ) & nr_ioctl_req , sizeof ( nr_ioctl_req ) ,
2019-03-29 06:32:49 +03:00
CIFSMaxBufSize , NULL , NULL /* no return info */ ) ;
2015-11-03 19:08:53 +03:00
if ( rc = = - EOPNOTSUPP ) {
cifs_dbg ( VFS ,
" resiliency not supported by server, disabling \n " ) ;
oparms - > tcon - > use_resilient = false ;
} else if ( rc )
cifs_dbg ( FYI , " error %d setting resiliency \n " , rc ) ;
rc = 0 ;
}
2012-09-19 03:20:26 +04:00
if ( buf ) {
2019-07-19 01:22:18 +03:00
/* if open response does not have IndexNumber field - get it */
if ( smb2_data - > IndexNumber = = 0 ) {
rc = SMB2_get_srv_num ( xid , oparms - > tcon ,
fid - > persistent_fid ,
2012-09-19 03:20:26 +04:00
fid - > volatile_fid ,
& smb2_data - > IndexNumber ) ;
2019-07-19 01:22:18 +03:00
if ( rc ) {
/*
* let get_inode_info disable server inode
* numbers
*/
smb2_data - > IndexNumber = 0 ;
rc = 0 ;
}
2012-09-19 03:20:26 +04:00
}
move_smb2_info_to_cifs ( buf , smb2_data ) ;
}
2018-07-05 16:10:02 +03:00
* oplock = smb2_oplock ;
2012-09-19 03:20:26 +04:00
out :
kfree ( smb2_data ) ;
kfree ( smb2_path ) ;
return rc ;
}
2012-09-19 17:22:43 +04:00
int
smb2_unlock_range ( struct cifsFileInfo * cfile , struct file_lock * flock ,
const unsigned int xid )
{
int rc = 0 , stored_rc ;
unsigned int max_num , num = 0 , max_buf ;
struct smb2_lock_element * buf , * cur ;
struct cifs_tcon * tcon = tlink_tcon ( cfile - > tlink ) ;
2015-03-18 01:25:59 +03:00
struct cifsInodeInfo * cinode = CIFS_I ( d_inode ( cfile - > dentry ) ) ;
2012-09-19 17:22:43 +04:00
struct cifsLockInfo * li , * tmp ;
__u64 length = 1 + flock - > fl_end - flock - > fl_start ;
struct list_head tmp_llist ;
INIT_LIST_HEAD ( & tmp_llist ) ;
/*
* Accessing maxBuf is racy with cifs_reconnect - need to store value
2019-01-08 21:30:57 +03:00
* and check it before using .
2012-09-19 17:22:43 +04:00
*/
max_buf = tcon - > ses - > server - > maxBuf ;
2019-01-08 21:30:57 +03:00
if ( max_buf < sizeof ( struct smb2_lock_element ) )
2012-09-19 17:22:43 +04:00
return - EINVAL ;
2019-01-08 21:30:56 +03:00
BUILD_BUG_ON ( sizeof ( struct smb2_lock_element ) > PAGE_SIZE ) ;
max_buf = min_t ( unsigned int , max_buf , PAGE_SIZE ) ;
2012-09-19 17:22:43 +04:00
max_num = max_buf / sizeof ( struct smb2_lock_element ) ;
2014-12-11 02:41:20 +03:00
buf = kcalloc ( max_num , sizeof ( struct smb2_lock_element ) , GFP_KERNEL ) ;
2012-09-19 17:22:43 +04:00
if ( ! buf )
return - ENOMEM ;
cur = buf ;
2019-10-23 12:02:33 +03:00
cifs_down_write ( & cinode - > lock_sem ) ;
2012-09-19 17:22:43 +04:00
list_for_each_entry_safe ( li , tmp , & cfile - > llist - > locks , llist ) {
if ( flock - > fl_start > li - > offset | |
( flock - > fl_start + length ) <
( li - > offset + li - > length ) )
continue ;
if ( current - > tgid ! = li - > pid )
2020-02-21 05:30:01 +03:00
/*
* flock and OFD lock are associated with an open
* file description , not the process .
*/
if ( ! ( flock - > fl_flags & ( FL_FLOCK | FL_OFDLCK ) ) )
continue ;
2012-09-19 17:22:43 +04:00
if ( cinode - > can_cache_brlcks ) {
/*
* We can cache brlock requests - simply remove a lock
* from the file ' s list .
*/
list_del ( & li - > llist ) ;
cifs_del_lock_waiters ( li ) ;
kfree ( li ) ;
continue ;
}
cur - > Length = cpu_to_le64 ( li - > length ) ;
cur - > Offset = cpu_to_le64 ( li - > offset ) ;
cur - > Flags = cpu_to_le32 ( SMB2_LOCKFLAG_UNLOCK ) ;
/*
* We need to save a lock here to let us add it again to the
* file ' s list if the unlock range request fails on the server .
*/
list_move ( & li - > llist , & tmp_llist ) ;
if ( + + num = = max_num ) {
stored_rc = smb2_lockv ( xid , tcon ,
cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ,
current - > tgid , num , buf ) ;
if ( stored_rc ) {
/*
* We failed on the unlock range request - add
* all locks from the tmp list to the head of
* the file ' s list .
*/
cifs_move_llist ( & tmp_llist ,
& cfile - > llist - > locks ) ;
rc = stored_rc ;
} else
/*
* The unlock range request succeed - free the
* tmp list .
*/
cifs_free_llist ( & tmp_llist ) ;
cur = buf ;
num = 0 ;
} else
cur + + ;
}
if ( num ) {
stored_rc = smb2_lockv ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid , current - > tgid ,
num , buf ) ;
if ( stored_rc ) {
cifs_move_llist ( & tmp_llist , & cfile - > llist - > locks ) ;
rc = stored_rc ;
} else
cifs_free_llist ( & tmp_llist ) ;
}
2012-09-19 17:22:44 +04:00
up_write ( & cinode - > lock_sem ) ;
2012-09-19 17:22:43 +04:00
kfree ( buf ) ;
return rc ;
}
2012-09-19 17:22:44 +04:00
static int
smb2_push_mand_fdlocks ( struct cifs_fid_locks * fdlocks , const unsigned int xid ,
struct smb2_lock_element * buf , unsigned int max_num )
{
int rc = 0 , stored_rc ;
struct cifsFileInfo * cfile = fdlocks - > cfile ;
struct cifsLockInfo * li ;
unsigned int num = 0 ;
struct smb2_lock_element * cur = buf ;
struct cifs_tcon * tcon = tlink_tcon ( cfile - > tlink ) ;
list_for_each_entry ( li , & fdlocks - > locks , llist ) {
cur - > Length = cpu_to_le64 ( li - > length ) ;
cur - > Offset = cpu_to_le64 ( li - > offset ) ;
cur - > Flags = cpu_to_le32 ( li - > type |
SMB2_LOCKFLAG_FAIL_IMMEDIATELY ) ;
if ( + + num = = max_num ) {
stored_rc = smb2_lockv ( xid , tcon ,
cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ,
current - > tgid , num , buf ) ;
if ( stored_rc )
rc = stored_rc ;
cur = buf ;
num = 0 ;
} else
cur + + ;
}
if ( num ) {
stored_rc = smb2_lockv ( xid , tcon ,
cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ,
current - > tgid , num , buf ) ;
if ( stored_rc )
rc = stored_rc ;
}
return rc ;
}
int
smb2_push_mandatory_locks ( struct cifsFileInfo * cfile )
{
int rc = 0 , stored_rc ;
unsigned int xid ;
unsigned int max_num , max_buf ;
struct smb2_lock_element * buf ;
2015-03-18 01:25:59 +03:00
struct cifsInodeInfo * cinode = CIFS_I ( d_inode ( cfile - > dentry ) ) ;
2012-09-19 17:22:44 +04:00
struct cifs_fid_locks * fdlocks ;
xid = get_xid ( ) ;
/*
* Accessing maxBuf is racy with cifs_reconnect - need to store value
* and check it for zero before using .
*/
max_buf = tlink_tcon ( cfile - > tlink ) - > ses - > server - > maxBuf ;
2016-11-30 03:14:43 +03:00
if ( max_buf < sizeof ( struct smb2_lock_element ) ) {
2012-09-19 17:22:44 +04:00
free_xid ( xid ) ;
return - EINVAL ;
}
2019-01-08 21:30:56 +03:00
BUILD_BUG_ON ( sizeof ( struct smb2_lock_element ) > PAGE_SIZE ) ;
max_buf = min_t ( unsigned int , max_buf , PAGE_SIZE ) ;
2012-09-19 17:22:44 +04:00
max_num = max_buf / sizeof ( struct smb2_lock_element ) ;
2014-12-11 02:41:20 +03:00
buf = kcalloc ( max_num , sizeof ( struct smb2_lock_element ) , GFP_KERNEL ) ;
2012-09-19 17:22:44 +04:00
if ( ! buf ) {
free_xid ( xid ) ;
return - ENOMEM ;
}
list_for_each_entry ( fdlocks , & cinode - > llist , llist ) {
stored_rc = smb2_push_mand_fdlocks ( fdlocks , xid , buf , max_num ) ;
if ( stored_rc )
rc = stored_rc ;
}
kfree ( buf ) ;
free_xid ( xid ) ;
return rc ;
}