mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 19:47:08 +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,
|
rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS,
|
||||||
&path, 1);
|
&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) {
|
if (!rc) {
|
||||||
file_present = true;
|
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);
|
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)
|
static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
|
||||||
{
|
{
|
||||||
if (fp->f_state != FP_INITED)
|
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);
|
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
|
||||||
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
|
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
|
||||||
void ksmbd_inode_put(struct ksmbd_inode *ci);
|
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_global_fd(unsigned long long id);
|
||||||
struct ksmbd_file *ksmbd_lookup_durable_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);
|
void ksmbd_put_durable_fd(struct ksmbd_file *fp);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user