lkml.org 
[lkml]   [2022]   [Sep]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
SubjectRe: [PATCH] ntfs3: fix junction point resolution
From


On 9/7/22 04:45, Daniel Pinto wrote:
> The ntfs3 file system driver does not convert the target path of
> junction points to a proper Linux path. As junction points targets
> are always absolute paths (they start with a drive letter), all
> junctions will result in broken links.
>
> Translate the targets of junction points to relative paths so they
> point to directories inside the mounted volume. Note that Windows
> allows junction points to reference directories in another drive.
> However, as there is no way to know which drive the junctions refer
> to, we assume they always target the same file system they are in.
>
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=214833
> Signed-off-by: Daniel Pinto <danielpinto52@gmail.com>
> ---
> fs/ntfs3/inode.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 101 insertions(+), 2 deletions(-)
>
> diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
> index 51363d4e8636..f7e8876f56d3 100644
> --- a/fs/ntfs3/inode.c
> +++ b/fs/ntfs3/inode.c
> @@ -1749,7 +1749,101 @@ void ntfs_evict_inode(struct inode *inode)
> ni_clear(ntfs_i(inode));
> }
>
> -static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer,
> +/*
> + * ntfs_translate_junction
> + *
> + * Translate a Windows junction target to the Linux equivalent.
> + * On junctions, targets are always absolute (they include the drive
> + * letter). We have no way of knowing if the target is for the current
> + * mounted device or not so we just assume it is.
> + */
> +static int ntfs_translate_junction(const struct super_block *sb,
> + const struct dentry *link_de, char *target,
> + int target_len, int target_max)
> +{
> + int tl_len, err = target_len;
> + char *link_path_buffer = NULL, *link_path;
> + char *translated = NULL;
> + char *target_start;
> + int copy_len;
> +
> + link_path_buffer = kmalloc(PATH_MAX, GFP_NOFS);
> + if (!link_path_buffer) {
> + err = -ENOMEM;
> + goto out;
> + }
> + /* Get link path, relative to mount point */
> + link_path = dentry_path_raw(link_de, link_path_buffer, PATH_MAX);
> + if (IS_ERR(link_path)) {
> + ntfs_err(sb, "Error getting link path");
> + err = -EINVAL;
> + goto out;
> + }
> +
> + translated = kmalloc(PATH_MAX, GFP_NOFS);
> + if (!translated) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + /* Make translated path a relative path to mount point */
> + strcpy(translated, "./");
> + ++link_path; /* Skip leading / */
> + for (tl_len = sizeof("./") - 1; *link_path; ++link_path) {
> + if (*link_path == '/') {
> + if (PATH_MAX - tl_len < sizeof("../")) {
> + ntfs_err(sb, "Link path %s has too many components",
> + link_path);
> + err = -EINVAL;
> + goto out;
> + }
> + strcpy(translated + tl_len, "../");
> + tl_len += sizeof("../") - 1;
> + }
> + }
> +
> + /* Skip drive letter */
> + target_start = target;
> + while (*target_start && *target_start != ':')
> + ++target_start;
> +
> + if (!*target_start) {
> + ntfs_err(sb, "Link target (%s) missing drive separator", target);
> + err = -EINVAL;
> + goto out;
> + }
> +
> + /* Skip drive separator and leading /, if exists */
> + target_start += 1 + (target_start[1] == '/');
> + copy_len = target_len - (target_start - target);
> +
> + if (PATH_MAX - tl_len <= copy_len) {
> + ntfs_err(sb, "Link target %s too large for buffer (%d <= %d)",
> + target_start, PATH_MAX - tl_len, copy_len);
> + err = -EINVAL;
> + goto out;
> + }
> +
> + /* translated path has a trailing / and target_start does not */
> + strcpy(translated + tl_len, target_start);
> + tl_len += copy_len;
> + if (target_max <= tl_len) {
> + ntfs_err(sb, "Target path %s too large for buffer (%d <= %d)",
> + translated, target_max, tl_len);
> + err = -EINVAL;
> + goto out;
> + }
> + strcpy(target, translated);
> + err = tl_len;
> +
> +out:
> + kfree(link_path_buffer);
> + kfree(translated);
> + return err;
> +}
> +
> +static noinline int ntfs_readlink_hlp(const struct dentry *link_de,
> + struct inode *inode, char *buffer,
> int buflen)
> {
> int i, err = -EINVAL;
> @@ -1892,6 +1986,11 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer,
>
> /* Always set last zero. */
> buffer[err] = 0;
> +
> + /* If this is a junction, translate the link target. */
> + if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
> + err = ntfs_translate_junction(sb, link_de, buffer, err, buflen);
> +
> out:
> kfree(to_free);
> return err;
> @@ -1910,7 +2009,7 @@ static const char *ntfs_get_link(struct dentry *de, struct inode *inode,
> if (!ret)
> return ERR_PTR(-ENOMEM);
>
> - err = ntfs_readlink_hlp(inode, ret, PAGE_SIZE);
> + err = ntfs_readlink_hlp(de, inode, ret, PAGE_SIZE);
> if (err < 0) {
> kfree(ret);
> return ERR_PTR(err);
>

Thank you for your work, applied!

\
 
 \ /
  Last update: 2022-09-30 17:57    [W:0.044 / U:1.296 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site