mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
smb: client: do not query reparse points twice on symlinks
Save a roundtrip by getting the reparse point tag and buffer at once in ->query_reparse_point() and then pass the buffer down to ->query_symlink(). Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
5f71ebc412
commit
9a49e221a6
4 changed files with 55 additions and 161 deletions
|
@ -336,10 +336,13 @@ struct smb_version_operations {
|
||||||
/* query file data from the server */
|
/* query file data from the server */
|
||||||
int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon,
|
int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
struct cifsFileInfo *cfile, struct cifs_open_info_data *data);
|
struct cifsFileInfo *cfile, struct cifs_open_info_data *data);
|
||||||
/* query reparse tag from srv to determine which type of special file */
|
/* query reparse point to determine which type of special file */
|
||||||
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon,
|
int (*query_reparse_point)(const unsigned int xid,
|
||||||
struct cifs_sb_info *cifs_sb, const char *path,
|
struct cifs_tcon *tcon,
|
||||||
__u32 *reparse_tag);
|
struct cifs_sb_info *cifs_sb,
|
||||||
|
const char *full_path,
|
||||||
|
u32 *tag, struct kvec *rsp,
|
||||||
|
int *rsp_buftype);
|
||||||
/* get server index number */
|
/* get server index number */
|
||||||
int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon,
|
int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid,
|
struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid,
|
||||||
|
@ -388,9 +391,12 @@ struct smb_version_operations {
|
||||||
const char *, const char *,
|
const char *, const char *,
|
||||||
struct cifs_sb_info *);
|
struct cifs_sb_info *);
|
||||||
/* query symlink target */
|
/* query symlink target */
|
||||||
int (*query_symlink)(const unsigned int, struct cifs_tcon *,
|
int (*query_symlink)(const unsigned int xid,
|
||||||
struct cifs_sb_info *, const char *,
|
struct cifs_tcon *tcon,
|
||||||
char **, bool);
|
struct cifs_sb_info *cifs_sb,
|
||||||
|
const char *full_path,
|
||||||
|
char **target_path,
|
||||||
|
struct kvec *rsp_iov);
|
||||||
/* open a file for non-posix mounts */
|
/* open a file for non-posix mounts */
|
||||||
int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
|
int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
|
||||||
void *buf);
|
void *buf);
|
||||||
|
|
|
@ -428,7 +428,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
|
||||||
if (!server->ops->query_symlink)
|
if (!server->ops->query_symlink)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
|
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
|
||||||
&fattr.cf_symlink_target, false);
|
&fattr.cf_symlink_target, NULL);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
|
cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
|
||||||
goto cgiiu_exit;
|
goto cgiiu_exit;
|
||||||
|
@ -988,17 +988,21 @@ static int query_reparse(struct cifs_open_info_data *data,
|
||||||
{
|
{
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||||
bool reparse_point = data->reparse_point;
|
struct kvec rsp_iov, *iov = NULL;
|
||||||
|
int rsp_buftype = CIFS_NO_BUFFER;
|
||||||
u32 tag = data->reparse_tag;
|
u32 tag = data->reparse_tag;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (!tag && server->ops->query_reparse_tag) {
|
if (!tag && server->ops->query_reparse_point) {
|
||||||
server->ops->query_reparse_tag(xid, tcon, cifs_sb,
|
rc = server->ops->query_reparse_point(xid, tcon, cifs_sb,
|
||||||
full_path, &tag);
|
full_path, &tag,
|
||||||
|
&rsp_iov, &rsp_buftype);
|
||||||
|
if (!rc)
|
||||||
|
iov = &rsp_iov;
|
||||||
}
|
}
|
||||||
switch ((data->reparse_tag = tag)) {
|
switch ((data->reparse_tag = tag)) {
|
||||||
case 0: /* SMB1 symlink */
|
case 0: /* SMB1 symlink */
|
||||||
reparse_point = false;
|
iov = NULL;
|
||||||
fallthrough;
|
fallthrough;
|
||||||
case IO_REPARSE_TAG_NFS:
|
case IO_REPARSE_TAG_NFS:
|
||||||
case IO_REPARSE_TAG_SYMLINK:
|
case IO_REPARSE_TAG_SYMLINK:
|
||||||
|
@ -1006,10 +1010,11 @@ static int query_reparse(struct cifs_open_info_data *data,
|
||||||
rc = server->ops->query_symlink(xid, tcon,
|
rc = server->ops->query_symlink(xid, tcon,
|
||||||
cifs_sb, full_path,
|
cifs_sb, full_path,
|
||||||
&data->symlink_target,
|
&data->symlink_target,
|
||||||
reparse_point);
|
iov);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -972,13 +972,16 @@ cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int cifs_query_symlink(const unsigned int xid,
|
||||||
cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
struct cifs_tcon *tcon,
|
||||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
struct cifs_sb_info *cifs_sb,
|
||||||
char **target_path, bool is_reparse_point)
|
const char *full_path,
|
||||||
|
char **target_path,
|
||||||
|
struct kvec *rsp_iov)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
int oplock = 0;
|
int oplock = 0;
|
||||||
|
bool is_reparse_point = !!rsp_iov;
|
||||||
struct cifs_fid fid;
|
struct cifs_fid fid;
|
||||||
struct cifs_open_parms oparms;
|
struct cifs_open_parms oparms;
|
||||||
|
|
||||||
|
|
|
@ -2948,153 +2948,30 @@ parse_reparse_point(struct reparse_data_buffer *buf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int smb2_query_symlink(const unsigned int xid,
|
||||||
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
struct cifs_tcon *tcon,
|
||||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
struct cifs_sb_info *cifs_sb,
|
||||||
char **target_path, bool is_reparse_point)
|
const char *full_path,
|
||||||
|
char **target_path,
|
||||||
|
struct kvec *rsp_iov)
|
||||||
{
|
{
|
||||||
int rc;
|
struct reparse_data_buffer *buf;
|
||||||
__le16 *utf16_path = NULL;
|
struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
|
||||||
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
u32 plen = le32_to_cpu(io->OutputCount);
|
||||||
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 = CIFS_CP_CREATE_CLOSE_OP;
|
|
||||||
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;
|
|
||||||
int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0;
|
|
||||||
u32 plen;
|
|
||||||
|
|
||||||
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
||||||
|
|
||||||
*target_path = NULL;
|
buf = (struct reparse_data_buffer *)((u8 *)io +
|
||||||
|
le32_to_cpu(io->OutputOffset));
|
||||||
if (smb3_encryption_required(tcon))
|
return parse_reparse_point(buf, plen, target_path, cifs_sb);
|
||||||
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;
|
|
||||||
|
|
||||||
/* Open */
|
|
||||||
memset(&open_iov, 0, sizeof(open_iov));
|
|
||||||
rqst[0].rq_iov = open_iov;
|
|
||||||
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
|
|
||||||
|
|
||||||
oparms = (struct cifs_open_parms) {
|
|
||||||
.tcon = tcon,
|
|
||||||
.path = full_path,
|
|
||||||
.desired_access = FILE_READ_ATTRIBUTES,
|
|
||||||
.disposition = FILE_OPEN,
|
|
||||||
.create_options = cifs_create_options(cifs_sb, create_options),
|
|
||||||
.fid = &fid,
|
|
||||||
};
|
|
||||||
|
|
||||||
rc = SMB2_open_init(tcon, server,
|
|
||||||
&rqst[0], &oplock, &oparms, utf16_path);
|
|
||||||
if (rc)
|
|
||||||
goto querty_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, NULL, 0,
|
|
||||||
CIFSMaxBufSize -
|
|
||||||
MAX_SMB2_CREATE_RESPONSE_SIZE -
|
|
||||||
MAX_SMB2_CLOSE_RESPONSE_SIZE);
|
|
||||||
if (rc)
|
|
||||||
goto querty_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 querty_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->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) && (is_reparse_point)) {
|
|
||||||
/* 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(VFS, "srv returned invalid ioctl len: %d\n",
|
|
||||||
plen);
|
|
||||||
rc = -EIO;
|
|
||||||
goto querty_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = parse_reparse_point(reparse_buf, plen, target_path,
|
|
||||||
cifs_sb);
|
|
||||||
goto querty_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rc || !err_iov.iov_base) {
|
|
||||||
rc = -ENOENT;
|
|
||||||
goto querty_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path);
|
|
||||||
|
|
||||||
querty_exit:
|
|
||||||
cifs_dbg(FYI, "query symlink rc %d\n", rc);
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int smb2_query_reparse_point(const unsigned int xid,
|
||||||
smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
|
struct cifs_tcon *tcon,
|
||||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
struct cifs_sb_info *cifs_sb,
|
||||||
__u32 *tag)
|
const char *full_path,
|
||||||
|
u32 *tag, struct kvec *rsp,
|
||||||
|
int *rsp_buftype)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
__le16 *utf16_path = NULL;
|
__le16 *utf16_path = NULL;
|
||||||
|
@ -3205,6 +3082,9 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
goto query_rp_exit;
|
goto query_rp_exit;
|
||||||
}
|
}
|
||||||
*tag = le32_to_cpu(reparse_buf->ReparseTag);
|
*tag = le32_to_cpu(reparse_buf->ReparseTag);
|
||||||
|
*rsp = rsp_iov[1];
|
||||||
|
*rsp_buftype = resp_buftype[1];
|
||||||
|
resp_buftype[1] = CIFS_NO_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_rp_exit:
|
query_rp_exit:
|
||||||
|
@ -5503,7 +5383,7 @@ struct smb_version_operations smb30_operations = {
|
||||||
.echo = SMB2_echo,
|
.echo = SMB2_echo,
|
||||||
.query_path_info = smb2_query_path_info,
|
.query_path_info = smb2_query_path_info,
|
||||||
/* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */
|
/* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */
|
||||||
.query_reparse_tag = smb2_query_reparse_tag,
|
.query_reparse_point = smb2_query_reparse_point,
|
||||||
.get_srv_inum = smb2_get_srv_inum,
|
.get_srv_inum = smb2_get_srv_inum,
|
||||||
.query_file_info = smb2_query_file_info,
|
.query_file_info = smb2_query_file_info,
|
||||||
.set_path_size = smb2_set_path_size,
|
.set_path_size = smb2_set_path_size,
|
||||||
|
@ -5616,7 +5496,7 @@ struct smb_version_operations smb311_operations = {
|
||||||
.can_echo = smb2_can_echo,
|
.can_echo = smb2_can_echo,
|
||||||
.echo = SMB2_echo,
|
.echo = SMB2_echo,
|
||||||
.query_path_info = smb2_query_path_info,
|
.query_path_info = smb2_query_path_info,
|
||||||
.query_reparse_tag = smb2_query_reparse_tag,
|
.query_reparse_point = smb2_query_reparse_point,
|
||||||
.get_srv_inum = smb2_get_srv_inum,
|
.get_srv_inum = smb2_get_srv_inum,
|
||||||
.query_file_info = smb2_query_file_info,
|
.query_file_info = smb2_query_file_info,
|
||||||
.set_path_size = smb2_set_path_size,
|
.set_path_size = smb2_set_path_size,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue