Commit d47d9886 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

ksmbd: send v2 lease break notification for directory

If client send different parent key, different client guid, or there is
no parent lease key flags in create context v2 lease, ksmbd send lease
break to client.
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent eb547407
...@@ -1253,6 +1253,7 @@ struct create_mxac_rsp { ...@@ -1253,6 +1253,7 @@ struct create_mxac_rsp {
#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) #define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04)
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02)
#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE cpu_to_le32(0x04)
#define SMB2_LEASE_KEY_SIZE 16 #define SMB2_LEASE_KEY_SIZE 16
......
...@@ -102,6 +102,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) ...@@ -102,6 +102,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
lease->new_state = 0; lease->new_state = 0;
lease->flags = lctx->flags; lease->flags = lctx->flags;
lease->duration = lctx->duration; lease->duration = lctx->duration;
lease->is_dir = lctx->is_dir;
memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
lease->version = lctx->version; lease->version = lctx->version;
lease->epoch = le16_to_cpu(lctx->epoch); lease->epoch = le16_to_cpu(lctx->epoch);
...@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, ...@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
/* upgrading lease */ /* upgrading lease */
if ((atomic_read(&ci->op_count) + if ((atomic_read(&ci->op_count) +
atomic_read(&ci->sop_count)) == 1) { atomic_read(&ci->sop_count)) == 1) {
if (lease->state == if (lease->state != SMB2_LEASE_NONE_LE &&
(lctx->req_state & lease->state)) { lease->state == (lctx->req_state & lease->state)) {
lease->state |= lctx->req_state; lease->state |= lctx->req_state;
if (lctx->req_state & if (lctx->req_state &
SMB2_LEASE_WRITE_CACHING_LE) SMB2_LEASE_WRITE_CACHING_LE)
lease_read_to_write(opinfo); lease_read_to_write(opinfo);
} }
} else if ((atomic_read(&ci->op_count) + } else if ((atomic_read(&ci->op_count) +
atomic_read(&ci->sop_count)) > 1) { atomic_read(&ci->sop_count)) > 1) {
...@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) ...@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
lease->new_state = lease->new_state =
SMB2_LEASE_READ_CACHING_LE; SMB2_LEASE_READ_CACHING_LE;
} else { } else {
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE &&
!lease->is_dir)
lease->new_state = lease->new_state =
SMB2_LEASE_READ_CACHING_LE; SMB2_LEASE_READ_CACHING_LE;
else else
...@@ -1082,6 +1085,48 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, ...@@ -1082,6 +1085,48 @@ static void set_oplock_level(struct oplock_info *opinfo, int level,
} }
} }
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
struct lease_ctx_info *lctx)
{
struct oplock_info *opinfo;
struct ksmbd_inode *p_ci = NULL;
if (lctx->version != 2)
return;
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
if (!p_ci)
return;
read_lock(&p_ci->m_lock);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
continue;
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
(!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) ||
!compare_guid_key(opinfo, fp->conn->ClientGUID,
lctx->parent_lease_key))) {
if (!atomic_inc_not_zero(&opinfo->refcount))
continue;
atomic_inc(&opinfo->conn->r_count);
if (ksmbd_conn_releasing(opinfo->conn)) {
atomic_dec(&opinfo->conn->r_count);
continue;
}
read_unlock(&p_ci->m_lock);
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
opinfo_conn_put(opinfo);
read_lock(&p_ci->m_lock);
}
}
read_unlock(&p_ci->m_lock);
ksmbd_inode_put(p_ci);
}
/** /**
* smb_grant_oplock() - handle oplock/lease request on file open * smb_grant_oplock() - handle oplock/lease request on file open
* @work: smb work * @work: smb work
...@@ -1420,10 +1465,11 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) ...@@ -1420,10 +1465,11 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
if (is_dir) if (is_dir) {
lreq->req_state = lc->lcontext.LeaseState & lreq->req_state = lc->lcontext.LeaseState &
~SMB2_LEASE_WRITE_CACHING_LE; ~SMB2_LEASE_WRITE_CACHING_LE;
else lreq->is_dir = true;
} else
lreq->req_state = lc->lcontext.LeaseState; lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags; lreq->flags = lc->lcontext.LeaseFlags;
lreq->epoch = lc->lcontext.Epoch; lreq->epoch = lc->lcontext.Epoch;
......
...@@ -36,6 +36,7 @@ struct lease_ctx_info { ...@@ -36,6 +36,7 @@ struct lease_ctx_info {
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
__le16 epoch; __le16 epoch;
int version; int version;
bool is_dir;
}; };
struct lease_table { struct lease_table {
...@@ -54,6 +55,7 @@ struct lease { ...@@ -54,6 +55,7 @@ struct lease {
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
int version; int version;
unsigned short epoch; unsigned short epoch;
bool is_dir;
struct lease_table *l_lb; struct lease_table *l_lb;
}; };
...@@ -125,4 +127,6 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, ...@@ -125,4 +127,6 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
struct lease_ctx_info *lctx); struct lease_ctx_info *lctx);
void destroy_lease_table(struct ksmbd_conn *conn); void destroy_lease_table(struct ksmbd_conn *conn);
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
struct lease_ctx_info *lctx);
#endif /* __KSMBD_OPLOCK_H */ #endif /* __KSMBD_OPLOCK_H */
...@@ -3225,6 +3225,13 @@ int smb2_open(struct ksmbd_work *work) ...@@ -3225,6 +3225,13 @@ int smb2_open(struct ksmbd_work *work)
} }
} else { } else {
if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
/*
* Compare parent lease using parent key. If there is no
* a lease that has same parent key, Send lease break
* notification.
*/
smb_send_parent_lease_break_noti(fp, lc);
req_op_level = smb2_map_lease_to_oplock(lc->req_state); req_op_level = smb2_map_lease_to_oplock(lc->req_state);
ksmbd_debug(SMB, ksmbd_debug(SMB,
"lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n",
......
...@@ -87,6 +87,17 @@ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) ...@@ -87,6 +87,17 @@ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
return __ksmbd_inode_lookup(fp->filp->f_path.dentry); return __ksmbd_inode_lookup(fp->filp->f_path.dentry);
} }
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d)
{
struct ksmbd_inode *ci;
read_lock(&inode_hash_lock);
ci = __ksmbd_inode_lookup(d);
read_unlock(&inode_hash_lock);
return ci;
}
int ksmbd_query_inode_status(struct dentry *dentry) int ksmbd_query_inode_status(struct dentry *dentry)
{ {
struct ksmbd_inode *ci; struct ksmbd_inode *ci;
...@@ -199,7 +210,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci) ...@@ -199,7 +210,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci)
kfree(ci); kfree(ci);
} }
static void ksmbd_inode_put(struct ksmbd_inode *ci) void ksmbd_inode_put(struct ksmbd_inode *ci)
{ {
if (atomic_dec_and_test(&ci->m_count)) if (atomic_dec_and_test(&ci->m_count))
ksmbd_inode_free(ci); ksmbd_inode_free(ci);
......
...@@ -138,6 +138,8 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); ...@@ -138,6 +138,8 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id);
struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
u64 pid); u64 pid);
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
void ksmbd_inode_put(struct ksmbd_inode *ci);
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment