Re: [Boost-bugs] [Boost C++ Libraries] #10900: read_symlink fails to correctly read NTFS junctions

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