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 <tanze@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
This commit is contained in:
Ze Tan 2026-08-11 14:00:47 +08:00 committed by Paulo Alcantara
parent 9437f2113b
commit 43549eb842
3 changed files with 19 additions and 13 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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;