Commit d5fc1400 authored by NeilBrown's avatar NeilBrown Committed by Steve French
Browse files

smb/server: avoid deadlock when linking with ReplaceIfExists



If smb2_create_link() is called with ReplaceIfExists set and the name
does exist then a deadlock will happen.

ksmbd_vfs_kern_path_locked() will return with success and the parent
directory will be locked.  ksmbd_vfs_remove_file() will then remove the
file.  ksmbd_vfs_link() will then be called while the parent is still
locked.  It will try to lock the same parent and will deadlock.

This patch moves the ksmbd_vfs_kern_path_unlock() call to *before*
ksmbd_vfs_link() and then simplifies the code, removing the file_present
flag variable.

Signed-off-by: default avatarNeilBrown <neil@brown.name>
Acked-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent a5dc90a9
Loading
Loading
Loading
Loading
+4 −12
Original line number Diff line number Diff line
@@ -6037,7 +6037,6 @@ static int smb2_create_link(struct ksmbd_work *work,
{
	char *link_name = NULL, *target_name = NULL, *pathname = NULL;
	struct path path, parent_path;
	bool file_present = false;
	int rc;

	if (buf_len < (u64)sizeof(struct smb2_file_link_info) +
@@ -6070,11 +6069,8 @@ static int smb2_create_link(struct ksmbd_work *work,
	if (rc) {
		if (rc != -ENOENT)
			goto out;
	} else
		file_present = true;

	} else {
		if (file_info->ReplaceIfExists) {
		if (file_present) {
			rc = ksmbd_vfs_remove_file(work, &path);
			if (rc) {
				rc = -EINVAL;
@@ -6082,21 +6078,17 @@ static int smb2_create_link(struct ksmbd_work *work,
					    link_name);
				goto out;
			}
		}
		} else {
		if (file_present) {
			rc = -EEXIST;
			ksmbd_debug(SMB, "link already exists\n");
			goto out;
		}
		ksmbd_vfs_kern_path_unlock(&parent_path, &path);
	}

	rc = ksmbd_vfs_link(work, target_name, link_name);
	if (rc)
		rc = -EINVAL;
out:
	if (file_present)
		ksmbd_vfs_kern_path_unlock(&parent_path, &path);

	if (!IS_ERR(link_name))
		kfree(link_name);