Subject: Re: [Boost-bugs] [Boost C++ Libraries] #10900: read_symlink fails to correctly read NTFS junctions
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2017-05-03 11:23:26
#10900: read_symlink fails to correctly read NTFS junctions
-------------------------------------------------+------------------------
Reporter: Benjamin Kummer-Hardt <benjamin@â¦> | Owner: bemandawes
Type: Bugs | Status: assigned
Milestone: To Be Determined | Component: filesystem
Version: Boost 1.57.0 | Severity: Problem
Resolution: | Keywords:
-------------------------------------------------+------------------------
Comment (by Biohazard):
I had a lot of grief because of this, Boost does not handle symlinks and
mount points on Windows properly (Testing Windows 7, 8.1, 10).
Just sharing my solution here, maybe it helps someone else.
'''path read_symlink(const path& p, system::error_code* ec)''' must be
able to deal with symlinks and mount points. None of the above solutions
are resolving the mount point volume name from a device ID if one is
returned.
{{{
if (!error(::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT,
0, 0, info.buf, sizeof(info), &sz, 0) == 0 ? BOOST_ERRNO : 0, p,
ec,
"boost::filesystem::read_symlink" ))
{
if (info.rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
{
symlink_path.assign(
static_cast<wchar_t*>(info.rdb.MountPointReparseBuffer.PathBuffer)
+
info.rdb.MountPointReparseBuffer.PrintNameOffset/sizeof(wchar_t),
static_cast<wchar_t*>(info.rdb.MountPointReparseBuffer.PathBuffer)
+
info.rdb.MountPointReparseBuffer.PrintNameOffset/sizeof(wchar_t)
+
info.rdb.MountPointReparseBuffer.PrintNameLength/sizeof(wchar_t));
const wchar_t *mountPointVolumeStart =
wcsstr(symlink_path.c_str(), L"\\??\\Volume{");
if (mountPointVolumeStart != nullptr)
{
wchar_t pathNames[MAX_PATH * 4];
DWORD retLen;
path volumePath(L"\\\\?\\");
volumePath += (mountPointVolumeStart + 4);
if
(GetVolumePathNamesForVolumeName(volumePath.c_str(), pathNames, MAX_PATH *
4, &retLen) != FALSE && retLen > 0)
{
symlink_path = pathNames;
}
}
}
else if (info.rdb.ReparseTag == IO_REPARSE_TAG_SYMLINK)
{
symlink_path.assign(
static_cast<wchar_t*>(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t),
static_cast<wchar_t*>(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameLength/sizeof(wchar_t));
}
}
}}}
In '''path canonical(const path& p, const path& base, system::error_code*
ec)''' a dead lock must now be fixed, because a drive solely mounted as a
directory will cause '''canonical()''' to loop indefinitely over the same
mount point. I fixed it by comparing the link and resolved link and
skipping further iteration if they are equal.
{{{
if (is_sym)
{
path link(detail::read_symlink(result, ec));
path cmpLink(link);
cmpLink.make_preferred().remove_trailing_separator();
path cmpResult(result);
cmpResult.make_preferred().remove_trailing_separator();
if (cmpResult != cmpLink)
{
if (ec && *ec)
return path();
}}}
There is also another issue in canonical I had to add a work around for,
namely '''C:''' being treated as a mount point, causing another dead lock.
I just excluded lonely drive letters in '''symlink_status'''. Clearly not
the best solution but I don't have time to find something better:
{{{
const bool isDriveLetter = p.size() == 2 && p.c_str()[1] == L':';
if (isDriveLetter)
{
return file_status(type_unknown, make_permissions(p, attr));
}
}}}
-- Ticket URL: <https://svn.boost.org/trac/boost/ticket/10900#comment:6> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-05-03 11:27:31 UTC