Skip to content

Commit 514d793

Browse files
Paulo Alcantarasmfrench
authored andcommitted
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: 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>
1 parent 5408990 commit 514d793

3 files changed

Lines changed: 86 additions & 5 deletions

File tree

fs/smb/client/cifsglob.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,12 @@ struct smb_version_operations {
574574
int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
575575
struct kvec *rsp_iov,
576576
struct cifs_open_info_data *data);
577+
int (*create_reparse_symlink)(const unsigned int xid,
578+
struct inode *inode,
579+
struct dentry *dentry,
580+
struct cifs_tcon *tcon,
581+
const char *full_path,
582+
const char *symname);
577583
};
578584

579585
struct smb_version_values {

fs/smb/client/link.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
569569
int rc = -EOPNOTSUPP;
570570
unsigned int xid;
571571
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
572+
struct TCP_Server_Info *server;
572573
struct tcon_link *tlink;
573574
struct cifs_tcon *pTcon;
574575
const char *full_path;
@@ -590,6 +591,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
590591
goto symlink_exit;
591592
}
592593
pTcon = tlink_tcon(tlink);
594+
server = cifs_pick_channel(pTcon->ses);
593595

594596
full_path = build_path_from_dentry(direntry, page);
595597
if (IS_ERR(full_path)) {
@@ -601,17 +603,20 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
601603
cifs_dbg(FYI, "symname is %s\n", symname);
602604

603605
/* BB what if DFS and this volume is on different share? BB */
604-
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
606+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
605607
rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
606608
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
607-
else if (pTcon->unix_ext)
609+
} else if (pTcon->unix_ext) {
608610
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
609611
cifs_sb->local_nls,
610612
cifs_remap(cifs_sb));
611613
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
612-
/* else
613-
rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
614-
cifs_sb_target->local_nls); */
614+
} else if (server->ops->create_reparse_symlink) {
615+
rc = server->ops->create_reparse_symlink(xid, inode, direntry,
616+
pTcon, full_path,
617+
symname);
618+
goto symlink_exit;
619+
}
615620

616621
if (rc == 0) {
617622
if (pTcon->posix_extensions) {

fs/smb/client/smb2ops.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5247,6 +5247,72 @@ static int nfs_make_node(unsigned int xid, struct inode *inode,
52475247
return rc;
52485248
}
52495249

5250+
static int smb2_create_reparse_symlink(const unsigned int xid,
5251+
struct inode *inode,
5252+
struct dentry *dentry,
5253+
struct cifs_tcon *tcon,
5254+
const char *full_path,
5255+
const char *symname)
5256+
{
5257+
struct reparse_symlink_data_buffer *buf = NULL;
5258+
struct cifs_open_info_data data;
5259+
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
5260+
struct inode *new;
5261+
struct kvec iov;
5262+
__le16 *path;
5263+
char *sym;
5264+
u16 len, plen;
5265+
int rc = 0;
5266+
5267+
sym = kstrdup(symname, GFP_KERNEL);
5268+
if (!sym)
5269+
return -ENOMEM;
5270+
5271+
data = (struct cifs_open_info_data) {
5272+
.reparse_point = true,
5273+
.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
5274+
.symlink_target = sym,
5275+
};
5276+
5277+
path = cifs_convert_path_to_utf16(symname, cifs_sb);
5278+
if (!path) {
5279+
rc = -ENOMEM;
5280+
goto out;
5281+
}
5282+
5283+
plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
5284+
len = sizeof(*buf) + plen * 2;
5285+
buf = kzalloc(len, GFP_KERNEL);
5286+
if (!buf) {
5287+
rc = -ENOMEM;
5288+
goto out;
5289+
}
5290+
5291+
buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
5292+
buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
5293+
buf->SubstituteNameOffset = cpu_to_le16(plen);
5294+
buf->SubstituteNameLength = cpu_to_le16(plen);
5295+
memcpy(&buf->PathBuffer[plen], path, plen);
5296+
buf->PrintNameOffset = 0;
5297+
buf->PrintNameLength = cpu_to_le16(plen);
5298+
memcpy(buf->PathBuffer, path, plen);
5299+
buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
5300+
5301+
iov.iov_base = buf;
5302+
iov.iov_len = len;
5303+
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
5304+
tcon, full_path, &iov);
5305+
if (!IS_ERR(new))
5306+
d_instantiate(dentry, new);
5307+
else
5308+
rc = PTR_ERR(new);
5309+
out:
5310+
kfree(path);
5311+
cifs_free_open_info(&data);
5312+
kfree(buf);
5313+
return rc;
5314+
}
5315+
52505316
static int smb2_make_node(unsigned int xid, struct inode *inode,
52515317
struct dentry *dentry, struct cifs_tcon *tcon,
52525318
const char *full_path, umode_t mode, dev_t dev)
@@ -5323,6 +5389,7 @@ struct smb_version_operations smb20_operations = {
53235389
.parse_reparse_point = smb2_parse_reparse_point,
53245390
.query_mf_symlink = smb3_query_mf_symlink,
53255391
.create_mf_symlink = smb3_create_mf_symlink,
5392+
.create_reparse_symlink = smb2_create_reparse_symlink,
53265393
.open = smb2_open_file,
53275394
.set_fid = smb2_set_fid,
53285395
.close = smb2_close_file,
@@ -5425,6 +5492,7 @@ struct smb_version_operations smb21_operations = {
54255492
.parse_reparse_point = smb2_parse_reparse_point,
54265493
.query_mf_symlink = smb3_query_mf_symlink,
54275494
.create_mf_symlink = smb3_create_mf_symlink,
5495+
.create_reparse_symlink = smb2_create_reparse_symlink,
54285496
.open = smb2_open_file,
54295497
.set_fid = smb2_set_fid,
54305498
.close = smb2_close_file,
@@ -5530,6 +5598,7 @@ struct smb_version_operations smb30_operations = {
55305598
.parse_reparse_point = smb2_parse_reparse_point,
55315599
.query_mf_symlink = smb3_query_mf_symlink,
55325600
.create_mf_symlink = smb3_create_mf_symlink,
5601+
.create_reparse_symlink = smb2_create_reparse_symlink,
55335602
.open = smb2_open_file,
55345603
.set_fid = smb2_set_fid,
55355604
.close = smb2_close_file,
@@ -5644,6 +5713,7 @@ struct smb_version_operations smb311_operations = {
56445713
.parse_reparse_point = smb2_parse_reparse_point,
56455714
.query_mf_symlink = smb3_query_mf_symlink,
56465715
.create_mf_symlink = smb3_create_mf_symlink,
5716+
.create_reparse_symlink = smb2_create_reparse_symlink,
56475717
.open = smb2_open_file,
56485718
.set_fid = smb2_set_fid,
56495719
.close = smb2_close_file,

0 commit comments

Comments
 (0)