[PATCH 3/3] smb/client: decode reparse metadata using its payload type

Ze Tan <[email protected]>
Newsgroups org.kernel.vger.linux-cifs
Message-ID <7b52e561833274ffb64fc48d49a5da84fb9d90ae.1786427463.git.tanze@kylinos.cn>
cifs_open_info_data stores FILE_ALL_INFORMATION and SMB3 POSIX query
information in a union. reparse_info_to_fattr() selects a union member
from the mount mode, while several directory checks always read
fi.Attributes.

The metadata can instead come from an SMB2 CREATE response on a POSIX
mount, or from a POSIX query while processing a reparse point. In those
cases the mount mode and hard-coded fi accesses select the wrong union
member.

See the procedures below:

  cifs_nt_open
    smb2_open_file
      SMB2_open
        data->fi = SMB2 CREATE response
        data->contains_posix_file_info = false
    cifs_get_inode_info
      reparse_info_to_fattr
        if (tcon->posix_extensions) // true
          smb311_posix_info_to_fattr
            data->posix_fi // wrong union member

  smb311_posix_get_fattr
    smb2_query_path_info
      smb2_compound_op
        data->posix_fi = SMB3 POSIX query response
        data->contains_posix_file_info = true
    reparse_info_to_fattr
      data->fi.Attributes // wrong union member

Add a common DOS attribute accessor and use contains_posix_file_info
both for attribute reads and for the final fattr conversion.

Signed-off-by: Ze Tan <[email protected]>
---
 fs/smb/client/inode.c     |  9 +++++----
 fs/smb/client/reparse.h   | 17 ++++++++++-------
 fs/smb/client/smb2inode.c |  6 ++++--
 3 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 0afff761aab9..e7ef50b74378 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1215,7 +1215,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
 		break;
 	case IO_REPARSE_TAG_INTERNAL:
 		rc = 0;
-		if (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY) {
+		if (cifs_open_data_attrs(data) & ATTR_DIRECTORY) {
 			cifs_create_junction_fattr(fattr, sb);
 			goto out;
 		}
@@ -1239,7 +1239,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
 			 */
 			if (rc == -EOPNOTSUPP &&
 			    IS_REPARSE_TAG_NAME_SURROGATE(data->reparse.tag) &&
-			    (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY)) {
+			    (cifs_open_data_attrs(data) & ATTR_DIRECTORY)) {
 				rc = 0;
 				cifs_create_junction_fattr(fattr, sb);
 				goto out;
@@ -1257,13 +1257,14 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
 		}
 
 		if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
-			bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
+			bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
+
 			rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
 		}
 		break;
 	}
 
-	if (tcon->posix_extensions)
+	if (data->contains_posix_file_info)
 		smb311_posix_info_to_fattr(fattr, data, sb);
 	else
 		cifs_open_info_to_fattr(fattr, data, sb);
diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
index 0164dc47bdfd..49efd85b1e94 100644
--- a/fs/smb/client/reparse.h
+++ b/fs/smb/client/reparse.h
@@ -98,15 +98,21 @@ static inline bool reparse_inode_match(struct inode *inode,
 		timespec64_equal(&ctime, &fattr->cf_ctime);
 }
 
+static inline u32 cifs_open_data_attrs(const struct cifs_open_info_data *data)
+{
+	if (data->contains_posix_file_info)
+		return le32_to_cpu(data->posix_fi.DosAttributes);
+
+	return le32_to_cpu(data->fi.Attributes);
+}
+
 static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
 {
-	u32 attrs;
-	bool ret;
+	u32 attrs = cifs_open_data_attrs(data);
 
 	if (data->contains_posix_file_info) {
 		struct smb311_posix_qinfo *fi = &data->posix_fi;
 
-		attrs = le32_to_cpu(fi->DosAttributes);
 		if (data->reparse_point) {
 			attrs |= ATTR_REPARSE_POINT;
 			fi->DosAttributes = cpu_to_le32(attrs);
@@ -115,16 +121,13 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
 	} else {
 		struct smb2_file_all_info *fi = &data->fi;
 
-		attrs = le32_to_cpu(fi->Attributes);
 		if (data->reparse_point) {
 			attrs |= ATTR_REPARSE_POINT;
 			fi->Attributes = cpu_to_le32(attrs);
 		}
 	}
 
-	ret = attrs & ATTR_REPARSE_POINT;
-
-	return ret;
+	return attrs & ATTR_REPARSE_POINT;
 }
 
 bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 058b05f7a3e5..bcaa44814b71 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -22,6 +22,7 @@
 #include "smb2glob.h"
 #include "smb2proto.h"
 #include "cached_dir.h"
+#include "reparse.h"
 #include "../common/smb2status.h"
 #include "../common/smbfsctl.h"
 
@@ -1002,12 +1003,13 @@ int smb2_query_path_info(const unsigned int xid,
 		/*
 		 * If the symlink was already parsed in create response then it is needed to fix
 		 * its type now (after the second call with OPEN_REPARSE_POINT which filled the
-		 * data->fi.Attributes). If the symlink was not parsed in create response then
+		 * metadata attributes). If the symlink was not parsed in create response then
 		 * the data->symlink_target was not filled yet and then the type will be fixed
 		 * later after data->symlink_target is filled.
 		 */
 		if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) {
-			bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
+			bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
+
 			rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
 		}
 		break;
-- 
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.