[PATCH 1/3] smb/client: mark missing nlink values as unknown

Ze Tan <[email protected]>
Newsgroups org.kernel.vger.linux-cifs
Message-ID <d379aa40574b89ecbb0ae6683441b2b422c2ceca.1786427463.git.tanze@kylinos.cn>
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 //<server>/<share> /mnt/cifs \
        -o username=<user>,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 <[email protected]>
---
 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);
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.