Commit 660618dd authored by Pali Rohár's avatar Pali Rohár Committed by Steve French
Browse files

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: default avatarPali Rohár <pali@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 12b466eb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -715,6 +715,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
					    cifs_sb->ctx->backupgid));
	seq_show_option(s, "reparse",
			cifs_reparse_type_str(cifs_sb->ctx->reparse_type));
	seq_show_option(s, "symlink",
			cifs_symlink_type_str(get_cifs_symlink_type(cifs_sb)));

	seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize);
	seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize);
+33 −0
Original line number Diff line number Diff line
@@ -177,6 +177,39 @@ static inline const char *cifs_reparse_type_str(enum cifs_reparse_type type)
	}
}

enum cifs_symlink_type {
	CIFS_SYMLINK_TYPE_DEFAULT,
	CIFS_SYMLINK_TYPE_NONE,
	CIFS_SYMLINK_TYPE_NATIVE,
	CIFS_SYMLINK_TYPE_UNIX,
	CIFS_SYMLINK_TYPE_MFSYMLINKS,
	CIFS_SYMLINK_TYPE_SFU,
	CIFS_SYMLINK_TYPE_NFS,
	CIFS_SYMLINK_TYPE_WSL,
};

static inline const char *cifs_symlink_type_str(enum cifs_symlink_type type)
{
	switch (type) {
	case CIFS_SYMLINK_TYPE_NONE:
		return "none";
	case CIFS_SYMLINK_TYPE_NATIVE:
		return "native";
	case CIFS_SYMLINK_TYPE_UNIX:
		return "unix";
	case CIFS_SYMLINK_TYPE_MFSYMLINKS:
		return "mfsymlinks";
	case CIFS_SYMLINK_TYPE_SFU:
		return "sfu";
	case CIFS_SYMLINK_TYPE_NFS:
		return "nfs";
	case CIFS_SYMLINK_TYPE_WSL:
		return "wsl";
	default:
		return "unknown";
	}
}

struct session_key {
	unsigned int len;
	char *response;
+2 −0
Original line number Diff line number Diff line
@@ -2849,6 +2849,8 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
		return 0;
	if (old->ctx->reparse_type != new->ctx->reparse_type)
		return 0;
	if (old->ctx->symlink_type != new->ctx->symlink_type)
		return 0;

	return 1;
}
+71 −0
Original line number Diff line number Diff line
@@ -185,6 +185,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
	fsparam_string("cache", Opt_cache),
	fsparam_string("reparse", Opt_reparse),
	fsparam_string("upcall_target", Opt_upcalltarget),
	fsparam_string("symlink", Opt_symlink),
	fsparam_string("symlinkroot", Opt_symlinkroot),

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

static const match_table_t symlink_flavor_tokens = {
	{ Opt_symlink_default,		"default" },
	{ Opt_symlink_none,		"none" },
	{ Opt_symlink_native,		"native" },
	{ Opt_symlink_unix,		"unix" },
	{ Opt_symlink_mfsymlinks,	"mfsymlinks" },
	{ Opt_symlink_sfu,		"sfu" },
	{ Opt_symlink_nfs,		"nfs" },
	{ Opt_symlink_wsl,		"wsl" },
	{ Opt_symlink_err,		NULL },
};

static int parse_symlink_flavor(struct fs_context *fc, char *value,
				struct smb3_fs_context *ctx)
{
	substring_t args[MAX_OPT_ARGS];

	switch (match_token(value, symlink_flavor_tokens, args)) {
	case Opt_symlink_default:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
		break;
	case Opt_symlink_none:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_NONE;
		break;
	case Opt_symlink_native:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_NATIVE;
		break;
	case Opt_symlink_unix:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_UNIX;
		break;
	case Opt_symlink_mfsymlinks:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_MFSYMLINKS;
		break;
	case Opt_symlink_sfu:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_SFU;
		break;
	case Opt_symlink_nfs:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_NFS;
		break;
	case Opt_symlink_wsl:
		ctx->symlink_type = CIFS_SYMLINK_TYPE_WSL;
		break;
	default:
		cifs_errorf(fc, "bad symlink= option: %s\n", value);
		return 1;
	}
	return 0;
}

#define DUP_CTX_STR(field)						\
do {									\
	if (ctx->field) {						\
@@ -1730,6 +1780,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
		if (parse_reparse_flavor(fc, param->string, ctx))
			goto cifs_parse_mount_err;
		break;
	case Opt_symlink:
		if (parse_symlink_flavor(fc, param->string, ctx))
			goto cifs_parse_mount_err;
		break;
	case Opt_symlinkroot:
		if (param->string[0] != '/') {
			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,
	return -EINVAL;
}

enum cifs_symlink_type get_cifs_symlink_type(struct cifs_sb_info *cifs_sb)
{
	if (cifs_sb->ctx->symlink_type == CIFS_SYMLINK_TYPE_DEFAULT) {
		if (cifs_sb->ctx->mfsymlinks)
			return CIFS_SYMLINK_TYPE_MFSYMLINKS;
		else if (cifs_sb->ctx->sfu_emul)
			return CIFS_SYMLINK_TYPE_SFU;
		else if (cifs_sb->ctx->linux_ext && !cifs_sb->ctx->no_linux_ext)
			return CIFS_SYMLINK_TYPE_UNIX;
		else
			return CIFS_SYMLINK_TYPE_NATIVE;
	} else {
		return cifs_sb->ctx->symlink_type;
	}
}

int smb3_init_fs_context(struct fs_context *fc)
{
	struct smb3_fs_context *ctx;
@@ -1841,6 +1911,7 @@ int smb3_init_fs_context(struct fs_context *fc)

	ctx->retrans = 1;
	ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
	ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;

/*
 *	short int override_uid = -1;
+16 −0
Original line number Diff line number Diff line
@@ -48,6 +48,18 @@ enum cifs_reparse_parm {
	Opt_reparse_err
};

enum cifs_symlink_parm {
	Opt_symlink_default,
	Opt_symlink_none,
	Opt_symlink_native,
	Opt_symlink_unix,
	Opt_symlink_mfsymlinks,
	Opt_symlink_sfu,
	Opt_symlink_nfs,
	Opt_symlink_wsl,
	Opt_symlink_err
};

enum cifs_sec_param {
	Opt_sec_krb5,
	Opt_sec_krb5i,
@@ -166,6 +178,7 @@ enum cifs_param {
	Opt_cache,
	Opt_reparse,
	Opt_upcalltarget,
	Opt_symlink,
	Opt_symlinkroot,

	/* Mount options to be ignored */
@@ -295,6 +308,7 @@ struct smb3_fs_context {
	struct cifs_ses *dfs_root_ses;
	bool dfs_automount:1; /* set for dfs automount only */
	enum cifs_reparse_type reparse_type;
	enum cifs_symlink_type symlink_type;
	bool dfs_conn:1; /* set for dfs mounts */
	char *dns_dom;
	char *symlinkroot; /* top level directory for native SMB symlinks in absolute format */
@@ -302,6 +316,8 @@ struct smb3_fs_context {

extern const struct fs_parameter_spec smb3_fs_parameters[];

extern enum cifs_symlink_type get_cifs_symlink_type(struct cifs_sb_info *cifs_sb);

extern int smb3_init_fs_context(struct fs_context *fc);
extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx);
extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx);
Loading