diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2018-08-14 22:05:04 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2018-08-14 22:05:04 +0300 |
commit | 83b7ad9bfda9af36414d8d6acac454288cb90a3a (patch) | |
tree | 6257bfa05626926de3c45a32bfad48cc6c068157 /include | |
parent | e2434aa9d42d50651ed9685b5125f58262366081 (diff) |
symlink_handle is finished and fully working on POSIX.
Also fixed a number of vptr slicing bugs across the handle implementations, mainly caused by missing operator=() implementations.
Fixed path_handle::clone() not working on POSIX.
Diffstat (limited to 'include')
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/algorithm/cached_parent_handle_adapter.hpp | 7 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/handle.ipp | 33 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/map_handle.ipp | 3 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp | 152 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/handle.ipp | 8 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/map_handle.ipp | 7 | ||||
-rw-r--r-- | include/llfio/v2.0/file_handle.hpp | 7 | ||||
-rw-r--r-- | include/llfio/v2.0/map_handle.hpp | 11 | ||||
-rw-r--r-- | include/llfio/v2.0/mapped.hpp | 8 | ||||
-rw-r--r-- | include/llfio/v2.0/native_handle_type.hpp | 4 | ||||
-rw-r--r-- | include/llfio/v2.0/path_handle.hpp | 35 | ||||
-rw-r--r-- | include/llfio/v2.0/symlink_handle.hpp | 34 |
13 files changed, 252 insertions, 63 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp index 244d4668..99cdfd0a 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 964d174c1c44d4810789b198bbf07271cebbe8c8 -#define LLFIO_PREVIOUS_COMMIT_DATE "2018-07-24 19:29:25 +00:00" -#define LLFIO_PREVIOUS_COMMIT_UNIQUE 964d174c +#define LLFIO_PREVIOUS_COMMIT_REF e2434aa9d42d50651ed9685b5125f58262366081 +#define LLFIO_PREVIOUS_COMMIT_DATE "2018-08-01 08:52:22 +00:00" +#define LLFIO_PREVIOUS_COMMIT_UNIQUE e2434aa9 diff --git a/include/llfio/v2.0/algorithm/cached_parent_handle_adapter.hpp b/include/llfio/v2.0/algorithm/cached_parent_handle_adapter.hpp index 4f5d6ba5..308d8451 100644 --- a/include/llfio/v2.0/algorithm/cached_parent_handle_adapter.hpp +++ b/include/llfio/v2.0/algorithm/cached_parent_handle_adapter.hpp @@ -91,7 +91,12 @@ namespace algorithm cached_parent_handle_adapter(const cached_parent_handle_adapter &) = default; cached_parent_handle_adapter(cached_parent_handle_adapter &&) = default; // NOLINT cached_parent_handle_adapter &operator=(const cached_parent_handle_adapter &) = default; - cached_parent_handle_adapter &operator=(cached_parent_handle_adapter &&) = default; // NOLINT + cached_parent_handle_adapter &operator=(cached_parent_handle_adapter &&o) noexcept + { + this->~cached_parent_handle_adapter(); + new(this) cached_parent_handle_adapter(std::move(o)); + return *this; + } cached_parent_handle_adapter(adapted_handle_type &&o, const path_handle &base, path_view path) : adapted_handle_type(std::move(o)) { diff --git a/include/llfio/v2.0/detail/impl/posix/handle.ipp b/include/llfio/v2.0/detail/impl/posix/handle.ipp index 03459064..37884e0b 100644 --- a/include/llfio/v2.0/detail/impl/posix/handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/handle.ipp @@ -134,6 +134,14 @@ result<void> handle::close() noexcept LLFIO_LOG_FUNCTION_CALL(this); if(_v) { +#ifndef NDEBUG + // Trap when refined handle implementations don't set their vptr properly (this took a while to debug!) + if((static_cast<unsigned>(_v.behaviour) & 0xff00) != 0 && !(_v.behaviour & native_handle_type::disposition::_child_close_executed)) + { + LLFIO_LOG_FATAL(this, "handle::close() called on a derived handle implementation, this suggests vptr is incorrect"); + abort(); + } +#endif if(are_safety_fsyncs_issued() && is_writable()) { if(-1 == fsync(_v.fd)) @@ -155,10 +163,31 @@ result<handle> handle::clone() const noexcept LLFIO_LOG_FUNCTION_CALL(this); result<handle> ret(handle(native_handle_type(), _caching, _flags)); ret.value()._v.behaviour = _v.behaviour; - ret.value()._v.fd = ::fcntl(_v.fd, F_DUPFD_CLOEXEC); + ret.value()._v.fd = ::fcntl(_v.fd, F_DUPFD_CLOEXEC, 0); if(-1 == ret.value()._v.fd) { - return posix_error(); + int e = errno; + if(e == EBADF) + { + // Fall back onto F_DUPFD + ret.value()._v.fd = ::fcntl(_v.fd, F_DUPFD, 0); + if(-1 != ret.value()._v.fd) + { + // Set close on exec + int attribs = ::fcntl(_v.fd, F_GETFL); + if(-1 == attribs) + { + return posix_error(); + } + attribs |= FD_CLOEXEC; + if(-1 == ::fcntl(_v.fd, F_SETFL, attribs)) + { + return posix_error(); + } + return ret; + } + } + return posix_error(e); } return ret; } diff --git a/include/llfio/v2.0/detail/impl/posix/map_handle.ipp b/include/llfio/v2.0/detail/impl/posix/map_handle.ipp index 60e4ec14..1c174f03 100644 --- a/include/llfio/v2.0/detail/impl/posix/map_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/map_handle.ipp @@ -305,8 +305,9 @@ static inline result<void *> do_mmap(native_handle_type &nativeh, void *ataddr, return addr; } -result<map_handle> map_handle::map(size_type bytes, section_handle::flag _flag) noexcept +result<map_handle> map_handle::map(size_type bytes, bool /*unused*/, section_handle::flag _flag) noexcept { + // TODO: Keep a cache of MADV_FREE pages deallocated if(bytes == 0u) { return errc::argument_out_of_domain; 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 a0d4e7cd..2233ec33 100644 --- a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp @@ -33,6 +33,8 @@ namespace detail LLFIO_HEADERS_ONLY_FUNC_SPEC result<void> stat_from_symlink(struct stat &s, const handle &_h) noexcept { #if !LLFIO_SYMLINK_HANDLE_IS_FAKED + (void) s; + (void) _h; return posix_error(EBADF); #else const auto &h = static_cast<const symlink_handle &>(_h); @@ -45,7 +47,7 @@ namespace detail } } -result<void> symlink_handle::_create_symlink(path_view target, deadline d, bool atomic_replace) noexcept +result<void> symlink_handle::_create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d, bool atomic_replace) noexcept { std::chrono::steady_clock::time_point began_steady; std::chrono::system_clock::time_point end_utc; @@ -63,17 +65,6 @@ result<void> symlink_handle::_create_symlink(path_view target, deadline d, bool path_view::c_str zpath(target); try { -#if !LLFIO_SYMLINK_HANDLE_IS_FAKED - if(_devid == 0 && _inode == 0) - { - OUTCOME_TRY(_fetch_inode()); - } - path_type filename; - OUTCOME_TRY(dirh, detail::containing_directory(std::ref(filename), *this, *this, d)); -#else - const path_handle &dirh = _dirh; - const path_type &filename = _leafname; -#endif if(atomic_replace) { // symlinkat() won't replace an existing symlink, so we need to create it @@ -82,7 +73,8 @@ result<void> symlink_handle::_create_symlink(path_view target, deadline d, bool { auto randomname = utils::random_string(32); randomname.append(".random"); - if(-1 == ::symlinkat(zpath.buffer, dirh.native_handle().fd, randomname.c_str())) + // std::cerr << "symlinkat " << zpath.buffer << " " << dirh.native_handle().fd << " " << randomname << std::endl; + if(-1 == ::symlinkat(zpath.buffer, dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, randomname.c_str())) { if(EEXIST == errno) { @@ -108,7 +100,8 @@ result<void> symlink_handle::_create_symlink(path_view target, deadline d, bool } return posix_error(); } - if(-1 == ::renameat(dirh.native_handle().fd, randomname.c_str(), dirh.native_handle().fd, filename.c_str())) + // 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())) { return posix_error(); } @@ -117,7 +110,8 @@ result<void> symlink_handle::_create_symlink(path_view target, deadline d, bool } else { - if(-1 == ::symlinkat(zpath.buffer, dirh.native_handle().fd, filename.c_str())) + // 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())) { return posix_error(); } @@ -221,6 +215,11 @@ result<symlink_handle::path_type> symlink_handle::current_path() const noexcept LLFIO_LOG_FUNCTION_CALL(this); try { + // Deleted? + if(!_dirh.is_valid() && _leafname.empty()) + { + return _leafname; + } for(;;) { // Sanity check that we still exist @@ -243,6 +242,42 @@ result<symlink_handle::path_type> symlink_handle::current_path() const noexcept return error_from_exception(); } } + +result<void> symlink_handle::relink(const path_handle &base, path_view_type path, bool atomic_replace, deadline d) noexcept +{ + LLFIO_LOG_FUNCTION_CALL(this); + OUTCOME_TRY(fs_handle::relink(base, path, atomic_replace, d)); + try + { + // Take a path handle to the directory containing the symlink + auto path_parent = path.parent_path(); + _leafname = path.filename().path(); + if(base.is_valid() && path_parent.empty()) + { + OUTCOME_TRY(dh, base.clone()); + _dirh = std::move(dh); + } + else if(!path_parent.empty()) + { + OUTCOME_TRY(dh, path_handle::path(base, path_parent)); + _dirh = std::move(dh); + } + } + catch(...) + { + return error_from_exception(); + } + return success(); +} + +result<void> symlink_handle::unlink(deadline d) noexcept +{ + LLFIO_LOG_FUNCTION_CALL(this); + OUTCOME_TRY(fs_handle::unlink(d)); + _dirh = {}; + _leafname = path_type(); + return success(); +} #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 @@ -258,37 +293,37 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c OUTCOME_TRY(attribs, attribs_from_handle_mode_caching_and_flags(nativeh, _mode, _creation, caching::all, flags)); nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable #if !LLFIO_SYMLINK_HANDLE_IS_FAKED - // Linux can open symbolic links directly like this - attribs |= O_PATH | O_NOFOLLOW; - path_view::c_str zpath(path); - if(base.is_valid()) - { - nativeh.fd = ::openat(base.native_handle().fd, zpath.buffer, attribs, 0x1b0 /*660*/); - } - else - { - nativeh.fd = ::open(zpath.buffer, attribs, 0x1b0 /*660*/); - } - if(-1 == nativeh.fd) - { - return posix_error(); - } + path_handle dirh; + path_type leafname; #else (void) attribs; + path_handle &dirh = ret.value()._dirh; + path_type &leafname = ret.value()._leafname; +#endif + int dirhfd = AT_FDCWD; try { // Take a path handle to the directory containing the symlink auto path_parent = path.parent_path(); - ret.value()._leafname = path.filename().path(); - if(base.is_valid() && !path_parent.empty()) + leafname = path.filename().path(); + if(base.is_valid() && path_parent.empty()) { +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED + dirhfd = base.native_handle().fd; +#else OUTCOME_TRY(dh, base.clone()); - ret.value()._dirh = std::move(dh); + dirh = std::move(dh); + dirhfd = dirh.native_handle().fd; +#endif } else +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED // always take a dirh if faking the handle for race safety + if(!path_parent.empty()) +#endif { OUTCOME_TRY(dh, path_handle::path(base, path_parent.empty() ? "." : path_parent)); - ret.value()._dirh = std::move(dh); + dirh = std::move(dh); + dirhfd = dirh.native_handle().fd; } } catch(...) @@ -301,7 +336,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c { // Complain if it doesn't exist struct stat s; - if(-1 == ::fstatat(ret.value()._dirh.native_handle().fd, ret.value()._leafname.c_str(), &s, AT_SYMLINK_NOFOLLOW)) + if(-1 == ::fstatat(dirhfd, leafname.c_str(), &s, AT_SYMLINK_NOFOLLOW)) { return posix_error(); } @@ -312,13 +347,13 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c case creation::truncate: { // Create an empty symlink, ignoring any file exists errors, unless only_if_not_exist - auto r = ret.value()._create_symlink( + auto r = ret.value()._create_symlink(dirh, leafname, #ifdef __linux__ - ".", // Linux is not POSIX conforming here, and refuses to create empty symlinks + ".", // Linux is not POSIX conforming here, and refuses to create empty symlinks #else - "", + "", #endif - std::chrono::seconds(10), false); + std::chrono::seconds(10), false); if(!r) { if(_creation == creation::only_if_not_exist || r.error() != errc::file_exists) @@ -327,6 +362,14 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c break; } } +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED + // Linux can open symbolic links directly like this + attribs |= O_PATH | O_NOFOLLOW; + nativeh.fd = ::openat(dirhfd, leafname.c_str(), attribs, 0x1b0 /*660*/); + if(-1 == nativeh.fd) + { + return posix_error(); + } #endif return ret; } @@ -389,7 +432,36 @@ 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 d) noexcept { LLFIO_LOG_FUNCTION_CALL(this); - OUTCOME_TRY(_create_symlink(req.buffers.path(), d, true)); +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED + if(_devid == 0 && _inode == 0) + { + OUTCOME_TRY(_fetch_inode()); + } + path_type filename; + OUTCOME_TRY(dirh, detail::containing_directory(std::ref(filename), *this, *this, d)); +#else + const path_handle &dirh = _dirh; + const path_type &filename = _leafname; +#endif + OUTCOME_TRY(_create_symlink(dirh, filename, req.buffers.path(), d, true)); +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED + { + // Current fd now points at the symlink we just atomically replaced, so need to reopen + // it onto the new symlink + auto newthis = symlink(dirh, filename, is_writable() ? mode::write : mode::read, creation::open_existing, _flags); + // Prevent unlink on first close if set + _flags &= ~flag::unlink_on_first_close; + // Close myself + OUTCOME_TRY(close()); + // If reopen failed, report that now + if(!newthis) + { + return newthis.error(); + } + // Swap myself with the new this + swap(newthis.value()); + } +#endif return success(std::move(req.buffers)); } diff --git a/include/llfio/v2.0/detail/impl/windows/handle.ipp b/include/llfio/v2.0/detail/impl/windows/handle.ipp index cebc354f..e3d0e851 100644 --- a/include/llfio/v2.0/detail/impl/windows/handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/handle.ipp @@ -75,6 +75,14 @@ result<void> handle::close() noexcept LLFIO_LOG_FUNCTION_CALL(this); if(_v) { +#ifndef NDEBUG + // Trap when refined handle implementations don't set their vptr properly (this took a while to debug!) + if((static_cast<unsigned>(_v.behaviour) & 0xff00) != 0 && !(_v.behaviour & native_handle_type::disposition::_child_close_executed)) + { + LLFIO_LOG_FATAL(this, "handle::close() called on a derived handle implementation, this suggests vptr is incorrect"); + abort(); + } +#endif if(are_safety_fsyncs_issued() && is_writable()) { if(FlushFileBuffers(_v.h) == 0) diff --git a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp index ca54e008..e1661072 100644 --- a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp @@ -59,6 +59,10 @@ result<void> section_handle::close() noexcept LLFIO_LOG_FUNCTION_CALL(this); if(_v) { +#ifndef NDEBUG + // Tell handle::close() that we have correctly executed + _v.behaviour |= native_handle_type::disposition::_child_close_executed; +#endif OUTCOME_TRYV(handle::close()); OUTCOME_TRYV(_anonymous.close()); _flag = flag::none; @@ -477,8 +481,9 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::barrier(map_ha } -result<map_handle> map_handle::map(size_type bytes, section_handle::flag _flag) noexcept +result<map_handle> map_handle::map(size_type bytes, bool /*unused*/, section_handle::flag _flag) noexcept { + // TODO: Keep a cache of DiscardVirtualMemory()/MEM_RESET pages deallocated bytes = win32_round_up_to_allocation_size(bytes); result<map_handle> ret(map_handle(nullptr)); native_handle_type &nativeh = ret.value()._v; diff --git a/include/llfio/v2.0/file_handle.hpp b/include/llfio/v2.0/file_handle.hpp index c41ac559..de8386f0 100644 --- a/include/llfio/v2.0/file_handle.hpp +++ b/include/llfio/v2.0/file_handle.hpp @@ -208,6 +208,13 @@ public: } } } +#ifndef NDEBUG + if(_v) + { + // Tell handle::close() that we have correctly executed + _v.behaviour |= native_handle_type::disposition::_child_close_executed; + } +#endif return io_handle::close(); } diff --git a/include/llfio/v2.0/map_handle.hpp b/include/llfio/v2.0/map_handle.hpp index 60deb440..d84df116 100644 --- a/include/llfio/v2.0/map_handle.hpp +++ b/include/llfio/v2.0/map_handle.hpp @@ -377,8 +377,9 @@ public: return ret; } - /*! Create new memory and map it into view. - \param bytes How many bytes to create and map. Typically will be rounded up to a multiple of the page size (see `utils::page_sizes()`) on POSIX, 64Kb on Windows. + /*! Map unused memory into view, creating new memory if insufficient unused memory is available. Note that the memory mapped by this call may contain non-zero bits (recycled memory) unless `zeroed` is true. + \param bytes How many bytes to map. Typically will be rounded up to a multiple of the page size (see `utils::page_sizes()`) on POSIX, 64Kb on Windows. + \param zeroed Set to true if only all bits zeroed memory is wanted. \param _flag The permissions with which to map the view. `flag::none` can be useful for reserving virtual address space without committing system resources, use commit() to later change availability of memory. \note On Microsoft Windows this constructor uses the faster VirtualAlloc() which creates less versatile page backed memory. If you want anonymous memory @@ -389,7 +390,7 @@ public: \errors Any of the values POSIX mmap() or VirtualAlloc() can return. */ LLFIO_MAKE_FREE_FUNCTION - static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<map_handle> map(size_type bytes, section_handle::flag _flag = section_handle::flag::readwrite) noexcept; + static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<map_handle> map(size_type bytes, bool zeroed = false, section_handle::flag _flag = section_handle::flag::readwrite) noexcept; /*! Create a memory mapped view of a backing storage, optionally reserving additional address space for later growth. \param section A memory section handle specifying the backing storage to use. @@ -646,9 +647,9 @@ VirtualAlloc() memory cannot do. \errors Any of the values POSIX mmap() or VirtualAlloc() can return. */ -inline result<map_handle> map(map_handle::size_type bytes, section_handle::flag _flag = section_handle::flag::readwrite) noexcept +inline result<map_handle> map(map_handle::size_type bytes, bool zeroed = false, section_handle::flag _flag = section_handle::flag::readwrite) noexcept { - return map_handle::map(std::forward<decltype(bytes)>(bytes), std::forward<decltype(_flag)>(_flag)); + return map_handle::map(std::forward<decltype(bytes)>(bytes), zeroed, std::forward<decltype(_flag)>(_flag)); } /*! Create a memory mapped view of a backing storage, optionally reserving additional address space for later growth. \param section A memory section handle specifying the backing storage to use. diff --git a/include/llfio/v2.0/mapped.hpp b/include/llfio/v2.0/mapped.hpp index e0385254..e723ca75 100644 --- a/include/llfio/v2.0/mapped.hpp +++ b/include/llfio/v2.0/mapped.hpp @@ -81,13 +81,15 @@ public: //! Returns a reference to the internal map handle const map_handle &map() const noexcept { return _maph; } - /*! Create a view of new memory. + /*! Create a view of newly allocated unused memory, creating new memory if insufficient unused memory is available. + Note that the memory mapped by this call may contain non-zero bits (recycled memory) unless `zeroed` is true. \param length The number of items to map. + \param zeroed Whether to ensure that the viewed memory returned is all bits zero or not. \param _flag The flags to pass to `map_handle::map()`. */ - explicit mapped(size_type length, section_handle::flag _flag = section_handle::flag::readwrite) - : _maph(map_handle::map(length * sizeof(T), _flag).value()) + explicit mapped(size_type length, bool zeroed = false, section_handle::flag _flag = section_handle::flag::readwrite) + : _maph(map_handle::map(length * sizeof(T), zeroed, _flag).value()) { byte *addr = _maph.address(); static_cast<span<T> &>(*this) = span<T>(reinterpret_cast<T *>(addr), length); // NOLINT diff --git a/include/llfio/v2.0/native_handle_type.hpp b/include/llfio/v2.0/native_handle_type.hpp index 0961db83..c4784f94 100644 --- a/include/llfio/v2.0/native_handle_type.hpp +++ b/include/llfio/v2.0/native_handle_type.hpp @@ -58,7 +58,9 @@ struct native_handle_type // NOLINT symlink = 1U << 10U, //!< Is a symlink multiplexer = 1U << 11U, //!< Is a kqueue/epoll/iocp process = 1U << 12U, //!< Is a child process - section = 1U << 13U //!< Is a memory section + section = 1U << 13U, //!< Is a memory section + + _child_close_executed = 1U << 28U // used to trap when vptr has become corrupted } QUICKCPPLIB_BITFIELD_END(disposition) disposition behaviour; //! The behaviour of the handle diff --git a/include/llfio/v2.0/path_handle.hpp b/include/llfio/v2.0/path_handle.hpp index 87798c04..1e308ca7 100644 --- a/include/llfio/v2.0/path_handle.hpp +++ b/include/llfio/v2.0/path_handle.hpp @@ -64,7 +64,6 @@ public: //! Default constructor constexpr path_handle() {} // NOLINT - ~path_handle() = default; //! Construct a handle from a supplied native handle explicit constexpr path_handle(native_handle_type h, caching caching = caching::all, flag flags = flag::none) : handle(h, caching, flags) @@ -77,9 +76,22 @@ public: //! No copy construction (use `clone()`) path_handle(const path_handle &) = delete; //! Move assignment permitted - path_handle &operator=(path_handle &&) = default; + path_handle &operator=(path_handle &&o) noexcept + { + this->~path_handle(); + new(this) path_handle(std::move(o)); + return *this; + } //! No copy assignment path_handle &operator=(const path_handle &) = delete; + //! Swap with another instance + LLFIO_MAKE_FREE_FUNCTION + void swap(path_handle &o) noexcept + { + path_handle temp(std::move(*this)); + *this = std::move(o); + o = std::move(temp); + } /*! Create a path handle opening access to some location on the filing system. Some operating systems provide a particularly lightweight method of doing this @@ -94,6 +106,25 @@ public: LLFIO_MAKE_FREE_FUNCTION static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<path_handle> path(path_view_type _path) noexcept { return path(path_handle(), _path); } + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~path_handle() override + { + if(_v) + { + (void) path_handle::close(); + } + } + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override + { + LLFIO_LOG_FUNCTION_CALL(this); +#ifndef NDEBUG + if(_v) + { + // Tell handle::close() that we have correctly executed + _v.behaviour |= native_handle_type::disposition::_child_close_executed; + } +#endif + return handle::close(); + } /*! Clone this handle (copy constructor is disabled to avoid accidental copying). */ result<path_handle> clone() const noexcept diff --git a/include/llfio/v2.0/symlink_handle.hpp b/include/llfio/v2.0/symlink_handle.hpp index 266f4d79..844afe51 100644 --- a/include/llfio/v2.0/symlink_handle.hpp +++ b/include/llfio/v2.0/symlink_handle.hpp @@ -31,7 +31,7 @@ Distributed under the Boost Software License, Version 1.0. //! \file symlink_handle.hpp Provides a handle to a symbolic link. #ifndef LLFIO_SYMLINK_HANDLE_IS_FAKED -#if defined(_WIN32) //|| defined(__linux__) +#if defined(_WIN32) || defined(__linux__) #define LLFIO_SYMLINK_HANDLE_IS_FAKED 0 #else #define LLFIO_SYMLINK_HANDLE_IS_FAKED 1 @@ -84,8 +84,8 @@ class LLFIO_DECL symlink_handle : public handle, public fs_handle LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; } #ifndef _WIN32 - friend LLFIO_HEADERS_ONLY_FUNC_SPEC result<void> detail::stat_from_symlink(struct stat &s, const handle &h) noexcept; - result<void> _create_symlink(path_view target, deadline d, bool atomic_replace) noexcept; + friend result<void> detail::stat_from_symlink(struct stat &s, const handle &h) noexcept; + result<void> _create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d, bool atomic_replace) noexcept; #endif public: @@ -311,9 +311,22 @@ public: //! No copy construction (use `clone()`) symlink_handle(const symlink_handle &) = delete; //! Move assignment permitted - symlink_handle &operator=(symlink_handle &&) = default; + symlink_handle &operator=(symlink_handle &&o) noexcept + { + this->~symlink_handle(); + new(this) symlink_handle(std::move(o)); + return *this; + } //! No copy assignment symlink_handle &operator=(const symlink_handle &) = delete; + //! Swap with another instance + LLFIO_MAKE_FREE_FUNCTION + void swap(symlink_handle &o) noexcept + { + symlink_handle temp(std::move(*this)); + *this = std::move(o); + o = std::move(temp); + } LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~symlink_handle() override { @@ -338,6 +351,13 @@ public: } } #if !LLFIO_SYMLINK_HANDLE_IS_FAKED +#ifndef NDEBUG + if(_v) + { + // Tell handle::close() that we have correctly executed + _v.behaviour |= native_handle_type::disposition::_child_close_executed; + } +#endif return handle::close(); #else _dirh = {}; @@ -362,6 +382,12 @@ public: #if LLFIO_SYMLINK_HANDLE_IS_FAKED LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<path_type> current_path() const noexcept override; + LLFIO_MAKE_FREE_FUNCTION + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC + result<void> relink(const path_handle &base, path_view_type path, bool atomic_replace = true, deadline d = std::chrono::seconds(30)) noexcept override; + LLFIO_MAKE_FREE_FUNCTION + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC + result<void> unlink(deadline d = std::chrono::seconds(30)) noexcept override; #endif /*! Create a symlink handle opening access to a symbolic link. |