Skip to content

Commit 10ebde9

Browse files
committed
smb3 client: fix open hardlink on deferred close file error
jira LE-3557 Rebuild_History Non-Buildable kernel-5.14.0-570.26.1.el9_6 commit-author Chunjie Zhu <[email protected]> commit 262b73e 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: [email protected] Signed-off-by: Chunjie Zhu <[email protected]> Signed-off-by: Steve French <[email protected]> (cherry picked from commit 262b73e) Signed-off-by: Jonathan Maple <[email protected]>
1 parent 123e6c2 commit 10ebde9

File tree

2 files changed

+30
-0
lines changed

2 files changed

+30
-0
lines changed

fs/smb/client/cifsproto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
158158
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
159159
extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
160160
struct cifsFileInfo **ret_file);
161+
extern int cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode,
162+
struct file *file);
161163
extern unsigned int smbCalcSize(void *buf);
162164
extern int decode_negTokenInit(unsigned char *security_blob, int length,
163165
struct TCP_Server_Info *server);

fs/smb/client/file.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,11 @@ int cifs_open(struct inode *inode, struct file *file)
694694
} else {
695695
_cifsFileInfo_put(cfile, true, false);
696696
}
697+
} else {
698+
/* hard link on the defeered close file */
699+
rc = cifs_get_hardlink_path(tcon, inode, file);
700+
if (rc)
701+
cifs_close_deferred_file(CIFS_I(inode));
697702
}
698703

699704
if (server->oplocks)
@@ -1754,6 +1759,29 @@ cifs_move_llist(struct list_head *source, struct list_head *dest)
17541759
list_move(li, dest);
17551760
}
17561761

1762+
int
1763+
cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode,
1764+
struct file *file)
1765+
{
1766+
struct cifsFileInfo *open_file = NULL;
1767+
struct cifsInodeInfo *cinode = CIFS_I(inode);
1768+
int rc = 0;
1769+
1770+
spin_lock(&tcon->open_file_lock);
1771+
spin_lock(&cinode->open_file_lock);
1772+
1773+
list_for_each_entry(open_file, &cinode->openFileList, flist) {
1774+
if (file->f_flags == open_file->f_flags) {
1775+
rc = -EINVAL;
1776+
break;
1777+
}
1778+
}
1779+
1780+
spin_unlock(&cinode->open_file_lock);
1781+
spin_unlock(&tcon->open_file_lock);
1782+
return rc;
1783+
}
1784+
17571785
void
17581786
cifs_free_llist(struct list_head *llist)
17591787
{

0 commit comments

Comments
 (0)