Skip to content

Commit 5f71ebc

Browse files
Paulo AlcantaraSteve French
authored andcommitted
smb: client: parse reparse point flag in create response
Check for reparse point flag on query info calls as specified in MS-SMB2 2.2.14. Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 348a04a commit 5f71ebc

File tree

5 files changed

+174
-95
lines changed

5 files changed

+174
-95
lines changed

fs/smb/client/cifsglob.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ struct cifs_open_info_data {
199199
};
200200
};
201201

202+
#define cifs_open_data_reparse(d) \
203+
((d)->reparse_point || \
204+
(le32_to_cpu((d)->fi.Attributes) & ATTR_REPARSE))
205+
202206
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
203207
{
204208
kfree(data->symlink_target);

fs/smb/client/cifsproto.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ extern struct inode *cifs_iget(struct super_block *sb,
207207
int cifs_get_inode_info(struct inode **inode, const char *full_path,
208208
struct cifs_open_info_data *data, struct super_block *sb, int xid,
209209
const struct cifs_fid *fid);
210+
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
211+
struct cifs_fattr *fattr,
212+
u32 tag);
210213
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
211214
struct super_block *sb, unsigned int xid);
212215
extern int cifs_get_inode_info_unix(struct inode **pinode,

fs/smb/client/inode.c

Lines changed: 83 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -687,14 +687,50 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
687687
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
688688
}
689689

690+
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
691+
struct cifs_fattr *fattr,
692+
u32 tag)
693+
{
694+
switch (tag) {
695+
case IO_REPARSE_TAG_LX_SYMLINK:
696+
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
697+
fattr->cf_dtype = DT_LNK;
698+
break;
699+
case IO_REPARSE_TAG_LX_FIFO:
700+
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
701+
fattr->cf_dtype = DT_FIFO;
702+
break;
703+
case IO_REPARSE_TAG_AF_UNIX:
704+
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
705+
fattr->cf_dtype = DT_SOCK;
706+
break;
707+
case IO_REPARSE_TAG_LX_CHR:
708+
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
709+
fattr->cf_dtype = DT_CHR;
710+
break;
711+
case IO_REPARSE_TAG_LX_BLK:
712+
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
713+
fattr->cf_dtype = DT_BLK;
714+
break;
715+
case 0: /* SMB1 symlink */
716+
case IO_REPARSE_TAG_SYMLINK:
717+
case IO_REPARSE_TAG_NFS:
718+
fattr->cf_mode = S_IFLNK;
719+
fattr->cf_dtype = DT_LNK;
720+
break;
721+
default:
722+
return false;
723+
}
724+
return true;
725+
}
726+
690727
static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
691728
struct cifs_open_info_data *data,
692729
struct super_block *sb)
693730
{
694731
struct smb2_file_all_info *info = &data->fi;
695732
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
696733
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
697-
u32 reparse_tag = data->reparse_tag;
698734

699735
memset(fattr, 0, sizeof(*fattr));
700736
fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
@@ -717,28 +753,13 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
717753
fattr->cf_eof = le64_to_cpu(info->EndOfFile);
718754
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
719755
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
720-
721756
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
722-
if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) {
723-
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
724-
fattr->cf_dtype = DT_LNK;
725-
} else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) {
726-
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
727-
fattr->cf_dtype = DT_FIFO;
728-
} else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) {
729-
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
730-
fattr->cf_dtype = DT_SOCK;
731-
} else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) {
732-
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
733-
fattr->cf_dtype = DT_CHR;
734-
} else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) {
735-
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
736-
fattr->cf_dtype = DT_BLK;
737-
} else if (data->symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK ||
738-
reparse_tag == IO_REPARSE_TAG_NFS) {
739-
fattr->cf_mode = S_IFLNK;
740-
fattr->cf_dtype = DT_LNK;
741-
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
757+
758+
if (cifs_open_data_reparse(data) &&
759+
cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag))
760+
goto out_reparse;
761+
762+
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
742763
fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
743764
fattr->cf_dtype = DT_DIR;
744765
/*
@@ -767,6 +788,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
767788
}
768789
}
769790

791+
out_reparse:
770792
if (S_ISLNK(fattr->cf_mode)) {
771793
fattr->cf_symlink_target = data->symlink_target;
772794
data->symlink_target = NULL;
@@ -957,6 +979,40 @@ static inline bool is_inode_cache_good(struct inode *ino)
957979
return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0;
958980
}
959981

982+
static int query_reparse(struct cifs_open_info_data *data,
983+
struct super_block *sb,
984+
const unsigned int xid,
985+
struct cifs_tcon *tcon,
986+
const char *full_path,
987+
struct cifs_fattr *fattr)
988+
{
989+
struct TCP_Server_Info *server = tcon->ses->server;
990+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
991+
bool reparse_point = data->reparse_point;
992+
u32 tag = data->reparse_tag;
993+
int rc = 0;
994+
995+
if (!tag && server->ops->query_reparse_tag) {
996+
server->ops->query_reparse_tag(xid, tcon, cifs_sb,
997+
full_path, &tag);
998+
}
999+
switch ((data->reparse_tag = tag)) {
1000+
case 0: /* SMB1 symlink */
1001+
reparse_point = false;
1002+
fallthrough;
1003+
case IO_REPARSE_TAG_NFS:
1004+
case IO_REPARSE_TAG_SYMLINK:
1005+
if (!data->symlink_target && server->ops->query_symlink) {
1006+
rc = server->ops->query_symlink(xid, tcon,
1007+
cifs_sb, full_path,
1008+
&data->symlink_target,
1009+
reparse_point);
1010+
}
1011+
break;
1012+
}
1013+
return rc;
1014+
}
1015+
9601016
int cifs_get_inode_info(struct inode **inode, const char *full_path,
9611017
struct cifs_open_info_data *data, struct super_block *sb, int xid,
9621018
const struct cifs_fid *fid)
@@ -1002,23 +1058,12 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
10021058
* since we have to check if its reparse tag matches a known
10031059
* special file type e.g. symlink or fifo or char etc.
10041060
*/
1005-
if (data->reparse_point && data->symlink_target) {
1006-
data->reparse_tag = IO_REPARSE_TAG_SYMLINK;
1007-
} else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) &&
1008-
server->ops->query_reparse_tag) {
1009-
tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path,
1010-
&data->reparse_tag);
1011-
cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc);
1012-
if (server->ops->query_symlink) {
1013-
tmprc = server->ops->query_symlink(xid, tcon, cifs_sb,
1014-
full_path,
1015-
&data->symlink_target,
1016-
data->reparse_point);
1017-
cifs_dbg(FYI, "%s: query_symlink: rc = %d\n",
1018-
__func__, tmprc);
1019-
}
1061+
if (cifs_open_data_reparse(data)) {
1062+
rc = query_reparse(data, sb, xid, tcon,
1063+
full_path, &fattr);
10201064
}
1021-
cifs_open_info_to_fattr(&fattr, data, sb);
1065+
if (!rc)
1066+
cifs_open_info_to_fattr(&fattr, data, sb);
10221067
break;
10231068
case -EREMOTE:
10241069
/* DFS link, no metadata available on this server */

fs/smb/client/readdir.c

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -163,29 +163,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
163163
* TODO: go through all documented reparse tags to see if we can
164164
* reasonably map some of them to directories vs. files vs. symlinks
165165
*/
166+
if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
167+
cifs_reparse_point_to_fattr(cifs_sb, fattr, fattr->cf_cifstag))
168+
goto out_reparse;
169+
166170
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
167171
fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
168172
fattr->cf_dtype = DT_DIR;
169-
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) {
170-
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
171-
fattr->cf_dtype = DT_LNK;
172-
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) {
173-
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
174-
fattr->cf_dtype = DT_FIFO;
175-
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) {
176-
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
177-
fattr->cf_dtype = DT_SOCK;
178-
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) {
179-
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
180-
fattr->cf_dtype = DT_CHR;
181-
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) {
182-
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
183-
fattr->cf_dtype = DT_BLK;
184-
} else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */
173+
} else {
185174
fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
186175
fattr->cf_dtype = DT_REG;
187176
}
188177

178+
out_reparse:
189179
/*
190180
* We need to revalidate it further to make a decision about whether it
191181
* is a symbolic link, DFS referral or a reparse point with a direct

fs/smb/client/smb2inode.c

Lines changed: 78 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,33 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
542542
return rc;
543543
}
544544

545+
static int parse_create_response(struct cifs_open_info_data *data,
546+
struct cifs_sb_info *cifs_sb,
547+
const struct kvec *iov)
548+
{
549+
struct smb2_create_rsp *rsp = iov->iov_base;
550+
bool reparse_point = false;
551+
u32 tag = 0;
552+
int rc = 0;
553+
554+
switch (rsp->hdr.Status) {
555+
case STATUS_STOPPED_ON_SYMLINK:
556+
rc = smb2_parse_symlink_response(cifs_sb, iov,
557+
&data->symlink_target);
558+
if (rc)
559+
return rc;
560+
tag = IO_REPARSE_TAG_SYMLINK;
561+
reparse_point = true;
562+
break;
563+
case STATUS_SUCCESS:
564+
reparse_point = !!(rsp->Flags & SMB2_CREATE_FLAG_REPARSEPOINT);
565+
break;
566+
}
567+
data->reparse_point = reparse_point;
568+
data->reparse_tag = tag;
569+
return rc;
570+
}
571+
545572
int smb2_query_path_info(const unsigned int xid,
546573
struct cifs_tcon *tcon,
547574
struct cifs_sb_info *cifs_sb,
@@ -551,6 +578,7 @@ int smb2_query_path_info(const unsigned int xid,
551578
__u32 create_options = 0;
552579
struct cifsFileInfo *cfile;
553580
struct cached_fid *cfid = NULL;
581+
struct smb2_hdr *hdr;
554582
struct kvec out_iov[3] = {};
555583
int out_buftype[3] = {};
556584
bool islink;
@@ -579,39 +607,43 @@ int smb2_query_path_info(const unsigned int xid,
579607
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
580608
create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile,
581609
NULL, NULL, out_iov, out_buftype);
582-
if (rc) {
583-
struct smb2_hdr *hdr = out_iov[0].iov_base;
584-
585-
if (unlikely(!hdr || out_buftype[0] == CIFS_NO_BUFFER))
610+
hdr = out_iov[0].iov_base;
611+
/*
612+
* If first iov is unset, then SMB session was dropped or we've got a
613+
* cached open file (@cfile).
614+
*/
615+
if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
616+
goto out;
617+
618+
switch (rc) {
619+
case 0:
620+
case -EOPNOTSUPP:
621+
rc = parse_create_response(data, cifs_sb, &out_iov[0]);
622+
if (rc || !data->reparse_point)
586623
goto out;
587-
if (rc == -EOPNOTSUPP && hdr->Command == SMB2_CREATE &&
588-
hdr->Status == STATUS_STOPPED_ON_SYMLINK) {
589-
rc = smb2_parse_symlink_response(cifs_sb, out_iov,
590-
&data->symlink_target);
591-
if (rc)
592-
goto out;
593-
594-
data->reparse_point = true;
595-
create_options |= OPEN_REPARSE_POINT;
596-
597-
/* Failed on a symbolic link - query a reparse point info */
598-
cifs_get_readable_path(tcon, full_path, &cfile);
599-
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
600-
FILE_READ_ATTRIBUTES, FILE_OPEN,
601-
create_options, ACL_NO_MODE, data,
602-
SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
603-
NULL, NULL);
624+
625+
create_options |= OPEN_REPARSE_POINT;
626+
/* Failed on a symbolic link - query a reparse point info */
627+
cifs_get_readable_path(tcon, full_path, &cfile);
628+
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
629+
FILE_READ_ATTRIBUTES, FILE_OPEN,
630+
create_options, ACL_NO_MODE, data,
631+
SMB2_OP_QUERY_INFO, cfile, NULL, NULL,
632+
NULL, NULL);
633+
break;
634+
case -EREMOTE:
635+
break;
636+
default:
637+
if (hdr->Status != STATUS_OBJECT_NAME_INVALID)
638+
break;
639+
rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
640+
full_path, &islink);
641+
if (rc2) {
642+
rc = rc2;
604643
goto out;
605-
} else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) {
606-
rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb,
607-
full_path, &islink);
608-
if (rc2) {
609-
rc = rc2;
610-
goto out;
611-
}
612-
if (islink)
613-
rc = -EREMOTE;
614644
}
645+
if (islink)
646+
rc = -EREMOTE;
615647
}
616648

617649
out:
@@ -653,26 +685,32 @@ int smb311_posix_query_path_info(const unsigned int xid,
653685
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
654686
create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile,
655687
&sidsbuf, &sidsbuflen, out_iov, out_buftype);
656-
if (rc == -EOPNOTSUPP) {
688+
/*
689+
* If first iov is unset, then SMB session was dropped or we've got a
690+
* cached open file (@cfile).
691+
*/
692+
if (!out_iov[0].iov_base || out_buftype[0] == CIFS_NO_BUFFER)
693+
goto out;
694+
695+
switch (rc) {
696+
case 0:
697+
case -EOPNOTSUPP:
657698
/* BB TODO: When support for special files added to Samba re-verify this path */
658-
if (out_iov[0].iov_base && out_buftype[0] != CIFS_NO_BUFFER &&
659-
((struct smb2_hdr *)out_iov[0].iov_base)->Command == SMB2_CREATE &&
660-
((struct smb2_hdr *)out_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) {
661-
rc = smb2_parse_symlink_response(cifs_sb, out_iov, &data->symlink_target);
662-
if (rc)
663-
goto out;
664-
}
665-
data->reparse_point = true;
666-
create_options |= OPEN_REPARSE_POINT;
699+
rc = parse_create_response(data, cifs_sb, &out_iov[0]);
700+
if (rc || !data->reparse_point)
701+
goto out;
667702

703+
create_options |= OPEN_REPARSE_POINT;
668704
/* Failed on a symbolic link - query a reparse point info */
669705
cifs_get_readable_path(tcon, full_path, &cfile);
670706
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
671707
FILE_OPEN, create_options, ACL_NO_MODE, data,
672708
SMB2_OP_POSIX_QUERY_INFO, cfile,
673709
&sidsbuf, &sidsbuflen, NULL, NULL);
710+
break;
674711
}
675712

713+
out:
676714
if (rc == 0) {
677715
sidsbuf_end = sidsbuf + sidsbuflen;
678716

@@ -692,7 +730,6 @@ int smb311_posix_query_path_info(const unsigned int xid,
692730
memcpy(group, sidsbuf + owner_len, group_len);
693731
}
694732

695-
out:
696733
kfree(sidsbuf);
697734
free_rsp_buf(out_buftype[0], out_iov[0].iov_base);
698735
free_rsp_buf(out_buftype[1], out_iov[1].iov_base);

0 commit comments

Comments
 (0)