Skip to content

Commit

Permalink
smb3 client: fix open hardlink on deferred close file error
Browse files Browse the repository at this point in the history
The following Python script results in unexpected behaviour when run on
a CIFS filesystem against a Windows Server:

    # Create file
    fd = os.open('test', os.O_WRONLY|os.O_CREAT)
    os.write(fd, b'foo')
    os.close(fd)

    # Open and close the file to leave a pending deferred close
    fd = os.open('test', os.O_RDONLY|os.O_DIRECT)
    os.close(fd)

    # Try to open the file via a hard link
    os.link('test', 'new')
    newfd = os.open('new', os.O_RDONLY|os.O_DIRECT)

The final open returns EINVAL due to the server returning
STATUS_INVALID_PARAMETER. The root cause of this is that the client
caches lease keys per inode, but the spec requires them to be related to
the filename which causes problems when hard links are involved:

From MS-SMB2 section 3.3.5.9.11:

"The server MUST attempt to locate a Lease by performing a lookup in the
LeaseTable.LeaseList using the LeaseKey in the
SMB2_CREATE_REQUEST_LEASE_V2 as the lookup key. If a lease is found,
Lease.FileDeleteOnClose is FALSE, and Lease.Filename does not match the
file name for the incoming request, the request MUST be failed with
STATUS_INVALID_PARAMETER"

On client side, we first check the context of file open, if it hits above
conditions, we first close all opening files which are belong to the same
inode, then we do open the hard link file.

Cc: stable@vger.kernel.org
Signed-off-by: Chunjie Zhu <chunjie.zhu@cloud.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
Chunjie Zhu authored and Steve French committed Apr 13, 2025
1 parent 8ffd015 commit 262b73e
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 0 deletions.
2 changes: 2 additions & 0 deletions fs/smb/client/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file);
extern int cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode,
struct file *file);
extern unsigned int smbCalcSize(void *buf);
extern int decode_negTokenInit(unsigned char *security_blob, int length,
struct TCP_Server_Info *server);
Expand Down
28 changes: 28 additions & 0 deletions fs/smb/client/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,11 @@ int cifs_open(struct inode *inode, struct file *file)
} else {
_cifsFileInfo_put(cfile, true, false);
}
} else {
/* hard link on the defeered close file */
rc = cifs_get_hardlink_path(tcon, inode, file);
if (rc)
cifs_close_deferred_file(CIFS_I(inode));
}

if (server->oplocks)
Expand Down Expand Up @@ -2071,6 +2076,29 @@ cifs_move_llist(struct list_head *source, struct list_head *dest)
list_move(li, dest);
}

int
cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode,
struct file *file)
{
struct cifsFileInfo *open_file = NULL;
struct cifsInodeInfo *cinode = CIFS_I(inode);
int rc = 0;

spin_lock(&tcon->open_file_lock);
spin_lock(&cinode->open_file_lock);

list_for_each_entry(open_file, &cinode->openFileList, flist) {
if (file->f_flags == open_file->f_flags) {
rc = -EINVAL;
break;
}
}

spin_unlock(&cinode->open_file_lock);
spin_unlock(&tcon->open_file_lock);
return rc;
}

void
cifs_free_llist(struct list_head *llist)
{
Expand Down

0 comments on commit 262b73e

Please sign in to comment.