diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2021-01-29 19:38:05 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2021-01-29 19:38:05 +0300 |
commit | fa2e4c095d5884dfc8185e31005606443edeae69 (patch) | |
tree | 7bb3766903ba25a57afb0675666899eb7d2d9d74 | |
parent | 48ee8bae656cb5d53f38feb462caa53e1128b76d (diff) |
Port from AFIO v1 `normalise_path()`, whose API I have improved and made more directed as `to_win32_path()`.
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/fs_handle.ipp | 163 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/handle.ipp | 3 | ||||
-rw-r--r-- | include/llfio/v2.0/fs_handle.hpp | 100 | ||||
-rw-r--r-- | include/llfio/v2.0/path_view.hpp | 47 | ||||
-rw-r--r-- | test/tests/current_path.cpp | 144 |
6 files changed, 405 insertions, 58 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp index 2eca1678..c823736d 100644 --- a/include/llfio/revision.hpp +++ b/include/llfio/revision.hpp @@ -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 7c2fd4d1070394326b39323e9beb53823336eca9 -#define LLFIO_PREVIOUS_COMMIT_DATE "2020-11-28 17:58:14 +00:00" -#define LLFIO_PREVIOUS_COMMIT_UNIQUE 7c2fd4d1 +#define LLFIO_PREVIOUS_COMMIT_REF 48ee8bae656cb5d53f38feb462caa53e1128b76d +#define LLFIO_PREVIOUS_COMMIT_DATE "2021-01-27 20:37:58 +00:00" +#define LLFIO_PREVIOUS_COMMIT_UNIQUE 48ee8bae diff --git a/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp b/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp index c08a3a89..58ef72ed 100644 --- a/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp @@ -391,4 +391,167 @@ result<void> fs_handle::unlink(deadline d) noexcept return success(); } + +/**************************************** to_win32_path() *******************************************/ + +LLFIO_HEADERS_ONLY_FUNC_SPEC result<filesystem::path> to_win32_path(const fs_handle &_h, win32_path_namespace mapping) noexcept +{ + windows_nt_kernel::init(); + using namespace windows_nt_kernel; + HANDLE h = _h._get_handle().native_handle().h; + LLFIO_LOG_FUNCTION_CALL(&h); + // Most efficient, least memory copying method is direct fill of a wstring which is moved into filesystem::path + filesystem::path::string_type buffer; + buffer.resize(32769); + auto *_buffer = const_cast<wchar_t *>(buffer.data()); + // Unlike h.current_path() which uses FILE_NAME_NORMALIZED, we shall use here FILE_NAME_OPENED + DWORD flags = FILE_NAME_OPENED; + switch(mapping) + { + case win32_path_namespace::device: + flags |= VOLUME_NAME_NT; + break; + case win32_path_namespace::dos: + flags |= VOLUME_NAME_DOS; + break; + case win32_path_namespace::any: // fallthrough + case win32_path_namespace::guid_volume: // fallthrough + // case win32_path_namespace::guid_all: + flags |= VOLUME_NAME_GUID; + break; + } + { + // Before Vista, I had to do this by hand, now I have a nice simple API. Thank you Microsoft! + DWORD len = GetFinalPathNameByHandleW(h, _buffer, (DWORD) buffer.size(), flags); // NOLINT + if(len == 0) + { + if(win32_path_namespace::any == mapping) + { + len = GetFinalPathNameByHandleW(h, _buffer, (DWORD) buffer.size(), FILE_NAME_OPENED | VOLUME_NAME_DOS); + if(len == 0) + { + return win32_error(); + } + mapping = win32_path_namespace::dos; + } + else + { + return win32_error(); + } + } + if(win32_path_namespace::any == mapping) + { + mapping = win32_path_namespace::guid_volume; + } + buffer.resize(len); + } + if(win32_path_namespace::guid_volume == mapping) + { + return filesystem::path(std::move(buffer)); + } +#if 0 + if(win32_path_namespace::guid_all == mapping) + { + FILE_OBJECTID_BUFFER fob; + DWORD out; + if(!DeviceIoControl(h, FSCTL_CREATE_OR_GET_OBJECT_ID, nullptr, 0, &fob, sizeof(fob), &out, nullptr)) + { + return win32_error(); + } + GUID *guid = (GUID *) &fob.ObjectId; + /* Form is `\\?\Volume{9f9bd10e-9003-4da5-b146-70584e30854a}\{5a13b46c-44b9-40f3-9303-23cf7d918708}` + */ + buffer.resize(87); + swprintf_s(_buffer + 49, 64, L"{%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x}", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], + guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + return filesystem::path(std::move(buffer)); + } +#endif + if(win32_path_namespace::device == mapping) + { + /* Paths of form \Device\... => \\.\ */ + if(0 == buffer.compare(0, 8, L"\\Device\\")) + { + buffer[1] = '\\'; + buffer[2] = '.'; + buffer[3] = '\\'; + memmove(&buffer[4], &buffer[8], (buffer.size() - 8) * sizeof(wchar_t)); + buffer.resize(buffer.size() - 4); + } + else + { + return errc::no_such_file_or_directory; + } + } + OUTCOME_TRY(_h._fetch_inode()); + // Ensure the mapped path exists and is the same file as our source + handle checkh(native_handle_type(native_handle_type::disposition::file | native_handle_type::disposition::_child_close_executed, + CreateFileW(buffer.c_str(), SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, nullptr))); + if(INVALID_HANDLE_VALUE != checkh.native_handle().h) + { + stat_t scheck; + OUTCOME_TRYV(scheck.fill(checkh, stat_t::want::dev | stat_t::want::ino)); + if(_h.st_dev() == scheck.st_dev && _h.st_ino() == scheck.st_ino) + { + if(win32_path_namespace::dos == mapping) + { + // Can we remove the \\?\ prefix safely from this DOS path? + bool needsExtendedPrefix = (buffer.size() > 260); + if(!needsExtendedPrefix) + { + // Are there any illegal Win32 characters in here? + static constexpr char reserved_chars[] = "\"*/:<>?|"; + for(size_t n = 7; !needsExtendedPrefix && n < buffer.size(); n++) + { + if(buffer[n] >= 1 && buffer[n] <= 31) + { + needsExtendedPrefix = true; + break; + } + for(size_t x = 0; x < sizeof(reserved_chars); x++) + { + if(buffer[n] == reserved_chars[x]) + { + needsExtendedPrefix = true; + break; + } + } + } + } + if(!needsExtendedPrefix) + { + // Are any segments of the filename a reserved name? + static constexpr const wstring_view reserved_names[] = { + L"\\CON\\", L"\\PRN\\", L"\\AUX\\", L"\\NUL\\", L"\\COM1\\", L"\\COM2\\", L"\\COM3\\", L"\\COM4\\", L"\\COM5\\", L"\\COM6\\", L"\\COM7\\", + L"\\COM8\\", L"\\COM9\\", L"\\LPT1\\", L"\\LPT2\\", L"\\LPT3\\", L"\\LPT4\\", L"\\LPT5\\", L"\\LPT6\\", L"\\LPT7\\", L"\\LPT8\\", L"\\LPT9\\"}; + wstring_view _buffer_(buffer); + for(auto name : reserved_names) + { + if(_buffer_.npos != _buffer_.find(name)) + { + needsExtendedPrefix = true; + break; + } + auto idx = _buffer_.find(name.substr(0, name.size() - 1)); + if(_buffer_.npos != idx && idx + name.size() - 1 == _buffer_.size()) + { + needsExtendedPrefix = true; + break; + } + } + } + if(!needsExtendedPrefix) + { + // This is safe to deprefix + memmove(&buffer[0], &buffer[4], (buffer.size() - 4) * sizeof(wchar_t)); + buffer.resize(buffer.size() - 4); + } + } + return filesystem::path(std::move(buffer)); + } + } + return errc::no_such_file_or_directory; +} + LLFIO_V2_NAMESPACE_END diff --git a/include/llfio/v2.0/detail/impl/windows/handle.ipp b/include/llfio/v2.0/detail/impl/windows/handle.ipp index 75271a1d..7f3a86d9 100644 --- a/include/llfio/v2.0/detail/impl/windows/handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/handle.ipp @@ -51,6 +51,9 @@ result<handle::path_type> handle::current_path() const noexcept buffer.resize(32769); auto *_buffer = const_cast<wchar_t *>(buffer.data()); memcpy(_buffer, L"\\!!", 6); + // Should I use FILE_NAME_OPENED here instead of the default FILE_NAME_NORMALIZED? + // I think the latter more likely to trap buggy assumptions, so let's do that. If + // people really want FILE_NAME_OPENED, see to_win32_path(). DWORD len = GetFinalPathNameByHandleW(_v.h, _buffer + 3, (DWORD)(buffer.size() - 4 * sizeof(wchar_t)), VOLUME_NAME_NT); // NOLINT if(len == 0) { diff --git a/include/llfio/v2.0/fs_handle.hpp b/include/llfio/v2.0/fs_handle.hpp index 091f2f2e..6b0b9aeb 100644 --- a/include/llfio/v2.0/fs_handle.hpp +++ b/include/llfio/v2.0/fs_handle.hpp @@ -39,6 +39,91 @@ Distributed under the Boost Software License, Version 1.0. LLFIO_V2_NAMESPACE_EXPORT_BEGIN +class fs_handle; + +//! \brief The kinds of win32 path namespace possible. +enum class win32_path_namespace +{ + /*! Map the input path to a valid win32 path as fast as possible for the input. + This is currently `guid_volume` followed by `dos`, but may change in the future. + */ + any, + /*! Map `\!!\Device\...` form input paths to `\\.\...` for which it is _usually_ + the case there is a mapping, which results in a valid Win32 path, but which + legacy code bases may not accept. This efficiently covers the vast majority of + what can be returned by `handle::current_path()` on Windows, but if the input + path cannot be mapped, a failure is returned. + */ + device, + /*! Map the input path to a DOS drive letter prefix, possibly with `\\?\` prefix + to opt out of strict DOS path parsing if the mapped DOS path is incompatible with + traditional DOS (e.g. it contains one of the forbidden character sequences such + as `CON`, or it exceeds 260 codepoints, and so on). Well written software will + correctly handle `\\?\` prefixes, but if the code you are handing the path to is + particularly legacy, you ought to ensure that the prefix is not present. + + \warning There is not a one-one mapping between NT kernel paths (which is what + LLFIO returns from `handle::current_path()`) and DOS style paths, so what you get + may be surprising. It is also possible that there is no mapping at all, in which + case a failure is returned. + */ + dos, + /*! Map the input path replacing the volume as a GUID, such that say an input path + of `C:\foo\bar` might be mapped to `\\?\Volume{9f9bd10e-9003-4da5-b146-70584e30854a}\foo\bar`. + This is a valid Win32 path, but legacy code bases may not accept it. This eliminates + problems with drive letters vanishing or being ambiguous, and unlike with `dos`, + there is a guaranteed one-one mapping between NT kernel paths and `guid_volume` paths. + The mapped path is NOT checked for equivalence to the input file. + */ + guid_volume +#if 0 + /*! Map the input path replacing the the whole path as a GUID, such that say an input + path of `C:\foo\bar` might be mapped to `\\?\Volume{9f9bd10e-9003-4da5-b146-70584e30854a}\{5a13b46c-44b9-40f3-9303-23cf7d918708}`. + This is a valid Win32 path, but legacy code bases may not accept it. This eliminates + problems with long paths or if the file could be renamed concurrently. Note this may + cause the creation of a GUID for the file on some filesystems (NTFS). The mapped path + is NOT checked for equivalence to the input file. + */ + guid_all + +/* +- `win32_path_namespace::guid_all` does the same as `guid_volume`, but additionally +asks Windows for the GUID for the file upon the volume, creating one if one +doesn't exist if necessary. The path returned consists of two GUIDs, and is a +perfectly valid Win32 path which most Win32 APIs will accept. +*/ +#endif +}; + +/*! \brief Maps the current path of `h` into a form suitable for Win32 APIs. +Passes through unmodified on POSIX, so you can use this in portable code. +\return The mapped current path of `h`, which may have been validated to refer to +the exact same inode via `.unique_id()` (see below). +\param h The handle whose `.current_path()` is to be mapped into a form suitable +for Win32 APIs. +\param mapping Which Win32 path namespace to map onto. + +This implementation may need to validate that the mapping of the current path of `h` +onto the desired Win32 path namespace does indeed refer to the same file: + +- `win32_path_namespace::device` transforms `\!!\Device\...` => `\\.\...` and +ensures that the mapped file's unique id matches the original, otherwise +returning failure. +- `win32_path_namespace::dos` enumerates all the DOS devices on the system and +what those map onto within the NT kernel namespace. This mapping is for +obvious reasons quite slow. +- `win32_path_namespace::guid_volume` simply fetches the GUID of the volume of +the handle, and constructs a valid Win32 path from that. +- `win32_path_namespace::any` means attempt `guid_volume` first, and if it fails +(e.g. your file is on a network share) then it attempts `dos`. This semantic may +change in the future, however any path emitted will always be a valid Win32 path. +*/ +#ifdef _WIN32 +LLFIO_HEADERS_ONLY_FUNC_SPEC result<filesystem::path> to_win32_path(const fs_handle &h, win32_path_namespace mapping = win32_path_namespace::any) noexcept; +#else +inline result<filesystem::path> to_win32_path(const fs_handle &h, win32_path_namespace mapping = win32_path_namespace::any) noexcept; +#endif + /*! \class fs_handle \brief A handle to something with a device and inode number. @@ -46,6 +131,12 @@ LLFIO_V2_NAMESPACE_EXPORT_BEGIN */ class LLFIO_DECL fs_handle { +#ifdef _WIN32 + friend LLFIO_HEADERS_ONLY_FUNC_SPEC result<filesystem::path> to_win32_path(const fs_handle &h, win32_path_namespace mapping) noexcept; +#else + friend inline result<filesystem::path> to_win32_path(const fs_handle &h, win32_path_namespace mapping) noexcept { return h._get_handle().current_path(); } +#endif + public: using dev_t = uint64_t; using ino_t = uint64_t; @@ -246,7 +337,8 @@ public: namespace detail { - LLFIO_HEADERS_ONLY_FUNC_SPEC result<path_handle> containing_directory(optional<std::reference_wrapper<filesystem::path>> out_filename, const handle &h, const fs_handle &fsh, deadline d) noexcept; + LLFIO_HEADERS_ONLY_FUNC_SPEC result<path_handle> containing_directory(optional<std::reference_wrapper<filesystem::path>> out_filename, const handle &h, + const fs_handle &fsh, deadline d) noexcept; } // BEGIN make_free_functions.py @@ -274,9 +366,11 @@ must succeed, else `errc::timed_out` will be returned. \mallocs Except on platforms with race free syscalls for renaming open handles (Windows), calls `current_path()` via `parent_path_handle()` and thus is both expensive and calls malloc many times. */ -inline result<void> relink(fs_handle &self, const path_handle &base, fs_handle::path_view_type path, bool atomic_replace = true, deadline d = std::chrono::seconds(30)) noexcept +inline result<void> relink(fs_handle &self, const path_handle &base, fs_handle::path_view_type path, bool atomic_replace = true, + deadline d = std::chrono::seconds(30)) noexcept { - return self.relink(std::forward<decltype(base)>(base), std::forward<decltype(path)>(path), std::forward<decltype(atomic_replace)>(atomic_replace), std::forward<decltype(d)>(d)); + return self.relink(std::forward<decltype(base)>(base), std::forward<decltype(path)>(path), std::forward<decltype(atomic_replace)>(atomic_replace), + std::forward<decltype(d)>(d)); } /*! Unlinks the current path of this open handle, causing its entry to immediately disappear from the filing system. On Windows unless `flag::win_disable_unlink_emulation` is set, this behaviour is diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp index 41589da3..b1faf800 100644 --- a/include/llfio/v2.0/path_view.hpp +++ b/include/llfio/v2.0/path_view.hpp @@ -2437,53 +2437,6 @@ inline filesystem::path operator/(T a, path_view_component b) } -namespace detail -{ - inline filesystem::path to_win32_path(filesystem::path &&p, bool need_dos_path_form) - { -#ifdef _WIN32 - if(need_dos_path_form) - { - throw std::runtime_error("Not implemented yet"); - } - auto &str = const_cast<filesystem::path::string_type &>(p.native()); - if(str.size() >= 4 && str[0] == '\\' && str[1] == '!' && str[2] == '!' && str[3] == '\\') - { - /* Paths of form \!!\Device\... => \\.\ */ - if(0 == str.compare(0, 11, L"\\!!\\Device\\")) - { - str[1] = '\\'; - str[2] = '.'; - memcpy(&str[4], &str[11], (str.size() - 7) * sizeof(wchar_t)); - str.resize(str.size() - 7); - } - else - { - throw std::runtime_error("cannot convert path into Win32 path"); - } - } -#endif - return std::move(p); - } -} // namespace detail - -/*! \brief Transforms the input path into a form suitable for Win32 APIs. -Passes through unmodified on POSIX. - -\throws If the path cannot be turned into a valid Win32 path, an exception -is thrown. -*/ -inline filesystem::path to_win32_path(path_view p, bool need_dos_path_form = false) -{ - return detail::to_win32_path(p.path(), need_dos_path_form); -} -//! \overload -inline filesystem::path to_win32_path(filesystem::path &&p, bool need_dos_path_form = false) -{ - return detail::to_win32_path(std::move(p), need_dos_path_form); -} - - #ifndef NDEBUG static_assert(std::is_trivially_copyable<path_view>::value, "path_view is not a trivially copyable!"); #endif diff --git a/test/tests/current_path.cpp b/test/tests/current_path.cpp index 626696c3..0048a744 100644 --- a/test/tests/current_path.cpp +++ b/test/tests/current_path.cpp @@ -43,8 +43,20 @@ template <class FileHandleType, class DirectoryHandleType> static inline void Te #pragma clang diagnostic ignored "-Wmissing-braces" #endif llfio::path_handle null_path_handle; - FileHandleType h1 = llfio::construct<FileHandleType>{null_path_handle, "tempfile", llfio::file_handle::mode::write, llfio::file_handle::creation::if_needed, llfio::file_handle::caching::temporary, llfio::file_handle::flag::none}().value(); // NOLINT - DirectoryHandleType h2 = llfio::construct<DirectoryHandleType>{null_path_handle, "tempdir", llfio::file_handle::mode::write, llfio::file_handle::creation::if_needed, llfio::file_handle::caching::all, llfio::file_handle::flag::none}().value(); // NOLINT + FileHandleType h1 = llfio::construct<FileHandleType>{null_path_handle, + "tempfile", + llfio::file_handle::mode::write, + llfio::file_handle::creation::if_needed, + llfio::file_handle::caching::temporary, + llfio::file_handle::flag::none}() + .value(); // NOLINT + DirectoryHandleType h2 = llfio::construct<DirectoryHandleType>{null_path_handle, + "tempdir", + llfio::file_handle::mode::write, + llfio::file_handle::creation::if_needed, + llfio::file_handle::caching::all, + llfio::file_handle::flag::none}() + .value(); // NOLINT #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -261,6 +273,128 @@ template <class FileHandleType, class DirectoryHandleType> static inline void Te h2.unlink().value(); } -KERNELTEST_TEST_KERNEL(integration, llfio, current_path, handle, "Tests that llfio::handle::current_path() works as expected", TestHandleCurrentPath<LLFIO_V2_NAMESPACE::file_handle, LLFIO_V2_NAMESPACE::directory_handle>()) -KERNELTEST_TEST_KERNEL(integration, llfio, current_path, cached_parent_handle_adapter, "Tests that llfio::cached_parent_handle_adapter::current_path() works as expected", - TestHandleCurrentPath<LLFIO_V2_NAMESPACE::algorithm::cached_parent_handle_adapter<LLFIO_V2_NAMESPACE::file_handle>, LLFIO_V2_NAMESPACE::algorithm::cached_parent_handle_adapter<LLFIO_V2_NAMESPACE::directory_handle>>()) +template <class FileHandleType, class DirectoryHandleType> static inline void TestToWin32Path() +{ + namespace llfio = LLFIO_V2_NAMESPACE; + { + std::error_code ec; + llfio::filesystem::current_path(llfio::filesystem::temp_directory_path()); + llfio::filesystem::remove_all("tempfile", ec); + llfio::filesystem::remove_all("tempfile2", ec); + llfio::filesystem::remove_all("tempfile3", ec); + llfio::filesystem::remove_all("tempfile4", ec); + llfio::filesystem::remove_all("tempdir", ec); + llfio::filesystem::remove_all("tempdir2", ec); + llfio::filesystem::remove_all("tempdir3", ec); + } // namespace ; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + llfio::path_handle null_path_handle; + FileHandleType h1 = llfio::construct<FileHandleType>{null_path_handle, + "tempfile", + llfio::file_handle::mode::write, + llfio::file_handle::creation::if_needed, + llfio::file_handle::caching::temporary, + llfio::file_handle::flag::none}() + .value(); // NOLINT + DirectoryHandleType h2 = llfio::construct<DirectoryHandleType>{null_path_handle, + "tempdir", + llfio::file_handle::mode::write, + llfio::file_handle::creation::if_needed, + llfio::file_handle::caching::all, + llfio::file_handle::flag::none}() + .value(); // NOLINT +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + auto check = [](llfio::result<llfio::filesystem::path> res, const char *desc) -> llfio::filesystem::path { + BOOST_CHECK(res); + if(!res) + { + std::cerr << " Getting the win32 path of a " << desc << " FAILED due to " << res.error().message().c_str() << std::endl; + } + else if(res.value().empty()) + { + BOOST_CHECK(!res.value().empty()); + std::cerr << " Getting the win32 path of a " << desc << " FAILED due to the returned path being empty" << std::endl; + } + else + { + std::cout << " The win32 path of a " << desc << " is " << res.value() << std::endl; + return std::move(res).value(); + } + return {}; + }; + std::cout << "\nwin32_path_namespace::any:\n"; + auto h1path_any = check(to_win32_path(h1, llfio::win32_path_namespace::any), "file"); + auto h2path_any = check(to_win32_path(h2, llfio::win32_path_namespace::any), "directory"); + std::cout << "\nwin32_path_namespace::device:\n"; + auto h1path_device = check(to_win32_path(h1, llfio::win32_path_namespace::device), "file"); + auto h2path_device = check(to_win32_path(h2, llfio::win32_path_namespace::device), "directory"); + std::cout << "\nwin32_path_namespace::dos:\n"; + auto h1path_dos = check(to_win32_path(h1, llfio::win32_path_namespace::dos), "file"); + auto h2path_dos = check(to_win32_path(h2, llfio::win32_path_namespace::dos), "directory"); + std::cout << "\nwin32_path_namespace::guid_volume:\n"; + auto h1path_guid_volume = check(to_win32_path(h1, llfio::win32_path_namespace::guid_volume), "file"); + auto h2path_guid_volume = check(to_win32_path(h2, llfio::win32_path_namespace::guid_volume), "directory"); + //std::cout << "\nwin32_path_namespace::guid_all:\n"; + //auto h1path_guid_all = check(to_win32_path(h1, llfio::win32_path_namespace::guid_all), "file"); + //auto h2path_guid_all = check(to_win32_path(h2, llfio::win32_path_namespace::guid_all), "directory"); + + for(auto &path : { + h1path_any, + h1path_device, + h1path_dos, + h1path_guid_volume, + //h1path_guid_all, + }) + { + if(!path.empty()) + { + std::cout << "\nChecking file " << path << " can be opened ..."; + auto res = llfio::file_handle::file({}, path); + BOOST_CHECK(res); + if(res) + { + BOOST_CHECK(res.value().st_dev() == h1.st_dev()); + BOOST_CHECK(res.value().st_ino() == h1.st_ino()); + } + } + } + std::cout << "\n"; + for(auto &path : { + h2path_any, + h2path_device, + h2path_dos, + h2path_guid_volume, + //h2path_guid_all, + }) + { + if(!path.empty()) + { + std::cout << "\nChecking directory " << path << " can be opened ..."; + auto res = llfio::directory_handle::directory({}, path); + BOOST_CHECK(res); + if(res) + { + BOOST_CHECK(res.value().st_dev() == h2.st_dev()); + BOOST_CHECK(res.value().st_ino() == h2.st_ino()); + } + } + } + std::cout << std::endl; + h1.unlink().value(); + h2.unlink().value(); +} + +KERNELTEST_TEST_KERNEL(integration, llfio, current_path, handle, "Tests that llfio::handle::current_path() works as expected", + TestHandleCurrentPath<LLFIO_V2_NAMESPACE::file_handle, LLFIO_V2_NAMESPACE::directory_handle>()) +KERNELTEST_TEST_KERNEL(integration, llfio, current_path, cached_parent_handle_adapter, + "Tests that llfio::cached_parent_handle_adapter::current_path() works as expected", + TestHandleCurrentPath<LLFIO_V2_NAMESPACE::algorithm::cached_parent_handle_adapter<LLFIO_V2_NAMESPACE::file_handle>, + LLFIO_V2_NAMESPACE::algorithm::cached_parent_handle_adapter<LLFIO_V2_NAMESPACE::directory_handle>>()) +KERNELTEST_TEST_KERNEL(integration, llfio, to_win32_path, handle, "Tests that llfio::to_win32_path() works as expected", + TestToWin32Path<LLFIO_V2_NAMESPACE::file_handle, LLFIO_V2_NAMESPACE::directory_handle>()) |