forked from Minki/linux
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 <stfrench@microsoft.com> Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
This commit is contained in:
parent
0613ed9190
commit
2e4564b31b
@ -298,6 +298,10 @@ struct smb_version_operations {
|
||||
/* query file data from the server */
|
||||
int (*query_file_info)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifs_fid *, FILE_ALL_INFO *);
|
||||
/* query reparse tag from srv to determine which type of special file */
|
||||
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *path,
|
||||
__u32 *reparse_tag);
|
||||
/* get server index number */
|
||||
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
|
||||
struct cifs_sb_info *, const char *,
|
||||
|
@ -656,7 +656,7 @@ smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *
|
||||
static void
|
||||
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
struct super_block *sb, bool adjust_tz,
|
||||
bool symlink)
|
||||
bool symlink, u32 reparse_tag)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
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,
|
||||
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
|
||||
|
||||
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
|
||||
|
||||
if (symlink) {
|
||||
if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) {
|
||||
fattr->cf_mode |= S_IFLNK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) {
|
||||
fattr->cf_mode |= S_IFIFO | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_dtype = DT_FIFO;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) {
|
||||
fattr->cf_mode |= S_IFSOCK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_dtype = DT_SOCK;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) {
|
||||
fattr->cf_mode |= S_IFCHR | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_dtype = DT_CHR;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) {
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_dtype = DT_BLK;
|
||||
} else if (symlink) { /* TODO add more reparse tag checks */
|
||||
fattr->cf_mode = S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
@ -740,8 +754,9 @@ cifs_get_file_info(struct file *filp)
|
||||
rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
/* TODO: add support to query reparse tag */
|
||||
cifs_all_info_to_fattr(&fattr, &find_data, inode->i_sb, false,
|
||||
false);
|
||||
false, 0 /* no reparse tag */);
|
||||
break;
|
||||
case -EREMOTE:
|
||||
cifs_create_dfs_fattr(&fattr, inode->i_sb);
|
||||
@ -910,12 +925,13 @@ cifs_get_inode_info(struct inode **inode,
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
bool adjust_tz = false;
|
||||
struct cifs_fattr fattr = {0};
|
||||
bool symlink = false;
|
||||
bool is_reparse_point = false;
|
||||
FILE_ALL_INFO *data = in_data;
|
||||
FILE_ALL_INFO *tmp_data = NULL;
|
||||
void *smb1_backup_rsp_buf = NULL;
|
||||
int rc = 0;
|
||||
int tmprc = 0;
|
||||
__u32 reparse_tag = 0;
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
@ -938,8 +954,8 @@ cifs_get_inode_info(struct inode **inode,
|
||||
goto out;
|
||||
}
|
||||
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
|
||||
full_path, tmp_data,
|
||||
&adjust_tz, &symlink);
|
||||
full_path, tmp_data,
|
||||
&adjust_tz, &is_reparse_point);
|
||||
data = tmp_data;
|
||||
}
|
||||
|
||||
@ -949,7 +965,19 @@ cifs_get_inode_info(struct inode **inode,
|
||||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz, symlink);
|
||||
/*
|
||||
* If the file is a reparse point, it is more complicated
|
||||
* since we have to check if its reparse tag matches a known
|
||||
* special file type e.g. symlink or fifo or char etc.
|
||||
*/
|
||||
if ((le32_to_cpu(data->Attributes) & ATTR_REPARSE) &&
|
||||
server->ops->query_reparse_tag) {
|
||||
rc = server->ops->query_reparse_tag(xid, tcon, cifs_sb,
|
||||
full_path, &reparse_tag);
|
||||
cifs_dbg(FYI, "reparse tag 0x%x\n", reparse_tag);
|
||||
}
|
||||
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz,
|
||||
is_reparse_point, reparse_tag);
|
||||
break;
|
||||
case -EREMOTE:
|
||||
/* DFS link, no metadata available on this server */
|
||||
|
@ -506,7 +506,7 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
|
||||
int
|
||||
smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink)
|
||||
FILE_ALL_INFO *data, bool *adjust_tz, bool *reparse)
|
||||
{
|
||||
int rc;
|
||||
struct smb2_file_all_info *smb2_data;
|
||||
@ -516,7 +516,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cached_fid *cfid = NULL;
|
||||
|
||||
*adjust_tz = false;
|
||||
*symlink = false;
|
||||
*reparse = false;
|
||||
|
||||
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
|
||||
GFP_KERNEL);
|
||||
@ -548,7 +548,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
|
||||
ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
*symlink = true;
|
||||
*reparse = true;
|
||||
create_options |= OPEN_REPARSE_POINT;
|
||||
|
||||
/* Failed on a symbolic link - query a reparse point info */
|
||||
@ -570,7 +570,7 @@ out:
|
||||
int
|
||||
smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
struct smb311_posix_qinfo *data, bool *adjust_tz, bool *symlink)
|
||||
struct smb311_posix_qinfo *data, bool *adjust_tz, bool *reparse)
|
||||
{
|
||||
int rc;
|
||||
__u32 create_options = 0;
|
||||
@ -578,7 +578,7 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct smb311_posix_qinfo *smb2_data;
|
||||
|
||||
*adjust_tz = false;
|
||||
*symlink = false;
|
||||
*reparse = false;
|
||||
|
||||
/* BB TODO: Make struct larger when add support for parsing owner SIDs */
|
||||
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,
|
||||
ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
/* BB TODO: When support for special files added to Samba re-verify this path */
|
||||
*symlink = true;
|
||||
*reparse = true;
|
||||
create_options |= OPEN_REPARSE_POINT;
|
||||
|
||||
/* Failed on a symbolic link - query a reparse point info */
|
||||
|
@ -3034,6 +3034,138 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
__u32 *tag)
|
||||
{
|
||||
int rc;
|
||||
__le16 *utf16_path = NULL;
|
||||
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||
struct cifs_open_parms oparms;
|
||||
struct cifs_fid fid;
|
||||
struct kvec err_iov = {NULL, 0};
|
||||
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
|
||||
int flags = 0;
|
||||
struct smb_rqst rqst[3];
|
||||
int resp_buftype[3];
|
||||
struct kvec rsp_iov[3];
|
||||
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
|
||||
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
|
||||
struct kvec close_iov[1];
|
||||
struct smb2_create_rsp *create_rsp;
|
||||
struct smb2_ioctl_rsp *ioctl_rsp;
|
||||
struct reparse_data_buffer *reparse_buf;
|
||||
u32 plen;
|
||||
|
||||
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
||||
|
||||
if (smb3_encryption_required(tcon))
|
||||
flags |= CIFS_TRANSFORM_REQ;
|
||||
|
||||
memset(rqst, 0, sizeof(rqst));
|
||||
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
|
||||
memset(rsp_iov, 0, sizeof(rsp_iov));
|
||||
|
||||
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
||||
if (!utf16_path)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* setup smb2open - TODO add optimization to call cifs_get_readable_path
|
||||
* to see if there is a handle already open that we can use
|
||||
*/
|
||||
memset(&open_iov, 0, sizeof(open_iov));
|
||||
rqst[0].rq_iov = open_iov;
|
||||
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
|
||||
|
||||
memset(&oparms, 0, sizeof(oparms));
|
||||
oparms.tcon = tcon;
|
||||
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
||||
oparms.disposition = FILE_OPEN;
|
||||
oparms.create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT);
|
||||
oparms.fid = &fid;
|
||||
oparms.reconnect = false;
|
||||
|
||||
rc = SMB2_open_init(tcon, server,
|
||||
&rqst[0], &oplock, &oparms, utf16_path);
|
||||
if (rc)
|
||||
goto query_rp_exit;
|
||||
smb2_set_next_command(tcon, &rqst[0]);
|
||||
|
||||
|
||||
/* IOCTL */
|
||||
memset(&io_iov, 0, sizeof(io_iov));
|
||||
rqst[1].rq_iov = io_iov;
|
||||
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
|
||||
|
||||
rc = SMB2_ioctl_init(tcon, server,
|
||||
&rqst[1], fid.persistent_fid,
|
||||
fid.volatile_fid, FSCTL_GET_REPARSE_POINT,
|
||||
true /* is_fctl */, NULL, 0,
|
||||
CIFSMaxBufSize -
|
||||
MAX_SMB2_CREATE_RESPONSE_SIZE -
|
||||
MAX_SMB2_CLOSE_RESPONSE_SIZE);
|
||||
if (rc)
|
||||
goto query_rp_exit;
|
||||
|
||||
smb2_set_next_command(tcon, &rqst[1]);
|
||||
smb2_set_related(&rqst[1]);
|
||||
|
||||
|
||||
/* Close */
|
||||
memset(&close_iov, 0, sizeof(close_iov));
|
||||
rqst[2].rq_iov = close_iov;
|
||||
rqst[2].rq_nvec = 1;
|
||||
|
||||
rc = SMB2_close_init(tcon, server,
|
||||
&rqst[2], COMPOUND_FID, COMPOUND_FID, false);
|
||||
if (rc)
|
||||
goto query_rp_exit;
|
||||
|
||||
smb2_set_related(&rqst[2]);
|
||||
|
||||
rc = compound_send_recv(xid, tcon->ses, server,
|
||||
flags, 3, rqst,
|
||||
resp_buftype, rsp_iov);
|
||||
|
||||
create_rsp = rsp_iov[0].iov_base;
|
||||
if (create_rsp && create_rsp->sync_hdr.Status)
|
||||
err_iov = rsp_iov[0];
|
||||
ioctl_rsp = rsp_iov[1].iov_base;
|
||||
|
||||
/*
|
||||
* Open was successful and we got an ioctl response.
|
||||
*/
|
||||
if (rc == 0) {
|
||||
/* See MS-FSCC 2.3.23 */
|
||||
|
||||
reparse_buf = (struct reparse_data_buffer *)
|
||||
((char *)ioctl_rsp +
|
||||
le32_to_cpu(ioctl_rsp->OutputOffset));
|
||||
plen = le32_to_cpu(ioctl_rsp->OutputCount);
|
||||
|
||||
if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
|
||||
rsp_iov[1].iov_len) {
|
||||
cifs_tcon_dbg(FYI, "srv returned invalid ioctl len: %d\n",
|
||||
plen);
|
||||
rc = -EIO;
|
||||
goto query_rp_exit;
|
||||
}
|
||||
*tag = le32_to_cpu(reparse_buf->ReparseTag);
|
||||
}
|
||||
|
||||
query_rp_exit:
|
||||
kfree(utf16_path);
|
||||
SMB2_open_free(&rqst[0]);
|
||||
SMB2_ioctl_free(&rqst[1]);
|
||||
SMB2_close_free(&rqst[2]);
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct cifs_ntsd *
|
||||
get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
|
||||
const struct cifs_fid *cifsfid, u32 *pacllen)
|
||||
@ -4986,6 +5118,8 @@ struct smb_version_operations smb30_operations = {
|
||||
.can_echo = smb2_can_echo,
|
||||
.echo = SMB2_echo,
|
||||
.query_path_info = smb2_query_path_info,
|
||||
/* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */
|
||||
.query_reparse_tag = smb2_query_reparse_tag,
|
||||
.get_srv_inum = smb2_get_srv_inum,
|
||||
.query_file_info = smb2_query_file_info,
|
||||
.set_path_size = smb2_set_path_size,
|
||||
@ -5097,6 +5231,7 @@ struct smb_version_operations smb311_operations = {
|
||||
.can_echo = smb2_can_echo,
|
||||
.echo = SMB2_echo,
|
||||
.query_path_info = smb2_query_path_info,
|
||||
.query_reparse_tag = smb2_query_reparse_tag,
|
||||
.get_srv_inum = smb2_get_srv_inum,
|
||||
.query_file_info = smb2_query_file_info,
|
||||
.set_path_size = smb2_set_path_size,
|
||||
|
@ -1691,6 +1691,11 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
|
||||
__le64 EndOfFile; /* new end of file value */
|
||||
} __packed; /* level 20 Set */
|
||||
|
||||
struct smb2_file_reparse_point_info {
|
||||
__le64 IndexNumber;
|
||||
__le32 Tag;
|
||||
} __packed;
|
||||
|
||||
struct smb2_file_network_open_info {
|
||||
__le64 CreationTime;
|
||||
__le64 LastAccessTime;
|
||||
|
@ -77,6 +77,9 @@ extern void close_shroot_lease(struct cached_fid *cfid);
|
||||
extern void close_shroot_lease_locked(struct cached_fid *cfid);
|
||||
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
|
||||
struct smb2_file_all_info *src);
|
||||
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *path,
|
||||
__u32 *reparse_tag);
|
||||
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
const char *full_path, FILE_ALL_INFO *data,
|
||||
|
Loading…
Reference in New Issue
Block a user