Skip to content

Commit 660618d

Browse files
paliSteve French
authored andcommitted
cifs: Add mount option -o symlink= for choosing symlink create type
Currently Linux CIFS client creates a new symlink of the first flavor which is allowed by mount options, parsed in this order: -o (no)mfsymlinks, -o (no)sfu, -o (no)unix (+ its aliases) and -o reparse=[type]. Introduce a new mount option -o symlink= for explicitly choosing a symlink flavor. Possible options are: -o symlink=default - The default behavior, like before this change. -o symlink=none - Disallow creating a new symlinks -o symlink=native - Create as native SMB symlink reparse point -o symlink=unix - Create via SMB1 unix extension command -o symlink=mfsymlinks - Create as regular file of mfsymlinks format -o symlink=sfu - Create as regular system file of SFU format -o symlink=nfs - Create as NFS reparse point -o symlink=wsl - Create as WSL reparse point So for example specifying -o sfu,mfsymlinks,symlink=native will allow to parse symlinks also of SFU and mfsymlinks types (which are disabled by default unless mount option is explicitly specified), but new symlinks will be created under native SMB type (which parsing is always enabled). Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 12b466e commit 660618d

File tree

8 files changed

+214
-24
lines changed

8 files changed

+214
-24
lines changed

fs/smb/client/cifsfs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
715715
cifs_sb->ctx->backupgid));
716716
seq_show_option(s, "reparse",
717717
cifs_reparse_type_str(cifs_sb->ctx->reparse_type));
718+
seq_show_option(s, "symlink",
719+
cifs_symlink_type_str(get_cifs_symlink_type(cifs_sb)));
718720

719721
seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize);
720722
seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize);

fs/smb/client/cifsglob.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,39 @@ static inline const char *cifs_reparse_type_str(enum cifs_reparse_type type)
177177
}
178178
}
179179

180+
enum cifs_symlink_type {
181+
CIFS_SYMLINK_TYPE_DEFAULT,
182+
CIFS_SYMLINK_TYPE_NONE,
183+
CIFS_SYMLINK_TYPE_NATIVE,
184+
CIFS_SYMLINK_TYPE_UNIX,
185+
CIFS_SYMLINK_TYPE_MFSYMLINKS,
186+
CIFS_SYMLINK_TYPE_SFU,
187+
CIFS_SYMLINK_TYPE_NFS,
188+
CIFS_SYMLINK_TYPE_WSL,
189+
};
190+
191+
static inline const char *cifs_symlink_type_str(enum cifs_symlink_type type)
192+
{
193+
switch (type) {
194+
case CIFS_SYMLINK_TYPE_NONE:
195+
return "none";
196+
case CIFS_SYMLINK_TYPE_NATIVE:
197+
return "native";
198+
case CIFS_SYMLINK_TYPE_UNIX:
199+
return "unix";
200+
case CIFS_SYMLINK_TYPE_MFSYMLINKS:
201+
return "mfsymlinks";
202+
case CIFS_SYMLINK_TYPE_SFU:
203+
return "sfu";
204+
case CIFS_SYMLINK_TYPE_NFS:
205+
return "nfs";
206+
case CIFS_SYMLINK_TYPE_WSL:
207+
return "wsl";
208+
default:
209+
return "unknown";
210+
}
211+
}
212+
180213
struct session_key {
181214
unsigned int len;
182215
char *response;

fs/smb/client/connect.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2849,6 +2849,8 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
28492849
return 0;
28502850
if (old->ctx->reparse_type != new->ctx->reparse_type)
28512851
return 0;
2852+
if (old->ctx->symlink_type != new->ctx->symlink_type)
2853+
return 0;
28522854

28532855
return 1;
28542856
}

fs/smb/client/fs_context.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
185185
fsparam_string("cache", Opt_cache),
186186
fsparam_string("reparse", Opt_reparse),
187187
fsparam_string("upcall_target", Opt_upcalltarget),
188+
fsparam_string("symlink", Opt_symlink),
188189
fsparam_string("symlinkroot", Opt_symlinkroot),
189190

190191
/* Arguments that should be ignored */
@@ -360,6 +361,55 @@ static int parse_reparse_flavor(struct fs_context *fc, char *value,
360361
return 0;
361362
}
362363

364+
static const match_table_t symlink_flavor_tokens = {
365+
{ Opt_symlink_default, "default" },
366+
{ Opt_symlink_none, "none" },
367+
{ Opt_symlink_native, "native" },
368+
{ Opt_symlink_unix, "unix" },
369+
{ Opt_symlink_mfsymlinks, "mfsymlinks" },
370+
{ Opt_symlink_sfu, "sfu" },
371+
{ Opt_symlink_nfs, "nfs" },
372+
{ Opt_symlink_wsl, "wsl" },
373+
{ Opt_symlink_err, NULL },
374+
};
375+
376+
static int parse_symlink_flavor(struct fs_context *fc, char *value,
377+
struct smb3_fs_context *ctx)
378+
{
379+
substring_t args[MAX_OPT_ARGS];
380+
381+
switch (match_token(value, symlink_flavor_tokens, args)) {
382+
case Opt_symlink_default:
383+
ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
384+
break;
385+
case Opt_symlink_none:
386+
ctx->symlink_type = CIFS_SYMLINK_TYPE_NONE;
387+
break;
388+
case Opt_symlink_native:
389+
ctx->symlink_type = CIFS_SYMLINK_TYPE_NATIVE;
390+
break;
391+
case Opt_symlink_unix:
392+
ctx->symlink_type = CIFS_SYMLINK_TYPE_UNIX;
393+
break;
394+
case Opt_symlink_mfsymlinks:
395+
ctx->symlink_type = CIFS_SYMLINK_TYPE_MFSYMLINKS;
396+
break;
397+
case Opt_symlink_sfu:
398+
ctx->symlink_type = CIFS_SYMLINK_TYPE_SFU;
399+
break;
400+
case Opt_symlink_nfs:
401+
ctx->symlink_type = CIFS_SYMLINK_TYPE_NFS;
402+
break;
403+
case Opt_symlink_wsl:
404+
ctx->symlink_type = CIFS_SYMLINK_TYPE_WSL;
405+
break;
406+
default:
407+
cifs_errorf(fc, "bad symlink= option: %s\n", value);
408+
return 1;
409+
}
410+
return 0;
411+
}
412+
363413
#define DUP_CTX_STR(field) \
364414
do { \
365415
if (ctx->field) { \
@@ -1730,6 +1780,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
17301780
if (parse_reparse_flavor(fc, param->string, ctx))
17311781
goto cifs_parse_mount_err;
17321782
break;
1783+
case Opt_symlink:
1784+
if (parse_symlink_flavor(fc, param->string, ctx))
1785+
goto cifs_parse_mount_err;
1786+
break;
17331787
case Opt_symlinkroot:
17341788
if (param->string[0] != '/') {
17351789
cifs_errorf(fc, "symlinkroot mount options must be absolute path\n");
@@ -1765,6 +1819,22 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
17651819
return -EINVAL;
17661820
}
17671821

1822+
enum cifs_symlink_type get_cifs_symlink_type(struct cifs_sb_info *cifs_sb)
1823+
{
1824+
if (cifs_sb->ctx->symlink_type == CIFS_SYMLINK_TYPE_DEFAULT) {
1825+
if (cifs_sb->ctx->mfsymlinks)
1826+
return CIFS_SYMLINK_TYPE_MFSYMLINKS;
1827+
else if (cifs_sb->ctx->sfu_emul)
1828+
return CIFS_SYMLINK_TYPE_SFU;
1829+
else if (cifs_sb->ctx->linux_ext && !cifs_sb->ctx->no_linux_ext)
1830+
return CIFS_SYMLINK_TYPE_UNIX;
1831+
else
1832+
return CIFS_SYMLINK_TYPE_NATIVE;
1833+
} else {
1834+
return cifs_sb->ctx->symlink_type;
1835+
}
1836+
}
1837+
17681838
int smb3_init_fs_context(struct fs_context *fc)
17691839
{
17701840
struct smb3_fs_context *ctx;
@@ -1841,6 +1911,7 @@ int smb3_init_fs_context(struct fs_context *fc)
18411911

18421912
ctx->retrans = 1;
18431913
ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
1914+
ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
18441915

18451916
/*
18461917
* short int override_uid = -1;

fs/smb/client/fs_context.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ enum cifs_reparse_parm {
4848
Opt_reparse_err
4949
};
5050

51+
enum cifs_symlink_parm {
52+
Opt_symlink_default,
53+
Opt_symlink_none,
54+
Opt_symlink_native,
55+
Opt_symlink_unix,
56+
Opt_symlink_mfsymlinks,
57+
Opt_symlink_sfu,
58+
Opt_symlink_nfs,
59+
Opt_symlink_wsl,
60+
Opt_symlink_err
61+
};
62+
5163
enum cifs_sec_param {
5264
Opt_sec_krb5,
5365
Opt_sec_krb5i,
@@ -166,6 +178,7 @@ enum cifs_param {
166178
Opt_cache,
167179
Opt_reparse,
168180
Opt_upcalltarget,
181+
Opt_symlink,
169182
Opt_symlinkroot,
170183

171184
/* Mount options to be ignored */
@@ -295,13 +308,16 @@ struct smb3_fs_context {
295308
struct cifs_ses *dfs_root_ses;
296309
bool dfs_automount:1; /* set for dfs automount only */
297310
enum cifs_reparse_type reparse_type;
311+
enum cifs_symlink_type symlink_type;
298312
bool dfs_conn:1; /* set for dfs mounts */
299313
char *dns_dom;
300314
char *symlinkroot; /* top level directory for native SMB symlinks in absolute format */
301315
};
302316

303317
extern const struct fs_parameter_spec smb3_fs_parameters[];
304318

319+
extern enum cifs_symlink_type get_cifs_symlink_type(struct cifs_sb_info *cifs_sb);
320+
305321
extern int smb3_init_fs_context(struct fs_context *fc);
306322
extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx);
307323
extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx);

fs/smb/client/link.c

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "cifs_unicode.h"
1919
#include "smb2proto.h"
2020
#include "cifs_ioctl.h"
21+
#include "fs_context.h"
2122

2223
/*
2324
* M-F Symlink Functions - Begin
@@ -604,22 +605,53 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
604605
cifs_dbg(FYI, "symname is %s\n", symname);
605606

606607
/* BB what if DFS and this volume is on different share? BB */
607-
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
608-
rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
609-
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
610-
rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
611-
full_path, S_IFLNK, 0, symname);
608+
rc = -EOPNOTSUPP;
609+
switch (get_cifs_symlink_type(cifs_sb)) {
610+
case CIFS_SYMLINK_TYPE_DEFAULT:
611+
/* should not happen, get_cifs_symlink_type() resolves the default */
612+
break;
613+
614+
case CIFS_SYMLINK_TYPE_NONE:
615+
break;
616+
617+
case CIFS_SYMLINK_TYPE_UNIX:
612618
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
613-
} else if (pTcon->unix_ext) {
614-
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
615-
cifs_sb->local_nls,
616-
cifs_remap(cifs_sb));
619+
if (pTcon->unix_ext) {
620+
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path,
621+
symname,
622+
cifs_sb->local_nls,
623+
cifs_remap(cifs_sb));
624+
}
617625
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
618-
} else if (server->ops->create_reparse_symlink) {
619-
rc = server->ops->create_reparse_symlink(xid, inode, direntry,
620-
pTcon, full_path,
621-
symname);
622-
goto symlink_exit;
626+
break;
627+
628+
case CIFS_SYMLINK_TYPE_MFSYMLINKS:
629+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
630+
rc = create_mf_symlink(xid, pTcon, cifs_sb,
631+
full_path, symname);
632+
}
633+
break;
634+
635+
case CIFS_SYMLINK_TYPE_SFU:
636+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
637+
rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
638+
full_path, S_IFLNK,
639+
0, symname);
640+
}
641+
break;
642+
643+
case CIFS_SYMLINK_TYPE_NATIVE:
644+
case CIFS_SYMLINK_TYPE_NFS:
645+
case CIFS_SYMLINK_TYPE_WSL:
646+
if (server->ops->create_reparse_symlink) {
647+
rc = server->ops->create_reparse_symlink(xid, inode,
648+
direntry,
649+
pTcon,
650+
full_path,
651+
symname);
652+
goto symlink_exit;
653+
}
654+
break;
623655
}
624656

625657
if (rc == 0) {

0 commit comments

Comments
 (0)