Skip to content

Commit 45e7240

Browse files
Paulo AlcantaraSteve French
authored andcommitted
smb: client: set correct file type from NFS reparse points
Handle all file types in NFS reparse points as specified in MS-FSCC 2.1.2.6 Network File System (NFS) Reparse Data Buffer. The client is now able to set all file types based on the parsed NFS reparse point, which used to support only symlinks. This works for SMB1+. Before patch: $ mount.cifs //srv/share /mnt -o ... $ ls -l /mnt ls: cannot access 'block': Operation not supported ls: cannot access 'char': Operation not supported ls: cannot access 'fifo': Operation not supported ls: cannot access 'sock': Operation not supported total 1 l????????? ? ? ? ? ? block l????????? ? ? ? ? ? char -rwxr-xr-x 1 root root 5 Nov 18 23:22 f0 l????????? ? ? ? ? ? fifo l--------- 1 root root 0 Nov 18 23:23 link -> f0 l????????? ? ? ? ? ? sock After patch: $ mount.cifs //srv/share /mnt -o ... $ ls -l /mnt total 1 brwxr-xr-x 1 root root 123, 123 Nov 18 00:34 block crwxr-xr-x 1 root root 1234, 1234 Nov 18 00:33 char -rwxr-xr-x 1 root root 5 Nov 18 23:22 f0 prwxr-xr-x 1 root root 0 Nov 18 23:23 fifo lrwxr-xr-x 1 root root 0 Nov 18 23:23 link -> f0 srwxr-xr-x 1 root root 0 Nov 19 2023 sock Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 539aad7 commit 45e7240

File tree

8 files changed

+116
-61
lines changed

8 files changed

+116
-61
lines changed

fs/smb/client/cifsglob.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,13 @@ struct cifs_open_info_data {
191191
bool reparse_point;
192192
bool symlink;
193193
};
194-
__u32 reparse_tag;
194+
struct {
195+
__u32 tag;
196+
union {
197+
struct reparse_data_buffer *buf;
198+
struct reparse_posix_data *posix;
199+
};
200+
} reparse;
195201
char *symlink_target;
196202
union {
197203
struct smb2_file_all_info fi;

fs/smb/client/cifspdu.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1509,7 +1509,7 @@ struct reparse_posix_data {
15091509
__le16 ReparseDataLength;
15101510
__u16 Reserved;
15111511
__le64 InodeType; /* LNK, FIFO, CHR etc. */
1512-
char PathBuffer[];
1512+
__u8 DataBuffer[];
15131513
} __attribute__((packed));
15141514

15151515
struct cifs_quota_data {

fs/smb/client/cifsproto.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
210210
const struct cifs_fid *fid);
211211
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
212212
struct cifs_fattr *fattr,
213-
u32 tag);
213+
struct cifs_open_info_data *data);
214214
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
215215
struct super_block *sb, unsigned int xid);
216216
extern int cifs_get_inode_info_unix(struct inode **pinode,
@@ -667,7 +667,7 @@ char *extract_hostname(const char *unc);
667667
char *extract_sharename(const char *unc);
668668
int parse_reparse_point(struct reparse_data_buffer *buf,
669669
u32 plen, struct cifs_sb_info *cifs_sb,
670-
bool unicode, char **target_path);
670+
bool unicode, struct cifs_open_info_data *data);
671671

672672
#ifdef CONFIG_CIFS_DFS_UPCALL
673673
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,

fs/smb/client/inode.c

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -721,10 +721,51 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
721721
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
722722
}
723723

724+
static inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
725+
{
726+
u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
727+
728+
return MKDEV(v >> 32, v & 0xffffffff);
729+
}
730+
724731
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
725732
struct cifs_fattr *fattr,
726-
u32 tag)
733+
struct cifs_open_info_data *data)
727734
{
735+
struct reparse_posix_data *buf = data->reparse.posix;
736+
u32 tag = data->reparse.tag;
737+
738+
if (tag == IO_REPARSE_TAG_NFS && buf) {
739+
switch (le64_to_cpu(buf->InodeType)) {
740+
case NFS_SPECFILE_CHR:
741+
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
742+
fattr->cf_dtype = DT_CHR;
743+
fattr->cf_rdev = nfs_mkdev(buf);
744+
break;
745+
case NFS_SPECFILE_BLK:
746+
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
747+
fattr->cf_dtype = DT_BLK;
748+
fattr->cf_rdev = nfs_mkdev(buf);
749+
break;
750+
case NFS_SPECFILE_FIFO:
751+
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
752+
fattr->cf_dtype = DT_FIFO;
753+
break;
754+
case NFS_SPECFILE_SOCK:
755+
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
756+
fattr->cf_dtype = DT_SOCK;
757+
break;
758+
case NFS_SPECFILE_LNK:
759+
fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
760+
fattr->cf_dtype = DT_LNK;
761+
break;
762+
default:
763+
WARN_ON_ONCE(1);
764+
return false;
765+
}
766+
return true;
767+
}
768+
728769
switch (tag) {
729770
case IO_REPARSE_TAG_LX_SYMLINK:
730771
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
@@ -790,7 +831,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
790831
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
791832

792833
if (cifs_open_data_reparse(data) &&
793-
cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag))
834+
cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
794835
goto out_reparse;
795836

796837
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
@@ -855,7 +896,7 @@ cifs_get_file_info(struct file *filp)
855896
data.adjust_tz = false;
856897
if (data.symlink_target) {
857898
data.symlink = true;
858-
data.reparse_tag = IO_REPARSE_TAG_SYMLINK;
899+
data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
859900
}
860901
cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
861902
break;
@@ -1024,7 +1065,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
10241065
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
10251066
struct kvec rsp_iov, *iov = NULL;
10261067
int rsp_buftype = CIFS_NO_BUFFER;
1027-
u32 tag = data->reparse_tag;
1068+
u32 tag = data->reparse.tag;
10281069
int rc = 0;
10291070

10301071
if (!tag && server->ops->query_reparse_point) {
@@ -1036,7 +1077,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
10361077
}
10371078

10381079
rc = -EOPNOTSUPP;
1039-
switch ((data->reparse_tag = tag)) {
1080+
switch ((data->reparse.tag = tag)) {
10401081
case 0: /* SMB1 symlink */
10411082
if (server->ops->query_symlink) {
10421083
rc = server->ops->query_symlink(xid, tcon,

fs/smb/client/readdir.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ static bool reparse_file_needs_reval(const struct cifs_fattr *fattr)
153153
static void
154154
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
155155
{
156+
struct cifs_open_info_data data = {
157+
.reparse = { .tag = fattr->cf_cifstag, },
158+
};
159+
156160
fattr->cf_uid = cifs_sb->ctx->linux_uid;
157161
fattr->cf_gid = cifs_sb->ctx->linux_gid;
158162

@@ -165,7 +169,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
165169
* reasonably map some of them to directories vs. files vs. symlinks
166170
*/
167171
if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
168-
cifs_reparse_point_to_fattr(cifs_sb, fattr, fattr->cf_cifstag))
172+
cifs_reparse_point_to_fattr(cifs_sb, fattr, &data))
169173
goto out_reparse;
170174

171175
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {

fs/smb/client/smb1ops.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,8 +1004,7 @@ static int cifs_parse_reparse_point(struct cifs_sb_info *cifs_sb,
10041004

10051005
buf = (struct reparse_data_buffer *)((__u8 *)&io->hdr.Protocol +
10061006
le32_to_cpu(io->DataOffset));
1007-
return parse_reparse_point(buf, plen, cifs_sb, unicode,
1008-
&data->symlink_target);
1007+
return parse_reparse_point(buf, plen, cifs_sb, unicode, data);
10091008
}
10101009

10111010
static bool

fs/smb/client/smb2inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ static int parse_create_response(struct cifs_open_info_data *data,
555555
break;
556556
}
557557
data->reparse_point = reparse_point;
558-
data->reparse_tag = tag;
558+
data->reparse.tag = tag;
559559
return rc;
560560
}
561561

fs/smb/client/smb2ops.c

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,98 +2866,104 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
28662866
return rc;
28672867
}
28682868

2869-
static int
2870-
parse_reparse_posix(struct reparse_posix_data *symlink_buf,
2871-
u32 plen, char **target_path,
2872-
struct cifs_sb_info *cifs_sb)
2869+
/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
2870+
static int parse_reparse_posix(struct reparse_posix_data *buf,
2871+
struct cifs_sb_info *cifs_sb,
2872+
struct cifs_open_info_data *data)
28732873
{
28742874
unsigned int len;
2875-
2876-
/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
2877-
len = le16_to_cpu(symlink_buf->ReparseDataLength);
2878-
2879-
if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) {
2880-
cifs_dbg(VFS, "%lld not a supported symlink type\n",
2881-
le64_to_cpu(symlink_buf->InodeType));
2875+
u64 type;
2876+
2877+
switch ((type = le64_to_cpu(buf->InodeType))) {
2878+
case NFS_SPECFILE_LNK:
2879+
len = le16_to_cpu(buf->ReparseDataLength);
2880+
data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
2881+
len, true,
2882+
cifs_sb->local_nls);
2883+
if (!data->symlink_target)
2884+
return -ENOMEM;
2885+
convert_delimiter(data->symlink_target, '/');
2886+
cifs_dbg(FYI, "%s: target path: %s\n",
2887+
__func__, data->symlink_target);
2888+
break;
2889+
case NFS_SPECFILE_CHR:
2890+
case NFS_SPECFILE_BLK:
2891+
case NFS_SPECFILE_FIFO:
2892+
case NFS_SPECFILE_SOCK:
2893+
break;
2894+
default:
2895+
cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
2896+
__func__, type);
28822897
return -EOPNOTSUPP;
28832898
}
2884-
2885-
*target_path = cifs_strndup_from_utf16(
2886-
symlink_buf->PathBuffer,
2887-
len, true, cifs_sb->local_nls);
2888-
if (!(*target_path))
2889-
return -ENOMEM;
2890-
2891-
convert_delimiter(*target_path, '/');
2892-
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
2893-
28942899
return 0;
28952900
}
28962901

28972902
static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
2898-
u32 plen, bool unicode, char **target_path,
2899-
struct cifs_sb_info *cifs_sb)
2903+
u32 plen, bool unicode,
2904+
struct cifs_sb_info *cifs_sb,
2905+
struct cifs_open_info_data *data)
29002906
{
2901-
unsigned int sub_len;
2902-
unsigned int sub_offset;
2907+
unsigned int len;
2908+
unsigned int offs;
29032909

29042910
/* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
29052911

2906-
sub_offset = le16_to_cpu(sym->SubstituteNameOffset);
2907-
sub_len = le16_to_cpu(sym->SubstituteNameLength);
2908-
if (sub_offset + 20 > plen ||
2909-
sub_offset + sub_len + 20 > plen) {
2912+
offs = le16_to_cpu(sym->SubstituteNameOffset);
2913+
len = le16_to_cpu(sym->SubstituteNameLength);
2914+
if (offs + 20 > plen || offs + len + 20 > plen) {
29102915
cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
29112916
return -EIO;
29122917
}
29132918

2914-
*target_path = cifs_strndup_from_utf16(sym->PathBuffer + sub_offset,
2915-
sub_len, unicode,
2916-
cifs_sb->local_nls);
2917-
if (!(*target_path))
2919+
data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
2920+
len, unicode,
2921+
cifs_sb->local_nls);
2922+
if (!data->symlink_target)
29182923
return -ENOMEM;
29192924

2920-
convert_delimiter(*target_path, '/');
2921-
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
2925+
convert_delimiter(data->symlink_target, '/');
2926+
cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
29222927

29232928
return 0;
29242929
}
29252930

29262931
int parse_reparse_point(struct reparse_data_buffer *buf,
29272932
u32 plen, struct cifs_sb_info *cifs_sb,
2928-
bool unicode, char **target_path)
2933+
bool unicode, struct cifs_open_info_data *data)
29292934
{
29302935
if (plen < sizeof(*buf)) {
2931-
cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n",
2932-
plen);
2936+
cifs_dbg(VFS, "%s: reparse buffer is too small. Must be at least 8 bytes but was %d\n",
2937+
__func__, plen);
29332938
return -EIO;
29342939
}
29352940

29362941
if (plen < le16_to_cpu(buf->ReparseDataLength) + sizeof(*buf)) {
2937-
cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n",
2938-
plen);
2942+
cifs_dbg(VFS, "%s: invalid reparse buf length: %d\n",
2943+
__func__, plen);
29392944
return -EIO;
29402945
}
29412946

2947+
data->reparse.buf = buf;
2948+
29422949
/* See MS-FSCC 2.1.2 */
29432950
switch (le32_to_cpu(buf->ReparseTag)) {
29442951
case IO_REPARSE_TAG_NFS:
2945-
return parse_reparse_posix(
2946-
(struct reparse_posix_data *)buf,
2947-
plen, target_path, cifs_sb);
2952+
return parse_reparse_posix((struct reparse_posix_data *)buf,
2953+
cifs_sb, data);
29482954
case IO_REPARSE_TAG_SYMLINK:
29492955
return parse_reparse_symlink(
29502956
(struct reparse_symlink_data_buffer *)buf,
2951-
plen, unicode, target_path, cifs_sb);
2957+
plen, unicode, cifs_sb, data);
29522958
case IO_REPARSE_TAG_LX_SYMLINK:
29532959
case IO_REPARSE_TAG_AF_UNIX:
29542960
case IO_REPARSE_TAG_LX_FIFO:
29552961
case IO_REPARSE_TAG_LX_CHR:
29562962
case IO_REPARSE_TAG_LX_BLK:
29572963
return 0;
29582964
default:
2959-
cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n",
2960-
le32_to_cpu(buf->ReparseTag));
2965+
cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
2966+
__func__, le32_to_cpu(buf->ReparseTag));
29612967
return -EOPNOTSUPP;
29622968
}
29632969
}
@@ -2972,8 +2978,7 @@ static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
29722978

29732979
buf = (struct reparse_data_buffer *)((u8 *)io +
29742980
le32_to_cpu(io->OutputOffset));
2975-
return parse_reparse_point(buf, plen, cifs_sb,
2976-
true, &data->symlink_target);
2981+
return parse_reparse_point(buf, plen, cifs_sb, true, data);
29772982
}
29782983

29792984
static int smb2_query_reparse_point(const unsigned int xid,

0 commit comments

Comments
 (0)