[PATCH 2/7] ksmbd: route stream FileDispositionInformation through stream delete flag

"Gaël Blivet-Bailly" <[email protected]>
Newsgroups org.kernel.vger.linux-cifs
Message-ID <[email protected]>
From: Gael Blivet <[email protected]>

set_file_disposition_info() calls ksmbd_set_inode_pending_delete() /
ksmbd_clear_inode_pending_delete() unconditionally, which always sets
S_DEL_PENDING on the whole inode (ci->m_flags), regardless of whether
the handle being closed is a regular file or an alternate data stream.

Requesting delete-pending on a single stream handle (e.g. deleting
just an alternate data stream some clients keep alongside a file)
would therefore incorrectly schedule deletion of the entire file's
data, not just the stream.

Add ksmbd_fd_set_delete_pending()/ksmbd_fd_clear_delete_pending(),
following the same stream-vs-whole-file routing pattern already used
by ksmbd_fd_set_delete_on_close() for the CREATE-time DeleteOnClose
option, and switch set_file_disposition_info() to use them.

Signed-off-by: Gael Blivet <[email protected]>
---
 fs/smb/server/smb2pdu.c   |  4 ++--
 fs/smb/server/vfs_cache.c | 32 ++++++++++++++++++++++++++++++++
 fs/smb/server/vfs_cache.h |  2 ++
 3 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 39188bb84..e6d0f58d2 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -7117,9 +7117,9 @@ static int set_file_disposition_info(struct ksmbd_work *work,
 		    ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY)
 			return -EBUSY;
 		smb_break_all_levII_oplock_for_delete(work, fp);
-		ksmbd_set_inode_pending_delete(fp);
+		ksmbd_fd_set_delete_pending(fp);
 	} else {
-		ksmbd_clear_inode_pending_delete(fp);
+		ksmbd_fd_clear_delete_pending(fp);
 	}
 	return 0;
 }
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index d95c405ea..2543dd7e8 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -294,6 +294,38 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
 	up_write(&ci->m_lock);
 }
 
+/*
+ * FileDispositionInformation (SET_INFO) on a stream handle must only
+ * mark the stream for deletion, not the whole file -- otherwise
+ * deleting a single alternate data stream (e.g. AFP_AfpInfo) deletes
+ * the entire file's data along with it.
+ */
+void ksmbd_fd_set_delete_pending(struct ksmbd_file *fp)
+{
+	struct ksmbd_inode *ci = fp->f_ci;
+
+	if (ksmbd_stream_fd(fp)) {
+		down_write(&ci->m_lock);
+		ci->m_flags |= S_DEL_ON_CLS_STREAM;
+		up_write(&ci->m_lock);
+	} else {
+		ksmbd_set_inode_pending_delete(fp);
+	}
+}
+
+void ksmbd_fd_clear_delete_pending(struct ksmbd_file *fp)
+{
+	struct ksmbd_inode *ci = fp->f_ci;
+
+	if (ksmbd_stream_fd(fp)) {
+		down_write(&ci->m_lock);
+		ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
+		up_write(&ci->m_lock);
+	} else {
+		ksmbd_clear_inode_pending_delete(fp);
+	}
+}
+
 static void ksmbd_inode_hash(struct ksmbd_inode *ci)
 {
 	struct hlist_head *b = inode_hashtable +
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index b9e27307a..111a4e315 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -214,6 +214,8 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
 void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
 void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
 				  int file_info);
+void ksmbd_fd_set_delete_pending(struct ksmbd_file *fp);
+void ksmbd_fd_clear_delete_pending(struct ksmbd_file *fp);
 int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp);
 int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
 				  struct ksmbd_file *fp, char *name);
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.