From aaac3b0b28c271a258902e16389a0e71c8fb074d Mon Sep 17 00:00:00 2001 From: "Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com)" Date: Tue, 4 May 2021 18:47:11 +0100 Subject: No longer have path_handle identify itself as a directory, but instead as a path. directory_handle now identifies itself as both a path and a directory. In algorithm/summarize, if fed not a directory_handle, clone a directory handle from the input. --- include/llfio/v2.0/algorithm/summarize.hpp | 12 +++-- .../v2.0/detail/impl/posix/directory_handle.ipp | 2 +- .../llfio/v2.0/detail/impl/posix/path_handle.ipp | 2 +- .../v2.0/detail/impl/windows/directory_handle.ipp | 2 +- .../llfio/v2.0/detail/impl/windows/path_handle.ipp | 2 +- include/llfio/v2.0/handle.hpp | 52 ++++++++++++++-------- include/llfio/v2.0/native_handle_type.hpp | 3 ++ 7 files changed, 50 insertions(+), 25 deletions(-) diff --git a/include/llfio/v2.0/algorithm/summarize.hpp b/include/llfio/v2.0/algorithm/summarize.hpp index 8bdbe29d..df797dad 100644 --- a/include/llfio/v2.0/algorithm/summarize.hpp +++ b/include/llfio/v2.0/algorithm/summarize.hpp @@ -174,7 +174,7 @@ namespace algorithm } }; - /*! \brief Summarise the directory identified `dirh`, and everything therein. + /*! \brief Summarise the directory identified `topdirh`, and everything therein. It can be useful to summarise a directory hierarchy, especially to determine how much storage it occupies, but also how many mounted filesystems it straddles etc. @@ -186,10 +186,10 @@ namespace algorithm implemented entirely as header code. You should review the documentation for `algorithm::traverse()`, as this algorithm is entirely implemented using that algorithm. */ - inline result summarize(const path_handle &dirh, stat_t::want want = traversal_summary::default_metadata(), + inline result summarize(const path_handle &topdirh, stat_t::want want = traversal_summary::default_metadata(), summarize_visitor *visitor = nullptr, size_t threads = 0, bool force_slow_path = false) noexcept { - LLFIO_LOG_FUNCTION_CALL(&dirh); + LLFIO_LOG_FUNCTION_CALL(&topdirh); summarize_visitor default_visitor; if(visitor == nullptr) { @@ -198,6 +198,12 @@ namespace algorithm result state(in_place_type); state.assume_value().want = want; directory_entry entry{{}, stat_t(nullptr)}; + directory_handle _dirh; + if(!topdirh.is_directory()) + { + OUTCOME_TRY(_dirh, directory_handle::directory(topdirh, {})); + } + const path_handle &dirh = _dirh.is_valid() ? _dirh : topdirh; OUTCOME_TRY(entry.stat.fill(dirh, want)); OUTCOME_TRY(summarize_visitor::accumulate(state.assume_value(), &state.assume_value(), nullptr, entry, want)); OUTCOME_TRY(traverse(dirh, visitor, threads, &state.assume_value(), force_slow_path)); diff --git a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp index bd0050d6..d9bfc2be 100644 --- a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp @@ -49,7 +49,7 @@ result directory_handle::directory(const path_handle &base, pa result ret(directory_handle(native_handle_type(), 0, 0, _caching, flags)); native_handle_type &nativeh = ret.value()._v; LLFIO_LOG_FUNCTION_CALL(&ret); - nativeh.behaviour |= native_handle_type::disposition::directory; + nativeh.behaviour |= native_handle_type::disposition::directory | native_handle_type::disposition::path; // POSIX does not permit directory opens with O_RDWR like Windows, so silently convert to read if(_mode == mode::attr_write) { diff --git a/include/llfio/v2.0/detail/impl/posix/path_handle.ipp b/include/llfio/v2.0/detail/impl/posix/path_handle.ipp index c349cca1..47b242f5 100644 --- a/include/llfio/v2.0/detail/impl/posix/path_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/path_handle.ipp @@ -56,7 +56,7 @@ result path_handle::path(const path_handle &base, path_handle::path result ret(in_place_type); native_handle_type &nativeh = ret.value()._v; LLFIO_LOG_FUNCTION_CALL(&ret); - nativeh.behaviour |= native_handle_type::disposition::directory; + nativeh.behaviour |= native_handle_type::disposition::path; nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable int attribs = O_CLOEXEC | O_RDONLY; #ifdef O_DIRECTORY diff --git a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp index 4da58a91..f8719e0c 100644 --- a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp @@ -43,7 +43,7 @@ result directory_handle::directory(const path_handle &base, pa result ret(directory_handle(native_handle_type(), 0, 0, _caching, flags)); native_handle_type &nativeh = ret.value()._v; LLFIO_LOG_FUNCTION_CALL(&ret); - nativeh.behaviour |= native_handle_type::disposition::directory; + nativeh.behaviour |= native_handle_type::disposition::directory | native_handle_type::disposition::path; DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; // Trying to truncate a directory returns EISDIR rather than some internal Win32 error code uncomparable to errc if(_creation == creation::truncate_existing) diff --git a/include/llfio/v2.0/detail/impl/windows/path_handle.ipp b/include/llfio/v2.0/detail/impl/windows/path_handle.ipp index bc938ad9..2d394c8f 100644 --- a/include/llfio/v2.0/detail/impl/windows/path_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/path_handle.ipp @@ -81,7 +81,7 @@ result path_handle::path(const path_handle &base, path_handle::path result ret{path_handle(native_handle_type(), caching::none, flag::none)}; native_handle_type &nativeh = ret.value()._v; LLFIO_LOG_FUNCTION_CALL(&ret); - nativeh.behaviour |= native_handle_type::disposition::directory; + nativeh.behaviour |= native_handle_type::disposition::path; DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; // Open directory with no access requested, this is much faster than asking for access OUTCOME_TRY(auto &&access, access_mask_from_handle_mode(nativeh, mode::none, flag::none)); diff --git a/include/llfio/v2.0/handle.hpp b/include/llfio/v2.0/handle.hpp index 5b737a15..22046e4b 100644 --- a/include/llfio/v2.0/handle.hpp +++ b/include/llfio/v2.0/handle.hpp @@ -70,9 +70,10 @@ public: attr_read = 4, //!< Ability to read attributes (FILE_READ_ATTRIBUTES|SYNCHRONIZE or O_RDONLY) attr_write = 5, //!< Ability to read and write attributes (FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES|SYNCHRONIZE or O_RDONLY) read = 6, //!< Ability to read (READ_CONTROL|FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA|SYNCHRONISE or O_RDONLY) - write = 7, //!< Ability to read and write (READ_CONTROL|FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA|FILE_APPEND_DATA|SYNCHRONISE or O_RDWR) - append = 9 //!< All mainstream OSs and CIFS guarantee this is atomic with respect to all other appenders (FILE_APPEND_DATA|SYNCHRONISE or O_APPEND) - // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!! + write = + 7, //!< Ability to read and write (READ_CONTROL|FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA|FILE_APPEND_DATA|SYNCHRONISE or O_RDWR) + append = 9 //!< All mainstream OSs and CIFS guarantee this is atomic with respect to all other appenders (FILE_APPEND_DATA|SYNCHRONISE or O_APPEND) + // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!! }; //! On opening, do we also create a new file or truncate an existing one? enum class creation : unsigned char @@ -88,14 +89,21 @@ public: enum class caching : unsigned char // bit 0 set means safety barriers enabled { unchanged = 0, - none = 1, //!< No caching whatsoever, all reads and writes come from storage (i.e. O_DIRECT|O_SYNC). Align all i/o to 4Kb boundaries for this to work. disable_safety_barriers can be used here. - only_metadata = 2, //!< Cache reads and writes of metadata but avoid caching data (O_DIRECT), thus i/o here does not affect other cached data for other handles. Align all i/o to 4Kb boundaries for this to work. - reads = 3, //!< Cache reads only. Writes of data and metadata do not complete until reaching storage (O_SYNC). disable_safety_barriers can be used here. - reads_and_metadata = 5, //!< Cache reads and writes of metadata, but writes of data do not complete until reaching storage (O_DSYNC). disable_safety_barriers can be used here. - all = 6, //!< Cache reads and writes of data and metadata so they complete immediately, sending writes to storage at some point when the kernel decides (this is the default file system caching on a system). - safety_barriers = 7, //!< Cache reads and writes of data and metadata so they complete immediately, but issue safety barriers at certain points. See documentation for disable_safety_barriers. - temporary = 8 //!< Cache reads and writes of data and metadata so they complete immediately, only sending any updates to storage on last handle close in the system or if memory becomes tight as this file is expected to be temporary (Windows and FreeBSD only). - // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!! + none = + 1, //!< No caching whatsoever, all reads and writes come from storage (i.e. O_DIRECT|O_SYNC). Align all i/o to 4Kb boundaries for this to work. disable_safety_barriers can be used here. + only_metadata = + 2, //!< Cache reads and writes of metadata but avoid caching data (O_DIRECT), thus i/o here does not affect other cached data for other handles. Align all i/o to 4Kb boundaries for this to work. + reads = + 3, //!< Cache reads only. Writes of data and metadata do not complete until reaching storage (O_SYNC). disable_safety_barriers can be used here. + reads_and_metadata = + 5, //!< Cache reads and writes of metadata, but writes of data do not complete until reaching storage (O_DSYNC). disable_safety_barriers can be used here. + all = + 6, //!< Cache reads and writes of data and metadata so they complete immediately, sending writes to storage at some point when the kernel decides (this is the default file system caching on a system). + safety_barriers = + 7, //!< Cache reads and writes of data and metadata so they complete immediately, but issue safety barriers at certain points. See documentation for disable_safety_barriers. + temporary = + 8 //!< Cache reads and writes of data and metadata so they complete immediately, only sending any updates to storage on last handle close in the system or if memory becomes tight as this file is expected to be temporary (Windows and FreeBSD only). + // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!! }; //! Bitwise flags which can be specified QUICKCPPLIB_BITFIELD_BEGIN(flag){ @@ -202,7 +210,9 @@ protected: static constexpr void _set_caching(native_handle_type &nativeh, caching caching) noexcept { - nativeh.behaviour &= ~(native_handle_type::disposition::safety_barriers | native_handle_type::disposition::cache_metadata | native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_writes | native_handle_type::disposition::cache_temporary); + nativeh.behaviour &= + ~(native_handle_type::disposition::safety_barriers | native_handle_type::disposition::cache_metadata | native_handle_type::disposition::cache_reads | + native_handle_type::disposition::cache_writes | native_handle_type::disposition::cache_temporary); switch(caching) { case caching::unchanged: @@ -217,16 +227,20 @@ protected: nativeh.behaviour |= native_handle_type::disposition::cache_reads | native_handle_type::disposition::safety_barriers; break; case caching::reads_and_metadata: - nativeh.behaviour |= native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_metadata | native_handle_type::disposition::safety_barriers; + nativeh.behaviour |= + native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_metadata | native_handle_type::disposition::safety_barriers; break; case caching::all: - nativeh.behaviour |= native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_writes | native_handle_type::disposition::cache_metadata; + nativeh.behaviour |= + native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_writes | native_handle_type::disposition::cache_metadata; break; case caching::safety_barriers: - nativeh.behaviour |= native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_writes | native_handle_type::disposition::cache_metadata | native_handle_type::disposition::safety_barriers; + nativeh.behaviour |= native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_writes | + native_handle_type::disposition::cache_metadata | native_handle_type::disposition::safety_barriers; break; case caching::temporary: - nativeh.behaviour |= native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_writes | native_handle_type::disposition::cache_metadata | native_handle_type::disposition::cache_temporary; + nativeh.behaviour |= native_handle_type::disposition::cache_reads | native_handle_type::disposition::cache_writes | + native_handle_type::disposition::cache_metadata | native_handle_type::disposition::cache_temporary; break; } } @@ -381,6 +395,8 @@ public: bool is_section() const noexcept { return _v.is_section(); } //! True if a memory allocation bool is_allocation() const noexcept { return _v.is_allocation(); } + //! True if a path or a directory + bool is_path() const noexcept { return _v.is_path(); } //! Kernel cache strategy used by this handle caching kernel_caching() const noexcept @@ -568,7 +584,7 @@ namespace detail #pragma warning(push) #pragma warning(disable : 4996) // the function may be unsafe #endif -#if (__GNUC__ >= 8) && !defined(__clang__) +#if(__GNUC__ >= 8) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif @@ -577,7 +593,7 @@ namespace detail // See https://godbolt.org/z/d69xzd for proof. // So I don't know why there is a warning here about overflowing a buffer of 16! strncpy(tls.next(dest._tls_path_id1), QUICKCPPLIB_NAMESPACE::ringbuffer_log::last190(currentpath), 190); -#if (__GNUC__ >= 8) && !defined(__clang__) +#if(__GNUC__ >= 8) && !defined(__clang__) #pragma GCC diagnostic pop #endif #ifdef _MSC_VER diff --git a/include/llfio/v2.0/native_handle_type.hpp b/include/llfio/v2.0/native_handle_type.hpp index 4dba6ee9..ccc9df4b 100644 --- a/include/llfio/v2.0/native_handle_type.hpp +++ b/include/llfio/v2.0/native_handle_type.hpp @@ -62,6 +62,7 @@ struct native_handle_type // NOLINT process = 1U << 14U, //!< Is a child process section = 1U << 15U, //!< Is a memory section allocation = 1U << 16U, //!< Is a memory allocation + path = 1U << 17U, //!< Is a path safety_barriers = 1U << 20U, //!< Issue write reordering barriers at various points cache_metadata = 1U << 21U, //!< Is serving metadata from the kernel cache @@ -181,6 +182,8 @@ struct native_handle_type // NOLINT constexpr bool is_section() const noexcept { return (behaviour & disposition::section) ? true : false; } //! True if a memory allocation constexpr bool is_allocation() const noexcept { return (behaviour & disposition::allocation) ? true : false; } + //! True if a path or a directory + constexpr bool is_path() const noexcept { return (behaviour & disposition::path) ? true : false; } }; static_assert((sizeof(void *) == 4 && sizeof(native_handle_type) == 8) || (sizeof(void *) == 8 && sizeof(native_handle_type) == 12), "native_handle_type is not 8 or 12 bytes in size!"); -- cgit v1.2.3