Hello,
I am bit disappointed to see that symlink and junctions are still not
really properly handled on Windows with boost (and even with standard MS
libs but that's another story.)
First my environment: Windows 10, VS2019 16.3.7, boost 1.71 (static,
x64).
I would like to write a small c++ utility that does not use os dependent
code to handle files so filesystem seems a good candidate.
Here is what I am doing on Windows:
cd "C:\Users\Vincent\AppData\Roaming\Apple Computer\MobileSync"
mklink /j Backup E:\Backup
So basically I am creating inside C:\..\MobileSync a junction folder
that points to my second harddisk E:\Backup
Then I am checking that Windows recognize it by entering dir command:
01/11/2019 23:48 <DIR> .
01/11/2019 23:48 <DIR> ..
01/11/2019 23:29 <JUNCTION> Backup [E:\Backup]
0 fichier(s) 0 octets
3 Rép(s) 59 393 630 208 octets libres
As you can see I have a junction targeting E:\Backup, so now let's see
what happens with boost:
fs::path p(_T("C:\\Users\\Vincent\\AppData\\Roaming\\Apple
Computer\\MobileSync\\Backup"));
sys::error_code ec;
if (fs::is_symlink(p))
{
fs::path pathTarget = fs::read_symlink(p, ec);
}
When I display the content of pathTarget I have: L"\\Backup" ie Backup
if I decode it into human string
I might not understand anything about junction but is it not supposed to
return E:\Backup ??
When I look at source code I can see the following lines:
/********************** boost_1_71_0\libs\filesystem\src\operations.cpp
**************************************/
symlink_path.assign(
static_cast(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t),
static_cast(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameLength/sizeof(wchar_t));
This is wrong since we need to distinguish between a symlink and a
junction folder so here is my contribution :
--- a/src/operations.cpp
+++ b/src/operations.cpp
@@ -201,6 +201,12 @@ typedef struct _REPARSE_DATA_BUFFER {
# ifndef IO_REPARSE_TAG_SYMLINK
# define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
# endif
+# ifndef IO_REPARSE_TAG_MOUNT_POINT
+# define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
+# endif
+# ifndef IO_REPARSE_TAG_DEDUP
+# define IO_REPARSE_TAG_DEDUP (0x80000013L)
+# endif
inline std::wstring wgetenv(const wchar_t* name)
{
@@ -1661,12 +1667,24 @@ namespace detail
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_SYMLINK)
+ {
symlink_path.assign(
static_cast(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t),
static_cast(info.rdb.SymbolicLinkReparseBuffer.PathBuffer)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(wchar_t)
+
info.rdb.SymbolicLinkReparseBuffer.PrintNameLength/sizeof(wchar_t));
+ }
+ else if (info.rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
+ {
+ symlink_path.assign(
+ static_cast(info.rdb.MountPointReparseBuffer.PathBuffer)
+ + info.rdb.MountPointReparseBuffer.PrintNameOffset /
sizeof(wchar_t),
+ static_cast(info.rdb.MountPointReparseBuffer.PathBuffer)
+ + info.rdb.MountPointReparseBuffer.PrintNameOffset /
sizeof(wchar_t)
+ + info.rdb.MountPointReparseBuffer.PrintNameLength /
sizeof(wchar_t));
+ }
# endif
return symlink_path;
}
I will add more suggestions in a next message because there is no
consistency between boost and MSVC approach. I don't know the last c++
specifications but it shouldn't be difficult to have a simple approach;