Skip to content

Commit 2e4564b

Browse files
author
Steve French
committed
smb3: add support for stat of WSL reparse points for special file types
This is needed so when mounting to Windows we do not misinterpret various special files created by Linux (WSL) as symlinks. An earlier patch addressed readdir. This patch fixes stat (getattr). With this patch:   File: /mnt1/char   Size: 0          Blocks: 0          IO Block: 16384  character special file Device: 34h/52d Inode: 844424930132069  Links: 1     Device type: 0,0 Access: (0755/crwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root) Access: 2020-10-21 17:46:51.839458900 -0500 Modify: 2020-10-21 17:46:51.839458900 -0500 Change: 2020-10-21 18:30:39.797358800 -0500  Birth: -   File: /mnt1/fifo   Size: 0          Blocks: 0          IO Block: 16384  fifo Device: 34h/52d Inode: 1125899906842722  Links: 1 Access: (0755/prwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root) Access: 2020-10-21 16:21:37.259249700 -0500 Modify: 2020-10-21 16:21:37.259249700 -0500 Change: 2020-10-21 18:30:39.797358800 -0500  Birth: -   File: /mnt1/block   Size: 0          Blocks: 0          IO Block: 16384  block special file Device: 34h/52d Inode: 844424930132068  Links: 1     Device type: 0,0 Access: (0755/brwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root) Access: 2020-10-21 17:10:47.913103200 -0500 Modify: 2020-10-21 17:10:47.913103200 -0500 Change: 2020-10-21 18:30:39.796725500 -0500  Birth: - without the patch all show up incorrectly as symlinks with annoying "operation not supported error also returned"   File: /mnt1/charstat: cannot read symbolic link '/mnt1/char': Operation not supported   Size: 0          Blocks: 0          IO Block: 16384  symbolic link Device: 34h/52d Inode: 844424930132069  Links: 1 Access: (0000/l---------)  Uid: (    0/    root)   Gid: (    0/    root) Access: 2020-10-21 17:46:51.839458900 -0500 Modify: 2020-10-21 17:46:51.839458900 -0500 Change: 2020-10-21 18:30:39.797358800 -0500  Birth: -   File: /mnt1/fifostat: cannot read symbolic link '/mnt1/fifo': Operation not supported   Size: 0          Blocks: 0          IO Block: 16384  symbolic link Device: 34h/52d Inode: 1125899906842722  Links: 1 Access: (0000/l---------)  Uid: (    0/    root)   Gid: (    0/    root) Access: 2020-10-21 16:21:37.259249700 -0500 Modify: 2020-10-21 16:21:37.259249700 -0500 Change: 2020-10-21 18:30:39.797358800 -0500  Birth: -   File: /mnt1/blockstat: cannot read symbolic link '/mnt1/block': Operation not supported   Size: 0          Blocks: 0          IO Block: 16384  symbolic link Device: 34h/52d Inode: 844424930132068  Links: 1 Access: (0000/l---------)  Uid: (    0/    root)   Gid: (    0/    root) Access: 2020-10-21 17:10:47.913103200 -0500 Modify: 2020-10-21 17:10:47.913103200 -0500 Change: 2020-10-21 18:30:39.796725500 -0500 Signed-off-by: Steve French <[email protected]> Reviewed-by: Ronnie Sahlberg <[email protected]>
1 parent 0613ed9 commit 2e4564b

File tree

6 files changed

+189
-14
lines changed

6 files changed

+189
-14
lines changed

fs/cifs/cifsglob.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ struct smb_version_operations {
298298
/* query file data from the server */
299299
int (*query_file_info)(const unsigned int, struct cifs_tcon *,
300300
struct cifs_fid *, FILE_ALL_INFO *);
301+
/* query reparse tag from srv to determine which type of special file */
302+
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon,
303+
struct cifs_sb_info *cifs_sb, const char *path,
304+
__u32 *reparse_tag);
301305
/* get server index number */
302306
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
303307
struct cifs_sb_info *, const char *,

fs/cifs/inode.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *
656656
static void
657657
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
658658
struct super_block *sb, bool adjust_tz,
659-
bool symlink)
659+
bool symlink, u32 reparse_tag)
660660
{
661661
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
662662
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
@@ -684,8 +684,22 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
684684
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
685685

686686
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
687-
688-
if (symlink) {
687+
if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) {
688+
fattr->cf_mode |= S_IFLNK | cifs_sb->mnt_file_mode;
689+
fattr->cf_dtype = DT_LNK;
690+
} else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) {
691+
fattr->cf_mode |= S_IFIFO | cifs_sb->mnt_file_mode;
692+
fattr->cf_dtype = DT_FIFO;
693+
} else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) {
694+
fattr->cf_mode |= S_IFSOCK | cifs_sb->mnt_file_mode;
695+
fattr->cf_dtype = DT_SOCK;
696+
} else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) {
697+
fattr->cf_mode |= S_IFCHR | cifs_sb->mnt_file_mode;
698+
fattr->cf_dtype = DT_CHR;
699+
} else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) {
700+
fattr->cf_mode |= S_IFBLK | cifs_sb->mnt_file_mode;
701+
fattr->cf_dtype = DT_BLK;
702+
} else if (symlink) { /* TODO add more reparse tag checks */
689703
fattr->cf_mode = S_IFLNK;
690704
fattr->cf_dtype = DT_LNK;
691705
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
@@ -740,8 +754,9 @@ cifs_get_file_info(struct file *filp)
740754
rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
741755
switch (rc) {
742756
case 0:
757+
/* TODO: add support to query reparse tag */
743758
cifs_all_info_to_fattr(&fattr, &find_data, inode->i_sb, false,
744-
false);
759+
false, 0 /* no reparse tag */);
745760
break;
746761
case -EREMOTE:
747762
cifs_create_dfs_fattr(&fattr, inode->i_sb);
@@ -910,12 +925,13 @@ cifs_get_inode_info(struct inode **inode,
910925
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
911926
bool adjust_tz = false;
912927
struct cifs_fattr fattr = {0};
913-
bool symlink = false;
928+
bool is_reparse_point = false;
914929
FILE_ALL_INFO *data = in_data;
915930
FILE_ALL_INFO *tmp_data = NULL;
916931
void *smb1_backup_rsp_buf = NULL;
917932
int rc = 0;
918933
int tmprc = 0;
934+
__u32 reparse_tag = 0;
919935

920936
tlink = cifs_sb_tlink(cifs_sb);
921937
if (IS_ERR(tlink))
@@ -938,8 +954,8 @@ cifs_get_inode_info(struct inode **inode,
938954
goto out;
939955
}
940956
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
941-
full_path, tmp_data,
942-
&adjust_tz, &symlink);
957+
full_path, tmp_data,
958+
&adjust_tz, &is_reparse_point);
943959
data = tmp_data;
944960
}
945961

@@ -949,7 +965,19 @@ cifs_get_inode_info(struct inode **inode,
949965

950966
switch (rc) {
951967
case 0:
952-
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz, symlink);
968+
/*
969+
* If the file is a reparse point, it is more complicated
970+
* since we have to check if its reparse tag matches a known
971+
* special file type e.g. symlink or fifo or char etc.
972+
*/
973+
if ((le32_to_cpu(data->Attributes) & ATTR_REPARSE) &&
974+
server->ops->query_reparse_tag) {
975+
rc = server->ops->query_reparse_tag(xid, tcon, cifs_sb,
976+
full_path, &reparse_tag);
977+
cifs_dbg(FYI, "reparse tag 0x%x\n", reparse_tag);
978+
}
979+
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz,
980+
is_reparse_point, reparse_tag);
953981
break;
954982
case -EREMOTE:
955983
/* DFS link, no metadata available on this server */

fs/cifs/smb2inode.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
506506
int
507507
smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
508508
struct cifs_sb_info *cifs_sb, const char *full_path,
509-
FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink)
509+
FILE_ALL_INFO *data, bool *adjust_tz, bool *reparse)
510510
{
511511
int rc;
512512
struct smb2_file_all_info *smb2_data;
@@ -516,7 +516,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
516516
struct cached_fid *cfid = NULL;
517517

518518
*adjust_tz = false;
519-
*symlink = false;
519+
*reparse = false;
520520

521521
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
522522
GFP_KERNEL);
@@ -548,7 +548,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
548548
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
549549
ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile);
550550
if (rc == -EOPNOTSUPP) {
551-
*symlink = true;
551+
*reparse = true;
552552
create_options |= OPEN_REPARSE_POINT;
553553

554554
/* Failed on a symbolic link - query a reparse point info */
@@ -570,15 +570,15 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
570570
int
571571
smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
572572
struct cifs_sb_info *cifs_sb, const char *full_path,
573-
struct smb311_posix_qinfo *data, bool *adjust_tz, bool *symlink)
573+
struct smb311_posix_qinfo *data, bool *adjust_tz, bool *reparse)
574574
{
575575
int rc;
576576
__u32 create_options = 0;
577577
struct cifsFileInfo *cfile;
578578
struct smb311_posix_qinfo *smb2_data;
579579

580580
*adjust_tz = false;
581-
*symlink = false;
581+
*reparse = false;
582582

583583
/* BB TODO: Make struct larger when add support for parsing owner SIDs */
584584
smb2_data = kzalloc(sizeof(struct smb311_posix_qinfo),
@@ -599,7 +599,7 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
599599
ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
600600
if (rc == -EOPNOTSUPP) {
601601
/* BB TODO: When support for special files added to Samba re-verify this path */
602-
*symlink = true;
602+
*reparse = true;
603603
create_options |= OPEN_REPARSE_POINT;
604604

605605
/* Failed on a symbolic link - query a reparse point info */

fs/cifs/smb2ops.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3034,6 +3034,138 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
30343034
return rc;
30353035
}
30363036

3037+
int
3038+
smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
3039+
struct cifs_sb_info *cifs_sb, const char *full_path,
3040+
__u32 *tag)
3041+
{
3042+
int rc;
3043+
__le16 *utf16_path = NULL;
3044+
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
3045+
struct cifs_open_parms oparms;
3046+
struct cifs_fid fid;
3047+
struct kvec err_iov = {NULL, 0};
3048+
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
3049+
int flags = 0;
3050+
struct smb_rqst rqst[3];
3051+
int resp_buftype[3];
3052+
struct kvec rsp_iov[3];
3053+
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
3054+
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
3055+
struct kvec close_iov[1];
3056+
struct smb2_create_rsp *create_rsp;
3057+
struct smb2_ioctl_rsp *ioctl_rsp;
3058+
struct reparse_data_buffer *reparse_buf;
3059+
u32 plen;
3060+
3061+
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
3062+
3063+
if (smb3_encryption_required(tcon))
3064+
flags |= CIFS_TRANSFORM_REQ;
3065+
3066+
memset(rqst, 0, sizeof(rqst));
3067+
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
3068+
memset(rsp_iov, 0, sizeof(rsp_iov));
3069+
3070+
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
3071+
if (!utf16_path)
3072+
return -ENOMEM;
3073+
3074+
/*
3075+
* setup smb2open - TODO add optimization to call cifs_get_readable_path
3076+
* to see if there is a handle already open that we can use
3077+
*/
3078+
memset(&open_iov, 0, sizeof(open_iov));
3079+
rqst[0].rq_iov = open_iov;
3080+
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
3081+
3082+
memset(&oparms, 0, sizeof(oparms));
3083+
oparms.tcon = tcon;
3084+
oparms.desired_access = FILE_READ_ATTRIBUTES;
3085+
oparms.disposition = FILE_OPEN;
3086+
oparms.create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT);
3087+
oparms.fid = &fid;
3088+
oparms.reconnect = false;
3089+
3090+
rc = SMB2_open_init(tcon, server,
3091+
&rqst[0], &oplock, &oparms, utf16_path);
3092+
if (rc)
3093+
goto query_rp_exit;
3094+
smb2_set_next_command(tcon, &rqst[0]);
3095+
3096+
3097+
/* IOCTL */
3098+
memset(&io_iov, 0, sizeof(io_iov));
3099+
rqst[1].rq_iov = io_iov;
3100+
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
3101+
3102+
rc = SMB2_ioctl_init(tcon, server,
3103+
&rqst[1], fid.persistent_fid,
3104+
fid.volatile_fid, FSCTL_GET_REPARSE_POINT,
3105+
true /* is_fctl */, NULL, 0,
3106+
CIFSMaxBufSize -
3107+
MAX_SMB2_CREATE_RESPONSE_SIZE -
3108+
MAX_SMB2_CLOSE_RESPONSE_SIZE);
3109+
if (rc)
3110+
goto query_rp_exit;
3111+
3112+
smb2_set_next_command(tcon, &rqst[1]);
3113+
smb2_set_related(&rqst[1]);
3114+
3115+
3116+
/* Close */
3117+
memset(&close_iov, 0, sizeof(close_iov));
3118+
rqst[2].rq_iov = close_iov;
3119+
rqst[2].rq_nvec = 1;
3120+
3121+
rc = SMB2_close_init(tcon, server,
3122+
&rqst[2], COMPOUND_FID, COMPOUND_FID, false);
3123+
if (rc)
3124+
goto query_rp_exit;
3125+
3126+
smb2_set_related(&rqst[2]);
3127+
3128+
rc = compound_send_recv(xid, tcon->ses, server,
3129+
flags, 3, rqst,
3130+
resp_buftype, rsp_iov);
3131+
3132+
create_rsp = rsp_iov[0].iov_base;
3133+
if (create_rsp && create_rsp->sync_hdr.Status)
3134+
err_iov = rsp_iov[0];
3135+
ioctl_rsp = rsp_iov[1].iov_base;
3136+
3137+
/*
3138+
* Open was successful and we got an ioctl response.
3139+
*/
3140+
if (rc == 0) {
3141+
/* See MS-FSCC 2.3.23 */
3142+
3143+
reparse_buf = (struct reparse_data_buffer *)
3144+
((char *)ioctl_rsp +
3145+
le32_to_cpu(ioctl_rsp->OutputOffset));
3146+
plen = le32_to_cpu(ioctl_rsp->OutputCount);
3147+
3148+
if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
3149+
rsp_iov[1].iov_len) {
3150+
cifs_tcon_dbg(FYI, "srv returned invalid ioctl len: %d\n",
3151+
plen);
3152+
rc = -EIO;
3153+
goto query_rp_exit;
3154+
}
3155+
*tag = le32_to_cpu(reparse_buf->ReparseTag);
3156+
}
3157+
3158+
query_rp_exit:
3159+
kfree(utf16_path);
3160+
SMB2_open_free(&rqst[0]);
3161+
SMB2_ioctl_free(&rqst[1]);
3162+
SMB2_close_free(&rqst[2]);
3163+
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
3164+
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
3165+
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
3166+
return rc;
3167+
}
3168+
30373169
static struct cifs_ntsd *
30383170
get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
30393171
const struct cifs_fid *cifsfid, u32 *pacllen)
@@ -4986,6 +5118,8 @@ struct smb_version_operations smb30_operations = {
49865118
.can_echo = smb2_can_echo,
49875119
.echo = SMB2_echo,
49885120
.query_path_info = smb2_query_path_info,
5121+
/* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */
5122+
.query_reparse_tag = smb2_query_reparse_tag,
49895123
.get_srv_inum = smb2_get_srv_inum,
49905124
.query_file_info = smb2_query_file_info,
49915125
.set_path_size = smb2_set_path_size,
@@ -5097,6 +5231,7 @@ struct smb_version_operations smb311_operations = {
50975231
.can_echo = smb2_can_echo,
50985232
.echo = SMB2_echo,
50995233
.query_path_info = smb2_query_path_info,
5234+
.query_reparse_tag = smb2_query_reparse_tag,
51005235
.get_srv_inum = smb2_get_srv_inum,
51015236
.query_file_info = smb2_query_file_info,
51025237
.set_path_size = smb2_set_path_size,

fs/cifs/smb2pdu.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,6 +1691,11 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
16911691
__le64 EndOfFile; /* new end of file value */
16921692
} __packed; /* level 20 Set */
16931693

1694+
struct smb2_file_reparse_point_info {
1695+
__le64 IndexNumber;
1696+
__le32 Tag;
1697+
} __packed;
1698+
16941699
struct smb2_file_network_open_info {
16951700
__le64 CreationTime;
16961701
__le64 LastAccessTime;

fs/cifs/smb2proto.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ extern void close_shroot_lease(struct cached_fid *cfid);
7777
extern void close_shroot_lease_locked(struct cached_fid *cfid);
7878
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
7979
struct smb2_file_all_info *src);
80+
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
81+
struct cifs_sb_info *cifs_sb, const char *path,
82+
__u32 *reparse_tag);
8083
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
8184
struct cifs_sb_info *cifs_sb,
8285
const char *full_path, FILE_ALL_INFO *data,

0 commit comments

Comments
 (0)