mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-11 11:55:28 +02:00
smb: client: allow creating symlinks via reparse points
[ Upstream commit 514d793e27
]
Add support for creating symlinks via IO_REPARSE_TAG_SYMLINK reparse
points in SMB2+.
These are fully supported by most SMB servers and documented in
MS-FSCC. Also have the advantage of requiring fewer roundtrips as
their symlink targets can be parsed directly from CREATE responses on
STATUS_STOPPED_ON_SYMLINK errors.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202311260838.nx5mkj1j-lkp@intel.com/
Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
a158bb66b1
commit
00ecebcb86
|
@ -577,6 +577,12 @@ struct smb_version_operations {
|
||||||
int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
|
int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
|
||||||
struct kvec *rsp_iov,
|
struct kvec *rsp_iov,
|
||||||
struct cifs_open_info_data *data);
|
struct cifs_open_info_data *data);
|
||||||
|
int (*create_reparse_symlink)(const unsigned int xid,
|
||||||
|
struct inode *inode,
|
||||||
|
struct dentry *dentry,
|
||||||
|
struct cifs_tcon *tcon,
|
||||||
|
const char *full_path,
|
||||||
|
const char *symname);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values {
|
struct smb_version_values {
|
||||||
|
|
|
@ -569,6 +569,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
|
||||||
int rc = -EOPNOTSUPP;
|
int rc = -EOPNOTSUPP;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifs_tcon *pTcon;
|
struct cifs_tcon *pTcon;
|
||||||
const char *full_path;
|
const char *full_path;
|
||||||
|
@ -590,6 +591,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
|
||||||
goto symlink_exit;
|
goto symlink_exit;
|
||||||
}
|
}
|
||||||
pTcon = tlink_tcon(tlink);
|
pTcon = tlink_tcon(tlink);
|
||||||
|
server = cifs_pick_channel(pTcon->ses);
|
||||||
|
|
||||||
full_path = build_path_from_dentry(direntry, page);
|
full_path = build_path_from_dentry(direntry, page);
|
||||||
if (IS_ERR(full_path)) {
|
if (IS_ERR(full_path)) {
|
||||||
|
@ -601,17 +603,20 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
|
||||||
cifs_dbg(FYI, "symname is %s\n", symname);
|
cifs_dbg(FYI, "symname is %s\n", symname);
|
||||||
|
|
||||||
/* BB what if DFS and this volume is on different share? BB */
|
/* BB what if DFS and this volume is on different share? BB */
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
|
||||||
rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
|
rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
|
||||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||||
else if (pTcon->unix_ext)
|
} else if (pTcon->unix_ext) {
|
||||||
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
|
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
|
||||||
cifs_sb->local_nls,
|
cifs_sb->local_nls,
|
||||||
cifs_remap(cifs_sb));
|
cifs_remap(cifs_sb));
|
||||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||||
/* else
|
} else if (server->ops->create_reparse_symlink) {
|
||||||
rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
|
rc = server->ops->create_reparse_symlink(xid, inode, direntry,
|
||||||
cifs_sb_target->local_nls); */
|
pTcon, full_path,
|
||||||
|
symname);
|
||||||
|
goto symlink_exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
if (pTcon->posix_extensions) {
|
if (pTcon->posix_extensions) {
|
||||||
|
|
|
@ -5246,6 +5246,72 @@ static int nfs_make_node(unsigned int xid, struct inode *inode,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int smb2_create_reparse_symlink(const unsigned int xid,
|
||||||
|
struct inode *inode,
|
||||||
|
struct dentry *dentry,
|
||||||
|
struct cifs_tcon *tcon,
|
||||||
|
const char *full_path,
|
||||||
|
const char *symname)
|
||||||
|
{
|
||||||
|
struct reparse_symlink_data_buffer *buf = NULL;
|
||||||
|
struct cifs_open_info_data data;
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
|
struct inode *new;
|
||||||
|
struct kvec iov;
|
||||||
|
__le16 *path;
|
||||||
|
char *sym;
|
||||||
|
u16 len, plen;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
sym = kstrdup(symname, GFP_KERNEL);
|
||||||
|
if (!sym)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
data = (struct cifs_open_info_data) {
|
||||||
|
.reparse_point = true,
|
||||||
|
.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
|
||||||
|
.symlink_target = sym,
|
||||||
|
};
|
||||||
|
|
||||||
|
path = cifs_convert_path_to_utf16(symname, cifs_sb);
|
||||||
|
if (!path) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
|
||||||
|
len = sizeof(*buf) + plen * 2;
|
||||||
|
buf = kzalloc(len, GFP_KERNEL);
|
||||||
|
if (!buf) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
|
||||||
|
buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
|
||||||
|
buf->SubstituteNameOffset = cpu_to_le16(plen);
|
||||||
|
buf->SubstituteNameLength = cpu_to_le16(plen);
|
||||||
|
memcpy(&buf->PathBuffer[plen], path, plen);
|
||||||
|
buf->PrintNameOffset = 0;
|
||||||
|
buf->PrintNameLength = cpu_to_le16(plen);
|
||||||
|
memcpy(buf->PathBuffer, path, plen);
|
||||||
|
buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
|
||||||
|
|
||||||
|
iov.iov_base = buf;
|
||||||
|
iov.iov_len = len;
|
||||||
|
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
|
||||||
|
tcon, full_path, &iov);
|
||||||
|
if (!IS_ERR(new))
|
||||||
|
d_instantiate(dentry, new);
|
||||||
|
else
|
||||||
|
rc = PTR_ERR(new);
|
||||||
|
out:
|
||||||
|
kfree(path);
|
||||||
|
cifs_free_open_info(&data);
|
||||||
|
kfree(buf);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static int smb2_make_node(unsigned int xid, struct inode *inode,
|
static int smb2_make_node(unsigned int xid, struct inode *inode,
|
||||||
struct dentry *dentry, struct cifs_tcon *tcon,
|
struct dentry *dentry, struct cifs_tcon *tcon,
|
||||||
const char *full_path, umode_t mode, dev_t dev)
|
const char *full_path, umode_t mode, dev_t dev)
|
||||||
|
@ -5322,6 +5388,7 @@ struct smb_version_operations smb20_operations = {
|
||||||
.parse_reparse_point = smb2_parse_reparse_point,
|
.parse_reparse_point = smb2_parse_reparse_point,
|
||||||
.query_mf_symlink = smb3_query_mf_symlink,
|
.query_mf_symlink = smb3_query_mf_symlink,
|
||||||
.create_mf_symlink = smb3_create_mf_symlink,
|
.create_mf_symlink = smb3_create_mf_symlink,
|
||||||
|
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||||
.open = smb2_open_file,
|
.open = smb2_open_file,
|
||||||
.set_fid = smb2_set_fid,
|
.set_fid = smb2_set_fid,
|
||||||
.close = smb2_close_file,
|
.close = smb2_close_file,
|
||||||
|
@ -5424,6 +5491,7 @@ struct smb_version_operations smb21_operations = {
|
||||||
.parse_reparse_point = smb2_parse_reparse_point,
|
.parse_reparse_point = smb2_parse_reparse_point,
|
||||||
.query_mf_symlink = smb3_query_mf_symlink,
|
.query_mf_symlink = smb3_query_mf_symlink,
|
||||||
.create_mf_symlink = smb3_create_mf_symlink,
|
.create_mf_symlink = smb3_create_mf_symlink,
|
||||||
|
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||||
.open = smb2_open_file,
|
.open = smb2_open_file,
|
||||||
.set_fid = smb2_set_fid,
|
.set_fid = smb2_set_fid,
|
||||||
.close = smb2_close_file,
|
.close = smb2_close_file,
|
||||||
|
@ -5530,6 +5598,7 @@ struct smb_version_operations smb30_operations = {
|
||||||
.parse_reparse_point = smb2_parse_reparse_point,
|
.parse_reparse_point = smb2_parse_reparse_point,
|
||||||
.query_mf_symlink = smb3_query_mf_symlink,
|
.query_mf_symlink = smb3_query_mf_symlink,
|
||||||
.create_mf_symlink = smb3_create_mf_symlink,
|
.create_mf_symlink = smb3_create_mf_symlink,
|
||||||
|
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||||
.open = smb2_open_file,
|
.open = smb2_open_file,
|
||||||
.set_fid = smb2_set_fid,
|
.set_fid = smb2_set_fid,
|
||||||
.close = smb2_close_file,
|
.close = smb2_close_file,
|
||||||
|
@ -5645,6 +5714,7 @@ struct smb_version_operations smb311_operations = {
|
||||||
.parse_reparse_point = smb2_parse_reparse_point,
|
.parse_reparse_point = smb2_parse_reparse_point,
|
||||||
.query_mf_symlink = smb3_query_mf_symlink,
|
.query_mf_symlink = smb3_query_mf_symlink,
|
||||||
.create_mf_symlink = smb3_create_mf_symlink,
|
.create_mf_symlink = smb3_create_mf_symlink,
|
||||||
|
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||||
.open = smb2_open_file,
|
.open = smb2_open_file,
|
||||||
.set_fid = smb2_set_fid,
|
.set_fid = smb2_set_fid,
|
||||||
.close = smb2_close_file,
|
.close = smb2_close_file,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user