diff options
author | Jenkins nedprod CI <jenkins-nedprod@europe5.nedproductions.biz> | 2020-07-14 02:00:12 +0300 |
---|---|---|
committer | Jenkins nedprod CI <jenkins-nedprod@europe5.nedproductions.biz> | 2020-07-14 02:00:12 +0300 |
commit | 794c13254d2532884d34568a416c097355e8f829 (patch) | |
tree | e6d46974893ee71ccb5ffc9a0ccc1a1cfeda3d03 | |
parent | debf7ce308c18aafdfa97966c33b1400cbd199bf (diff) | |
parent | 597cacee2df213e291d3db0f463665a1a8753a44 (diff) |
Merged from develop branch as CDash reports all green
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/fs_handle.ipp | 86 | ||||
-rw-r--r-- | include/llfio/v2.0/fs_handle.hpp | 9 | ||||
-rw-r--r-- | include/llfio/v2.0/stat.hpp | 120 | ||||
-rw-r--r-- | test/tests/current_path.cpp | 117 |
5 files changed, 299 insertions, 39 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp index 1e942204..b4bbc621 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 804dcb157c208bf918aea37eb98e149934b51e4f -#define LLFIO_PREVIOUS_COMMIT_DATE "2020-07-10 14:04:55 +00:00" -#define LLFIO_PREVIOUS_COMMIT_UNIQUE 804dcb15 +#define LLFIO_PREVIOUS_COMMIT_REF 02a501799dbf689063b09f9f557b2057c53022d9 +#define LLFIO_PREVIOUS_COMMIT_DATE "2020-07-10 15:05:00 +00:00" +#define LLFIO_PREVIOUS_COMMIT_UNIQUE 02a50179 diff --git a/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp b/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp index 5e936068..b17dafc0 100644 --- a/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp @@ -29,6 +29,11 @@ Distributed under the Boost Software License, Version 1.0. #include <climits> // for PATH_MAX +#ifdef __linux__ +#include <sys/syscall.h> +#include <sys/sysmacros.h> +#endif + LLFIO_V2_NAMESPACE_BEGIN result<void> fs_handle::_fetch_inode() const noexcept @@ -208,21 +213,92 @@ result<void> fs_handle::relink(const path_handle &base, path_view_type path, boo { OUTCOME_TRY(_fetch_inode()); } - OUTCOME_TRY(auto &&dirh, detail::containing_directory(std::ref(filename), h, *this, d)); + OUTCOME_TRY(auto dirh, detail::containing_directory(std::ref(filename), h, *this, d)); if(!atomic_replace) { -// Some systems provide an extension for atomic non-replacing renames -#ifdef RENAME_NOREPLACE - if(-1 != ::renameat2(dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, RENAME_NOREPLACE)) +// Linux has an extension for atomic non-replacing renames +#ifdef __linux__ + errno = 0; + if(-1 != +#if defined __aarch64__ + syscall(276 /*__NR_renameat2*/, dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, 1 /*RENAME_NOREPLACE*/) +#elif defined __arm__ + syscall(382 /*__NR_renameat2*/, dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, 1 /*RENAME_NOREPLACE*/) +#elif defined __i386__ + syscall(353 /*__NR_renameat2*/, dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, 1 /*RENAME_NOREPLACE*/) +#elif defined __powerpc64__ + syscall(357 /*__NR_renameat2*/, dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, 1 /*RENAME_NOREPLACE*/) +#elif defined __sparc__ + syscall(345 /*__NR_renameat2*/, dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, 1 /*RENAME_NOREPLACE*/) +#elif defined __x86_64__ + syscall(316 /*__NR_renameat2*/, dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, 1 /*RENAME_NOREPLACE*/) +#else +#error Unknown Linux platform +#endif + ) + { return success(); + } if(EEXIST == errno) + { return posix_error(); + } #endif - // Otherwise we need to use linkat followed by renameat (non-atomic) + // Otherwise we need to use linkat followed by unlinkat, carefully reopening the file descriptor + // to preserve path tracking if(-1 == ::linkat(dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, 0)) { return posix_error(); } + int attribs = fcntl(h.native_handle().fd, F_GETFL); + int errcode = errno; + if(-1 != attribs) + { +#if 0 + attribs &= (O_RDONLY | O_WRONLY | O_RDWR +#ifdef O_EXEC + | O_EXEC +#endif + | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW +#ifdef O_TMPFILE + | O_TMPFILE +#endif + | O_APPEND | O_DIRECT +#ifdef O_DSYNC + | O_DSYNC +#endif +#ifdef O_LARGEFILE + | O_LARGEFILE +#endif +#ifdef O_NOATIME + | O_NOATIME +#endif +#ifdef O_PATH + | O_PATH +#endif + | O_SYNC); +#endif + int fd = ::openat(base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, attribs, 0x1b0 /*660*/); + if(-1 == fd) + { + errcode = errno; + } + else + { + ::close(h._v.fd); + h._v.fd = fd; + errcode = 0; + } + } + if(-1 == ::unlinkat(dirh.native_handle().fd, filename.c_str(), 0)) + { + return posix_error(); + } + if(errcode != 0) + { + return posix_error(errcode); + } + return success(); } if(-1 == ::renameat(dirh.native_handle().fd, filename.c_str(), base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer)) { diff --git a/include/llfio/v2.0/fs_handle.hpp b/include/llfio/v2.0/fs_handle.hpp index 2cbabc51..091f2f2e 100644 --- a/include/llfio/v2.0/fs_handle.hpp +++ b/include/llfio/v2.0/fs_handle.hpp @@ -151,9 +151,16 @@ public: /*! Relinks the current path of this open handle to the new path specified. If `atomic_replace` is true, the relink \b atomically and silently replaces any item at the new path specified. This - operation is both atomic and silent matching POSIX behaviour even on Microsoft Windows where + operation is both atomic and matching POSIX behaviour even on Microsoft Windows where no Win32 API can match POSIX semantics. + Note that if `atomic_replace` is false, the operation *may* be implemented as creating a hard + link to the destination (which fails if the destination exists), opening a new file descriptor + to the destination, closing the existing file descriptor, replacing the existing file descriptor + with the new one (this is to ensure path tracking continues to work), then unlinking the previous + link. Thus `native_handle()`'s value *may* change. This is not the case on Microsoft Windows nor + Linux, both of which provide syscalls capable of refusing to rename if the destination exists. + If the handle refers to a pipe, on Microsoft Windows the base path handle is ignored as there is a single global named pipe namespace. Unless the path fragment begins with `\`, the string `\??\` is prefixed to the name before passing it to the NT kernel API which performs the rename. This diff --git a/include/llfio/v2.0/stat.hpp b/include/llfio/v2.0/stat.hpp index 03a861e8..8229c9f0 100644 --- a/include/llfio/v2.0/stat.hpp +++ b/include/llfio/v2.0/stat.hpp @@ -186,21 +186,127 @@ 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)); + bool operator==(const stat_t &o) const noexcept + { + return st_dev == o.st_dev && st_ino == o.st_ino && st_type == o.st_type +#ifndef _WIN32 + && st_perms == o.st_perms +#endif + && st_nlink == o.st_nlink +#ifndef _WIN32 + && st_uid == o.st_uid && st_gid == o.st_gid && st_rdev == o.st_rdev +#endif + && st_atim == o.st_atim && st_mtim == o.st_mtim && st_ctim == o.st_ctim && st_size == o.st_size && st_allocated == o.st_allocated && + st_blocks == o.st_blocks && st_blksize == o.st_blksize && st_flags == o.st_flags && st_gen == o.st_gen && st_birthtim == o.st_birthtim && + st_sparse == o.st_sparse && st_compressed == o.st_compressed && st_reparse_point == o.st_reparse_point; } //! Inequality comparison bool operator!=(const stat_t &o) const noexcept { - // This is probably bold ... - return 0 != memcmp(this, &o, sizeof(o)); + return st_dev != o.st_dev || st_ino != o.st_ino || st_type != o.st_type +#ifndef _WIN32 + || st_perms != o.st_perms +#endif + || st_nlink != o.st_nlink +#ifndef _WIN32 + || st_uid != o.st_uid || st_gid != o.st_gid || st_rdev != o.st_rdev +#endif + || st_atim != o.st_atim || st_mtim != o.st_mtim || st_ctim != o.st_ctim || st_size != o.st_size || st_allocated != o.st_allocated || + st_blocks != o.st_blocks || st_blksize != o.st_blksize || st_flags != o.st_flags || st_gen != o.st_gen || st_birthtim != o.st_birthtim || + st_sparse != o.st_sparse || st_compressed != o.st_compressed || st_reparse_point != o.st_reparse_point; } //! Ordering bool operator<(const stat_t &o) const noexcept { - // This is probably bold ... - return memcmp(this, &o, sizeof(o)) < 0; + if(st_dev != o.st_dev) + { + return st_dev < o.st_dev; + } + if(st_ino != o.st_ino) + { + return st_ino < o.st_ino; + } + if(st_type != o.st_type) + { + return st_type < o.st_type; + } +#ifndef _WIN32 + if(st_perms != o.st_perms) + { + return st_perms < o.st_perms; + } +#endif + if(st_nlink != o.st_nlink) + { + return st_nlink < o.st_nlink; + } +#ifndef _WIN32 + if(st_uid != o.st_uid) + { + return st_uid < o.st_uid; + } + if(st_gid != o.st_gid) + { + return st_gid < o.st_gid; + } + if(st_rdev != o.st_rdev) + { + return st_rdev < o.st_rdev; + } +#endif + if(st_atim != o.st_atim) + { + return st_atim < o.st_atim; + } + if(st_mtim != o.st_mtim) + { + return st_mtim < o.st_mtim; + } + if(st_ctim != o.st_ctim) + { + return st_ctim < o.st_ctim; + } + if(st_size != o.st_size) + { + return st_size < o.st_size; + } + if(st_allocated != o.st_allocated) + { + return st_allocated < o.st_allocated; + } + if(st_blocks != o.st_blocks) + { + return st_blocks < o.st_blocks; + } + if(st_blksize != o.st_blksize) + { + return st_blksize < o.st_blksize; + } + if(st_flags != o.st_flags) + { + return st_flags < o.st_flags; + } + if(st_gen != o.st_gen) + { + return st_gen < o.st_gen; + } + if(st_birthtim != o.st_birthtim) + { + return st_birthtim < o.st_birthtim; + } + if(st_sparse != o.st_sparse) + { + return st_sparse < o.st_sparse; + } + if(st_compressed != o.st_compressed) + { + return st_compressed < o.st_compressed; + } + if(st_reparse_point != o.st_reparse_point) + { + return st_reparse_point < o.st_reparse_point; + } + return false; } /*! Fills the structure with metadata. diff --git a/test/tests/current_path.cpp b/test/tests/current_path.cpp index 4ddf0825..626696c3 100644 --- a/test/tests/current_path.cpp +++ b/test/tests/current_path.cpp @@ -1,5 +1,5 @@ /* Integration test kernel for whether current path works -(C) 2017 Niall Douglas <http://www.nedproductions.biz/> (2 commits) +(C) 2017-2020 Niall Douglas <http://www.nedproductions.biz/> (2 commits) File Created: Aug 2017 @@ -29,11 +29,15 @@ template <class FileHandleType, class DirectoryHandleType> static inline void Te namespace llfio = LLFIO_V2_NAMESPACE; { std::error_code ec; + llfio::filesystem::current_path(llfio::filesystem::temp_directory_path()); llfio::filesystem::remove_all("tempfile", ec); llfio::filesystem::remove_all("tempfile2", ec); + llfio::filesystem::remove_all("tempfile3", ec); + llfio::filesystem::remove_all("tempfile4", ec); llfio::filesystem::remove_all("tempdir", ec); llfio::filesystem::remove_all("tempdir2", ec); - } + llfio::filesystem::remove_all("tempdir3", ec); + } // namespace ; #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-braces" @@ -79,109 +83,176 @@ template <class FileHandleType, class DirectoryHandleType> static inline void Te s.fill(h1).value(); print(s); } + std::cout << "\ncurrent_path() works at all:\n"; { auto h1path = h1.current_path(); BOOST_CHECK(h1path); if(!h1path) { - std::cerr << "Getting the current path of a file FAILED due to " << h1path.error().message().c_str() << std::endl; + std::cerr << " Getting the current path of a file FAILED due to " << h1path.error().message().c_str() << std::endl; } else if(h1path.value().empty()) { BOOST_CHECK(!h1path.value().empty()); - std::cerr << "Getting the current path of a file FAILED due to the returned path being empty" << std::endl; + std::cerr << " Getting the current path of a file FAILED due to the returned path being empty" << std::endl; } else { - std::cout << "The path of the file is " << h1path.value() << std::endl; + std::cout << " The path of the file is " << h1path.value() << std::endl; } auto h2path = h2.current_path(); BOOST_CHECK(h2path); if(!h2path) { - std::cerr << "Getting the current path of a directory FAILED due to " << h2path.error().message().c_str() << std::endl; + std::cerr << " Getting the current path of a directory FAILED due to " << h2path.error().message().c_str() << std::endl; } else if(h2path.value().empty()) { BOOST_CHECK(!h2path.value().empty()); - std::cerr << "Getting the current path of a directory FAILED due to the returned path being empty" << std::endl; + std::cerr << " Getting the current path of a directory FAILED due to the returned path being empty" << std::endl; } else { - std::cout << "The path of the directory is " << h2path.value() << std::endl; + std::cout << " The path of the directory is " << h2path.value() << std::endl; } } - h1.relink({}, "tempfile2").value(); - h2.relink({}, "tempdir2").value(); + // Atomic relink + std::cout << "\ncurrent_path() after atomic relink:\n"; + h1.relink({}, "tempfile2", true).value(); + h2.relink({}, "tempdir2", true).value(); { auto h1path = h1.current_path(); BOOST_CHECK(h1path); if(!h1path) { - std::cerr << "Getting the current path of a file FAILED due to " << h1path.error().message().c_str() << std::endl; + std::cerr << " Getting the current path of a file FAILED due to " << h1path.error().message().c_str() << std::endl; } else if(h1path.value().empty()) { BOOST_CHECK(!h1path.value().empty()); - std::cerr << "Getting the current path of a file FAILED due to the returned path being empty" << std::endl; + std::cerr << " Getting the current path of a file FAILED due to the returned path being empty" << std::endl; } else if(h1path.value().filename() != "tempfile2") { BOOST_CHECK(h1path.value().filename() == "tempfile2"); - std::cerr << "Getting the current path of a file FAILED due to the wrong path being returned: " << h1path.value() << std::endl; + std::cerr << " Getting the current path of a file FAILED due to the wrong path being returned: " << h1path.value() << std::endl; } else { - std::cout << "The path of the file is " << h1path.value() << std::endl; + std::cout << " The path of the file is " << h1path.value() << std::endl; } auto h2path = h2.current_path(); BOOST_CHECK(h2path); if(!h2path) { - std::cerr << "Getting the current path of a directory FAILED due to " << h2path.error().message().c_str() << std::endl; + std::cerr << " Getting the current path of a directory FAILED due to " << h2path.error().message().c_str() << std::endl; } else if(h2path.value().empty()) { BOOST_CHECK(!h2path.value().empty()); - std::cerr << "Getting the current path of a directory FAILED due to the returned path being empty" << std::endl; + std::cerr << " Getting the current path of a directory FAILED due to the returned path being empty" << std::endl; } else if(h2path.value().filename() != "tempdir2") { BOOST_CHECK(h2path.value().filename() == "tempdir2"); - std::cerr << "Getting the current path of a directory FAILED due to the wrong path being returned: " << h2path.value() << std::endl; + std::cerr << " Getting the current path of a directory FAILED due to the wrong path being returned: " << h2path.value() << std::endl; } else { - std::cout << "The path of the directory is " << h2path.value() << std::endl; + std::cout << " The path of the directory is " << h2path.value() << std::endl; } } - h1.relink(h2, "tempfile3").value(); + // Non-atomic relink + std::cout << "\ncurrent_path() after non-atomic relink:\n"; + h1.relink({}, "tempfile3", false).value(); { auto h1path = h1.current_path(); BOOST_CHECK(h1path); if(!h1path) { - std::cerr << "Getting the current path of a file FAILED due to " << h1path.error().message().c_str() << std::endl; + std::cerr << " Getting the current path of a file FAILED due to " << h1path.error().message().c_str() << std::endl; } else if(h1path.value().empty()) { BOOST_CHECK(!h1path.value().empty()); - std::cerr << "Getting the current path of a file FAILED due to the returned path being empty" << std::endl; + std::cerr << " Getting the current path of a file FAILED due to the returned path being empty" << std::endl; } else if(h1path.value().filename() != "tempfile3") { BOOST_CHECK(h1path.value().filename() == "tempfile3"); - std::cerr << "Getting the current path of a file FAILED due to the wrong path being returned: " << h1path.value() << std::endl; + std::cerr << " Getting the current path of a file FAILED due to the wrong path being returned: " << h1path.value() << std::endl; + } + else + { + std::cout << " The path of the file is " << h1path.value() << std::endl; + } + } + { + llfio::stat_t s(nullptr); + auto print = [](const llfio::stat_t &s) { + auto print_dt = [](const std::chrono::system_clock::time_point &tp) { + time_t tt = std::chrono::system_clock::to_time_t(tp); + std::stringstream ss; + ss << std::ctime(&tt); + ss.seekp(-1, ss.cur); + ss << "." << std::chrono::duration_cast<std::chrono::nanoseconds>(tp - std::chrono::system_clock::from_time_t(tt)).count(); + return ss.str(); + }; + std::cout << "Handle has stat_t:"; + std::cout << "\n dev : " << s.st_dev; + std::cout << "\n ino : " << s.st_ino; + std::cout << "\n type : " << (int) s.st_type; + std::cout << "\n nlink : " << s.st_nlink; + std::cout << "\n atim : " << print_dt(s.st_atim); + std::cout << "\n mtim : " << print_dt(s.st_mtim); + std::cout << "\n ctim : " << print_dt(s.st_ctim); + std::cout << "\n size : " << s.st_size; + std::cout << "\n allocated : " << s.st_allocated; + std::cout << "\n blocks : " << s.st_blocks; + std::cout << "\n blksize : " << s.st_blksize; + std::cout << "\n flags : " << s.st_flags; + std::cout << "\n gen : " << s.st_gen; + std::cout << "\n birthtim : " << print_dt(s.st_birthtim); + std::cout << "\n sparse : " << s.st_sparse; + std::cout << "\n compressed : " << s.st_compressed; + std::cout << "\n reparse_point : " << s.st_reparse_point; + std::cout << "\n" << std::endl; + }; + s.fill(h1).value(); + print(s); + BOOST_CHECK(s.st_nlink == 1); + } + + // Non-atomic relink file into dir + std::cout << "\ncurrent_path() after relink into directory:\n"; + h1.relink(h2, "tempfile4", false).value(); + + { + auto h1path = h1.current_path(); + BOOST_CHECK(h1path); + if(!h1path) + { + std::cerr << " Getting the current path of a file FAILED due to " << h1path.error().message().c_str() << std::endl; + } + else if(h1path.value().empty()) + { + BOOST_CHECK(!h1path.value().empty()); + std::cerr << " Getting the current path of a file FAILED due to the returned path being empty" << std::endl; + } + else if(h1path.value().filename() != "tempfile4") + { + BOOST_CHECK(h1path.value().filename() == "tempfile4"); + std::cerr << " Getting the current path of a file FAILED due to the wrong path being returned: " << h1path.value() << std::endl; } else { - std::cout << "The path of the file is " << h1path.value() << std::endl; + std::cout << " The path of the file is " << h1path.value() << std::endl; } } |