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

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1253,6 +1253,7 @@ struct create_mxac_rsp {
#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_PARENT_LEASE_KEY_SET_LE	cpu_to_le32(0x04)

#define SMB2_LEASE_KEY_SIZE			16

+51 −5
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
	lease->new_state = 0;
	lease->flags = lctx->flags;
	lease->duration = lctx->duration;
	lease->is_dir = lctx->is_dir;
	memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
	lease->version = lctx->version;
	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 */
			if ((atomic_read(&ci->op_count) +
			     atomic_read(&ci->sop_count)) == 1) {
				if (lease->state ==
				    (lctx->req_state & lease->state)) {
				if (lease->state != SMB2_LEASE_NONE_LE &&
				    lease->state == (lctx->req_state & lease->state)) {
					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) {
@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
					lease->new_state =
						SMB2_LEASE_READ_CACHING_LE;
			} else {
				if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
				if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE &&
						!lease->is_dir)
					lease->new_state =
						SMB2_LEASE_READ_CACHING_LE;
				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
 * @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;

		memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
		if (is_dir)
		if (is_dir) {
			lreq->req_state = lc->lcontext.LeaseState &
				~SMB2_LEASE_WRITE_CACHING_LE;
		else
			lreq->is_dir = true;
		} else
			lreq->req_state = lc->lcontext.LeaseState;
		lreq->flags = lc->lcontext.LeaseFlags;
		lreq->epoch = lc->lcontext.Epoch;
+4 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ struct lease_ctx_info {
	__u8			parent_lease_key[SMB2_LEASE_KEY_SIZE];
	__le16			epoch;
	int			version;
	bool			is_dir;
};

struct lease_table {
@@ -54,6 +55,7 @@ struct lease {
	__u8			parent_lease_key[SMB2_LEASE_KEY_SIZE];
	int			version;
	unsigned short		epoch;
	bool			is_dir;
	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,
			struct lease_ctx_info *lctx);
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 */
+7 −0
Original line number Diff line number Diff line
@@ -3225,6 +3225,13 @@ int smb2_open(struct ksmbd_work *work)
		}
	} else {
		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);
			ksmbd_debug(SMB,
				    "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n",
+12 −1
Original line number Diff line number Diff line
@@ -87,6 +87,17 @@ static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
	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)
{
	struct ksmbd_inode *ci;
@@ -199,7 +210,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *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))
		ksmbd_inode_free(ci);
Loading