From 43549eb84266c424ee6c004f149574e2ae4b6515 Mon Sep 17 00:00:00 2001 From: Ze Tan Date: Tue, 11 Aug 2026 14:00:47 +0800 Subject: [PATCH] smb/client: decode reparse metadata using its payload type 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 Signed-off-by: Namjae Jeon Signed-off-by: Paulo Alcantara --- 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 3a0263df104b..cb5515a50f74 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;