Skip to content

Commit

Permalink
Fix inability to open directory junctions on Windows using `symlink_h…
Browse files Browse the repository at this point in the history
…andle` (issue #73).
  • Loading branch information
ned14 committed Mar 15, 2021
1 parent 545a722 commit 17a1547
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 15 deletions.
1 change: 1 addition & 0 deletions cmake/tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ set(llfio_TESTS
"test/tests/issue0009.cpp"
"test/tests/issue0027.cpp"
"test/tests/issue0028.cpp"
"test/tests/issue0073.cpp"
"test/tests/large_pages.cpp"
"test/tests/map_handle_create_close/kernel_map_handle.cpp.hpp"
"test/tests/map_handle_create_close/runner.cpp"
Expand Down
6 changes: 3 additions & 3 deletions include/llfio/revision.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time
#define LLFIO_PREVIOUS_COMMIT_REF 17d8156ac89e3b8c6aa19a14e0dd723bd65e0902
#define LLFIO_PREVIOUS_COMMIT_DATE "2021-03-09 09:38:56 +00:00"
#define LLFIO_PREVIOUS_COMMIT_UNIQUE 17d8156a
#define LLFIO_PREVIOUS_COMMIT_REF 545a722a055dcc38e7f80520a7a34008bfa9a86f
#define LLFIO_PREVIOUS_COMMIT_DATE "2021-03-15 10:40:32 +00:00"
#define LLFIO_PREVIOUS_COMMIT_UNIQUE 545a722a
29 changes: 19 additions & 10 deletions include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ result<symlink_handle> symlink_handle::reopen(mode mode_, deadline /*unused*/) c
return ret;
}

LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(const path_handle &base, symlink_handle::path_view_type path, symlink_handle::mode _mode, symlink_handle::creation _creation, flag flags) noexcept
LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(const path_handle &base, symlink_handle::path_view_type path,
symlink_handle::mode _mode, symlink_handle::creation _creation,
flag flags) noexcept
{
windows_nt_kernel::init();
using namespace windows_nt_kernel;
Expand Down Expand Up @@ -104,7 +106,6 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
attribs &= 0x00ffffff; // the real attributes only, not the win32 flags
OUTCOME_TRY(auto &&ntflags, ntflags_from_handle_caching_and_flags(nativeh, caching::all, flags));
ntflags |= 0x4000 /*FILE_OPEN_FOR_BACKUP_INTENT*/ | 0x00200000 /*FILE_OPEN_REPARSE_POINT*/;
ntflags |= 0x040 /*FILE_NON_DIRECTORY_FILE*/; // do not open a directory
IO_STATUS_BLOCK isb = make_iostatus();

path_view::c_str<> zpath(path, path_view::not_zero_terminated);
Expand Down Expand Up @@ -195,7 +196,8 @@ result<symlink_handle::buffers_type> symlink_handle::read(symlink_handle::io_req
size_t bytes;
for(;;)
{
rpd = req.kernelbuffer.empty() ? reinterpret_cast<REPARSE_DATA_BUFFER *>(tofill._kernel_buffer.get()) : reinterpret_cast<REPARSE_DATA_BUFFER *>(req.kernelbuffer.data());
rpd = req.kernelbuffer.empty() ? reinterpret_cast<REPARSE_DATA_BUFFER *>(tofill._kernel_buffer.get()) :
reinterpret_cast<REPARSE_DATA_BUFFER *>(req.kernelbuffer.data());
bytes = req.kernelbuffer.empty() ? static_cast<ULONG>(tofill._kernel_buffer_size) : static_cast<ULONG>(req.kernelbuffer.size());
DWORD written = 0;
if(!DeviceIoControl(_v.h, FSCTL_GET_REPARSE_POINT, NULL, 0, rpd, (DWORD) bytes, &written, NULL))
Expand Down Expand Up @@ -235,7 +237,8 @@ result<symlink_handle::buffers_type> symlink_handle::read(symlink_handle::io_req
}
}

result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle::io_request<symlink_handle::const_buffers_type> req, deadline /*unused*/) noexcept
result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle::io_request<symlink_handle::const_buffers_type> req,
deadline /*unused*/) noexcept
{
windows_nt_kernel::init();
using namespace windows_nt_kernel;
Expand Down Expand Up @@ -267,7 +270,8 @@ result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle:
rpd->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes - 6;
rpd->SymbolicLinkReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes - 6 + sizeof(wchar_t));
rpd->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT) destpathbytes - 6;
memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer + 3, rpd->SymbolicLinkReparseBuffer.PrintNameLength - 6 + sizeof(wchar_t));
memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer + 3,
rpd->SymbolicLinkReparseBuffer.PrintNameLength - 6 + sizeof(wchar_t));
}
else
{
Expand All @@ -276,10 +280,12 @@ result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle:
rpd->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes;
rpd->SymbolicLinkReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes + sizeof(wchar_t));
rpd->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT) destpathbytes;
memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer, rpd->SymbolicLinkReparseBuffer.PrintNameLength + sizeof(wchar_t));
memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer,
rpd->SymbolicLinkReparseBuffer.PrintNameLength + sizeof(wchar_t));
}
rpd->SymbolicLinkReparseBuffer.Flags = req.buffers.path().is_relative() ? 0x1 /*SYMLINK_FLAG_RELATIVE*/ : 0;
rpd->ReparseDataLength = (USHORT)(rpd->SymbolicLinkReparseBuffer.SubstituteNameLength + rpd->SymbolicLinkReparseBuffer.PrintNameLength + 2 * sizeof(wchar_t) + reparsebufferheaderlen);
rpd->ReparseDataLength = (USHORT)(rpd->SymbolicLinkReparseBuffer.SubstituteNameLength + rpd->SymbolicLinkReparseBuffer.PrintNameLength +
2 * sizeof(wchar_t) + reparsebufferheaderlen);
break;
}
case symlink_type::win_wsl:
Expand All @@ -296,7 +302,8 @@ result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle:
rpd->MountPointReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes - 6;
rpd->MountPointReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes - 6 + sizeof(wchar_t));
rpd->MountPointReparseBuffer.PrintNameLength = (USHORT) destpathbytes - 6;
memcpy(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer + 3, rpd->MountPointReparseBuffer.PrintNameLength - 6 + sizeof(wchar_t));
memcpy(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer + 3,
rpd->MountPointReparseBuffer.PrintNameLength - 6 + sizeof(wchar_t));
}
else
{
Expand All @@ -305,9 +312,11 @@ result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle:
rpd->MountPointReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes;
rpd->MountPointReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes + sizeof(wchar_t));
rpd->MountPointReparseBuffer.PrintNameLength = (USHORT) destpathbytes;
memcpy(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer, rpd->MountPointReparseBuffer.PrintNameLength + sizeof(wchar_t));
memcpy(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer,
rpd->MountPointReparseBuffer.PrintNameLength + sizeof(wchar_t));
}
rpd->ReparseDataLength = (USHORT)(rpd->MountPointReparseBuffer.SubstituteNameLength + rpd->MountPointReparseBuffer.PrintNameLength + 2 * sizeof(wchar_t) + reparsebufferheaderlen);
rpd->ReparseDataLength =
(USHORT)(rpd->MountPointReparseBuffer.SubstituteNameLength + rpd->MountPointReparseBuffer.PrintNameLength + 2 * sizeof(wchar_t) + reparsebufferheaderlen);
break;
}
}
Expand Down
7 changes: 5 additions & 2 deletions include/llfio/v2.0/symlink_handle.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* A handle to a symbolic link
(C) 2018 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
(C) 2018-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
File Created: Jul 2018
Expand Down Expand Up @@ -78,6 +78,9 @@ code comparing equal to `errc::protocol_not_supported`. One should note that mod
links was not historically permitted by users with ordinary permissions on Microsoft Windows,
however recent versions of Windows 10 do support symbolic links for ordinary users. All versions
of Windows support directory symbolic links (junctions), these work for all users in any configuration.
(Note that to create a directory junction on Windows, first create a directory, open that
empty directory for modification using symlink_handle, then write using `symlink_type::win_junction`)
*/
class LLFIO_DECL symlink_handle : public handle, public fs_handle
{
Expand Down Expand Up @@ -112,7 +115,7 @@ class LLFIO_DECL symlink_handle : public handle, public fs_handle
none, //!<! No link
symbolic, //!< Standard symbolic link

win_wsl, //!< WSL symbolic link (Windows only)
win_wsl, //!< WSL symbolic link (Windows only, not actually implemented currently)
win_junction //!< NTFS directory junction (Windows only, directories and volumes only)
};

Expand Down
67 changes: 67 additions & 0 deletions test/tests/issue0073.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* Integration test kernel for issue #73 Windows directory junctions cannot be opened with symlink_handle
(C) 2021 Niall Douglas <http://www.nedproductions.biz/> (2 commits)
File Created: Mar 2021
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License in the accompanying file
Licence.txt or at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file Licence.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/

#include "../test_kernel_decl.hpp"

static inline void TestIssue0073()
{
#ifndef _WIN32
return;
#endif
namespace llfio = LLFIO_V2_NAMESPACE;

auto tempdirh = llfio::temp_directory().value();
auto untempdir = llfio::make_scope_exit([&]() noexcept { (void) llfio::algorithm::reduce(std::move(tempdirh)); });
std::cout << "NOTE: Test directory can be found at " << tempdirh.current_path().value() << std::endl;

// Test windows junctions
{
auto dirh =
llfio::directory_handle::directory(tempdirh, "testjunction", llfio::symlink_handle::mode::write, llfio::symlink_handle::creation::if_needed).value();
auto h = llfio::symlink_handle::symlink(dirh, {}, llfio::symlink_handle::mode::write).value();
h.write({llfio::symlink_handle::const_buffers_type("linkcontent", llfio::symlink_handle::symlink_type::win_junction)}).value();
auto rb = h.read().value();
BOOST_CHECK(rb.type() == llfio::symlink_handle::symlink_type::win_junction);
BOOST_CHECK(rb.path().path() == "linkcontent");
h.unlink().value();
}

// Test normal symbolic links
{
auto h_ = llfio::symlink_handle::symlink(tempdirh, "testlink", llfio::symlink_handle::mode::write, llfio::symlink_handle::creation::if_needed);
if(!h_ && h_.error() == llfio::errc::function_not_supported)
{
std::cout << "NOTE: Failed to create symbolic link, assuming lack of SeCreateSymbolicLinkPrivilege privileges." << std::endl;
return;
}
auto h = std::move(h_).value();
h.write("linkcontent").value();
auto rb = h.read().value();
BOOST_CHECK(rb.type() == llfio::symlink_handle::symlink_type::symbolic);
BOOST_CHECK(rb.path().path() == "linkcontent");
h.unlink().value();
}
}

KERNELTEST_TEST_KERNEL(regression, llfio, issues, 0073, "Tests issue #0073 Windows directory junctions cannot be opened with symlink_handle", TestIssue0073())

0 comments on commit 17a1547

Please sign in to comment.