[PATCH v4 3/3] smb/client: fix incorrect nlink returned by fstat()
ChenXiaoSong <[email protected]>
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <[email protected]> |
From: ChenXiaoSong <[email protected]> Reproducer: 1. mount -t cifs //${server_ip}/export /mnt 2. touch /mnt/file1; ln /mnt/file1 /mnt/file2; ln /mnt/file1 /mnt/file3 3. C program: int fd = open("/mnt/file1", O_RDONLY); 4. C program: struct stat stbuf; fstat(fd, &stbuf); stbuf.st_nlink is always 1, should be 3 `cifs_atomic_open()` already obtains the correct nlink. Setting `CIFS_FATTR_UNKNOWN_NLINK` flag in `SMB2_open()` will safely preserve the existing i_nlink in `cifs_nlink_fattr_to_inode()`. Refer to the detailed procedure below: path_openat open_last_lookups lookup_open atomic_open cifs_atomic_open // dir->i_op->atomic_open cifs_lookup cifs_get_inode_info cifs_get_fattr smb2_query_path_info // server->ops->query_path_info smb2_compound_op SMB2_open_init case SMB2_OP_QUERY_INFO SMB2_query_info_init(FILE_ALL_INFORMATION,) cifs_open_info_to_fattr fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks) update_inode_info cifs_iget cifs_fattr_to_inode cifs_nlink_fattr_to_inode set_nlink(inode, fattr->cf_nlink) do_open vfs_open do_dentry_open cifs_open cifs_nt_open smb2_open_file // server->ops->open SMB2_open buf->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK cifs_get_inode_info cifs_get_fattr cifs_open_info_to_fattr fattr->cf_flags |= data->cf_flags // set CIFS_FATTR_UNKNOWN_NLINK update_inode_info cifs_fattr_to_inode cifs_nlink_fattr_to_inode if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) // true return // do not modify nlink Signed-off-by: ChenXiaoSong <[email protected]> --- fs/smb/client/cifsglob.h | 1 + fs/smb/client/inode.c | 1 + fs/smb/client/smb2pdu.c | 1 + 3 files changed, 3 insertions(+) diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index ea1c5eb2356a..08f36e49df2a 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -250,6 +250,7 @@ struct cifs_open_info_data { bool adjust_tz; bool reparse_point; bool contains_posix_file_info; + u32 cf_flags; struct { /* ioctl response buffer */ struct { diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 1dbcfd163ff0..3fe89ab7c651 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -909,6 +909,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); memset(fattr, 0, sizeof(*fattr)); + fattr->cf_flags |= data->cf_flags; fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); if (info->DeletePending) fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 5707d76a8647..720efa60fe70 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3380,6 +3380,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, file_info->EndOfFile = rsp->EndofFile; file_info->Attributes = rsp->FileAttributes; file_info->NumberOfLinks = cpu_to_le32(1); + buf->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; file_info->DeletePending = 0; /* successful open = not delete pending */ } -- 2.54.0