ksmbd: send v2 lease break notification for directory
authorNamjae Jeon <linkinjeon@kernel.org>
Sun, 31 Dec 2023 07:19:17 +0000 (16:19 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 5 Jan 2024 14:19:40 +0000 (15:19 +0100)
[ 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>
fs/smb/common/smb2pdu.h
fs/smb/server/oplock.c
fs/smb/server/oplock.h
fs/smb/server/smb2pdu.c
fs/smb/server/vfs_cache.c
fs/smb/server/vfs_cache.h

index ec20c83..d58550c 100644 (file)
@@ -1228,6 +1228,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
 
index 57950ba..147d984 100644 (file)
@@ -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;
index 6721273..b64d153 100644 (file)
@@ -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 */
index c4b6adc..cbd5c55 100644 (file)
@@ -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",
index ddf2339..4e82ff6 100644 (file)
@@ -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);
index 8325cf4..4d4938d 100644 (file)
@@ -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,
                                        u64 pid);
 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_fd_cguid(char *cguid);
 struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);