From ebdc1afb1e268de4c01814fa960286392801b604 Mon Sep 17 00:00:00 2001 From: Ze Tan Date: Tue, 11 Aug 2026 14:00:45 +0800 Subject: [PATCH] smb/client: mark missing nlink values as unknown Several SMB1 fallback and open responses do not provide the hard link count. The SMB2 create-only query fallback has the same limitation. These paths currently leave a zero link count or synthesize a value of one and then expose it as authoritative metadata. Mark those results with unknown_nlink so existing inodes keep their cached link count and new inodes receive the usual sane default. This was tested against Samba with "server min protocol = NT1". Mount the share using SMB1 with Unix extensions disabled: mount -t cifs /// /mnt/cifs \ -o username=,vers=1.0,nounix Create three names for the same inode and cache its real link count: TESTDIR=/mnt/cifs/nlink-repro-$$ mkdir "$TESTDIR" touch "$TESTDIR/file1" ln "$TESTDIR/file1" "$TESTDIR/file2" ln "$TESTDIR/file1" "$TESTDIR/file3" stat -c 'before open: %h' "$TESTDIR/file1" Open the file and read the link count through the open descriptor: exec 3<"$TESTDIR/file1" stat -Lc 'after open: %h' /proc/$$/fd/3 exec 3<&- Clean up the test files: rm -f "$TESTDIR/file1" "$TESTDIR/file2" "$TESTDIR/file3" rmdir "$TESTDIR" Before this change, the two stat commands report 3 and 1 because the SMB1 open response overwrites the known link count. With this change, both commands report 3. Signed-off-by: Ze Tan Signed-off-by: Namjae Jeon Signed-off-by: Paulo Alcantara --- fs/smb/client/smb1ops.c | 8 +++++++- fs/smb/client/smb2inode.c | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index dc5a8c1da623..7e2b29060f51 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -542,6 +542,7 @@ static int cifs_query_path_info(const unsigned int xid, data->reparse_point = false; data->adjust_tz = false; + data->unknown_nlink = false; /* * First try CIFSSMBQPathInfo() function which returns more info @@ -608,6 +609,7 @@ static int cifs_query_path_info(const unsigned int xid, fi.EASize = di->EaSize; } fi.NumberOfLinks = cpu_to_le32(1); + data->unknown_nlink = true; fi.DeletePending = 0; fi.Directory = !!(le32_to_cpu(fi.Attributes) & ATTR_DIRECTORY); cifs_buf_release(search_info.ntwrk_buf_start); @@ -630,6 +632,8 @@ static int cifs_query_path_info(const unsigned int xid, rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls, cifs_remap(cifs_sb)); data->adjust_tz = true; + if (!rc) + data->unknown_nlink = true; } else if ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) { /* Path with non-UNICODE wildcard character cannot exist. */ rc = -ENOENT; @@ -893,8 +897,10 @@ static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms else rc = CIFS_open(xid, oparms, oplock, &fi); - if (!rc && data) + if (!rc && data) { move_cifs_info_to_smb2(&data->fi, &fi); + data->unknown_nlink = true; + } return rc; } diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 213bc298cdf2..d4ae8a5ad463 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -576,6 +576,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, idata->fi.EndOfFile = create_rsp->EndofFile; if (le32_to_cpu(idata->fi.NumberOfLinks) == 0) idata->fi.NumberOfLinks = cpu_to_le32(1); /* dummy value */ + idata->unknown_nlink = true; idata->fi.DeletePending = 0; /* successful open = not delete pending */ idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY);