mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
ksmbd: supersede disconnected delete-on-close durable handle
A durable handle opened with FILE_DELETE_ON_CLOSE is preserved across a disconnect so it can be reclaimed by a durable reconnect. smb2.durable-open.delete_on_close2 disconnects such a handle and then reconnects it, expecting the reconnect to succeed. When the client does not reconnect but instead opens the same name with a new delete-on-close create, the preserved handle keeps the file present with delete-on-close set. ksmbd then rejects the new open with STATUS_ACCESS_DENIED on the file_present + FILE_DELETE_ON_CLOSE + OPEN_IF/OVERWRITE_IF path. smb2.durable-open.delete_on_close1 expects this open to create a fresh, empty file instead, i.e. the disconnected handle's delete-on-close must take effect first. Add ksmbd_close_disconnected_durable_delete_on_close(), which closes disconnected (conn == NULL) durable handles that keep a delete-on-close file present. The final close promotes S_DEL_ON_CLS to S_DEL_PENDING and unlinks the file, so a re-resolved path is absent and the new open creates it fresh. Call it from smb2_open() before the delete-on-close conflict check, only for the conflicting open shapes. A live (connected) handle still keeps the file and blocks the open as before. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
a04159d96c
commit
166e4c0702
|
|
@ -3272,6 +3272,23 @@ int smb2_open(struct ksmbd_work *work)
|
|||
|
||||
rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS,
|
||||
&path, 1);
|
||||
|
||||
/*
|
||||
* A durable handle opened with delete-on-close is preserved across a
|
||||
* disconnect so it can be reclaimed by a durable reconnect. When a new
|
||||
* delete-on-close open for the same name arrives instead, the
|
||||
* disconnected handle must give way: close it so its delete-on-close
|
||||
* removes the file, then re-resolve so this open can create a fresh one.
|
||||
*/
|
||||
if (!rc && (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) &&
|
||||
(req->CreateDisposition == FILE_OVERWRITE_IF_LE ||
|
||||
req->CreateDisposition == FILE_OPEN_IF_LE) &&
|
||||
ksmbd_close_disconnected_durable_delete_on_close(path.dentry)) {
|
||||
path_put(&path);
|
||||
rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS,
|
||||
&path, 1);
|
||||
}
|
||||
|
||||
if (!rc) {
|
||||
file_present = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -517,6 +517,63 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
|
|||
kmem_cache_free(filp_cache, fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_close_disconnected_durable_delete_on_close() - drop a delete-on-close
|
||||
* file kept present only by disconnected durable handles
|
||||
* @dentry: dentry of the file being opened
|
||||
*
|
||||
* A durable handle opened with delete-on-close is preserved across a
|
||||
* disconnect so it can be reclaimed by a durable reconnect. When a new
|
||||
* (non-reconnect) open arrives for the same name instead, the disconnected
|
||||
* handle has to give way. Close such handles so their delete-on-close is
|
||||
* applied and the file is removed once the last handle is gone, letting the
|
||||
* new open create a fresh file.
|
||||
*
|
||||
* The caller's inode reference is dropped before closing so that the final
|
||||
* close can promote S_DEL_ON_CLS to S_DEL_PENDING and unlink the file.
|
||||
*
|
||||
* Return: true if a disconnected durable handle was closed.
|
||||
*/
|
||||
bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry)
|
||||
{
|
||||
struct ksmbd_inode *ci;
|
||||
struct ksmbd_file *fp, *tmp;
|
||||
LIST_HEAD(dispose);
|
||||
bool closed = false;
|
||||
|
||||
ci = ksmbd_inode_lookup_lock(dentry);
|
||||
if (!ci)
|
||||
return false;
|
||||
|
||||
down_write(&ci->m_lock);
|
||||
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_ON_CLS_STREAM | S_DEL_PENDING)) {
|
||||
list_for_each_entry_safe(fp, tmp, &ci->m_fp_list, node) {
|
||||
if (fp->conn || !fp->is_durable ||
|
||||
fp->f_state != FP_INITED)
|
||||
continue;
|
||||
list_move_tail(&fp->node, &dispose);
|
||||
}
|
||||
}
|
||||
up_write(&ci->m_lock);
|
||||
|
||||
/*
|
||||
* Drop our lookup reference before closing so the last __ksmbd_close_fd()
|
||||
* can drop m_count to zero and unlink the delete-on-close file. The
|
||||
* collected handles still hold references, so ci stays valid until they
|
||||
* are closed below.
|
||||
*/
|
||||
ksmbd_inode_put(ci);
|
||||
|
||||
while (!list_empty(&dispose)) {
|
||||
fp = list_first_entry(&dispose, struct ksmbd_file, node);
|
||||
list_del_init(&fp->node);
|
||||
__ksmbd_close_fd(NULL, fp);
|
||||
closed = true;
|
||||
}
|
||||
|
||||
return closed;
|
||||
}
|
||||
|
||||
static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
|
||||
{
|
||||
if (fp->f_state != FP_INITED)
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
|
|||
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);
|
||||
bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry);
|
||||
struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id);
|
||||
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
|
||||
void ksmbd_put_durable_fd(struct ksmbd_file *fp);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user