ntfs: add native_symlink mount option

Because bind-mounted subtrees of the volume may resolve to unexpected
locations, change converting junctions and non-relative symbolic links
into paths relative to the NTFS volume to be allowed only if the
native_symlink=rel mount option is specified.

Add the native_symlink=<value> mount option to configure how absolute
symbolic links and mount points (junctions) are handled.
The option accepts "raw" or "rel", with "raw" being the default.

Under "raw", the absolute target path (ni->target) is returned as-is
without translation. Under "rel", ntfs_translate_junction() is called
to rewrite the absolute path as a relative path anchored at the volume
root.

Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Hyunchul Lee 2026-06-15 08:49:55 +09:00 committed by Namjae Jeon
parent 2b58f93a13
commit 115446b06f
4 changed files with 34 additions and 6 deletions

View File

@ -22,6 +22,7 @@
#include "ea.h"
#include "iomap.h"
#include "bitmap.h"
#include "volume.h"
#include <linux/filelock.h>
@ -688,12 +689,13 @@ static const char *ntfs_get_link(struct dentry *dentry, struct inode *inode,
if (ni->reparse_tag == IO_REPARSE_TAG_MOUNT_POINT ||
(ni->reparse_tag == IO_REPARSE_TAG_SYMLINK &&
!(ni->reparse_flags & cpu_to_le32(SYMLINK_FLAG_RELATIVE)))) {
err = ntfs_translate_symlink_path(dentry, ni->target, &target);
if (err < 0)
return ERR_PTR(err);
set_delayed_call(done, kfree_link, target);
return target;
if (NVolNativeSymlinkRel(ni->vol)) {
err = ntfs_translate_symlink_path(dentry, ni->target, &target);
if (err < 0)
return ERR_PTR(err);
set_delayed_call(done, kfree_link, target);
return target;
}
}
return ni->target;

View File

@ -2378,6 +2378,10 @@ int ntfs_show_options(struct seq_file *sf, struct dentry *root)
seq_puts(sf, ",discard");
if (NVolDisableSparse(vol))
seq_puts(sf, ",disable_sparse");
if (NVolNativeSymlinkRel(vol))
seq_puts(sf, ",native_symlink=rel");
else
seq_puts(sf, ",native_symlink=raw");
if (vol->sb->s_flags & SB_POSIXACL)
seq_puts(sf, ",acl");
return 0;

View File

@ -43,6 +43,17 @@ static const struct constant_table ntfs_param_enums[] = {
{}
};
enum {
NATIVE_SYMLINK_RAW,
NATIVE_SYMLINK_REL,
};
static const struct constant_table ntfs_native_symlink_enums[] = {
{ "raw", NATIVE_SYMLINK_RAW },
{ "rel", NATIVE_SYMLINK_REL },
{}
};
enum {
Opt_uid,
Opt_gid,
@ -66,6 +77,7 @@ enum {
Opt_acl,
Opt_discard,
Opt_nocase,
Opt_native_symlink,
};
static const struct fs_parameter_spec ntfs_parameters[] = {
@ -91,6 +103,7 @@ static const struct fs_parameter_spec ntfs_parameters[] = {
fsparam_flag("discard", Opt_discard),
fsparam_flag("sparse", Opt_sparse),
fsparam_flag("nocase", Opt_nocase),
fsparam_enum("native_symlink", Opt_native_symlink, ntfs_native_symlink_enums),
{}
};
@ -215,6 +228,12 @@ static int ntfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
else
NVolClearDisableSparse(vol);
break;
case Opt_native_symlink:
if (result.uint_32 == NATIVE_SYMLINK_REL)
NVolSetNativeSymlinkRel(vol);
else
NVolClearNativeSymlinkRel(vol);
break;
case Opt_sparse:
break;
default:

View File

@ -177,6 +177,7 @@ struct ntfs_volume {
*
* NV_Discard Issue discard/TRIM commands for freed clusters.
* NV_DisableSparse Disable creation of sparse regions.
* NV_NativeSymlinkRel Translate absolute Windows reparse targets (native_symlink=rel).
*/
enum {
NV_Errors,
@ -194,6 +195,7 @@ enum {
NV_CheckWindowsNames,
NV_Discard,
NV_DisableSparse,
NV_NativeSymlinkRel,
};
/*
@ -230,6 +232,7 @@ DEFINE_NVOL_BIT_OPS(HideDotFiles)
DEFINE_NVOL_BIT_OPS(CheckWindowsNames)
DEFINE_NVOL_BIT_OPS(Discard)
DEFINE_NVOL_BIT_OPS(DisableSparse)
DEFINE_NVOL_BIT_OPS(NativeSymlinkRel)
static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr)
{