diff options
| author | ChenXiaoSong <chenxiaosong@kylinos.cn> | 2026-07-09 10:57:03 +0800 |
|---|---|---|
| committer | Steve French <stfrench@microsoft.com> | 2026-07-09 07:55:55 -0500 |
| commit | 9dd1964ac59d293c3684e71b4bbcd10e28f04bb4 (patch) | |
| tree | a74ca770ab5833fcfae2618932ea15e46c8b6260 | |
| parent | 8fce4cf4369c766a3293a05419500cbfde72e60d (diff) | |
smb/client: fix incorrect nlink returned by fstat()
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
Setting `unknown_nlink` to true in `SMB2_open()` triggers the
`CIFS_FATTR_UNKNOWN_NLINK` flag in `cifs_open_info_to_fattr()`,
which safely preserves the existing i_nlink in
`cifs_nlink_fattr_to_inode()`.
See 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->unknown_nlink = true
cifs_get_inode_info
cifs_get_fattr
cifs_open_info_to_fattr
if (data->unknown_nlink) // true
fattr->cf_flags |= 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 <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
| -rw-r--r-- | fs/smb/client/cifsglob.h | 1 | ||||
| -rw-r--r-- | fs/smb/client/inode.c | 2 | ||||
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 1 |
3 files changed, 4 insertions, 0 deletions
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 99f9e6dca62b..08e94633a9c1 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; + bool unknown_nlink; struct { /* ioctl response buffer */ struct { diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 2ed1c79c1132..16a3ddff060f 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -909,6 +909,8 @@ 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)); + if (data->unknown_nlink) + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; 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 bc77170458b3..8f83ab377db1 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3380,6 +3380,7 @@ replay_again: file_info->EndOfFile = rsp->EndofFile; file_info->Attributes = rsp->FileAttributes; file_info->NumberOfLinks = cpu_to_le32(1); + buf->unknown_nlink = true; file_info->DeletePending = 0; /* successful open = not delete pending */ } |
