2021-03-16 04:49:09 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( C ) 2016 Namjae Jeon < linkinjeon @ kernel . org >
* Copyright ( C ) 2018 Samsung Electronics Co . , Ltd .
*/
# include <linux/moduleparam.h>
# include "glob.h"
# include "oplock.h"
# include "smb_common.h"
# include "smbstatus.h"
# include "connection.h"
# include "mgmt/user_session.h"
# include "mgmt/share_config.h"
# include "mgmt/tree_connect.h"
static LIST_HEAD ( lease_table_list ) ;
static DEFINE_RWLOCK ( lease_list_lock ) ;
/**
2021-03-21 11:05:56 +03:00
* alloc_opinfo ( ) - allocate a new opinfo object for oplock info
* @ work : smb work
2021-03-16 04:49:09 +03:00
* @ id : fid of open file
* @ Tid : tree id of connection
*
* Return : allocated opinfo object on success , otherwise NULL
*/
static struct oplock_info * alloc_opinfo ( struct ksmbd_work * work ,
2021-05-26 11:57:12 +03:00
u64 id , __u16 Tid )
2021-03-16 04:49:09 +03:00
{
struct ksmbd_session * sess = work - > sess ;
struct oplock_info * opinfo ;
opinfo = kzalloc ( sizeof ( struct oplock_info ) , GFP_KERNEL ) ;
if ( ! opinfo )
return NULL ;
opinfo - > sess = sess ;
opinfo - > conn = sess - > conn ;
2021-06-30 03:37:09 +03:00
opinfo - > level = SMB2_OPLOCK_LEVEL_NONE ;
2021-03-16 04:49:09 +03:00
opinfo - > op_state = OPLOCK_STATE_NONE ;
opinfo - > pending_break = 0 ;
opinfo - > fid = id ;
opinfo - > Tid = Tid ;
INIT_LIST_HEAD ( & opinfo - > op_entry ) ;
INIT_LIST_HEAD ( & opinfo - > interim_list ) ;
init_waitqueue_head ( & opinfo - > oplock_q ) ;
init_waitqueue_head ( & opinfo - > oplock_brk ) ;
atomic_set ( & opinfo - > refcount , 1 ) ;
atomic_set ( & opinfo - > breaking_cnt , 0 ) ;
return opinfo ;
}
static void lease_add_list ( struct oplock_info * opinfo )
{
struct lease_table * lb = opinfo - > o_lease - > l_lb ;
spin_lock ( & lb - > lb_lock ) ;
list_add_rcu ( & opinfo - > lease_entry , & lb - > lease_list ) ;
spin_unlock ( & lb - > lb_lock ) ;
}
static void lease_del_list ( struct oplock_info * opinfo )
{
struct lease_table * lb = opinfo - > o_lease - > l_lb ;
if ( ! lb )
return ;
spin_lock ( & lb - > lb_lock ) ;
if ( list_empty ( & opinfo - > lease_entry ) ) {
spin_unlock ( & lb - > lb_lock ) ;
return ;
}
list_del_init ( & opinfo - > lease_entry ) ;
opinfo - > o_lease - > l_lb = NULL ;
spin_unlock ( & lb - > lb_lock ) ;
}
static void lb_add ( struct lease_table * lb )
{
write_lock ( & lease_list_lock ) ;
list_add ( & lb - > l_entry , & lease_table_list ) ;
write_unlock ( & lease_list_lock ) ;
}
2021-03-30 08:25:35 +03:00
static int alloc_lease ( struct oplock_info * opinfo , struct lease_ctx_info * lctx )
2021-03-16 04:49:09 +03:00
{
struct lease * lease ;
lease = kmalloc ( sizeof ( struct lease ) , GFP_KERNEL ) ;
if ( ! lease )
return - ENOMEM ;
memcpy ( lease - > lease_key , lctx - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
lease - > state = lctx - > req_state ;
lease - > new_state = 0 ;
lease - > flags = lctx - > flags ;
lease - > duration = lctx - > duration ;
2021-06-07 03:22:22 +03:00
memcpy ( lease - > parent_lease_key , lctx - > parent_lease_key , SMB2_LEASE_KEY_SIZE ) ;
lease - > version = lctx - > version ;
lease - > epoch = 0 ;
2021-03-16 04:49:09 +03:00
INIT_LIST_HEAD ( & opinfo - > lease_entry ) ;
opinfo - > o_lease = lease ;
return 0 ;
}
static void free_lease ( struct oplock_info * opinfo )
{
struct lease * lease ;
lease = opinfo - > o_lease ;
kfree ( lease ) ;
}
static void free_opinfo ( struct oplock_info * opinfo )
{
if ( opinfo - > is_lease )
free_lease ( opinfo ) ;
kfree ( opinfo ) ;
}
static inline void opinfo_free_rcu ( struct rcu_head * rcu_head )
{
struct oplock_info * opinfo ;
opinfo = container_of ( rcu_head , struct oplock_info , rcu_head ) ;
free_opinfo ( opinfo ) ;
}
struct oplock_info * opinfo_get ( struct ksmbd_file * fp )
{
struct oplock_info * opinfo ;
rcu_read_lock ( ) ;
opinfo = rcu_dereference ( fp - > f_opinfo ) ;
if ( opinfo & & ! atomic_inc_not_zero ( & opinfo - > refcount ) )
opinfo = NULL ;
rcu_read_unlock ( ) ;
return opinfo ;
}
static struct oplock_info * opinfo_get_list ( struct ksmbd_inode * ci )
{
struct oplock_info * opinfo ;
if ( list_empty ( & ci - > m_op_list ) )
return NULL ;
rcu_read_lock ( ) ;
opinfo = list_first_or_null_rcu ( & ci - > m_op_list , struct oplock_info ,
2021-05-26 11:57:12 +03:00
op_entry ) ;
2021-03-16 04:49:09 +03:00
if ( opinfo & & ! atomic_inc_not_zero ( & opinfo - > refcount ) )
opinfo = NULL ;
rcu_read_unlock ( ) ;
return opinfo ;
}
void opinfo_put ( struct oplock_info * opinfo )
{
if ( ! atomic_dec_and_test ( & opinfo - > refcount ) )
return ;
call_rcu ( & opinfo - > rcu_head , opinfo_free_rcu ) ;
}
static void opinfo_add ( struct oplock_info * opinfo )
{
struct ksmbd_inode * ci = opinfo - > o_fp - > f_ci ;
write_lock ( & ci - > m_lock ) ;
list_add_rcu ( & opinfo - > op_entry , & ci - > m_op_list ) ;
write_unlock ( & ci - > m_lock ) ;
}
static void opinfo_del ( struct oplock_info * opinfo )
{
struct ksmbd_inode * ci = opinfo - > o_fp - > f_ci ;
if ( opinfo - > is_lease ) {
write_lock ( & lease_list_lock ) ;
lease_del_list ( opinfo ) ;
write_unlock ( & lease_list_lock ) ;
}
write_lock ( & ci - > m_lock ) ;
list_del_rcu ( & opinfo - > op_entry ) ;
write_unlock ( & ci - > m_lock ) ;
}
static unsigned long opinfo_count ( struct ksmbd_file * fp )
{
if ( ksmbd_stream_fd ( fp ) )
return atomic_read ( & fp - > f_ci - > sop_count ) ;
else
return atomic_read ( & fp - > f_ci - > op_count ) ;
}
static void opinfo_count_inc ( struct ksmbd_file * fp )
{
if ( ksmbd_stream_fd ( fp ) )
return atomic_inc ( & fp - > f_ci - > sop_count ) ;
else
return atomic_inc ( & fp - > f_ci - > op_count ) ;
}
static void opinfo_count_dec ( struct ksmbd_file * fp )
{
if ( ksmbd_stream_fd ( fp ) )
return atomic_dec ( & fp - > f_ci - > sop_count ) ;
else
return atomic_dec ( & fp - > f_ci - > op_count ) ;
}
/**
* opinfo_write_to_read ( ) - convert a write oplock to read oplock
* @ opinfo : current oplock info
*
* Return : 0 on success , otherwise - EINVAL
*/
int opinfo_write_to_read ( struct oplock_info * opinfo )
{
struct lease * lease = opinfo - > o_lease ;
2021-03-30 08:25:35 +03:00
if ( ! ( opinfo - > level = = SMB2_OPLOCK_LEVEL_BATCH | |
opinfo - > level = = SMB2_OPLOCK_LEVEL_EXCLUSIVE ) ) {
2021-06-28 09:23:19 +03:00
pr_err ( " bad oplock(0x%x) \n " , opinfo - > level ) ;
2021-03-16 04:49:09 +03:00
if ( opinfo - > is_lease )
2021-06-28 09:23:19 +03:00
pr_err ( " lease state(0x%x) \n " , lease - > state ) ;
2021-03-16 04:49:09 +03:00
return - EINVAL ;
}
opinfo - > level = SMB2_OPLOCK_LEVEL_II ;
if ( opinfo - > is_lease )
lease - > state = lease - > new_state ;
return 0 ;
}
/**
* opinfo_read_handle_to_read ( ) - convert a read / handle oplock to read oplock
* @ opinfo : current oplock info
*
* Return : 0 on success , otherwise - EINVAL
*/
int opinfo_read_handle_to_read ( struct oplock_info * opinfo )
{
struct lease * lease = opinfo - > o_lease ;
lease - > state = lease - > new_state ;
opinfo - > level = SMB2_OPLOCK_LEVEL_II ;
return 0 ;
}
/**
* opinfo_write_to_none ( ) - convert a write oplock to none
* @ opinfo : current oplock info
*
* Return : 0 on success , otherwise - EINVAL
*/
int opinfo_write_to_none ( struct oplock_info * opinfo )
{
struct lease * lease = opinfo - > o_lease ;
2021-03-30 08:25:35 +03:00
if ( ! ( opinfo - > level = = SMB2_OPLOCK_LEVEL_BATCH | |
opinfo - > level = = SMB2_OPLOCK_LEVEL_EXCLUSIVE ) ) {
2021-06-28 09:23:19 +03:00
pr_err ( " bad oplock(0x%x) \n " , opinfo - > level ) ;
2021-03-16 04:49:09 +03:00
if ( opinfo - > is_lease )
2021-06-28 09:23:19 +03:00
pr_err ( " lease state(0x%x) \n " , lease - > state ) ;
2021-03-16 04:49:09 +03:00
return - EINVAL ;
}
opinfo - > level = SMB2_OPLOCK_LEVEL_NONE ;
if ( opinfo - > is_lease )
lease - > state = lease - > new_state ;
return 0 ;
}
/**
* opinfo_read_to_none ( ) - convert a write read to none
* @ opinfo : current oplock info
*
* Return : 0 on success , otherwise - EINVAL
*/
int opinfo_read_to_none ( struct oplock_info * opinfo )
{
struct lease * lease = opinfo - > o_lease ;
if ( opinfo - > level ! = SMB2_OPLOCK_LEVEL_II ) {
2021-06-28 09:23:19 +03:00
pr_err ( " bad oplock(0x%x) \n " , opinfo - > level ) ;
2021-03-16 04:49:09 +03:00
if ( opinfo - > is_lease )
2021-06-28 09:23:19 +03:00
pr_err ( " lease state(0x%x) \n " , lease - > state ) ;
2021-03-16 04:49:09 +03:00
return - EINVAL ;
}
opinfo - > level = SMB2_OPLOCK_LEVEL_NONE ;
if ( opinfo - > is_lease )
lease - > state = lease - > new_state ;
return 0 ;
}
/**
* lease_read_to_write ( ) - upgrade lease state from read to write
* @ opinfo : current lease info
*
* Return : 0 on success , otherwise - EINVAL
*/
int lease_read_to_write ( struct oplock_info * opinfo )
{
struct lease * lease = opinfo - > o_lease ;
if ( ! ( lease - > state & SMB2_LEASE_READ_CACHING_LE ) ) {
2021-05-26 11:57:12 +03:00
ksmbd_debug ( OPLOCK , " bad lease state(0x%x) \n " , lease - > state ) ;
2021-03-16 04:49:09 +03:00
return - EINVAL ;
}
lease - > new_state = SMB2_LEASE_NONE_LE ;
lease - > state | = SMB2_LEASE_WRITE_CACHING_LE ;
if ( lease - > state & SMB2_LEASE_HANDLE_CACHING_LE )
opinfo - > level = SMB2_OPLOCK_LEVEL_BATCH ;
else
opinfo - > level = SMB2_OPLOCK_LEVEL_EXCLUSIVE ;
return 0 ;
}
/**
* lease_none_upgrade ( ) - upgrade lease state from none
* @ opinfo : current lease info
* @ new_state : new lease state
*
* Return : 0 on success , otherwise - EINVAL
*/
2021-03-30 08:25:35 +03:00
static int lease_none_upgrade ( struct oplock_info * opinfo , __le32 new_state )
2021-03-16 04:49:09 +03:00
{
struct lease * lease = opinfo - > o_lease ;
if ( ! ( lease - > state = = SMB2_LEASE_NONE_LE ) ) {
2021-05-26 11:57:12 +03:00
ksmbd_debug ( OPLOCK , " bad lease state(0x%x) \n " , lease - > state ) ;
2021-03-16 04:49:09 +03:00
return - EINVAL ;
}
lease - > new_state = SMB2_LEASE_NONE_LE ;
lease - > state = new_state ;
if ( lease - > state & SMB2_LEASE_HANDLE_CACHING_LE )
if ( lease - > state & SMB2_LEASE_WRITE_CACHING_LE )
opinfo - > level = SMB2_OPLOCK_LEVEL_BATCH ;
else
opinfo - > level = SMB2_OPLOCK_LEVEL_II ;
else if ( lease - > state & SMB2_LEASE_WRITE_CACHING_LE )
opinfo - > level = SMB2_OPLOCK_LEVEL_EXCLUSIVE ;
else if ( lease - > state & SMB2_LEASE_READ_CACHING_LE )
opinfo - > level = SMB2_OPLOCK_LEVEL_II ;
return 0 ;
}
/**
* close_id_del_oplock ( ) - release oplock object at file close time
* @ fp : ksmbd file pointer
*/
void close_id_del_oplock ( struct ksmbd_file * fp )
{
struct oplock_info * opinfo ;
if ( S_ISDIR ( file_inode ( fp - > filp ) - > i_mode ) )
return ;
opinfo = opinfo_get ( fp ) ;
if ( ! opinfo )
return ;
opinfo_del ( opinfo ) ;
rcu_assign_pointer ( fp - > f_opinfo , NULL ) ;
if ( opinfo - > op_state = = OPLOCK_ACK_WAIT ) {
opinfo - > op_state = OPLOCK_CLOSING ;
wake_up_interruptible_all ( & opinfo - > oplock_q ) ;
if ( opinfo - > is_lease ) {
atomic_set ( & opinfo - > breaking_cnt , 0 ) ;
wake_up_interruptible_all ( & opinfo - > oplock_brk ) ;
}
}
opinfo_count_dec ( fp ) ;
atomic_dec ( & opinfo - > refcount ) ;
opinfo_put ( opinfo ) ;
}
/**
* grant_write_oplock ( ) - grant exclusive / batch oplock or write lease
* @ opinfo_new : new oplock info object
* @ req_oplock : request oplock
* @ lctx : lease context information
*
* Return : 0
*/
static void grant_write_oplock ( struct oplock_info * opinfo_new , int req_oplock ,
2021-05-26 11:57:12 +03:00
struct lease_ctx_info * lctx )
2021-03-16 04:49:09 +03:00
{
struct lease * lease = opinfo_new - > o_lease ;
if ( req_oplock = = SMB2_OPLOCK_LEVEL_BATCH )
opinfo_new - > level = SMB2_OPLOCK_LEVEL_BATCH ;
else
opinfo_new - > level = SMB2_OPLOCK_LEVEL_EXCLUSIVE ;
if ( lctx ) {
lease - > state = lctx - > req_state ;
2021-05-26 11:57:12 +03:00
memcpy ( lease - > lease_key , lctx - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
2021-03-16 04:49:09 +03:00
}
}
/**
* grant_read_oplock ( ) - grant level2 oplock or read lease
* @ opinfo_new : new oplock info object
* @ lctx : lease context information
*
* Return : 0
*/
static void grant_read_oplock ( struct oplock_info * opinfo_new ,
2021-05-26 11:57:12 +03:00
struct lease_ctx_info * lctx )
2021-03-16 04:49:09 +03:00
{
struct lease * lease = opinfo_new - > o_lease ;
opinfo_new - > level = SMB2_OPLOCK_LEVEL_II ;
if ( lctx ) {
lease - > state = SMB2_LEASE_READ_CACHING_LE ;
if ( lctx - > req_state & SMB2_LEASE_HANDLE_CACHING_LE )
lease - > state | = SMB2_LEASE_HANDLE_CACHING_LE ;
2021-05-26 11:57:12 +03:00
memcpy ( lease - > lease_key , lctx - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
2021-03-16 04:49:09 +03:00
}
}
/**
* grant_none_oplock ( ) - grant none oplock or none lease
* @ opinfo_new : new oplock info object
* @ lctx : lease context information
*
* Return : 0
*/
static void grant_none_oplock ( struct oplock_info * opinfo_new ,
2021-05-26 11:57:12 +03:00
struct lease_ctx_info * lctx )
2021-03-16 04:49:09 +03:00
{
struct lease * lease = opinfo_new - > o_lease ;
opinfo_new - > level = SMB2_OPLOCK_LEVEL_NONE ;
if ( lctx ) {
lease - > state = 0 ;
2021-05-26 11:57:12 +03:00
memcpy ( lease - > lease_key , lctx - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
2021-03-16 04:49:09 +03:00
}
}
static inline int compare_guid_key ( struct oplock_info * opinfo ,
2021-05-26 11:57:12 +03:00
const char * guid1 , const char * key1 )
2021-03-16 04:49:09 +03:00
{
const char * guid2 , * key2 ;
guid2 = opinfo - > conn - > ClientGUID ;
key2 = opinfo - > o_lease - > lease_key ;
if ( ! memcmp ( guid1 , guid2 , SMB2_CLIENT_GUID_SIZE ) & &
2021-03-30 08:25:35 +03:00
! memcmp ( key1 , key2 , SMB2_LEASE_KEY_SIZE ) )
2021-03-16 04:49:09 +03:00
return 1 ;
return 0 ;
}
/**
* same_client_has_lease ( ) - check whether current lease request is
* from lease owner of file
* @ ci : master file pointer
* @ client_guid : Client GUID
* @ lctx : lease context information
*
* Return : oplock ( lease ) object on success , otherwise NULL
*/
static struct oplock_info * same_client_has_lease ( struct ksmbd_inode * ci ,
2021-05-26 11:57:12 +03:00
char * client_guid ,
struct lease_ctx_info * lctx )
2021-03-16 04:49:09 +03:00
{
int ret ;
struct lease * lease ;
struct oplock_info * opinfo ;
struct oplock_info * m_opinfo = NULL ;
if ( ! lctx )
return NULL ;
/*
* Compare lease key and client_guid to know request from same owner
* of same client
*/
read_lock ( & ci - > m_lock ) ;
list_for_each_entry ( opinfo , & ci - > m_op_list , op_entry ) {
if ( ! opinfo - > is_lease )
continue ;
read_unlock ( & ci - > m_lock ) ;
lease = opinfo - > o_lease ;
ret = compare_guid_key ( opinfo , client_guid , lctx - > lease_key ) ;
if ( ret ) {
m_opinfo = opinfo ;
/* skip upgrading lease about breaking lease */
if ( atomic_read ( & opinfo - > breaking_cnt ) ) {
read_lock ( & ci - > m_lock ) ;
continue ;
}
/* upgrading lease */
if ( ( atomic_read ( & ci - > op_count ) +
atomic_read ( & ci - > sop_count ) ) = = 1 ) {
if ( lease - > state = =
2021-05-26 11:57:12 +03:00
( lctx - > req_state & lease - > state ) ) {
2021-03-16 04:49:09 +03:00
lease - > state | = lctx - > req_state ;
if ( lctx - > req_state &
SMB2_LEASE_WRITE_CACHING_LE )
lease_read_to_write ( opinfo ) ;
}
} else if ( ( atomic_read ( & ci - > op_count ) +
atomic_read ( & ci - > sop_count ) ) > 1 ) {
if ( lctx - > req_state = =
2021-05-26 11:57:12 +03:00
( SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE ) )
2021-03-16 04:49:09 +03:00
lease - > state = lctx - > req_state ;
}
if ( lctx - > req_state & & lease - > state = =
2021-05-26 11:57:12 +03:00
SMB2_LEASE_NONE_LE )
2021-03-16 04:49:09 +03:00
lease_none_upgrade ( opinfo , lctx - > req_state ) ;
}
read_lock ( & ci - > m_lock ) ;
}
read_unlock ( & ci - > m_lock ) ;
return m_opinfo ;
}
static void wait_for_break_ack ( struct oplock_info * opinfo )
{
int rc = 0 ;
rc = wait_event_interruptible_timeout ( opinfo - > oplock_q ,
2021-05-26 11:57:12 +03:00
opinfo - > op_state = = OPLOCK_STATE_NONE | |
opinfo - > op_state = = OPLOCK_CLOSING ,
OPLOCK_WAIT_TIME ) ;
2021-03-16 04:49:09 +03:00
/* is this a timeout ? */
if ( ! rc ) {
if ( opinfo - > is_lease )
opinfo - > o_lease - > state = SMB2_LEASE_NONE_LE ;
opinfo - > level = SMB2_OPLOCK_LEVEL_NONE ;
opinfo - > op_state = OPLOCK_STATE_NONE ;
}
}
static void wake_up_oplock_break ( struct oplock_info * opinfo )
{
clear_bit_unlock ( 0 , & opinfo - > pending_break ) ;
/* memory barrier is needed for wake_up_bit() */
smp_mb__after_atomic ( ) ;
wake_up_bit ( & opinfo - > pending_break , 0 ) ;
}
static int oplock_break_pending ( struct oplock_info * opinfo , int req_op_level )
{
while ( test_and_set_bit ( 0 , & opinfo - > pending_break ) ) {
wait_on_bit ( & opinfo - > pending_break , 0 , TASK_UNINTERRUPTIBLE ) ;
/* Not immediately break to none. */
opinfo - > open_trunc = 0 ;
if ( opinfo - > op_state = = OPLOCK_CLOSING )
return - ENOENT ;
else if ( ! opinfo - > is_lease & & opinfo - > level < = req_op_level )
return 1 ;
}
if ( ! opinfo - > is_lease & & opinfo - > level < = req_op_level ) {
wake_up_oplock_break ( opinfo ) ;
return 1 ;
}
return 0 ;
}
static inline int allocate_oplock_break_buf ( struct ksmbd_work * work )
{
2021-03-30 06:40:47 +03:00
work - > response_buf = kzalloc ( MAX_CIFS_SMALL_BUFFER_SIZE , GFP_KERNEL ) ;
2021-03-16 04:49:09 +03:00
if ( ! work - > response_buf )
return - ENOMEM ;
work - > response_sz = MAX_CIFS_SMALL_BUFFER_SIZE ;
return 0 ;
}
/**
2021-03-21 11:05:56 +03:00
* __smb2_oplock_break_noti ( ) - send smb2 oplock break cmd from conn
2021-03-16 04:49:09 +03:00
* to client
2021-03-21 11:05:56 +03:00
* @ wk : smb work object
2021-03-16 04:49:09 +03:00
*
* There are two ways this function can be called . 1 - while file open we break
* from exclusive / batch lock to levelII oplock and 2 - while file write / truncate
* we break from levelII oplock no oplock .
2021-03-30 06:35:23 +03:00
* work - > request_buf contains oplock_info .
2021-03-16 04:49:09 +03:00
*/
static void __smb2_oplock_break_noti ( struct work_struct * wk )
{
struct smb2_oplock_break * rsp = NULL ;
struct ksmbd_work * work = container_of ( wk , struct ksmbd_work , work ) ;
struct ksmbd_conn * conn = work - > conn ;
2021-03-30 06:35:23 +03:00
struct oplock_break_info * br_info = work - > request_buf ;
2021-03-16 04:49:09 +03:00
struct smb2_hdr * rsp_hdr ;
struct ksmbd_file * fp ;
fp = ksmbd_lookup_durable_fd ( br_info - > fid ) ;
if ( ! fp ) {
atomic_dec ( & conn - > r_count ) ;
ksmbd_free_work_struct ( work ) ;
return ;
}
if ( allocate_oplock_break_buf ( work ) ) {
2021-06-28 09:23:19 +03:00
pr_err ( " smb2_allocate_rsp_buf failed! " ) ;
2021-03-16 04:49:09 +03:00
atomic_dec ( & conn - > r_count ) ;
ksmbd_fd_put ( work , fp ) ;
2021-03-18 16:12:54 +03:00
ksmbd_free_work_struct ( work ) ;
2021-03-16 04:49:09 +03:00
return ;
}
2021-03-30 06:35:23 +03:00
rsp_hdr = work - > response_buf ;
2021-03-16 04:49:09 +03:00
memset ( rsp_hdr , 0 , sizeof ( struct smb2_hdr ) + 2 ) ;
2021-06-25 05:53:26 +03:00
rsp_hdr - > smb2_buf_length =
cpu_to_be32 ( smb2_hdr_size_no_buflen ( conn - > vals ) ) ;
2021-03-16 04:49:09 +03:00
rsp_hdr - > ProtocolId = SMB2_PROTO_NUMBER ;
rsp_hdr - > StructureSize = SMB2_HEADER_STRUCTURE_SIZE ;
rsp_hdr - > CreditRequest = cpu_to_le16 ( 0 ) ;
rsp_hdr - > Command = SMB2_OPLOCK_BREAK ;
rsp_hdr - > Flags = ( SMB2_FLAGS_SERVER_TO_REDIR ) ;
rsp_hdr - > NextCommand = 0 ;
rsp_hdr - > MessageId = cpu_to_le64 ( - 1 ) ;
rsp_hdr - > Id . SyncId . ProcessId = 0 ;
rsp_hdr - > Id . SyncId . TreeId = 0 ;
rsp_hdr - > SessionId = 0 ;
memset ( rsp_hdr - > Signature , 0 , 16 ) ;
2021-03-30 06:35:23 +03:00
rsp = work - > response_buf ;
2021-03-16 04:49:09 +03:00
rsp - > StructureSize = cpu_to_le16 ( 24 ) ;
if ( ! br_info - > open_trunc & &
2021-03-30 08:25:35 +03:00
( br_info - > level = = SMB2_OPLOCK_LEVEL_BATCH | |
br_info - > level = = SMB2_OPLOCK_LEVEL_EXCLUSIVE ) )
2021-03-16 04:49:09 +03:00
rsp - > OplockLevel = SMB2_OPLOCK_LEVEL_II ;
else
rsp - > OplockLevel = SMB2_OPLOCK_LEVEL_NONE ;
rsp - > Reserved = 0 ;
rsp - > Reserved2 = 0 ;
rsp - > PersistentFid = cpu_to_le64 ( fp - > persistent_id ) ;
rsp - > VolatileFid = cpu_to_le64 ( fp - > volatile_id ) ;
inc_rfc1001_len ( rsp , 24 ) ;
ksmbd_debug ( OPLOCK ,
2021-05-26 11:57:12 +03:00
" sending oplock break v_id %llu p_id = %llu lock level = %d \n " ,
rsp - > VolatileFid , rsp - > PersistentFid , rsp - > OplockLevel ) ;
2021-03-16 04:49:09 +03:00
ksmbd_fd_put ( work , fp ) ;
ksmbd_conn_write ( work ) ;
ksmbd_free_work_struct ( work ) ;
atomic_dec ( & conn - > r_count ) ;
}
/**
2021-03-21 11:05:56 +03:00
* smb2_oplock_break_noti ( ) - send smb2 exclusive / batch to level2 oplock
2021-03-16 04:49:09 +03:00
* break command from server to client
* @ opinfo : oplock info object
*
* Return : 0 on success , otherwise error
*/
static int smb2_oplock_break_noti ( struct oplock_info * opinfo )
{
struct ksmbd_conn * conn = opinfo - > conn ;
struct oplock_break_info * br_info ;
int ret = 0 ;
struct ksmbd_work * work = ksmbd_alloc_work_struct ( ) ;
if ( ! work )
return - ENOMEM ;
br_info = kmalloc ( sizeof ( struct oplock_break_info ) , GFP_KERNEL ) ;
if ( ! br_info ) {
ksmbd_free_work_struct ( work ) ;
return - ENOMEM ;
}
br_info - > level = opinfo - > level ;
br_info - > fid = opinfo - > fid ;
br_info - > open_trunc = opinfo - > open_trunc ;
work - > request_buf = ( char * ) br_info ;
work - > conn = conn ;
work - > sess = opinfo - > sess ;
atomic_inc ( & conn - > r_count ) ;
if ( opinfo - > op_state = = OPLOCK_ACK_WAIT ) {
INIT_WORK ( & work - > work , __smb2_oplock_break_noti ) ;
ksmbd_queue_work ( work ) ;
wait_for_break_ack ( opinfo ) ;
} else {
__smb2_oplock_break_noti ( & work - > work ) ;
if ( opinfo - > level = = SMB2_OPLOCK_LEVEL_II )
opinfo - > level = SMB2_OPLOCK_LEVEL_NONE ;
}
return ret ;
}
/**
* __smb2_lease_break_noti ( ) - send lease break command from server
* to client
2021-03-21 11:05:56 +03:00
* @ wk : smb work object
2021-03-16 04:49:09 +03:00
*/
static void __smb2_lease_break_noti ( struct work_struct * wk )
{
struct smb2_lease_break * rsp = NULL ;
struct ksmbd_work * work = container_of ( wk , struct ksmbd_work , work ) ;
2021-03-30 06:35:23 +03:00
struct lease_break_info * br_info = work - > request_buf ;
2021-03-16 04:49:09 +03:00
struct ksmbd_conn * conn = work - > conn ;
struct smb2_hdr * rsp_hdr ;
if ( allocate_oplock_break_buf ( work ) ) {
ksmbd_debug ( OPLOCK , " smb2_allocate_rsp_buf failed! " ) ;
ksmbd_free_work_struct ( work ) ;
atomic_dec ( & conn - > r_count ) ;
return ;
}
2021-03-30 06:35:23 +03:00
rsp_hdr = work - > response_buf ;
2021-03-16 04:49:09 +03:00
memset ( rsp_hdr , 0 , sizeof ( struct smb2_hdr ) + 2 ) ;
2021-06-25 05:53:26 +03:00
rsp_hdr - > smb2_buf_length =
cpu_to_be32 ( smb2_hdr_size_no_buflen ( conn - > vals ) ) ;
2021-03-16 04:49:09 +03:00
rsp_hdr - > ProtocolId = SMB2_PROTO_NUMBER ;
rsp_hdr - > StructureSize = SMB2_HEADER_STRUCTURE_SIZE ;
rsp_hdr - > CreditRequest = cpu_to_le16 ( 0 ) ;
rsp_hdr - > Command = SMB2_OPLOCK_BREAK ;
rsp_hdr - > Flags = ( SMB2_FLAGS_SERVER_TO_REDIR ) ;
rsp_hdr - > NextCommand = 0 ;
rsp_hdr - > MessageId = cpu_to_le64 ( - 1 ) ;
rsp_hdr - > Id . SyncId . ProcessId = 0 ;
rsp_hdr - > Id . SyncId . TreeId = 0 ;
rsp_hdr - > SessionId = 0 ;
memset ( rsp_hdr - > Signature , 0 , 16 ) ;
2021-03-30 06:35:23 +03:00
rsp = work - > response_buf ;
2021-03-16 04:49:09 +03:00
rsp - > StructureSize = cpu_to_le16 ( 44 ) ;
2021-06-07 03:22:22 +03:00
rsp - > Epoch = br_info - > epoch ;
2021-03-16 04:49:09 +03:00
rsp - > Flags = 0 ;
if ( br_info - > curr_state & ( SMB2_LEASE_WRITE_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE ) )
rsp - > Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED ;
memcpy ( rsp - > LeaseKey , br_info - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
rsp - > CurrentLeaseState = br_info - > curr_state ;
rsp - > NewLeaseState = br_info - > new_state ;
rsp - > BreakReason = 0 ;
rsp - > AccessMaskHint = 0 ;
rsp - > ShareMaskHint = 0 ;
inc_rfc1001_len ( rsp , 44 ) ;
ksmbd_conn_write ( work ) ;
ksmbd_free_work_struct ( work ) ;
atomic_dec ( & conn - > r_count ) ;
}
/**
2021-03-21 11:05:56 +03:00
* smb2_lease_break_noti ( ) - break lease when a new client request
2021-03-16 04:49:09 +03:00
* write lease
* @ opinfo : conains lease state information
*
* Return : 0 on success , otherwise error
*/
static int smb2_lease_break_noti ( struct oplock_info * opinfo )
{
struct ksmbd_conn * conn = opinfo - > conn ;
struct list_head * tmp , * t ;
struct ksmbd_work * work ;
struct lease_break_info * br_info ;
struct lease * lease = opinfo - > o_lease ;
work = ksmbd_alloc_work_struct ( ) ;
if ( ! work )
return - ENOMEM ;
br_info = kmalloc ( sizeof ( struct lease_break_info ) , GFP_KERNEL ) ;
if ( ! br_info ) {
ksmbd_free_work_struct ( work ) ;
return - ENOMEM ;
}
br_info - > curr_state = lease - > state ;
br_info - > new_state = lease - > new_state ;
2021-06-07 03:22:22 +03:00
if ( lease - > version = = 2 )
br_info - > epoch = cpu_to_le16 ( + + lease - > epoch ) ;
else
br_info - > epoch = 0 ;
2021-03-16 04:49:09 +03:00
memcpy ( br_info - > lease_key , lease - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
work - > request_buf = ( char * ) br_info ;
work - > conn = conn ;
work - > sess = opinfo - > sess ;
atomic_inc ( & conn - > r_count ) ;
if ( opinfo - > op_state = = OPLOCK_ACK_WAIT ) {
list_for_each_safe ( tmp , t , & opinfo - > interim_list ) {
struct ksmbd_work * in_work ;
in_work = list_entry ( tmp , struct ksmbd_work ,
2021-05-26 11:57:12 +03:00
interim_entry ) ;
2021-03-16 04:49:09 +03:00
setup_async_work ( in_work , NULL , NULL ) ;
smb2_send_interim_resp ( in_work , STATUS_PENDING ) ;
list_del ( & in_work - > interim_entry ) ;
}
INIT_WORK ( & work - > work , __smb2_lease_break_noti ) ;
ksmbd_queue_work ( work ) ;
wait_for_break_ack ( opinfo ) ;
} else {
__smb2_lease_break_noti ( & work - > work ) ;
if ( opinfo - > o_lease - > new_state = = SMB2_LEASE_NONE_LE ) {
opinfo - > level = SMB2_OPLOCK_LEVEL_NONE ;
opinfo - > o_lease - > state = SMB2_LEASE_NONE_LE ;
}
}
return 0 ;
}
static void wait_lease_breaking ( struct oplock_info * opinfo )
{
if ( ! opinfo - > is_lease )
return ;
wake_up_interruptible_all ( & opinfo - > oplock_brk ) ;
if ( atomic_read ( & opinfo - > breaking_cnt ) ) {
int ret = 0 ;
2021-03-30 08:25:35 +03:00
ret = wait_event_interruptible_timeout ( opinfo - > oplock_brk ,
2021-05-26 11:57:12 +03:00
atomic_read ( & opinfo - > breaking_cnt ) = = 0 ,
HZ ) ;
2021-03-16 04:49:09 +03:00
if ( ! ret )
atomic_set ( & opinfo - > breaking_cnt , 0 ) ;
}
}
static int oplock_break ( struct oplock_info * brk_opinfo , int req_op_level )
{
int err = 0 ;
/* Need to break exclusive/batch oplock, write lease or overwrite_if */
ksmbd_debug ( OPLOCK ,
2021-05-26 11:57:12 +03:00
" request to send oplock(level : 0x%x) break notification \n " ,
brk_opinfo - > level ) ;
2021-03-16 04:49:09 +03:00
if ( brk_opinfo - > is_lease ) {
struct lease * lease = brk_opinfo - > o_lease ;
atomic_inc ( & brk_opinfo - > breaking_cnt ) ;
err = oplock_break_pending ( brk_opinfo , req_op_level ) ;
if ( err )
return err < 0 ? err : 0 ;
if ( brk_opinfo - > open_trunc ) {
/*
* Create overwrite break trigger the lease break to
* none .
*/
lease - > new_state = SMB2_LEASE_NONE_LE ;
} else {
if ( lease - > state & SMB2_LEASE_WRITE_CACHING_LE ) {
if ( lease - > state & SMB2_LEASE_HANDLE_CACHING_LE )
lease - > new_state =
SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE ;
else
lease - > new_state =
SMB2_LEASE_READ_CACHING_LE ;
} else {
if ( lease - > state & SMB2_LEASE_HANDLE_CACHING_LE )
lease - > new_state =
SMB2_LEASE_READ_CACHING_LE ;
else
lease - > new_state = SMB2_LEASE_NONE_LE ;
}
}
if ( lease - > state & ( SMB2_LEASE_WRITE_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE ) )
brk_opinfo - > op_state = OPLOCK_ACK_WAIT ;
else
atomic_dec ( & brk_opinfo - > breaking_cnt ) ;
} else {
err = oplock_break_pending ( brk_opinfo , req_op_level ) ;
if ( err )
return err < 0 ? err : 0 ;
if ( brk_opinfo - > level = = SMB2_OPLOCK_LEVEL_BATCH | |
2021-03-30 08:25:35 +03:00
brk_opinfo - > level = = SMB2_OPLOCK_LEVEL_EXCLUSIVE )
2021-03-16 04:49:09 +03:00
brk_opinfo - > op_state = OPLOCK_ACK_WAIT ;
}
if ( brk_opinfo - > is_lease )
err = smb2_lease_break_noti ( brk_opinfo ) ;
else
err = smb2_oplock_break_noti ( brk_opinfo ) ;
ksmbd_debug ( OPLOCK , " oplock granted = %d \n " , brk_opinfo - > level ) ;
if ( brk_opinfo - > op_state = = OPLOCK_CLOSING )
err = - ENOENT ;
wake_up_oplock_break ( brk_opinfo ) ;
wait_lease_breaking ( brk_opinfo ) ;
return err ;
}
void destroy_lease_table ( struct ksmbd_conn * conn )
{
struct lease_table * lb , * lbtmp ;
struct oplock_info * opinfo ;
write_lock ( & lease_list_lock ) ;
if ( list_empty ( & lease_table_list ) ) {
write_unlock ( & lease_list_lock ) ;
return ;
}
list_for_each_entry_safe ( lb , lbtmp , & lease_table_list , l_entry ) {
if ( conn & & memcmp ( lb - > client_guid , conn - > ClientGUID ,
2021-03-30 08:25:35 +03:00
SMB2_CLIENT_GUID_SIZE ) )
2021-03-16 04:49:09 +03:00
continue ;
again :
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( opinfo , & lb - > lease_list ,
2021-05-26 11:57:12 +03:00
lease_entry ) {
2021-03-16 04:49:09 +03:00
rcu_read_unlock ( ) ;
lease_del_list ( opinfo ) ;
goto again ;
}
rcu_read_unlock ( ) ;
list_del ( & lb - > l_entry ) ;
kfree ( lb ) ;
}
write_unlock ( & lease_list_lock ) ;
}
int find_same_lease_key ( struct ksmbd_session * sess , struct ksmbd_inode * ci ,
2021-05-26 11:57:12 +03:00
struct lease_ctx_info * lctx )
2021-03-16 04:49:09 +03:00
{
struct oplock_info * opinfo ;
int err = 0 ;
struct lease_table * lb ;
if ( ! lctx )
return err ;
read_lock ( & lease_list_lock ) ;
if ( list_empty ( & lease_table_list ) ) {
read_unlock ( & lease_list_lock ) ;
return 0 ;
}
list_for_each_entry ( lb , & lease_table_list , l_entry ) {
if ( ! memcmp ( lb - > client_guid , sess - > conn - > ClientGUID ,
2021-03-30 08:25:35 +03:00
SMB2_CLIENT_GUID_SIZE ) )
2021-03-16 04:49:09 +03:00
goto found ;
}
read_unlock ( & lease_list_lock ) ;
return 0 ;
found :
rcu_read_lock ( ) ;
2021-05-26 11:57:12 +03:00
list_for_each_entry_rcu ( opinfo , & lb - > lease_list , lease_entry ) {
2021-03-16 04:49:09 +03:00
if ( ! atomic_inc_not_zero ( & opinfo - > refcount ) )
continue ;
rcu_read_unlock ( ) ;
if ( opinfo - > o_fp - > f_ci = = ci )
goto op_next ;
2021-05-26 11:57:12 +03:00
err = compare_guid_key ( opinfo , sess - > conn - > ClientGUID ,
lctx - > lease_key ) ;
2021-03-16 04:49:09 +03:00
if ( err ) {
err = - EINVAL ;
ksmbd_debug ( OPLOCK ,
2021-05-26 11:57:12 +03:00
" found same lease key is already used in other files \n " ) ;
2021-03-16 04:49:09 +03:00
opinfo_put ( opinfo ) ;
goto out ;
}
op_next :
opinfo_put ( opinfo ) ;
rcu_read_lock ( ) ;
}
rcu_read_unlock ( ) ;
out :
read_unlock ( & lease_list_lock ) ;
return err ;
}
static void copy_lease ( struct oplock_info * op1 , struct oplock_info * op2 )
{
struct lease * lease1 = op1 - > o_lease ;
struct lease * lease2 = op2 - > o_lease ;
op2 - > level = op1 - > level ;
lease2 - > state = lease1 - > state ;
memcpy ( lease2 - > lease_key , lease1 - > lease_key ,
2021-05-26 11:57:12 +03:00
SMB2_LEASE_KEY_SIZE ) ;
2021-03-16 04:49:09 +03:00
lease2 - > duration = lease1 - > duration ;
lease2 - > flags = lease1 - > flags ;
}
static int add_lease_global_list ( struct oplock_info * opinfo )
{
struct lease_table * lb ;
read_lock ( & lease_list_lock ) ;
list_for_each_entry ( lb , & lease_table_list , l_entry ) {
if ( ! memcmp ( lb - > client_guid , opinfo - > conn - > ClientGUID ,
2021-03-30 08:25:35 +03:00
SMB2_CLIENT_GUID_SIZE ) ) {
2021-03-16 04:49:09 +03:00
opinfo - > o_lease - > l_lb = lb ;
lease_add_list ( opinfo ) ;
read_unlock ( & lease_list_lock ) ;
return 0 ;
}
}
read_unlock ( & lease_list_lock ) ;
lb = kmalloc ( sizeof ( struct lease_table ) , GFP_KERNEL ) ;
if ( ! lb )
return - ENOMEM ;
memcpy ( lb - > client_guid , opinfo - > conn - > ClientGUID ,
2021-05-26 11:57:12 +03:00
SMB2_CLIENT_GUID_SIZE ) ;
2021-03-16 04:49:09 +03:00
INIT_LIST_HEAD ( & lb - > lease_list ) ;
spin_lock_init ( & lb - > lb_lock ) ;
opinfo - > o_lease - > l_lb = lb ;
lease_add_list ( opinfo ) ;
lb_add ( lb ) ;
return 0 ;
}
static void set_oplock_level ( struct oplock_info * opinfo , int level ,
2021-05-26 11:57:12 +03:00
struct lease_ctx_info * lctx )
2021-03-16 04:49:09 +03:00
{
switch ( level ) {
case SMB2_OPLOCK_LEVEL_BATCH :
case SMB2_OPLOCK_LEVEL_EXCLUSIVE :
2021-03-30 08:25:35 +03:00
grant_write_oplock ( opinfo , level , lctx ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_OPLOCK_LEVEL_II :
grant_read_oplock ( opinfo , lctx ) ;
break ;
default :
grant_none_oplock ( opinfo , lctx ) ;
break ;
}
}
/**
* smb_grant_oplock ( ) - handle oplock / lease request on file open
2021-03-21 11:05:56 +03:00
* @ work : smb work
* @ req_op_level : oplock level
* @ pid : id of open file
* @ fp : ksmbd file pointer
* @ tid : Tree id of connection
* @ lctx : lease context information on file open
* @ share_ret : share mode
2021-03-16 04:49:09 +03:00
*
* Return : 0 on success , otherwise error
*/
2021-03-30 08:25:35 +03:00
int smb_grant_oplock ( struct ksmbd_work * work , int req_op_level , u64 pid ,
2021-05-26 11:57:12 +03:00
struct ksmbd_file * fp , __u16 tid ,
struct lease_ctx_info * lctx , int share_ret )
2021-03-16 04:49:09 +03:00
{
struct ksmbd_session * sess = work - > sess ;
int err = 0 ;
struct oplock_info * opinfo = NULL , * prev_opinfo = NULL ;
struct ksmbd_inode * ci = fp - > f_ci ;
bool prev_op_has_lease ;
__le32 prev_op_state = 0 ;
/* not support directory lease */
2021-06-07 03:22:22 +03:00
if ( S_ISDIR ( file_inode ( fp - > filp ) - > i_mode ) )
2021-03-16 04:49:09 +03:00
return 0 ;
opinfo = alloc_opinfo ( work , pid , tid ) ;
if ( ! opinfo )
return - ENOMEM ;
if ( lctx ) {
err = alloc_lease ( opinfo , lctx ) ;
if ( err )
goto err_out ;
opinfo - > is_lease = 1 ;
}
/* ci does not have any oplock */
if ( ! opinfo_count ( fp ) )
goto set_lev ;
/* grant none-oplock if second open is trunc */
2021-06-29 03:24:31 +03:00
if ( fp - > attrib_only & & fp - > cdoption ! = FILE_OVERWRITE_IF_LE & &
fp - > cdoption ! = FILE_OVERWRITE_LE & &
fp - > cdoption ! = FILE_SUPERSEDE_LE ) {
2021-03-16 04:49:09 +03:00
req_op_level = SMB2_OPLOCK_LEVEL_NONE ;
goto set_lev ;
}
if ( lctx ) {
struct oplock_info * m_opinfo ;
/* is lease already granted ? */
m_opinfo = same_client_has_lease ( ci , sess - > conn - > ClientGUID ,
2021-05-26 11:57:12 +03:00
lctx ) ;
2021-03-16 04:49:09 +03:00
if ( m_opinfo ) {
copy_lease ( m_opinfo , opinfo ) ;
if ( atomic_read ( & m_opinfo - > breaking_cnt ) )
opinfo - > o_lease - > flags =
SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE ;
goto out ;
}
}
prev_opinfo = opinfo_get_list ( ci ) ;
if ( ! prev_opinfo | |
( prev_opinfo - > level = = SMB2_OPLOCK_LEVEL_NONE & & lctx ) )
goto set_lev ;
prev_op_has_lease = prev_opinfo - > is_lease ;
if ( prev_op_has_lease )
prev_op_state = prev_opinfo - > o_lease - > state ;
if ( share_ret < 0 & &
2021-03-30 08:25:35 +03:00
prev_opinfo - > level = = SMB2_OPLOCK_LEVEL_EXCLUSIVE ) {
2021-03-16 04:49:09 +03:00
err = share_ret ;
opinfo_put ( prev_opinfo ) ;
goto err_out ;
}
2021-03-30 08:25:35 +03:00
if ( prev_opinfo - > level ! = SMB2_OPLOCK_LEVEL_BATCH & &
prev_opinfo - > level ! = SMB2_OPLOCK_LEVEL_EXCLUSIVE ) {
2021-03-16 04:49:09 +03:00
opinfo_put ( prev_opinfo ) ;
goto op_break_not_needed ;
}
list_add ( & work - > interim_entry , & prev_opinfo - > interim_list ) ;
err = oplock_break ( prev_opinfo , SMB2_OPLOCK_LEVEL_II ) ;
opinfo_put ( prev_opinfo ) ;
if ( err = = - ENOENT )
goto set_lev ;
/* Check all oplock was freed by close */
else if ( err < 0 )
goto err_out ;
op_break_not_needed :
if ( share_ret < 0 ) {
err = share_ret ;
goto err_out ;
}
if ( req_op_level ! = SMB2_OPLOCK_LEVEL_NONE )
req_op_level = SMB2_OPLOCK_LEVEL_II ;
/* grant fixed oplock on stacked locking between lease and oplock */
if ( prev_op_has_lease & & ! lctx )
if ( prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE )
req_op_level = SMB2_OPLOCK_LEVEL_NONE ;
if ( ! prev_op_has_lease & & lctx ) {
req_op_level = SMB2_OPLOCK_LEVEL_II ;
lctx - > req_state = SMB2_LEASE_READ_CACHING_LE ;
}
set_lev :
set_oplock_level ( opinfo , req_op_level , lctx ) ;
out :
rcu_assign_pointer ( fp - > f_opinfo , opinfo ) ;
opinfo - > o_fp = fp ;
opinfo_count_inc ( fp ) ;
opinfo_add ( opinfo ) ;
if ( opinfo - > is_lease ) {
err = add_lease_global_list ( opinfo ) ;
if ( err )
goto err_out ;
}
return 0 ;
err_out :
free_opinfo ( opinfo ) ;
return err ;
}
/**
2021-03-21 11:05:56 +03:00
* smb_break_all_write_oplock ( ) - break batch / exclusive oplock to level2
2021-03-16 04:49:09 +03:00
* @ work : smb work
* @ fp : ksmbd file pointer
2021-03-21 11:05:56 +03:00
* @ is_trunc : truncate on open
2021-03-16 04:49:09 +03:00
*/
static void smb_break_all_write_oplock ( struct ksmbd_work * work ,
2021-05-26 11:57:12 +03:00
struct ksmbd_file * fp , int is_trunc )
2021-03-16 04:49:09 +03:00
{
struct oplock_info * brk_opinfo ;
brk_opinfo = opinfo_get_list ( fp - > f_ci ) ;
if ( ! brk_opinfo )
return ;
if ( brk_opinfo - > level ! = SMB2_OPLOCK_LEVEL_BATCH & &
2021-03-30 08:25:35 +03:00
brk_opinfo - > level ! = SMB2_OPLOCK_LEVEL_EXCLUSIVE ) {
2021-03-16 04:49:09 +03:00
opinfo_put ( brk_opinfo ) ;
return ;
}
brk_opinfo - > open_trunc = is_trunc ;
list_add ( & work - > interim_entry , & brk_opinfo - > interim_list ) ;
oplock_break ( brk_opinfo , SMB2_OPLOCK_LEVEL_II ) ;
opinfo_put ( brk_opinfo ) ;
}
/**
* smb_break_all_levII_oplock ( ) - send level2 oplock or read lease break command
* from server to client
2021-03-21 11:05:56 +03:00
* @ work : smb work
2021-03-16 04:49:09 +03:00
* @ fp : ksmbd file pointer
* @ is_trunc : truncate on open
*/
2021-03-30 08:25:35 +03:00
void smb_break_all_levII_oplock ( struct ksmbd_work * work , struct ksmbd_file * fp ,
2021-05-26 11:57:12 +03:00
int is_trunc )
2021-03-16 04:49:09 +03:00
{
struct oplock_info * op , * brk_op ;
struct ksmbd_inode * ci ;
struct ksmbd_conn * conn = work - > sess - > conn ;
if ( ! test_share_config_flag ( work - > tcon - > share_conf ,
2021-03-30 08:25:35 +03:00
KSMBD_SHARE_FLAG_OPLOCKS ) )
2021-03-16 04:49:09 +03:00
return ;
ci = fp - > f_ci ;
op = opinfo_get ( fp ) ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( brk_op , & ci - > m_op_list , op_entry ) {
if ( ! atomic_inc_not_zero ( & brk_op - > refcount ) )
continue ;
rcu_read_unlock ( ) ;
if ( brk_op - > is_lease & & ( brk_op - > o_lease - > state &
( ~ ( SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE ) ) ) ) {
ksmbd_debug ( OPLOCK , " unexpected lease state(0x%x) \n " ,
2021-05-26 11:57:12 +03:00
brk_op - > o_lease - > state ) ;
2021-03-16 04:49:09 +03:00
goto next ;
} else if ( brk_op - > level ! =
SMB2_OPLOCK_LEVEL_II ) {
ksmbd_debug ( OPLOCK , " unexpected oplock(0x%x) \n " ,
2021-05-26 11:57:12 +03:00
brk_op - > level ) ;
2021-03-16 04:49:09 +03:00
goto next ;
}
/* Skip oplock being break to none */
2021-05-26 11:57:12 +03:00
if ( brk_op - > is_lease & &
2021-05-26 11:59:56 +03:00
brk_op - > o_lease - > new_state = = SMB2_LEASE_NONE_LE & &
2021-03-16 04:49:09 +03:00
atomic_read ( & brk_op - > breaking_cnt ) )
goto next ;
2021-03-30 08:25:35 +03:00
if ( op & & op - > is_lease & & brk_op - > is_lease & &
! memcmp ( conn - > ClientGUID , brk_op - > conn - > ClientGUID ,
SMB2_CLIENT_GUID_SIZE ) & &
! memcmp ( op - > o_lease - > lease_key , brk_op - > o_lease - > lease_key ,
SMB2_LEASE_KEY_SIZE ) )
2021-03-16 04:49:09 +03:00
goto next ;
brk_op - > open_trunc = is_trunc ;
oplock_break ( brk_op , SMB2_OPLOCK_LEVEL_NONE ) ;
next :
opinfo_put ( brk_op ) ;
rcu_read_lock ( ) ;
}
rcu_read_unlock ( ) ;
if ( op )
opinfo_put ( op ) ;
}
/**
* smb_break_all_oplock ( ) - break both batch / exclusive and level2 oplock
* @ work : smb work
* @ fp : ksmbd file pointer
*/
void smb_break_all_oplock ( struct ksmbd_work * work , struct ksmbd_file * fp )
{
if ( ! test_share_config_flag ( work - > tcon - > share_conf ,
2021-03-30 08:25:35 +03:00
KSMBD_SHARE_FLAG_OPLOCKS ) )
2021-03-16 04:49:09 +03:00
return ;
smb_break_all_write_oplock ( work , fp , 1 ) ;
smb_break_all_levII_oplock ( work , fp , 1 ) ;
}
/**
* smb2_map_lease_to_oplock ( ) - map lease state to corresponding oplock type
* @ lease_state : lease type
*
* Return : 0 if no mapping , otherwise corresponding oplock type
*/
__u8 smb2_map_lease_to_oplock ( __le32 lease_state )
{
if ( lease_state = = ( SMB2_LEASE_HANDLE_CACHING_LE |
2021-03-30 08:25:35 +03:00
SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_WRITE_CACHING_LE ) ) {
2021-03-16 04:49:09 +03:00
return SMB2_OPLOCK_LEVEL_BATCH ;
2021-03-30 08:25:35 +03:00
} else if ( lease_state ! = SMB2_LEASE_WRITE_CACHING_LE & &
lease_state & SMB2_LEASE_WRITE_CACHING_LE ) {
2021-03-16 04:49:09 +03:00
if ( ! ( lease_state & SMB2_LEASE_HANDLE_CACHING_LE ) )
return SMB2_OPLOCK_LEVEL_EXCLUSIVE ;
2021-03-30 08:25:35 +03:00
} else if ( lease_state & SMB2_LEASE_READ_CACHING_LE ) {
2021-03-16 04:49:09 +03:00
return SMB2_OPLOCK_LEVEL_II ;
2021-03-30 08:25:35 +03:00
}
2021-03-16 04:49:09 +03:00
return 0 ;
}
/**
* create_lease_buf ( ) - create lease context for open cmd response
* @ rbuf : buffer to create lease context response
2021-03-21 11:05:56 +03:00
* @ lease : buffer to stored parsed lease state information
2021-03-16 04:49:09 +03:00
*/
void create_lease_buf ( u8 * rbuf , struct lease * lease )
{
char * LeaseKey = ( char * ) & lease - > lease_key ;
2021-06-07 03:22:22 +03:00
if ( lease - > version = = 2 ) {
struct create_lease_v2 * buf = ( struct create_lease_v2 * ) rbuf ;
char * ParentLeaseKey = ( char * ) & lease - > parent_lease_key ;
memset ( buf , 0 , sizeof ( struct create_lease_v2 ) ) ;
buf - > lcontext . LeaseKeyLow = * ( ( __le64 * ) LeaseKey ) ;
buf - > lcontext . LeaseKeyHigh = * ( ( __le64 * ) ( LeaseKey + 8 ) ) ;
buf - > lcontext . LeaseFlags = lease - > flags ;
buf - > lcontext . LeaseState = lease - > state ;
buf - > lcontext . ParentLeaseKeyLow = * ( ( __le64 * ) ParentLeaseKey ) ;
buf - > lcontext . ParentLeaseKeyHigh = * ( ( __le64 * ) ( ParentLeaseKey + 8 ) ) ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_lease_v2 , lcontext ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( sizeof ( struct lease_context_v2 ) ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_lease_v2 , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
buf - > Name [ 0 ] = ' R ' ;
buf - > Name [ 1 ] = ' q ' ;
buf - > Name [ 2 ] = ' L ' ;
buf - > Name [ 3 ] = ' s ' ;
} else {
struct create_lease * buf = ( struct create_lease * ) rbuf ;
memset ( buf , 0 , sizeof ( struct create_lease ) ) ;
buf - > lcontext . LeaseKeyLow = * ( ( __le64 * ) LeaseKey ) ;
buf - > lcontext . LeaseKeyHigh = * ( ( __le64 * ) ( LeaseKey + 8 ) ) ;
buf - > lcontext . LeaseFlags = lease - > flags ;
buf - > lcontext . LeaseState = lease - > state ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_lease , lcontext ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( sizeof ( struct lease_context ) ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
2021-03-16 04:49:09 +03:00
( struct create_lease , Name ) ) ;
2021-06-07 03:22:22 +03:00
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
buf - > Name [ 0 ] = ' R ' ;
buf - > Name [ 1 ] = ' q ' ;
buf - > Name [ 2 ] = ' L ' ;
buf - > Name [ 3 ] = ' s ' ;
}
2021-03-16 04:49:09 +03:00
}
/**
* parse_lease_state ( ) - parse lease context containted in file open request
* @ open_req : buffer containing smb2 file open ( create ) request
*
* Return : oplock state , - ENOENT if create lease context not found
*/
struct lease_ctx_info * parse_lease_state ( void * open_req )
{
char * data_offset ;
struct create_context * cc ;
unsigned int next = 0 ;
char * name ;
bool found = false ;
struct smb2_create_req * req = ( struct smb2_create_req * ) open_req ;
struct lease_ctx_info * lreq = kzalloc ( sizeof ( struct lease_ctx_info ) ,
GFP_KERNEL ) ;
if ( ! lreq )
return NULL ;
data_offset = ( char * ) req + 4 + le32_to_cpu ( req - > CreateContextsOffset ) ;
cc = ( struct create_context * ) data_offset ;
do {
cc = ( struct create_context * ) ( ( char * ) cc + next ) ;
name = le16_to_cpu ( cc - > NameOffset ) + ( char * ) cc ;
if ( le16_to_cpu ( cc - > NameLength ) ! = 4 | |
2021-03-30 08:25:35 +03:00
strncmp ( name , SMB2_CREATE_REQUEST_LEASE , 4 ) ) {
2021-03-16 04:49:09 +03:00
next = le32_to_cpu ( cc - > Next ) ;
continue ;
}
found = true ;
break ;
} while ( next ! = 0 ) ;
if ( found ) {
2021-06-07 03:22:22 +03:00
if ( sizeof ( struct lease_context_v2 ) = = le32_to_cpu ( cc - > DataLength ) ) {
struct create_lease_v2 * lc = ( struct create_lease_v2 * ) cc ;
* ( ( __le64 * ) lreq - > lease_key ) = lc - > lcontext . LeaseKeyLow ;
* ( ( __le64 * ) ( lreq - > lease_key + 8 ) ) = lc - > lcontext . LeaseKeyHigh ;
lreq - > req_state = lc - > lcontext . LeaseState ;
lreq - > flags = lc - > lcontext . LeaseFlags ;
lreq - > duration = lc - > lcontext . LeaseDuration ;
* ( ( __le64 * ) lreq - > parent_lease_key ) = lc - > lcontext . ParentLeaseKeyLow ;
* ( ( __le64 * ) ( lreq - > parent_lease_key + 8 ) ) = lc - > lcontext . ParentLeaseKeyHigh ;
lreq - > version = 2 ;
} else {
struct create_lease * lc = ( struct create_lease * ) cc ;
* ( ( __le64 * ) lreq - > lease_key ) = lc - > lcontext . LeaseKeyLow ;
* ( ( __le64 * ) ( lreq - > lease_key + 8 ) ) = lc - > lcontext . LeaseKeyHigh ;
lreq - > req_state = lc - > lcontext . LeaseState ;
lreq - > flags = lc - > lcontext . LeaseFlags ;
lreq - > duration = lc - > lcontext . LeaseDuration ;
lreq - > version = 1 ;
}
2021-03-16 04:49:09 +03:00
return lreq ;
}
kfree ( lreq ) ;
return NULL ;
}
/**
* smb2_find_context_vals ( ) - find a particular context info in open request
* @ open_req : buffer containing smb2 file open ( create ) request
2021-03-21 11:05:56 +03:00
* @ tag : context name to search for
2021-03-16 04:49:09 +03:00
*
2021-07-13 03:59:34 +03:00
* Return : pointer to requested context , NULL if @ str context not found
* or error pointer if name length is invalid .
2021-03-16 04:49:09 +03:00
*/
struct create_context * smb2_find_context_vals ( void * open_req , const char * tag )
{
struct create_context * cc ;
unsigned int next = 0 ;
char * name ;
struct smb2_create_req * req = ( struct smb2_create_req * ) open_req ;
2021-09-24 16:22:22 +03:00
unsigned int remain_len , name_off , name_len , value_off , value_len ,
cc_len ;
2021-03-16 04:49:09 +03:00
2021-09-24 16:22:22 +03:00
/*
* CreateContextsOffset and CreateContextsLength are guaranteed to
* be valid because of ksmbd_smb2_check_message ( ) .
*/
cc = ( struct create_context * ) ( ( char * ) req + 4 +
le32_to_cpu ( req - > CreateContextsOffset ) ) ;
remain_len = le32_to_cpu ( req - > CreateContextsLength ) ;
2021-03-16 04:49:09 +03:00
do {
cc = ( struct create_context * ) ( ( char * ) cc + next ) ;
2021-09-24 16:22:22 +03:00
if ( remain_len < offsetof ( struct create_context , Buffer ) )
2021-03-16 04:49:09 +03:00
return ERR_PTR ( - EINVAL ) ;
next = le32_to_cpu ( cc - > Next ) ;
2021-09-24 16:22:22 +03:00
name_off = le16_to_cpu ( cc - > NameOffset ) ;
name_len = le16_to_cpu ( cc - > NameLength ) ;
value_off = le16_to_cpu ( cc - > DataOffset ) ;
value_len = le32_to_cpu ( cc - > DataLength ) ;
cc_len = next ? next : remain_len ;
if ( ( next & 0x7 ) ! = 0 | |
next > remain_len | |
name_off ! = offsetof ( struct create_context , Buffer ) | |
name_len < 4 | |
name_off + name_len > cc_len | |
( value_off & 0x7 ) ! = 0 | |
( value_off & & ( value_off < name_off + name_len ) ) | |
( ( u64 ) value_off + value_len > cc_len ) )
return ERR_PTR ( - EINVAL ) ;
name = ( char * ) cc + name_off ;
if ( memcmp ( name , tag , name_len ) = = 0 )
return cc ;
remain_len - = next ;
2021-03-16 04:49:09 +03:00
} while ( next ! = 0 ) ;
2021-07-09 11:06:33 +03:00
return NULL ;
2021-03-16 04:49:09 +03:00
}
/**
2021-03-30 08:42:05 +03:00
* create_durable_rsp_buf ( ) - create durable handle context
2021-03-16 04:49:09 +03:00
* @ cc : buffer to create durable context response
*/
void create_durable_rsp_buf ( char * cc )
{
struct create_durable_rsp * buf ;
buf = ( struct create_durable_rsp * ) cc ;
memset ( buf , 0 , sizeof ( struct create_durable_rsp ) ) ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_durable_rsp , Data ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 8 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_durable_rsp , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
/* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' n ' ;
buf - > Name [ 3 ] = ' Q ' ;
}
/**
2021-03-21 11:05:56 +03:00
* create_durable_v2_rsp_buf ( ) - create durable handle v2 context
2021-03-16 04:49:09 +03:00
* @ cc : buffer to create durable context response
2021-03-21 11:05:56 +03:00
* @ fp : ksmbd file pointer
2021-03-16 04:49:09 +03:00
*/
void create_durable_v2_rsp_buf ( char * cc , struct ksmbd_file * fp )
{
struct create_durable_v2_rsp * buf ;
buf = ( struct create_durable_v2_rsp * ) cc ;
memset ( buf , 0 , sizeof ( struct create_durable_rsp ) ) ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_durable_rsp , Data ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 8 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_durable_rsp , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
/* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' 2 ' ;
buf - > Name [ 3 ] = ' Q ' ;
buf - > Timeout = cpu_to_le32 ( fp - > durable_timeout ) ;
}
/**
2021-03-21 11:05:56 +03:00
* create_mxac_rsp_buf ( ) - create query maximal access context
* @ cc : buffer to create maximal access context response
* @ maximal_access : maximal access
2021-03-16 04:49:09 +03:00
*/
void create_mxac_rsp_buf ( char * cc , int maximal_access )
{
struct create_mxac_rsp * buf ;
buf = ( struct create_mxac_rsp * ) cc ;
memset ( buf , 0 , sizeof ( struct create_mxac_rsp ) ) ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_mxac_rsp , QueryStatus ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 8 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_mxac_rsp , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
/* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */
buf - > Name [ 0 ] = ' M ' ;
buf - > Name [ 1 ] = ' x ' ;
buf - > Name [ 2 ] = ' A ' ;
buf - > Name [ 3 ] = ' c ' ;
buf - > QueryStatus = STATUS_SUCCESS ;
buf - > MaximalAccess = cpu_to_le32 ( maximal_access ) ;
}
void create_disk_id_rsp_buf ( char * cc , __u64 file_id , __u64 vol_id )
{
struct create_disk_id_rsp * buf ;
buf = ( struct create_disk_id_rsp * ) cc ;
memset ( buf , 0 , sizeof ( struct create_disk_id_rsp ) ) ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_disk_id_rsp , DiskFileId ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 32 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_mxac_rsp , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
/* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */
buf - > Name [ 0 ] = ' Q ' ;
buf - > Name [ 1 ] = ' F ' ;
buf - > Name [ 2 ] = ' i ' ;
buf - > Name [ 3 ] = ' d ' ;
buf - > DiskFileId = cpu_to_le64 ( file_id ) ;
buf - > VolumeId = cpu_to_le64 ( vol_id ) ;
}
/**
2021-03-21 11:05:56 +03:00
* create_posix_rsp_buf ( ) - create posix extension context
2021-03-16 04:49:09 +03:00
* @ cc : buffer to create posix on posix response
2021-03-21 11:05:56 +03:00
* @ fp : ksmbd file pointer
2021-03-16 04:49:09 +03:00
*/
void create_posix_rsp_buf ( char * cc , struct ksmbd_file * fp )
{
struct create_posix_rsp * buf ;
2021-06-29 03:20:13 +03:00
struct inode * inode = file_inode ( fp - > filp ) ;
2021-07-03 06:10:36 +03:00
struct user_namespace * user_ns = file_mnt_user_ns ( fp - > filp ) ;
2021-03-16 04:49:09 +03:00
buf = ( struct create_posix_rsp * ) cc ;
memset ( buf , 0 , sizeof ( struct create_posix_rsp ) ) ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_posix_rsp , nlink ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 52 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_posix_rsp , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( POSIX_CTXT_DATA_LEN ) ;
/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
buf - > Name [ 0 ] = 0x93 ;
buf - > Name [ 1 ] = 0xAD ;
buf - > Name [ 2 ] = 0x25 ;
buf - > Name [ 3 ] = 0x50 ;
buf - > Name [ 4 ] = 0x9C ;
buf - > Name [ 5 ] = 0xB4 ;
buf - > Name [ 6 ] = 0x11 ;
buf - > Name [ 7 ] = 0xE7 ;
buf - > Name [ 8 ] = 0xB4 ;
buf - > Name [ 9 ] = 0x23 ;
buf - > Name [ 10 ] = 0x83 ;
buf - > Name [ 11 ] = 0xDE ;
buf - > Name [ 12 ] = 0x96 ;
buf - > Name [ 13 ] = 0x8B ;
buf - > Name [ 14 ] = 0xCD ;
buf - > Name [ 15 ] = 0x7C ;
buf - > nlink = cpu_to_le32 ( inode - > i_nlink ) ;
buf - > reparse_tag = cpu_to_le32 ( fp - > volatile_id ) ;
buf - > mode = cpu_to_le32 ( inode - > i_mode ) ;
2021-08-23 18:13:49 +03:00
id_to_sid ( from_kuid_munged ( & init_user_ns ,
i_uid_into_mnt ( user_ns , inode ) ) ,
2021-05-26 11:57:12 +03:00
SIDNFS_USER , ( struct smb_sid * ) & buf - > SidBuffer [ 0 ] ) ;
2021-08-23 18:13:49 +03:00
id_to_sid ( from_kgid_munged ( & init_user_ns ,
i_gid_into_mnt ( user_ns , inode ) ) ,
2021-05-26 11:57:12 +03:00
SIDNFS_GROUP , ( struct smb_sid * ) & buf - > SidBuffer [ 20 ] ) ;
2021-03-16 04:49:09 +03:00
}
/*
* Find lease object ( opinfo ) for given lease key / fid from lease
* break / file close path .
*/
/**
* lookup_lease_in_table ( ) - find a matching lease info object
* @ conn : connection instance
* @ lease_key : lease key to be searched for
*
* Return : opinfo if found matching opinfo , otherwise NULL
*/
struct oplock_info * lookup_lease_in_table ( struct ksmbd_conn * conn ,
2021-05-26 11:57:12 +03:00
char * lease_key )
2021-03-16 04:49:09 +03:00
{
struct oplock_info * opinfo = NULL , * ret_op = NULL ;
struct lease_table * lt ;
int ret ;
read_lock ( & lease_list_lock ) ;
list_for_each_entry ( lt , & lease_table_list , l_entry ) {
if ( ! memcmp ( lt - > client_guid , conn - > ClientGUID ,
2021-03-30 08:25:35 +03:00
SMB2_CLIENT_GUID_SIZE ) )
2021-03-16 04:49:09 +03:00
goto found ;
}
read_unlock ( & lease_list_lock ) ;
return NULL ;
found :
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( opinfo , & lt - > lease_list , lease_entry ) {
if ( ! atomic_inc_not_zero ( & opinfo - > refcount ) )
continue ;
rcu_read_unlock ( ) ;
2021-03-30 08:25:35 +03:00
if ( ! opinfo - > op_state | | opinfo - > op_state = = OPLOCK_CLOSING )
2021-03-16 04:49:09 +03:00
goto op_next ;
if ( ! ( opinfo - > o_lease - > state &
2021-03-30 08:25:35 +03:00
( SMB2_LEASE_HANDLE_CACHING_LE |
SMB2_LEASE_WRITE_CACHING_LE ) ) )
2021-03-16 04:49:09 +03:00
goto op_next ;
ret = compare_guid_key ( opinfo , conn - > ClientGUID ,
2021-05-26 11:57:12 +03:00
lease_key ) ;
2021-03-16 04:49:09 +03:00
if ( ret ) {
ksmbd_debug ( OPLOCK , " found opinfo \n " ) ;
ret_op = opinfo ;
goto out ;
}
op_next :
opinfo_put ( opinfo ) ;
rcu_read_lock ( ) ;
}
rcu_read_unlock ( ) ;
out :
read_unlock ( & lease_list_lock ) ;
return ret_op ;
}
int smb2_check_durable_oplock ( struct ksmbd_file * fp ,
2021-05-26 11:57:12 +03:00
struct lease_ctx_info * lctx , char * name )
2021-03-16 04:49:09 +03:00
{
struct oplock_info * opinfo = opinfo_get ( fp ) ;
int ret = 0 ;
if ( opinfo & & opinfo - > is_lease ) {
if ( ! lctx ) {
2021-06-28 09:23:19 +03:00
pr_err ( " open does not include lease \n " ) ;
2021-03-16 04:49:09 +03:00
ret = - EBADF ;
goto out ;
}
if ( memcmp ( opinfo - > o_lease - > lease_key , lctx - > lease_key ,
2021-03-30 08:25:35 +03:00
SMB2_LEASE_KEY_SIZE ) ) {
2021-06-28 09:23:19 +03:00
pr_err ( " invalid lease key \n " ) ;
2021-03-16 04:49:09 +03:00
ret = - EBADF ;
goto out ;
}
if ( name & & strcmp ( fp - > filename , name ) ) {
2021-06-28 09:23:19 +03:00
pr_err ( " invalid name reconnect %s \n " , name ) ;
2021-03-16 04:49:09 +03:00
ret = - EINVAL ;
goto out ;
}
}
out :
if ( opinfo )
opinfo_put ( opinfo ) ;
return ret ;
}