Skip to content

Commit 0a4e7ce

Browse files
Jigsaw52aalexandrovich
authored andcommitted
fs/ntfs3: Fix junction point resolution
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> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
1 parent caad9dd commit 0a4e7ce

File tree

1 file changed

+101
-2
lines changed

1 file changed

+101
-2
lines changed

fs/ntfs3/inode.c

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,7 +1758,101 @@ void ntfs_evict_inode(struct inode *inode)
17581758
ni_clear(ntfs_i(inode));
17591759
}
17601760

1761-
static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer,
1761+
/*
1762+
* ntfs_translate_junction
1763+
*
1764+
* Translate a Windows junction target to the Linux equivalent.
1765+
* On junctions, targets are always absolute (they include the drive
1766+
* letter). We have no way of knowing if the target is for the current
1767+
* mounted device or not so we just assume it is.
1768+
*/
1769+
static int ntfs_translate_junction(const struct super_block *sb,
1770+
const struct dentry *link_de, char *target,
1771+
int target_len, int target_max)
1772+
{
1773+
int tl_len, err = target_len;
1774+
char *link_path_buffer = NULL, *link_path;
1775+
char *translated = NULL;
1776+
char *target_start;
1777+
int copy_len;
1778+
1779+
link_path_buffer = kmalloc(PATH_MAX, GFP_NOFS);
1780+
if (!link_path_buffer) {
1781+
err = -ENOMEM;
1782+
goto out;
1783+
}
1784+
/* Get link path, relative to mount point */
1785+
link_path = dentry_path_raw(link_de, link_path_buffer, PATH_MAX);
1786+
if (IS_ERR(link_path)) {
1787+
ntfs_err(sb, "Error getting link path");
1788+
err = -EINVAL;
1789+
goto out;
1790+
}
1791+
1792+
translated = kmalloc(PATH_MAX, GFP_NOFS);
1793+
if (!translated) {
1794+
err = -ENOMEM;
1795+
goto out;
1796+
}
1797+
1798+
/* Make translated path a relative path to mount point */
1799+
strcpy(translated, "./");
1800+
++link_path; /* Skip leading / */
1801+
for (tl_len = sizeof("./") - 1; *link_path; ++link_path) {
1802+
if (*link_path == '/') {
1803+
if (PATH_MAX - tl_len < sizeof("../")) {
1804+
ntfs_err(sb, "Link path %s has too many components",
1805+
link_path);
1806+
err = -EINVAL;
1807+
goto out;
1808+
}
1809+
strcpy(translated + tl_len, "../");
1810+
tl_len += sizeof("../") - 1;
1811+
}
1812+
}
1813+
1814+
/* Skip drive letter */
1815+
target_start = target;
1816+
while (*target_start && *target_start != ':')
1817+
++target_start;
1818+
1819+
if (!*target_start) {
1820+
ntfs_err(sb, "Link target (%s) missing drive separator", target);
1821+
err = -EINVAL;
1822+
goto out;
1823+
}
1824+
1825+
/* Skip drive separator and leading /, if exists */
1826+
target_start += 1 + (target_start[1] == '/');
1827+
copy_len = target_len - (target_start - target);
1828+
1829+
if (PATH_MAX - tl_len <= copy_len) {
1830+
ntfs_err(sb, "Link target %s too large for buffer (%d <= %d)",
1831+
target_start, PATH_MAX - tl_len, copy_len);
1832+
err = -EINVAL;
1833+
goto out;
1834+
}
1835+
1836+
/* translated path has a trailing / and target_start does not */
1837+
strcpy(translated + tl_len, target_start);
1838+
tl_len += copy_len;
1839+
if (target_max <= tl_len) {
1840+
ntfs_err(sb, "Target path %s too large for buffer (%d <= %d)",
1841+
translated, target_max, tl_len);
1842+
err = -EINVAL;
1843+
goto out;
1844+
}
1845+
strcpy(target, translated);
1846+
err = tl_len;
1847+
1848+
out:
1849+
kfree(link_path_buffer);
1850+
kfree(translated);
1851+
return err;
1852+
}
1853+
1854+
static noinline int ntfs_readlink_hlp(const struct dentry *link_de,
1855+
struct inode *inode, char *buffer,
17621856
int buflen)
17631857
{
17641858
int i, err = -EINVAL;
@@ -1901,6 +1995,11 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer,
19011995

19021996
/* Always set last zero. */
19031997
buffer[err] = 0;
1998+
1999+
/* If this is a junction, translate the link target. */
2000+
if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
2001+
err = ntfs_translate_junction(sb, link_de, buffer, err, buflen);
2002+
19042003
out:
19052004
kfree(to_free);
19062005
return err;
@@ -1919,7 +2018,7 @@ static const char *ntfs_get_link(struct dentry *de, struct inode *inode,
19192018
if (!ret)
19202019
return ERR_PTR(-ENOMEM);
19212020

1922-
err = ntfs_readlink_hlp(inode, ret, PAGE_SIZE);
2021+
err = ntfs_readlink_hlp(de, inode, ret, PAGE_SIZE);
19232022
if (err < 0) {
19242023
kfree(ret);
19252024
return ERR_PTR(err);

0 commit comments

Comments
 (0)