diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2018-07-24 22:29:21 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2018-07-24 22:29:21 +0300 |
commit | 964d174c1c44d4810789b198bbf07271cebbe8c8 (patch) | |
tree | dc3d0ebb672e919caa2f9d2b8fb5654d230ae604 /include/llfio/v2.0/symlink_handle.hpp | |
parent | 084b3eb8aaae639e9a63e97245386fd78b8d8413 (diff) |
i/o buffers now work as if they were span<byte>.
Implemented many of the missing functions in path_view.
Implemented symlink_handle for Windows.
Diffstat (limited to 'include/llfio/v2.0/symlink_handle.hpp')
-rw-r--r-- | include/llfio/v2.0/symlink_handle.hpp | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/include/llfio/v2.0/symlink_handle.hpp b/include/llfio/v2.0/symlink_handle.hpp new file mode 100644 index 00000000..179b820a --- /dev/null +++ b/include/llfio/v2.0/symlink_handle.hpp @@ -0,0 +1,417 @@ +/* A handle to a symbolic link +(C) 2018 Niall Douglas <http://www.nedproductions.biz/> (20 commits) +File Created: Jul 2018 + + +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_SYMLINK_HANDLE_H +#define LLFIO_SYMLINK_HANDLE_H + +#include "handle.hpp" +#include "path_view.hpp" + +//! \file symlink_handle.hpp Provides a handle to a symbolic link. + +#ifndef LLFIO_SYMLINK_HANDLE_IS_FAKED +#if defined(_WIN32) || defined(__Linux__) +#define LLFIO_SYMLINK_HANDLE_IS_FAKED 0 +#else +#define LLFIO_SYMLINK_HANDLE_IS_FAKED 1 +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) // dll interface +#endif + +LLFIO_V2_NAMESPACE_EXPORT_BEGIN + +class symlink_handle; + +/*! \class symlink_handle +\brief A handle to an inode which redirects to a different path. + +Microsoft Windows and Linux provide the ability to open the contents of a symbolic link directly, +for those platforms this handle works exactly like any ordinary handle. For other POSIX platforms +without proprietary extensions, it is not possible to get a valid file descriptor to the contents +of a symlink, and in this situation the native handle returned will be `-1` and the preprocessor +macro `LLFIO_SYMLINK_HANDLE_IS_FAKED` will be non-zero. + +If `LLFIO_SYMLINK_HANDLE_IS_FAKED` is on, the handle is race free up to the containing directory +only. If a third party relocates the symbolic link into a different directory, and race free +checking is enabled, this class will simply refuse to work as it no longer has any way of finding +the symbolic link. You should take care that this does not become a denial of service attack. + +On Microsoft Windows, there are many kinds of symbolic link: this implementation supports +directory junctions, NTFS symbolic links and WSL symbolic links. Any others will return an error +code comparing equal to `errc::protocol_not_supported`. One should note that modifying symbolic +links is not permitted by users with ordinary permissions on Microsoft Windows, however when +Windows is in Developer Mode, they are. This can cause developer testing to be an inaccurate +view of the user experience. Windows supports directory symbolic links (junctions), these work +for all users in any configuration. +*/ +class LLFIO_DECL symlink_handle : public handle, public fs_handle +{ +#if LLFIO_SYMLINK_HANDLE_IS_FAKED + // Need to retain a handle to our base and our leafname + path_handle _dirh; + handle::path_type _leafname; +#endif + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC const handle &_get_handle() const noexcept final { return *this; } + +public: + using path_type = handle::path_type; + using extent_type = handle::extent_type; + using size_type = handle::size_type; + using mode = handle::mode; + using creation = handle::creation; + using caching = handle::caching; + using flag = handle::flag; + using dev_t = fs_handle::dev_t; + using ino_t = fs_handle::ino_t; + using path_view_type = fs_handle::path_view_type; + + //! The type of symbolic link this is + enum class symlink_type + { + none, //!<! No link + symbolic, //!< Standard symbolic link + + win_wsl, //!< WSL symbolic link (Windows only) + win_junction //!< NTFS directory junction (Windows only, directories and volumes only) + }; + + //! The buffer type used by this handle, which is a `path_view` + using buffer_type = path_view; + /*! The buffers type used by this handle for reads, which is a single item sequence of `path_view`. + + \warning Unless you supply your own kernel buffer, you need to keep this around as long as you + use the path view, as the path is a view of the original buffer filled by + the kernel and the existence of this keeps that original buffer around. + */ + struct buffers_type + { + //! Type of the pointer to the buffer. + using pointer = path_view *; + //! Type of the iterator to the buffer. + using iterator = path_view *; + //! Type of the iterator to the buffer. + using const_iterator = const path_view *; + //! Type of the length of the buffers. + using size_type = size_t; + + //! Default constructor + constexpr buffers_type() {} // NOLINT + + //! Constructor + constexpr buffers_type(path_view link, symlink_type type = symlink_type::symbolic) + : _link(link) + , _type(type) + { + } + ~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) + { + o._link = {}; + o._type = symlink_type::none; + o._kernel_buffer_size = 0; + } + //! No copy construction + buffers_type(const buffers_type &) = delete; + //! Move assignment + buffers_type &operator=(buffers_type &&o) noexcept + { + this->~buffers_type(); + new(this) buffers_type(std::move(o)); + return *this; + } + //! No copy assignment + buffers_type &operator=(const buffers_type &) = delete; + + //! Returns an iterator to the beginning of the buffers + constexpr iterator begin() noexcept { return &_link; } + //! Returns an iterator to the beginning of the buffers + constexpr const_iterator begin() const noexcept { return &_link; } + //! Returns an iterator to the beginning of the buffers + constexpr const_iterator cbegin() const noexcept { return &_link; } + //! Returns an iterator to after the end of the buffers + constexpr iterator end() noexcept { return &_link + 1; } + //! Returns an iterator to after the end of the buffers + constexpr const_iterator end() const noexcept { return &_link + 1; } + //! Returns an iterator to after the end of the buffers + constexpr const_iterator cend() const noexcept { return &_link + 1; } + + //! The path referenced by the symbolic link + path_view path() const noexcept { return _link; } + //! The type of the symbolic link + symlink_type type() const noexcept { return _type; } + + private: + friend class symlink_handle; + path_view _link; + symlink_type _type{symlink_type::none}; + std::unique_ptr<char[]> _kernel_buffer; + 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. + using pointer = const path_view *; + //! Type of the iterator to the buffer. + using iterator = const path_view *; + //! Type of the iterator to the buffer. + using const_iterator = const path_view *; + //! Type of the length of the buffers. + using size_type = size_t; + + //! Constructor + constexpr const_buffers_type(path_view link, symlink_type type = symlink_type::symbolic) + : _link(link) + , _type(type) + { + } + ~const_buffers_type() = default; + //! Move constructor + const_buffers_type(const_buffers_type &&o) noexcept : _link(o._link), _type(o._type) + { + o._link = {}; + o._type = symlink_type::none; + } + //! No copy construction + const_buffers_type(const buffers_type &) = delete; + //! Move assignment + const_buffers_type &operator=(const_buffers_type &&o) noexcept + { + this->~const_buffers_type(); + new(this) const_buffers_type(std::move(o)); + return *this; + } + //! No copy assignment + const_buffers_type &operator=(const const_buffers_type &) = delete; + + //! Returns an iterator to the beginning of the buffers + constexpr iterator begin() noexcept { return &_link; } + //! Returns an iterator to the beginning of the buffers + constexpr const_iterator begin() const noexcept { return &_link; } + //! Returns an iterator to the beginning of the buffers + constexpr const_iterator cbegin() const noexcept { return &_link; } + //! Returns an iterator to after the end of the buffers + constexpr iterator end() noexcept { return &_link + 1; } + //! Returns an iterator to after the end of the buffers + constexpr const_iterator end() const noexcept { return &_link + 1; } + //! Returns an iterator to after the end of the buffers + constexpr const_iterator cend() const noexcept { return &_link + 1; } + + //! The path referenced by the symbolic link + path_view path() const noexcept { return _link; } + //! The type of the symbolic link + symlink_type type() const noexcept { return _type; } + + private: + friend class symlink_handle; + path_view _link; + symlink_type _type{symlink_type::none}; + }; + //! The i/o request type used by this handle. Guaranteed to be `TrivialType` apart from construction, and `StandardLayoutType`. + template <class T> struct io_request; + //! Specialisation for reading symlinks + template <> struct io_request<buffers_type> + { + span<char> kernelbuffer{}; + + constexpr io_request() {} // NOLINT + //! Construct a request to read a link with optionally specified kernel buffer + constexpr io_request(span<char> _kernelbuffer) + : kernelbuffer(_kernelbuffer) + { + } + //! 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)...)) {} + }; + //! Specialisation for writing symlinks + template <> struct io_request<const_buffers_type> + { + const_buffers_type buffers; + span<char> kernelbuffer; + //! Construct a request to write a link with optionally specified kernel buffer + constexpr io_request(const_buffers_type _buffers, span<char> _kernelbuffer = span<char>()) + : buffers(std::move(_buffers)) + , kernelbuffer(_kernelbuffer) + { + } + //! 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)...)) {} + //! 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) {} + }; + +//! Default constructor +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED + constexpr +#endif + symlink_handle() + { + } // NOLINT +//! Construct a handle from a supplied native handle +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED + constexpr +#endif + explicit symlink_handle(native_handle_type h, dev_t devid, ino_t inode, flag flags = flag::none) + : handle(std::move(h), caching::all, flags) + , fs_handle(devid, inode) + { + } +//! Explicit conversion from handle permitted +#if !LLFIO_SYMLINK_HANDLE_IS_FAKED + constexpr +#endif + explicit symlink_handle(handle &&o) noexcept : handle(std::move(o)) + { + } + //! Move construction permitted + symlink_handle(symlink_handle &&) = default; + //! No copy construction (use `clone()`) + symlink_handle(const symlink_handle &) = delete; + //! Move assignment permitted + symlink_handle &operator=(symlink_handle &&) = default; + //! No copy assignment + symlink_handle &operator=(const symlink_handle &) = delete; + + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~symlink_handle() override + { + if(_v) + { + (void) symlink_handle::close(); + } + } + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override + { + LLFIO_LOG_FUNCTION_CALL(this); + if(_flags & flag::unlink_on_first_close) + { + auto ret = unlink(); + if(!ret) + { + // File may have already been deleted, if so ignore + if(ret.error() != errc::no_such_file_or_directory) + { + return ret.error(); + } + } + } + return handle::close(); + } + + /*! Clone this handle (copy constructor is disabled to avoid accidental copying), + optionally race free reopening the handle with different access or caching. + + Microsoft Windows provides a syscall for cloning an existing handle but with new + access. On POSIX, we must loop calling `current_path()`, + trying to open the path returned and making sure it is the same inode. + + \errors Any of the values POSIX dup() or DuplicateHandle() can return. + \mallocs On POSIX if changing the mode, we must loop calling `current_path()` and + trying to open the path returned. Thus many allocations may occur. + */ + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<symlink_handle> clone(mode mode_ = mode::unchanged, deadline d = std::chrono::seconds(30)) const noexcept; + +#if LLFIO_SYMLINK_HANDLE_IS_FAKED + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC + result<void> relink(const path_handle &base, path_view_type newpath, bool atomic_replace = true, deadline d = std::chrono::seconds(30)) noexcept override; + 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. + + For obvious reasons, one cannot append to a symbolic link, nor create with truncate. + */ + 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; + + /*! Read the contents of the symbolic link. + + If supplying your own `kernelbuffer`, be aware that the length of the contents of the symbolic + link may change at any time. You should therefore retry reading the symbolic link, expanding + your `kernelbuffer` each time, until a successful read occurs. + + \return Returns the buffers filled, with its size adjusted to the bytes filled. + \param tofill A buffer to fill with the contents of the symbolic link. + \param kernelbuffer A buffer to use for the kernel to fill. If left defaulted, a kernel buffer + is allocated internally and stored into `tofill` which needs to not be destructed until one + is no longer using any items within (the path returned is a view onto the original kernel data). + \errors Any of the errors which `readlink()` or `DeviceIoControl()` might return, or failure + to allocate memory if the user did not supply a kernel buffer to use. + \mallocs If the `kernelbuffer` parameter is set on entry, no memory allocations. + If unset, then at least one memory allocation, possibly more is performed. + */ + LLFIO_MAKE_FREE_FUNCTION + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<buffers_type> read(io_request<buffers_type> req = {}) noexcept; + + /*! Write the contents of the symbolic link. + + \param req A buffer with which to replace the contents of the symbolic link. + \errors Any of the errors which `symlink()` or `DeviceIoControl()` might return. + \mallocs If the `kernelbuffer` parameter is set on entry, no memory allocations. + If unset, then at least one memory allocation, possibly more is performed. + */ + LLFIO_MAKE_FREE_FUNCTION + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<const_buffers_type> write(io_request<const_buffers_type> req) noexcept; +}; + +//! \brief Constructor for `symlink_handle` +template <> struct construct<symlink_handle> +{ + const path_handle &base; + symlink_handle::path_view_type _path; + symlink_handle::mode _mode{symlink_handle::mode::read}; + symlink_handle::creation _creation{symlink_handle::creation::open_existing}; + result<symlink_handle> operator()() const noexcept { return symlink_handle::symlink(base, _path, _mode, _creation); } +}; + + +LLFIO_V2_NAMESPACE_END + +#if LLFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS) +#define LLFIO_INCLUDED_BY_HEADER 1 +#ifdef _WIN32 +#include "detail/impl/windows/symlink_handle.ipp" +#else +#include "detail/impl/posix/symlink_handle.ipp" +#endif +#undef LLFIO_INCLUDED_BY_HEADER +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif |