Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins nedprod CI <jenkins-nedprod@europe5.nedproductions.biz>2020-07-10 02:00:16 +0300
committerJenkins nedprod CI <jenkins-nedprod@europe5.nedproductions.biz>2020-07-10 02:00:16 +0300
commit9699920798dc7786e543ac7832ed9e6f8cb1e500 (patch)
tree12ee75015aac2732104daaa16b2f91bc542cbc4b
parent9050f5c8446e147ea5ed1f1470da6cd40f8d883a (diff)
parent804dcb157c208bf918aea37eb98e149934b51e4f (diff)
Merged from develop branch as CDash reports all green
-rw-r--r--include/llfio/v2.0/algorithm/clone.hpp4
-rw-r--r--include/llfio/v2.0/algorithm/contents.hpp242
-rw-r--r--include/llfio/v2.0/algorithm/difference.hpp137
-rw-r--r--include/llfio/v2.0/detail/impl/clone.ipp3
-rw-r--r--include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp44
-rw-r--r--include/llfio/v2.0/llfio.hpp1
-rw-r--r--include/llfio/v2.0/path_view.hpp198
-rw-r--r--include/llfio/v2.0/stat.hpp131
-rw-r--r--include/llfio/v2.0/symlink_handle.hpp43
9 files changed, 697 insertions, 106 deletions
diff --git a/include/llfio/v2.0/algorithm/clone.hpp b/include/llfio/v2.0/algorithm/clone.hpp
index 570ef089..9175a93f 100644
--- a/include/llfio/v2.0/algorithm/clone.hpp
+++ b/include/llfio/v2.0/algorithm/clone.hpp
@@ -46,8 +46,8 @@ namespace algorithm
\param force_copy_now Parameter to pass to `file_handle::clone_extents()` to force
extents to be copied now, not copy-on-write lazily later.
\param creation How to create the destination file handle. NOTE that if this
- is NOT `always_new`, if the destination has identical maximum extent and
- timestamps (and permissions on POSIX) to the source, it is NOT copied, and
+ is NOT `always_new`, if the destination has identical maximum extent and last
+ modified timestamp (and permissions on POSIX) to the source, it is NOT copied, and
zero is returned.
\param d Deadline by which to complete the operation.
diff --git a/include/llfio/v2.0/algorithm/contents.hpp b/include/llfio/v2.0/algorithm/contents.hpp
new file mode 100644
index 00000000..813fa8eb
--- /dev/null
+++ b/include/llfio/v2.0/algorithm/contents.hpp
@@ -0,0 +1,242 @@
+/* A filesystem algorithm which returns the contents of a directory tree
+(C) 2020 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
+File Created: July 2020
+
+
+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)
+*/
+
+#ifndef LLFIO_ALGORITHM_CONTENTS_HPP
+#define LLFIO_ALGORITHM_CONTENTS_HPP
+
+#include "traverse.hpp"
+
+#include <memory>
+#include <mutex>
+
+//! \file contents.hpp Provides a directory tree contents algorithm.
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+namespace algorithm
+{
+ /*! \brief A visitor for the filesystem contents algorithm.
+ */
+ struct contents_visitor : traverse_visitor
+ {
+ //! Whether to include files in the contents.
+ bool contents_include_files{true};
+ //! Whether to include directories in the contents.
+ bool contents_include_directories{true};
+ //! Whether to include symlinks in the contents.
+ bool contents_include_symlinks{true};
+ //! What `stat_t::want` to include, if enumeration doesn't provide these, they will be additionally fetched.
+ stat_t::want contents_include_metadata{stat_t::want::none};
+
+ /*! \brief Enumerated contents, and what parts of their `stat_t` is valid.
+ */
+ struct contents_type : public std::vector<std::pair<filesystem::path, stat_t>>
+ {
+ //! The metadata valid within all the `stat_t` in the contents traversed.
+ stat_t::want metadata{stat_t::want::none};
+ };
+
+ //! Default construtor
+ contents_visitor() = default;
+ //! Construct an instance
+ explicit contents_visitor(stat_t::want _metadata, bool _include_files = true, bool _include_directories = true, bool _include_symlinks = true)
+ : contents_include_files(_include_files)
+ , contents_include_directories(_include_directories)
+ , contents_include_symlinks(_include_symlinks)
+ , contents_include_metadata(_metadata)
+ {
+ }
+
+ friend inline result<contents_type> contents(const path_handle &dirh, contents_visitor *visitor, size_t threads, bool force_slow_path) noexcept;
+
+ protected:
+ struct _state_type
+ {
+ const path_handle &rootdirh;
+ std::atomic<size_t> rootdirpathlen{0};
+ std::atomic<stat_t::want> metadata{stat_t::want::all};
+ contents_type contents;
+
+ std::mutex lock;
+ std::vector<std::shared_ptr<contents_type>> all_thread_contents;
+
+ explicit _state_type(const path_handle &_rootdirh)
+ : rootdirh(_rootdirh)
+ {
+ }
+ };
+
+ static std::shared_ptr<contents_type> _thread_contents(_state_type *state) noexcept
+ {
+ try
+ {
+ static thread_local std::weak_ptr<contents_type> mycontents;
+ auto ret = mycontents.lock();
+ if(ret)
+ {
+ return ret;
+ }
+ ret = std::make_unique<contents_type>();
+ mycontents = ret;
+ std::lock_guard<std::mutex> g(state->lock);
+ state->all_thread_contents.push_back(ret);
+ return ret;
+ }
+ catch(...)
+ {
+ return {};
+ }
+ }
+
+ public:
+ /*! \brief The default implementation accumulates the contents into thread
+ local storage. At traverse end, all the thread local storages are coalesced
+ into a single result, the member variable `contents`.
+ */
+ virtual result<void> post_enumeration(void *data, const directory_handle &dirh, directory_handle::buffers_type &contents, size_t depth) noexcept
+ {
+ try
+ {
+ auto *state = (_state_type *) data;
+ (void) depth;
+ if(!contents.empty())
+ {
+ contents_type toadd;
+ toadd.reserve(contents.size());
+ filesystem::path dirhpath;
+ for(;;)
+ {
+ OUTCOME_TRY(dirhpath, dirh.current_path());
+ auto rootdirpathlen = state->rootdirpathlen.load(std::memory_order_relaxed);
+ if(dirhpath.native().size() <= rootdirpathlen)
+ {
+ break;
+ }
+ dirhpath = dirhpath.native().substr(rootdirpathlen);
+ auto r = directory_handle::directory(state->rootdirh, dirhpath);
+ if(r && r.value().unique_id() == dirh.unique_id())
+ {
+ break;
+ }
+ OUTCOME_TRY(dirhpath, state->rootdirh.current_path());
+ state->rootdirpathlen.store(dirhpath.native().size() + 1, std::memory_order_relaxed);
+ }
+ auto _metadata_ = state->metadata.load(std::memory_order_relaxed);
+ if((_metadata_ & (contents_include_metadata | contents.metadata())) != _metadata_)
+ {
+ state->metadata.store(_metadata_ & (contents_include_metadata | contents.metadata()), std::memory_order_relaxed);
+ }
+ auto into = _thread_contents(state);
+ for(auto &entry : contents)
+ {
+ auto need_stat = contents_include_metadata & ~contents.metadata();
+ if((contents_include_files && entry.stat.st_type == filesystem::file_type::regular) ||
+ (contents_include_directories && entry.stat.st_type == filesystem::file_type::directory) ||
+ (contents_include_symlinks && entry.stat.st_type == filesystem::file_type::symlink))
+ {
+ if(!need_stat)
+ {
+ into->emplace_back(dirhpath / entry.leafname, entry.stat);
+ continue;
+ }
+ auto r = file_handle::file(dirh, entry.leafname, file_handle::mode::attr_read);
+ if(r)
+ {
+ OUTCOME_TRY(entry.stat.fill(r.value(), need_stat));
+ into->emplace_back(dirhpath / entry.leafname, entry.stat);
+ }
+ }
+ }
+ }
+ return success();
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+ }
+
+ /*! \brief Called when a traversal finishes, this default implementation merges
+ all the thread local results into `contents`, and deallocates the thread local
+ results.
+ */
+ virtual result<size_t> finished(void *data, result<size_t> result) noexcept
+ {
+ try
+ {
+ auto *state = (_state_type *) data;
+ state->contents.clear();
+ state->contents.metadata = state->metadata.load(std::memory_order_relaxed);
+ size_t count = 0;
+ for(auto &i : state->all_thread_contents)
+ {
+ count += i->size();
+ }
+ state->contents.reserve(count);
+ for(auto &i : state->all_thread_contents)
+ {
+ state->contents.insert(state->contents.end(), std::make_move_iterator(i->begin()), std::make_move_iterator(i->end()));
+ }
+ state->all_thread_contents.clear();
+ return result;
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+ }
+ };
+
+
+ /*! \brief Calculate the contents of everything within and under `dirh`. What
+ is returned is unordered.
+
+ This is a very thin veneer over `traverse()` which came out of the fact that
+ I kept writing "get me the contents" traversal visitors again and again, so
+ eventually I just wrote a library edition. Its only "clever" bit is that it
+ stores the contents in thread local storage, and merges the contents afterwards.
+
+ It is race free to concurrent relocations of `dirh`. It is entirely implemented
+ in header-only code, as it is very simple.
+ */
+ inline result<contents_visitor::contents_type> contents(const path_handle &dirh, contents_visitor *visitor = nullptr, size_t threads = 0,
+ bool force_slow_path = false) noexcept
+ {
+ contents_visitor default_visitor;
+ if(visitor == nullptr)
+ {
+ visitor = &default_visitor;
+ }
+ contents_visitor::_state_type state(dirh);
+ OUTCOME_TRY(auto dirhpath, dirh.current_path());
+ state.rootdirpathlen.store(dirhpath.native().size() + 1, std::memory_order_relaxed);
+ OUTCOME_TRY(traverse(dirh, visitor, threads, &state, force_slow_path));
+ return {std::move(state.contents)};
+ }
+
+} // namespace algorithm
+
+LLFIO_V2_NAMESPACE_END
+
+#endif
diff --git a/include/llfio/v2.0/algorithm/difference.hpp b/include/llfio/v2.0/algorithm/difference.hpp
new file mode 100644
index 00000000..81de992e
--- /dev/null
+++ b/include/llfio/v2.0/algorithm/difference.hpp
@@ -0,0 +1,137 @@
+/* A filesystem algorithm which generates the difference between two directory trees
+(C) 2020 Niall Douglas <http://www.nedproductions.biz/> (12 commits)
+File Created: July 2020
+
+
+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)
+*/
+
+#ifndef LLFIO_ALGORITHM_DIFFERENCE_HPP
+#define LLFIO_ALGORITHM_DIFFERENCE_HPP
+
+#include "traverse.hpp"
+
+//! \file compare.hpp Provides a directory tree difference algorithm.
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+namespace algorithm
+{
+ /*!
+ */
+ struct difference_item
+ {
+ enum change_t : uint8_t
+ {
+ unknown,
+ content_metadata_changed, //!< Maximum extent or modified timestamp metadata has changed
+ noncontent_metadata_changed, //!< Non-content metadata (perms, non-modified timestamps etc) has changed
+ file_added, //!< A new file was added
+ file_renamed, //!< A file was renamed within this tree
+ file_linked, //!< A hard link to something else also in this tree was added
+ file_removed, //!< A file was removed
+ directory_added, //!< A new directory was added
+ directory_renamed, //!< A directory was renamed within this tree
+ directory_removed, //!< A directory was removed
+ symlink_added, //!< A symlink was added
+ symlink_removed //!< A symlink was removed
+ } changed{change_t::unknown};
+ int8_t content_comparison{0}; //!< `memcmp()` of content, if requested
+ };
+ /*! \brief A visitor for the filesystem traversal and comparison algorithm.
+
+ Note that at any time, returning a failure causes `compare()` to exit as soon
+ as possible with the same failure.
+
+ You can override the members here inherited from `traverse_visitor`, however note
+ that `compare()` is entirely implemented using `traverse()`, so not calling the
+ implementations here will affect operation.
+ */
+ struct compare_visitor : public traverse_visitor
+ {
+ //! This override ignores failures to traverse into the directory.
+ virtual result<directory_handle> directory_open_failed(void *data, result<void>::error_type &&error, const directory_handle &dirh, path_view leaf,
+ size_t depth) noexcept override
+ {
+ (void) error;
+ (void) dirh;
+ (void) leaf;
+ (void) depth;
+ auto *state = (traversal_summary *) data;
+ lock_guard<spinlock> g(state->_lock);
+ state->directory_opens_failed++;
+ return success(); // ignore failure to enter
+ }
+ //! This override implements the summary
+ virtual result<void> post_enumeration(void *data, const directory_handle &dirh, directory_handle::buffers_type &contents, size_t depth) noexcept override
+ {
+ try
+ {
+ auto *state = (traversal_summary *) data;
+ traversal_summary acc;
+ acc.max_depth = depth;
+ for(auto &entry : contents)
+ {
+ OUTCOME_TRY(accumulate(acc, state, &dirh, entry, contents.metadata()));
+ }
+ state->operator+=(acc);
+ return success();
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+ }
+ };
+
+ /*! \brief Summarise the directory identified `dirh`, 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.
+ You should specify what metadata you wish to summarise, if this is a subset of
+ what metadata `directory_handle::read()` returns, performance will be considerably
+ better. The default summarises all possible metadata.
+
+ This is a trivial implementation on top of `algorithm::traverse()`, indeed it is
+ implemented entirely as header code. You should review the documentation for
+ `algorithm::traverse()`, as this algorithm is entirely implemented using that algorithm.
+ */
+ inline result<traversal_summary> summarize(const path_handle &dirh, 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);
+ summarize_visitor default_visitor;
+ if(visitor == nullptr)
+ {
+ visitor = &default_visitor;
+ }
+ result<traversal_summary> state(in_place_type<traversal_summary>);
+ state.assume_value().want = want;
+ directory_entry entry{{}, stat_t(nullptr)};
+ 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));
+ return state;
+ }
+
+} // namespace algorithm
+
+LLFIO_V2_NAMESPACE_END
+
+#endif
diff --git a/include/llfio/v2.0/detail/impl/clone.ipp b/include/llfio/v2.0/detail/impl/clone.ipp
index 4ceb58e9..12317038 100644
--- a/include/llfio/v2.0/detail/impl/clone.ipp
+++ b/include/llfio/v2.0/detail/impl/clone.ipp
@@ -53,8 +53,7 @@ namespace algorithm
{
stat_t deststat(nullptr);
OUTCOME_TRY(deststat.fill(r.value()));
- if((stat.st_type == deststat.st_type) && (stat.st_mtim == deststat.st_mtim) && (stat.st_ctim == deststat.st_ctim) &&
- (stat.st_birthtim == deststat.st_birthtim) && (stat.st_size == deststat.st_size)
+ if((stat.st_type == deststat.st_type) && (stat.st_mtim == deststat.st_mtim) && (stat.st_size == deststat.st_size)
#ifndef _WIN32
&& (stat.st_perms == deststat.st_perms) && (stat.st_uid == deststat.st_uid) && (stat.st_gid == deststat.st_gid) && (stat.st_rdev == deststat.st_rdev)
#endif
diff --git a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
index b13c5909..9a013611 100644
--- a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
@@ -45,9 +45,10 @@ namespace detail
return success();
#endif
}
-}
+} // namespace detail
-LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d, bool atomic_replace) noexcept
+LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target,
+ deadline d, bool atomic_replace, bool exists_is_ok) noexcept
{
std::chrono::steady_clock::time_point began_steady;
std::chrono::system_clock::time_point end_utc;
@@ -101,7 +102,8 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(con
return posix_error();
}
// std::cerr << "renameat " << dirh.native_handle().fd << " " << randomname << " " << filename << std::endl;
- if(-1 == ::renameat(dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, randomname.c_str(), dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, filename.c_str()))
+ if(-1 == ::renameat(dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, randomname.c_str(), dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD,
+ filename.c_str()))
{
return posix_error();
}
@@ -113,6 +115,10 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(con
// std::cerr << "symlinkat " << zpath.buffer << " " << dirh.native_handle().fd << " " << filename << std::endl;
if(-1 == ::symlinkat(zpath.buffer, dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, filename.c_str()))
{
+ if(exists_is_ok && EEXIST == errno)
+ {
+ return success();
+ }
return posix_error();
}
return success();
@@ -280,7 +286,9 @@ result<void> symlink_handle::unlink(deadline d) noexcept
}
#endif
-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
{
result<symlink_handle> ret(symlink_handle(native_handle_type(), 0, 0, flags));
native_handle_type &nativeh = ret.value()._v;
@@ -295,11 +303,11 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
nativeh.behaviour &= ~native_handle_type::disposition::nonblocking;
nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
#if !LLFIO_SYMLINK_HANDLE_IS_FAKED
- path_handle dirh;
+ path_handle _dirh_, *dirh = &_dirh_;
path_type leafname;
#else
(void) attribs;
- path_handle &dirh = ret.value()._dirh;
+ path_handle *dirh = &ret.value()._dirh;
path_type &leafname = ret.value()._leafname;
#endif
int dirhfd = AT_FDCWD;
@@ -312,10 +320,11 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
{
#if !LLFIO_SYMLINK_HANDLE_IS_FAKED
dirhfd = base.native_handle().fd;
+ dirh = const_cast<path_handle *>(&base);
#else
- OUTCOME_TRY(auto &&dh, base.clone());
- dirh = path_handle(std::move(dh));
- dirhfd = dirh.native_handle().fd;
+ OUTCOME_TRY(auto dh, base.clone());
+ *dirh = path_handle(std::move(dh));
+ dirhfd = dirh->native_handle().fd;
#endif
}
else
@@ -323,9 +332,9 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
if(!path_parent.empty())
#endif
{
- OUTCOME_TRY(auto &&dh, path_handle::path(base, path_parent.empty() ? "." : path_parent));
- dirh = std::move(dh);
- dirhfd = dirh.native_handle().fd;
+ // If faking the symlink, write this directly into the member variable cache
+ OUTCOME_TRY(*dirh, path_handle::path(base, path_parent.empty() ? "." : path_parent));
+ dirhfd = dirh->native_handle().fd;
}
}
catch(...)
@@ -350,19 +359,16 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
case creation::always_new:
{
// Create an empty symlink, ignoring any file exists errors, unless only_if_not_exist
- auto r = ret.value()._create_symlink(dirh, leafname,
+ auto r = ret.value()._create_symlink(*dirh, leafname,
#if defined(__linux__) || defined(__APPLE__)
".", // Linux and Mac OS is not POSIX conforming here, and refuses to create empty symlinks
#else
"",
#endif
- std::chrono::seconds(10), creation::always_new == _creation);
+ std::chrono::seconds(10), creation::always_new == _creation, creation::if_needed == _creation);
if(!r)
{
- if(_creation == creation::only_if_not_exist || r.error() != errc::file_exists)
- {
- return std::move(r).error();
- }
+ return std::move(r).error();
}
break;
}
@@ -448,7 +454,7 @@ result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle:
const path_handle &dirh = _dirh;
const path_type &filename = _leafname;
#endif
- OUTCOME_TRY(_create_symlink(dirh, filename, req.buffers.path(), d, true));
+ OUTCOME_TRY(_create_symlink(dirh, filename, req.buffers.path(), d, true, false));
#if !LLFIO_SYMLINK_HANDLE_IS_FAKED
{
// Current fd now points at the symlink we just atomically replaced, so need to reopen
diff --git a/include/llfio/v2.0/llfio.hpp b/include/llfio/v2.0/llfio.hpp
index 01da3bb9..7beb7620 100644
--- a/include/llfio/v2.0/llfio.hpp
+++ b/include/llfio/v2.0/llfio.hpp
@@ -73,6 +73,7 @@ import LLFIO_MODULE_NAME;
#include "symlink_handle.hpp"
#include "algorithm/clone.hpp"
+#include "algorithm/contents.hpp"
#include "algorithm/handle_adapter/cached_parent.hpp"
#include "algorithm/reduce.hpp"
#include "algorithm/shared_fs_mutex/atomic_append.hpp"
diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp
index 8fb5787c..87d3dc64 100644
--- a/include/llfio/v2.0/path_view.hpp
+++ b/include/llfio/v2.0/path_view.hpp
@@ -49,7 +49,8 @@ clang defines __cpp_char8_t if there is a char8_t.
MSVC seems to only implement char8_t if C++ 20 is enabled.
*/
#ifndef LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED
-#if(defined(_MSC_VER) && !defined(__clang__) && !_HAS_CXX20) || (defined(__GNUC__) && !defined(__clang__) && !defined(__CHAR8_TYPE__)) || (defined(__clang__) && !defined(__cpp_char8_t))
+#if(defined(_MSC_VER) && !defined(__clang__) && !_HAS_CXX20) || (defined(__GNUC__) && !defined(__clang__) && !defined(__CHAR8_TYPE__)) || \
+(defined(__clang__) && !defined(__cpp_char8_t))
#define LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED 1
#else
#define LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED 0
@@ -123,17 +124,27 @@ namespace detail
{
};
- LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer, size_t src_buffer_length);
-
- LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer, size_t src_buffer_length);
- LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer, size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length,
+ const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char *src_buffer,
+ size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer,
+ size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer,
+ size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer,
+ size_t src_buffer_length);
+
+ LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length,
+ const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char *src_buffer,
+ size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer,
+ size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer,
+ size_t src_buffer_length);
+ LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer,
+ size_t src_buffer_length);
class path_view_iterator;
} // namespace detail
@@ -187,7 +198,8 @@ public:
private:
static constexpr auto _npos = string_view::npos;
- union {
+ union
+ {
const byte *_bytestr{nullptr};
const char *_charstr;
const wchar_t *_wcharstr;
@@ -442,9 +454,14 @@ private:
return a.compare(b);
}
// Identical source encodings compare lexiographically
- template <class DestT, class Deleter, size_t _internal_buffer_size, class CharT> static int _compare(basic_string_view<CharT> a, basic_string_view<CharT> b) noexcept { return a.compare(b); }
+ template <class DestT, class Deleter, size_t _internal_buffer_size, class CharT>
+ static int _compare(basic_string_view<CharT> a, basic_string_view<CharT> b) noexcept
+ {
+ return a.compare(b);
+ }
// Disparate source encodings compare via c_str
- template <class DestT, class Deleter, size_t _internal_buffer_size, class Char1T, class Char2T> static int _compare(basic_string_view<Char1T> a, basic_string_view<Char2T> b) noexcept
+ template <class DestT, class Deleter, size_t _internal_buffer_size, class Char1T, class Char2T>
+ static int _compare(basic_string_view<Char1T> a, basic_string_view<Char2T> b) noexcept
{
c_str<DestT, Deleter, _internal_buffer_size> _a({a.data(), a.size(), false}, true);
c_str<DestT, Deleter, _internal_buffer_size> _b({b.data(), b.size(), false}, true);
@@ -482,18 +499,25 @@ public:
and `c_str` is never invoked as the two sources are byte
compared directly.
*/
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>))
constexpr int compare(path_view_component p) const
{
- return _invoke([&p](const auto &self) { return p._invoke([&self](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, other); }); });
+ return _invoke(
+ [&p](const auto &self) { return p._invoke([&self](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, other); }); });
}
//! \overload
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size, class Char)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_acceptable<Char>))
- constexpr int compare(const Char *s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true)); }
+ constexpr int compare(const Char *s) const noexcept
+ {
+ return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true));
+ }
//! \overload
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size, class Char)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_chartype_acceptable<Char>))
constexpr int compare(const basic_string_view<Char> s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s)); }
@@ -518,7 +542,8 @@ public:
`operator new[]`. You can use an externally supplied larger temporary buffer to avoid
dynamic memory allocation in all situations.
*/
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>))
struct c_str
{
@@ -537,12 +562,16 @@ public:
const value_type *buffer{nullptr};
private:
- template <class U, class source_type> void _make_passthrough(path_view_component /*unused*/, bool /*unused*/, U & /*unused*/, const source_type * /*unused*/) {}
+ template <class U, class source_type>
+ void _make_passthrough(path_view_component /*unused*/, bool /*unused*/, U & /*unused*/, const source_type * /*unused*/)
+ {
+ }
template <class U> void _make_passthrough(path_view_component view, bool no_zero_terminate, U &allocate, const value_type *source)
{
using LLFIO_V2_NAMESPACE::basic_string_view;
// If the consuming API is a NT kernel API, and we have / in the path, we shall need to do slash conversion
- const bool needs_slash_translation = (filesystem::path::preferred_separator != '/') && no_zero_terminate && view._invoke([](auto sv) { return sv.find('/') != _npos; });
+ const bool needs_slash_translation =
+ (filesystem::path::preferred_separator != '/') && no_zero_terminate && view._invoke([](auto sv) { return sv.find('/') != _npos; });
length = view._length;
if(!needs_slash_translation && (no_zero_terminate || view._zero_terminated))
{
@@ -896,11 +925,16 @@ inline constexpr bool operator!=(const CharT * /*unused*/, path_view_component /
static_assert(!path_view_component::is_source_acceptable<CharT>, "Do not use operator!= with path_view_component and a string literal, use .compare<>()");
return false;
}
-//! \brief Visit the underlying source for a `path_view_component`
+//! \brief Visit the underlying source for a `path_view_component` (LLFIO backwards compatible overload)
template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view_component view, F &&f)
{
return view._invoke(static_cast<F &&>(f));
}
+//! \brief Visit the underlying source for a `path_view_component` (std compatible overload)
+template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(F &&f, path_view_component view)
+{
+ return view._invoke(static_cast<F &&>(f));
+}
namespace detail
{
@@ -1097,22 +1131,26 @@ public:
{
}
- //! Implicitly constructs a path view from a zero terminated `const char *`. Convenience wrapper for the `byte` constructor. The input string MUST continue to exist for this view to be valid.
+ //! Implicitly constructs a path view from a zero terminated `const char *`. Convenience wrapper for the `byte` constructor. The input string MUST continue to
+ //! exist for this view to be valid.
constexpr path_view(const char *v) noexcept // NOLINT
: _state(v, detail::constexpr_strlen(v), true)
{
}
- //! Implicitly constructs a path view from a zero terminated `const wchar_t *`. Convenience wrapper for the `byte` constructor. The input string MUST continue to exist for this view to be valid.
+ //! Implicitly constructs a path view from a zero terminated `const wchar_t *`. Convenience wrapper for the `byte` constructor. The input string MUST continue
+ //! to exist for this view to be valid.
constexpr path_view(const wchar_t *v) noexcept // NOLINT
: _state(v, detail::constexpr_strlen(v), true)
{
}
- //! Implicitly constructs a path view from a zero terminated `const char8_t *`. Performs a UTF-8 to native encoding if necessary. The input string MUST continue to exist for this view to be valid.
+ //! Implicitly constructs a path view from a zero terminated `const char8_t *`. Performs a UTF-8 to native encoding if necessary. The input string MUST
+ //! continue to exist for this view to be valid.
constexpr path_view(const char8_t *v) noexcept // NOLINT
: _state(v, detail::constexpr_strlen(v), true)
{
}
- //! Implicitly constructs a path view from a zero terminated `const char16_t *`. Performs a UTF-16 to native encoding if necessary. The input string MUST continue to exist for this view to be valid.
+ //! Implicitly constructs a path view from a zero terminated `const char16_t *`. Performs a UTF-16 to native encoding if necessary. The input string MUST
+ //! continue to exist for this view to be valid.
constexpr path_view(const char16_t *v) noexcept // NOLINT
: _state(v, detail::constexpr_strlen(v), true)
{
@@ -1457,21 +1495,28 @@ public:
and `c_str` is never invoked as the two sources are byte
compared directly.
*/
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size)
LLFIO_TREQUIRES(LLFIO_TPRED(path_view::is_source_acceptable<T>))
constexpr inline int compare(path_view p) const;
//! \overload
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size, class Char)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_acceptable<Char>))
- constexpr int compare(const Char *s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true)); }
+ constexpr int compare(const Char *s) const noexcept
+ {
+ return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true));
+ }
//! \overload
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size, class Char)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_chartype_acceptable<Char>))
constexpr int compare(const basic_string_view<Char> s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s)); }
//! Instantiate from a `path_view` to get a path suitable for feeding to other code. See `path_view_component::c_str`.
- LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size)
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>,
+ size_t _internal_buffer_size = default_internal_buffer_size)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>))
struct c_str : public path_view_component::c_str<T, Deleter, _internal_buffer_size>
{
@@ -1507,11 +1552,16 @@ template <class T, class Deleter, size_t _internal_buffer_size, typename std::en
#endif
friend struct c_str;
};
-//! \brief Visit the underlying source for a `path_view`
+//! \brief Visit the underlying source for a `path_view` (LLFIO backwards compatible overload)
template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view view, F &&f)
{
return view._state._invoke(static_cast<F &&>(f));
}
+//! \brief Visit the underlying source for a `path_view` (std compatible overload)
+template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(F &&f, path_view view)
+{
+ return view._state._invoke(static_cast<F &&>(f));
+}
inline std::ostream &operator<<(std::ostream &s, const path_view &v)
{
return s << v._state;
@@ -1807,6 +1857,86 @@ constexpr inline int path_view::compare(path_view o) const
return 0; // identical
}
+namespace detail
+{
+ template <bool do_concat> struct path_view_concatenate_visitor
+ {
+ filesystem::path &a;
+ explicit path_view_concatenate_visitor(filesystem::path &_a)
+ : a(_a)
+ {
+ }
+ template <class CharT> void operator()(basic_string_view<CharT> sv) const
+ {
+ if(do_concat)
+ {
+ a.concat(sv);
+ }
+ else
+ {
+ a.append(sv);
+ }
+ }
+ void operator()(basic_string_view<char8_t> sv)
+ {
+#if(__cplusplus >= 202000 || _HAS_CXX20) && (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION > 10000 /* approx start of 2020 */)
+ if(do_concat)
+ {
+ a.concat(sv);
+ }
+ else
+ {
+ a.append(sv);
+ }
+#else
+ if(do_concat)
+ {
+ a+=filesystem::u8path((const char *) sv.data(), (const char *) sv.data() + sv.size());
+ }
+ else
+ {
+ a/=filesystem::u8path((const char *) sv.data(), (const char *) sv.data() + sv.size());
+ }
+#endif
+ }
+ };
+} // namespace detail
+
+//! Append a path view component to a path
+inline filesystem::path &operator+=(filesystem::path &a, path_view_component b)
+{
+ visit(b, detail::path_view_concatenate_visitor<true>(a));
+ return a;
+}
+//! Append a path view component to a path
+inline filesystem::path &operator/=(filesystem::path &a, path_view_component b)
+{
+ visit(b, detail::path_view_concatenate_visitor<false>(a));
+ return a;
+}
+//! Append a path view component to a path
+inline filesystem::path operator/(filesystem::path a, path_view_component b)
+{
+ return a /= b;
+}
+//! Append a path view to a path
+inline filesystem::path &operator+=(filesystem::path &a, path_view b)
+{
+ visit(b, detail::path_view_concatenate_visitor<true>(a));
+ return a;
+}
+//! Append a path view to a path
+inline filesystem::path &operator/=(filesystem::path &a, path_view b)
+{
+ visit(b, detail::path_view_concatenate_visitor<false>(a));
+ return a;
+}
+//! Append a path view to a path
+inline filesystem::path operator/(filesystem::path a, path_view b)
+{
+ return a /= b;
+}
+
#ifndef NDEBUG
static_assert(std::is_trivially_copyable<path_view>::value, "path_view is not a trivially copyable!");
diff --git a/include/llfio/v2.0/stat.hpp b/include/llfio/v2.0/stat.hpp
index 265207b0..03a861e8 100644
--- a/include/llfio/v2.0/stat.hpp
+++ b/include/llfio/v2.0/stat.hpp
@@ -85,54 +85,92 @@ struct LLFIO_DECL stat_t // NOLINT
int16_t st_gid; /*!< group ID of the file (POSIX only) */
dev_t st_rdev; /*!< id of file if special (POSIX only) */
#endif
- std::chrono::system_clock::time_point st_atim; /*!< time of last access (Windows, POSIX) */
- std::chrono::system_clock::time_point st_mtim; /*!< time of last data modification (Windows, POSIX) */
- std::chrono::system_clock::time_point st_ctim; /*!< time of last status change (Windows, POSIX) */
- handle::extent_type st_size; /*!< file size, in bytes (Windows, POSIX) */
- handle::extent_type st_allocated; /*!< bytes allocated for file (Windows, POSIX) */
- handle::extent_type st_blocks; /*!< number of blocks allocated (Windows, POSIX) */
- uint16_t st_blksize; /*!< block size used by this device (Windows, POSIX) */
- uint32_t st_flags; /*!< user defined flags for file (FreeBSD, OS X, zero
- otherwise) */
- uint32_t st_gen; /*!< file generation number (FreeBSD, OS X, zero
- otherwise)*/
- std::chrono::system_clock::time_point st_birthtim; /*!< time of file creation (Windows, POSIX) */
+ union
+ {
+ std::chrono::system_clock::time_point st_atim; /*!< time of last access (Windows, POSIX) */
+ };
+ union
+ {
+ std::chrono::system_clock::time_point st_mtim; /*!< time of last data modification (Windows, POSIX) */
+ };
+ union
+ {
+ std::chrono::system_clock::time_point st_ctim; /*!< time of last status change (Windows, POSIX) */
+ };
+ handle::extent_type st_size; /*!< file size, in bytes (Windows, POSIX) */
+ handle::extent_type st_allocated; /*!< bytes allocated for file (Windows, POSIX) */
+ handle::extent_type st_blocks; /*!< number of blocks allocated (Windows, POSIX) */
+ uint16_t st_blksize; /*!< block size used by this device (Windows, POSIX) */
+ uint32_t st_flags; /*!< user defined flags for file (FreeBSD, OS X, zero
+ otherwise) */
+ uint32_t st_gen; /*!< file generation number (FreeBSD, OS X, zero
+ otherwise)*/
+ union
+ {
+ std::chrono::system_clock::time_point st_birthtim; /*!< time of file creation (Windows, POSIX) */
+ };
+ static_assert(std::is_trivially_destructible<std::chrono::system_clock::time_point>::value,
+ "std::chrono::system_clock::time_point is not trivally destructible!");
unsigned st_sparse : 1; /*!< if this file is sparse, or this directory capable of sparse files (Windows, POSIX) */
unsigned st_compressed : 1; /*!< if this file is compressed, or this directory capable of compressed files (Windows, Linux) */
unsigned st_reparse_point : 1; /*!< if this file or directory is a reparse point (Windows) */
//! Used to indicate what metadata should be filled in
- QUICKCPPLIB_BITFIELD_BEGIN(want)
- {
- dev = 1 << 0, ino = 1 << 1, type = 1 << 2, perms = 1 << 3, nlink = 1 << 4, uid = 1 << 5, gid = 1 << 6, rdev = 1 << 7, atim = 1 << 8, mtim = 1 << 9, ctim = 1 << 10, size = 1 << 11, allocated = 1 << 12, blocks = 1 << 13, blksize = 1 << 14, flags = 1 << 15, gen = 1 << 16, birthtim = 1 << 17, sparse = 1 << 24,
- compressed = 1 << 25, reparse_point = 1 << 26, all = static_cast<unsigned>(-1), none = 0
- }
- QUICKCPPLIB_BITFIELD_END(want)
+ QUICKCPPLIB_BITFIELD_BEGIN(want){dev = 1 << 0,
+ ino = 1 << 1,
+ type = 1 << 2,
+ perms = 1 << 3,
+ nlink = 1 << 4,
+ uid = 1 << 5,
+ gid = 1 << 6,
+ rdev = 1 << 7,
+ atim = 1 << 8,
+ mtim = 1 << 9,
+ ctim = 1 << 10,
+ size = 1 << 11,
+ allocated = 1 << 12,
+ blocks = 1 << 13,
+ blksize = 1 << 14,
+ flags = 1 << 15,
+ gen = 1 << 16,
+ birthtim = 1 << 17,
+ sparse = 1 << 24,
+ compressed = 1 << 25,
+ reparse_point = 1 << 26,
+ all = static_cast<unsigned>(-1),
+ none = 0} QUICKCPPLIB_BITFIELD_END(want)
//! Constructs a UNINITIALIZED instance i.e. full of random garbage
- stat_t() {} // NOLINT CANNOT be constexpr because we are INTENTIONALLY not initialising the storage
+ stat_t()
+ {
+ } // NOLINT CANNOT be constexpr because we are INTENTIONALLY not initialising the storage
//! Constructs a zeroed instance
- constexpr explicit stat_t(std::nullptr_t) noexcept : st_dev(0), // NOLINT
- st_ino(0), // NOLINT
- st_type(filesystem::file_type::unknown), // NOLINT
+ constexpr explicit stat_t(std::nullptr_t) noexcept
+ : st_dev(0) // NOLINT
+ , st_ino(0) // NOLINT
+ , st_type(filesystem::file_type::unknown) // NOLINT
#ifndef _WIN32
- st_perms(0), // NOLINT
+ , st_perms(0) // NOLINT
#endif
- st_nlink(0), // NOLINT
+ , st_nlink(0) // NOLINT
#ifndef _WIN32
- st_uid(0), // NOLINT
- st_gid(0), // NOLINT
- st_rdev(0), // NOLINT
+ , st_uid(0) // NOLINT
+ , st_gid(0) // NOLINT
+ , st_rdev(0) // NOLINT
#endif
- st_size(0), // NOLINT
- st_allocated(0), // NOLINT
- st_blocks(0), // NOLINT
- st_blksize(0), // NOLINT
- st_flags(0), // NOLINT
- st_gen(0), // NOLINT
- st_sparse(0), // NOLINT
- st_compressed(0), // NOLINT
- st_reparse_point(0) // NOLINT
+ , st_atim{} // NOLINT
+ , st_mtim{} // NOLINT
+ , st_ctim{} // NOLINT
+ , st_size(0) // NOLINT
+ , st_allocated(0) // NOLINT
+ , st_blocks(0) // NOLINT
+ , st_blksize(0) // NOLINT
+ , st_flags(0) // NOLINT
+ , st_gen(0) // NOLINT
+ , st_birthtim{} // NOLINT
+ , st_sparse(0) // NOLINT
+ , st_compressed(0) // NOLINT
+ , st_reparse_point(0) // NOLINT
{
}
#ifdef __cpp_exceptions
@@ -147,6 +185,24 @@ struct LLFIO_DECL stat_t // NOLINT
}
}
#endif
+ //! Equality comparison
+ bool operator==(const stat_t &o) const noexcept {
+ // This is probably bold ...
+ return 0 == memcmp(this, &o, sizeof(o));
+ }
+ //! Inequality comparison
+ bool operator!=(const stat_t &o) const noexcept
+ {
+ // This is probably bold ...
+ return 0 != memcmp(this, &o, sizeof(o));
+ }
+ //! Ordering
+ bool operator<(const stat_t &o) const noexcept
+ {
+ // This is probably bold ...
+ return memcmp(this, &o, sizeof(o)) < 0;
+ }
+
/*! Fills the structure with metadata.
\return The number of items filled in. You should use a nullptr constructed structure if you wish
@@ -176,7 +232,7 @@ struct LLFIO_DECL stat_t // NOLINT
- `perms`, `uid`, `gid` (POSIX only)
- `atim` (Windows, POSIX)
- `mtim` (Windows, POSIX)
- - `birthtim` (Windows, FreeBSD, OS X)
+ - `birthtim` (Windows, POSIX)
Note that on POSIX, setting birth time involves two syscalls, the first of which
temporarily sets the modified date to the birth time, which is racy. This is
@@ -188,6 +244,7 @@ struct LLFIO_DECL stat_t // NOLINT
*/
LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<want> stamp(handle &h, want wanted = want::all) noexcept;
};
+static_assert(std::is_trivially_copyable<stat_t>::value, "stat_t is not trivally copyable!");
LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/symlink_handle.hpp b/include/llfio/v2.0/symlink_handle.hpp
index 39706343..9190f109 100644
--- a/include/llfio/v2.0/symlink_handle.hpp
+++ b/include/llfio/v2.0/symlink_handle.hpp
@@ -43,9 +43,10 @@ Distributed under the Boost Software License, Version 1.0.
#pragma warning(disable : 4251) // dll interface
#endif
-extern "C" {
+extern "C"
+{
struct stat;
-}
+}
LLFIO_V2_NAMESPACE_EXPORT_BEGIN
@@ -89,7 +90,8 @@ class LLFIO_DECL symlink_handle : public handle, public fs_handle
#ifndef _WIN32
friend result<void> detail::stat_from_symlink(struct stat &s, const handle &h) noexcept;
- LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> _create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d, bool atomic_replace) noexcept;
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> _create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d,
+ bool atomic_replace, bool exists_is_ok) noexcept;
#endif
public:
@@ -139,7 +141,7 @@ public:
constexpr buffers_type() {} // NOLINT
/*! Constructor
- */
+ */
constexpr buffers_type(path_view link, symlink_type type = symlink_type::symbolic)
: _link(link)
, _type(type)
@@ -147,7 +149,11 @@ public:
}
~buffers_type() = default;
//! Move constructor
- buffers_type(buffers_type &&o) noexcept : _link(o._link), _type(o._type), _kernel_buffer(std::move(o._kernel_buffer)), _kernel_buffer_size(o._kernel_buffer_size)
+ buffers_type(buffers_type &&o) noexcept
+ : _link(o._link)
+ , _type(o._type)
+ , _kernel_buffer(std::move(o._kernel_buffer))
+ , _kernel_buffer_size(o._kernel_buffer_size)
{
o._link = {};
o._type = symlink_type::none;
@@ -195,7 +201,7 @@ public:
size_t _kernel_buffer_size{0};
};
/*! The constant buffers type used by this handle for writes, which is a single item sequence of `path_view`.
- */
+ */
struct const_buffers_type
{
//! Type of the pointer to the buffer.
@@ -215,7 +221,9 @@ public:
}
~const_buffers_type() = default;
//! Move constructor
- const_buffers_type(const_buffers_type &&o) noexcept : _link(o._link), _type(o._type)
+ const_buffers_type(const_buffers_type &&o) noexcept
+ : _link(o._link)
+ , _type(o._type)
{
o._link = {};
o._type = symlink_type::none;
@@ -275,7 +283,10 @@ public:
//! Convenience constructor constructing from anything a `span<char>` can construct from
LLFIO_TEMPLATE(class... Args)
LLFIO_TREQUIRES(LLFIO_TPRED(std::is_constructible<span<char>, Args...>::value))
- constexpr io_request(Args &&... args) noexcept : io_request(span<char>(static_cast<Args &&>(args)...)) {}
+ constexpr io_request(Args &&... args) noexcept
+ : io_request(span<char>(static_cast<Args &&>(args)...))
+ {
+ }
};
//! Specialisation for writing symlinks
template <bool ____> struct io_request<const_buffers_type, ____> // workaround lack of nested specialisation support on older compilers
@@ -291,11 +302,17 @@ public:
//! Convenience constructor constructing from anything a `path_view` can construct from
LLFIO_TEMPLATE(class... Args)
LLFIO_TREQUIRES(LLFIO_TPRED(std::is_constructible<path_view, Args...>::value))
- constexpr io_request(Args &&... args) noexcept : buffers(path_view(static_cast<Args &&>(args)...)) {}
+ constexpr io_request(Args &&... args) noexcept
+ : buffers(path_view(static_cast<Args &&>(args)...))
+ {
+ }
//! Convenience constructor constructing a specific type of link from anything a `path_view` can construct from
LLFIO_TEMPLATE(class... Args)
LLFIO_TREQUIRES(LLFIO_TPRED(std::is_constructible<path_view, Args...>::value))
- constexpr io_request(symlink_type type, Args &&... args) noexcept : buffers(path_view(static_cast<Args &&>(args)...), type) {}
+ constexpr io_request(symlink_type type, Args &&... args) noexcept
+ : buffers(path_view(static_cast<Args &&>(args)...), type)
+ {
+ }
};
//! Default constructor
@@ -318,7 +335,8 @@ public:
#if !LLFIO_SYMLINK_HANDLE_IS_FAKED
constexpr
#endif
- explicit symlink_handle(handle &&o) noexcept : handle(std::move(o))
+ explicit symlink_handle(handle &&o) noexcept
+ : handle(std::move(o))
{
}
//! Move construction permitted
@@ -420,7 +438,8 @@ public:
\mallocs None, unless `LLFIO_SYMLINK_HANDLE_IS_FAKED` is on, in which case one.
*/
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink(const path_handle &base, path_view_type path, mode _mode = mode::read, creation _creation = creation::open_existing, flag flags = flag::none) noexcept;
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink(const path_handle &base, path_view_type path, mode _mode = mode::read,
+ creation _creation = creation::open_existing, flag flags = flag::none) noexcept;
/*! Create a symlink handle creating a uniquely named symlink on a path.
The symlink is opened exclusively with `creation::only_if_not_exist` so it
will never collide with nor overwrite any existing symlink.