mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-09 14:43:16 +00:00
ksmbd: send v2 lease break notification for directory
[ Upstream commit d47d9886aeef79feba7adac701a510d65f3682b5 ] 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: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
1993959460
commit
500c7a5e9a
@ -1196,6 +1196,7 @@ struct create_posix {
|
|||||||
#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)
|
|||||||
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,
|
|||||||
/* 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)
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|||||||
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 {
|
|||||||
__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 {
|
|||||||
__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,
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
} 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",
|
||||||
|
@ -86,6 +86,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;
|
||||||
@ -198,7 +209,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);
|
|||||||
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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user