Commit 514d793e authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French

smb: client: allow creating symlinks via reparse points

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: default avatarkernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202311260838.nx5mkj1j-lkp@intel.com/Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 5408990a
...@@ -574,6 +574,12 @@ struct smb_version_operations { ...@@ -574,6 +574,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, ...@@ -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, ...@@ -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, ...@@ -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) {
......
...@@ -5247,6 +5247,72 @@ static int nfs_make_node(unsigned int xid, struct inode *inode, ...@@ -5247,6 +5247,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)
...@@ -5323,6 +5389,7 @@ struct smb_version_operations smb20_operations = { ...@@ -5323,6 +5389,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,
...@@ -5425,6 +5492,7 @@ struct smb_version_operations smb21_operations = { ...@@ -5425,6 +5492,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 = { ...@@ -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,
...@@ -5644,6 +5713,7 @@ struct smb_version_operations smb311_operations = { ...@@ -5644,6 +5713,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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment