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-14 02:00:12 +0300
committerJenkins nedprod CI <jenkins-nedprod@europe5.nedproductions.biz>2020-07-14 02:00:12 +0300
commit794c13254d2532884d34568a416c097355e8f829 (patch)
treee6d46974893ee71ccb5ffc9a0ccc1a1cfeda3d03
parentdebf7ce308c18aafdfa97966c33b1400cbd199bf (diff)
parent597cacee2df213e291d3db0f463665a1a8753a44 (diff)
Merged from develop branch as CDash reports all green
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/fs_handle.ipp86
-rw-r--r--include/llfio/v2.0/fs_handle.hpp9
-rw-r--r--include/llfio/v2.0/stat.hpp120
-rw-r--r--test/tests/current_path.cpp117
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;
}
}