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);
 
        }
 }
 
+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;
 
                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;
 }
 
        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 */
        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) {                                               \
                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");
        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;
 
        ctx->retrans = 1;
        ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
+       ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
 
 /*
  *     short int override_uid = -1;
 
        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,
        Opt_cache,
        Opt_reparse,
        Opt_upcalltarget,
+       Opt_symlink,
        Opt_symlinkroot,
 
        /* Mount options to be ignored */
        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 */
 
 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);
 
 #include "cifs_unicode.h"
 #include "smb2proto.h"
 #include "cifs_ioctl.h"
+#include "fs_context.h"
 
 /*
  * M-F Symlink Functions - Begin
        cifs_dbg(FYI, "symname is %s\n", symname);
 
        /* BB what if DFS and this volume is on different share? BB */
-       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
-               rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
-       } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
-               rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
-                                         full_path, S_IFLNK, 0, symname);
+       rc = -EOPNOTSUPP;
+       switch (get_cifs_symlink_type(cifs_sb)) {
+       case CIFS_SYMLINK_TYPE_DEFAULT:
+               /* should not happen, get_cifs_symlink_type() resolves the default */
+               break;
+
+       case CIFS_SYMLINK_TYPE_NONE:
+               break;
+
+       case CIFS_SYMLINK_TYPE_UNIX:
 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-       } else if (pTcon->unix_ext) {
-               rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
-                                          cifs_sb->local_nls,
-                                          cifs_remap(cifs_sb));
+               if (pTcon->unix_ext) {
+                       rc = CIFSUnixCreateSymLink(xid, pTcon, full_path,
+                                                  symname,
+                                                  cifs_sb->local_nls,
+                                                  cifs_remap(cifs_sb));
+               }
 #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-       } else if (server->ops->create_reparse_symlink) {
-               rc =  server->ops->create_reparse_symlink(xid, inode, direntry,
-                                                         pTcon, full_path,
-                                                         symname);
-               goto symlink_exit;
+               break;
+
+       case CIFS_SYMLINK_TYPE_MFSYMLINKS:
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
+                       rc = create_mf_symlink(xid, pTcon, cifs_sb,
+                                              full_path, symname);
+               }
+               break;
+
+       case CIFS_SYMLINK_TYPE_SFU:
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+                       rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
+                                                 full_path, S_IFLNK,
+                                                 0, symname);
+               }
+               break;
+
+       case CIFS_SYMLINK_TYPE_NATIVE:
+       case CIFS_SYMLINK_TYPE_NFS:
+       case CIFS_SYMLINK_TYPE_WSL:
+               if (server->ops->create_reparse_symlink) {
+                       rc = server->ops->create_reparse_symlink(xid, inode,
+                                                                direntry,
+                                                                pTcon,
+                                                                full_path,
+                                                                symname);
+                       goto symlink_exit;
+               }
+               break;
        }
 
        if (rc == 0) {
 
 #include "fs_context.h"
 #include "reparse.h"
 
+static int mknod_nfs(unsigned int xid, struct inode *inode,
+                    struct dentry *dentry, struct cifs_tcon *tcon,
+                    const char *full_path, umode_t mode, dev_t dev,
+                    const char *symname);
+
+static int mknod_wsl(unsigned int xid, struct inode *inode,
+                    struct dentry *dentry, struct cifs_tcon *tcon,
+                    const char *full_path, umode_t mode, dev_t dev,
+                    const char *symname);
+
+static int create_native_symlink(const unsigned int xid, struct inode *inode,
+                                struct dentry *dentry, struct cifs_tcon *tcon,
+                                const char *full_path, const char *symname);
+
 static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
                                           const unsigned int xid,
                                           const char *full_path,
 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)
+{
+       switch (get_cifs_symlink_type(CIFS_SB(inode->i_sb))) {
+       case CIFS_SYMLINK_TYPE_NATIVE:
+               return create_native_symlink(xid, inode, dentry, tcon, full_path, symname);
+       case CIFS_SYMLINK_TYPE_NFS:
+               return mknod_nfs(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname);
+       case CIFS_SYMLINK_TYPE_WSL:
+               return mknod_wsl(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int create_native_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 = {};
        case NFS_SPECFILE_SOCK:
                dlen = 0;
                break;
+       case NFS_SPECFILE_LNK: /* TODO: add support for NFS symlinks */
        default:
                return -EOPNOTSUPP;
        }
 
 static int mknod_nfs(unsigned int xid, struct inode *inode,
                     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,
+                    const char *symname)
 {
        struct cifs_open_info_data data;
        struct reparse_nfs_data_buffer *p;
        case IO_REPARSE_TAG_LX_FIFO:
        case IO_REPARSE_TAG_AF_UNIX:
                break;
+       case IO_REPARSE_TAG_LX_SYMLINK: /* TODO: add support for WSL symlinks */
        default:
                return -EOPNOTSUPP;
        }
 
 static int mknod_wsl(unsigned int xid, struct inode *inode,
                     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,
+                    const char *symname)
 {
        struct cifs_open_info_data data;
        struct reparse_data_buffer buf;
                       const char *full_path, umode_t mode, dev_t dev)
 {
        struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
-       int rc = -EOPNOTSUPP;
 
        switch (ctx->reparse_type) {
        case CIFS_REPARSE_TYPE_NFS:
-               rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);
-               break;
+               return mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev, NULL);
        case CIFS_REPARSE_TYPE_WSL:
-               rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);
-               break;
+               return mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev, NULL);
+       default:
+               return -EOPNOTSUPP;
        }
-       return rc;
 }
 
 /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
        return rc;
 }
 
-static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
+static int parse_reparse_native_symlink(struct reparse_symlink_data_buffer *sym,
                                 u32 plen,
                                 struct cifs_sb_info *cifs_sb,
                                 const char *full_path,
                return parse_reparse_nfs((struct reparse_nfs_data_buffer *)buf,
                                           cifs_sb, data);
        case IO_REPARSE_TAG_SYMLINK:
-               return parse_reparse_symlink(
+               return parse_reparse_native_symlink(
                        (struct reparse_symlink_data_buffer *)buf,
                        plen, cifs_sb, full_path, data);
        case IO_REPARSE_TAG_LX_SYMLINK:
 
 static inline u64 reparse_mode_nfs_type(mode_t mode)
 {
        switch (mode & S_IFMT) {
+       case S_IFLNK: return NFS_SPECFILE_LNK;
        case S_IFBLK: return NFS_SPECFILE_BLK;
        case S_IFCHR: return NFS_SPECFILE_CHR;
        case S_IFIFO: return NFS_SPECFILE_FIFO;
 static inline u32 reparse_mode_wsl_tag(mode_t mode)
 {
        switch (mode & S_IFMT) {
+       case S_IFLNK: return IO_REPARSE_TAG_LX_SYMLINK;
        case S_IFBLK: return IO_REPARSE_TAG_LX_BLK;
        case S_IFCHR: return IO_REPARSE_TAG_LX_CHR;
        case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO;