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:
authorNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2017-09-20 03:40:29 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2017-09-20 03:40:29 +0300
commit61148a3e84837a7e7c433b893916ace9404106b6 (patch)
tree6dc60e915a22aa96b64ebba7e8a03bebc09d40f8
parent552867a000005b13d170c6cbd36ab0f476d9f805 (diff)
handle::clone() can now optionally reopen the handle with new privs. Thanks to Bruce Adams from std-proposals for suggesting this.
-rw-r--r--Readme.md1
-rw-r--r--include/afio/revision.hpp6
-rw-r--r--include/afio/v2.0/async_file_handle.hpp16
-rw-r--r--include/afio/v2.0/detail/impl/posix/async_file_handle.ipp8
-rw-r--r--include/afio/v2.0/detail/impl/posix/directory_handle.ipp65
-rw-r--r--include/afio/v2.0/detail/impl/posix/file_handle.ipp113
-rw-r--r--include/afio/v2.0/detail/impl/posix/handle.ipp55
-rw-r--r--include/afio/v2.0/detail/impl/posix/mapped_file_handle.ipp2
-rw-r--r--include/afio/v2.0/detail/impl/windows/async_file_handle.ipp8
-rw-r--r--include/afio/v2.0/detail/impl/windows/directory_handle.ipp63
-rw-r--r--include/afio/v2.0/detail/impl/windows/file_handle.ipp45
-rw-r--r--include/afio/v2.0/detail/impl/windows/handle.ipp22
-rw-r--r--include/afio/v2.0/directory_handle.hpp11
-rw-r--r--include/afio/v2.0/file_handle.hpp12
-rw-r--r--include/afio/v2.0/handle.hpp11
-rw-r--r--include/afio/v2.0/mapped_file_handle.hpp11
-rw-r--r--test/tests/directory_handle_create_close/runner.cpp5
17 files changed, 290 insertions, 164 deletions
diff --git a/Readme.md b/Readme.md
index bccb0fe8..d125dbb7 100644
--- a/Readme.md
+++ b/Readme.md
@@ -6,7 +6,6 @@ Tarballs of source and prebuilt binaries for Linux x64 and Windows x64: http://m
### Immediate todos in order of priority:
-- [ ] Implement file/directory handle reopening.
- [ ] `atomic_append` isn't actually being tested in shared_fs_mutex
- [ ] Implement a non-toy ACID key-value BLOB store and send it to Boost for peer review.
- [ ] All time based kernel tests need to use soak test based API and auto adjust to
diff --git a/include/afio/revision.hpp b/include/afio/revision.hpp
index 03a4292f..4233b8bc 100644
--- a/include/afio/revision.hpp
+++ b/include/afio/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 AFIO_PREVIOUS_COMMIT_REF 5e0683d452700941c589f10dcfb8ead5409ed20d
-#define AFIO_PREVIOUS_COMMIT_DATE "2017-09-19 22:35:48 +00:00"
-#define AFIO_PREVIOUS_COMMIT_UNIQUE 5e0683d4
+#define AFIO_PREVIOUS_COMMIT_REF 552867a000005b13d170c6cbd36ab0f476d9f805
+#define AFIO_PREVIOUS_COMMIT_DATE "2017-09-19 23:11:42 +00:00"
+#define AFIO_PREVIOUS_COMMIT_UNIQUE 552867a0
diff --git a/include/afio/v2.0/async_file_handle.hpp b/include/afio/v2.0/async_file_handle.hpp
index 5d5dd56f..550c182d 100644
--- a/include/afio/v2.0/async_file_handle.hpp
+++ b/include/afio/v2.0/async_file_handle.hpp
@@ -216,8 +216,20 @@ public:
\errors Any of the values POSIX dup() or DuplicateHandle() can return.
*/
- AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<async_file_handle> clone(io_service &service) const noexcept;
- using file_handle::clone;
+ result<async_file_handle> clone(io_service &service, mode _mode = mode::unchanged, caching _caching = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept
+ {
+ OUTCOME_TRY(v, file_handle::clone(_mode, _caching, d));
+ async_file_handle ret(std::move(v));
+ ret._service = &service;
+ return std::move(ret);
+ }
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<file_handle> clone(mode _mode = mode::unchanged, caching _caching = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept override
+ {
+ OUTCOME_TRY(v, file_handle::clone(_mode, _caching, d));
+ async_file_handle ret(std::move(v));
+ ret._service = _service;
+ return static_cast<file_handle &&>(ret);
+ }
#if DOXYGEN_SHOULD_SKIP_THIS
private:
diff --git a/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp b/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp
index a99bca2b..52e8222c 100644
--- a/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp
+++ b/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp
@@ -58,14 +58,6 @@ async_file_handle::io_result<async_file_handle::const_buffers_type> async_file_h
return *ret;
}
-result<async_file_handle> async_file_handle::clone(io_service &service) const noexcept
-{
- OUTCOME_TRY(v, clone());
- async_file_handle ret(std::move(v));
- ret._service = &service;
- return std::move(ret);
-}
-
template <class CompletionRoutine, class BuffersType, class IORoutine>
result<async_file_handle::io_state_ptr<CompletionRoutine, BuffersType>> async_file_handle::_begin_io(async_file_handle::operation_t operation, async_file_handle::io_request<BuffersType> reqs, CompletionRoutine &&completion, IORoutine && /*ioroutine*/) noexcept
{
diff --git a/include/afio/v2.0/detail/impl/posix/directory_handle.ipp b/include/afio/v2.0/detail/impl/posix/directory_handle.ipp
index 37205343..578adfea 100644
--- a/include/afio/v2.0/detail/impl/posix/directory_handle.ipp
+++ b/include/afio/v2.0/detail/impl/posix/directory_handle.ipp
@@ -41,8 +41,10 @@ AFIO_V2_NAMESPACE_BEGIN
result<directory_handle> directory_handle::directory(const path_handle &base, path_view_type path, mode _mode, creation _creation, caching _caching, flag flags) noexcept
{
- // We don't implement unlink on close
- flags &= ~flag::unlink_on_close;
+ if(flags & flag::unlink_on_close)
+ {
+ return std::errc::invalid_argument;
+ }
result<directory_handle> ret(directory_handle(native_handle_type(), 0, 0, _caching, flags));
native_handle_type &nativeh = ret.value()._v;
AFIO_LOG_FUNCTION_CALL(&ret);
@@ -116,15 +118,60 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
return ret;
}
-result<directory_handle> directory_handle::clone() const noexcept
+result<directory_handle> directory_handle::clone(mode mode_, caching caching_, deadline d) const noexcept
{
AFIO_LOG_FUNCTION_CALL(this);
- result<directory_handle> ret(directory_handle(native_handle_type(), _devid, _inode, _caching, _flags));
- ret.value()._v.behaviour = _v.behaviour;
- ret.value()._v.fd = ::dup(_v.fd);
- if(-1 == ret.value()._v.fd)
- return {errno, std::system_category()};
- return ret;
+ // Fast path
+ if(mode_ == mode::unchanged && caching_ == caching::unchanged)
+ {
+ result<directory_handle> ret(directory_handle(native_handle_type(), _devid, _inode, _caching, _flags));
+ ret.value()._v.behaviour = _v.behaviour;
+ ret.value()._v.fd = ::dup(_v.fd);
+ if(-1 == ret.value()._v.fd)
+ return {errno, std::system_category()};
+ return ret;
+ }
+ // Slow path
+ std::chrono::steady_clock::time_point began_steady;
+ std::chrono::system_clock::time_point end_utc;
+ if(d)
+ {
+ if(d.steady)
+ began_steady = std::chrono::steady_clock::now();
+ else
+ end_utc = d.to_time_point();
+ }
+ for(;;)
+ {
+ // Get the current path of myself
+ OUTCOME_TRY(currentpath, current_path());
+ // Open myself
+ auto fh = directory({}, currentpath, mode_, creation::open_existing, caching_, _flags);
+ if(fh)
+ {
+ if(fh.value().unique_id() == unique_id())
+ return fh;
+ }
+ else
+ {
+ if(fh.error() != std::errc::no_such_file_or_directory)
+ return fh.error();
+ }
+ // Check timeout
+ if(d)
+ {
+ if(d.steady)
+ {
+ if(std::chrono::steady_clock::now() >= (began_steady + std::chrono::nanoseconds(d.nsecs)))
+ return std::errc::timed_out;
+ }
+ else
+ {
+ if(std::chrono::system_clock::now() >= end_utc)
+ return std::errc::timed_out;
+ }
+ }
+ }
}
result<directory_handle::enumerate_info> directory_handle::enumerate(buffers_type &&tofill, path_view_type glob, filter /*unused*/, span<char> kernelbuffer) const noexcept
diff --git a/include/afio/v2.0/detail/impl/posix/file_handle.ipp b/include/afio/v2.0/detail/impl/posix/file_handle.ipp
index 6b193f90..1f2bc58e 100644
--- a/include/afio/v2.0/detail/impl/posix/file_handle.ipp
+++ b/include/afio/v2.0/detail/impl/posix/file_handle.ipp
@@ -163,16 +163,113 @@ file_handle::io_result<file_handle::const_buffers_type> file_handle::barrier(fil
return io_handle::io_result<const_buffers_type>(std::move(reqs.buffers));
}
-result<file_handle> file_handle::clone() const noexcept
+result<file_handle> file_handle::clone(mode mode_, caching caching_, deadline d) const noexcept
{
AFIO_LOG_FUNCTION_CALL(this);
- result<file_handle> ret(file_handle(native_handle_type(), _devid, _inode, _caching, _flags));
- ret.value()._service = _service;
- ret.value()._v.behaviour = _v.behaviour;
- ret.value()._v.fd = ::dup(_v.fd);
- if(-1 == ret.value()._v.fd)
- return {errno, std::system_category()};
- return ret;
+ // Fast path
+ if(mode_ == mode::unchanged)
+ {
+ result<file_handle> ret(file_handle(native_handle_type(), _devid, _inode, caching_, _flags));
+ ret.value()._service = _service;
+ ret.value()._v.behaviour = _v.behaviour;
+ ret.value()._v.fd = ::dup(_v.fd);
+ if(-1 == ret.value()._v.fd)
+ return {errno, std::system_category()};
+ if(caching_ == caching::unchanged)
+ return ret;
+
+ int attribs = fcntl(ret.value()._v.fd, F_GETFL);
+ if(-1 != attribs)
+ {
+ attribs &= ~(O_SYNC | O_DIRECT
+#ifdef O_DSYNC
+ | O_DSYNC
+#endif
+ );
+ switch(caching_)
+ {
+ case caching::unchanged:
+ break;
+ case caching::none:
+ attribs |= O_SYNC | O_DIRECT;
+ if(-1 == fcntl(ret.value()._v.fd, F_SETFL, attribs))
+ return {errno, std::system_category()};
+ ret.value()._v.behaviour |= native_handle_type::disposition::aligned_io;
+ break;
+ case caching::only_metadata:
+ attribs |= O_DIRECT;
+ if(-1 == fcntl(ret.value()._v.fd, F_SETFL, attribs))
+ return {errno, std::system_category()};
+ ret.value()._v.behaviour |= native_handle_type::disposition::aligned_io;
+ break;
+ case caching::reads:
+ attribs |= O_SYNC;
+ if(-1 == fcntl(ret.value()._v.fd, F_SETFL, attribs))
+ return {errno, std::system_category()};
+ ret.value()._v.behaviour &= ~native_handle_type::disposition::aligned_io;
+ break;
+ case caching::reads_and_metadata:
+#ifdef O_DSYNC
+ attribs |= O_DSYNC;
+#else
+ attribs |= O_SYNC;
+#endif
+ if(-1 == fcntl(ret.value()._v.fd, F_SETFL, attribs))
+ return {errno, std::system_category()};
+ ret.value()._v.behaviour &= ~native_handle_type::disposition::aligned_io;
+ break;
+ case caching::all:
+ case caching::safety_fsyncs:
+ case caching::temporary:
+ if(-1 == fcntl(ret.value()._v.fd, F_SETFL, attribs))
+ return {errno, std::system_category()};
+ ret.value()._v.behaviour &= ~native_handle_type::disposition::aligned_io;
+ break;
+ }
+ return ret;
+ }
+ }
+ // Slow path
+ std::chrono::steady_clock::time_point began_steady;
+ std::chrono::system_clock::time_point end_utc;
+ if(d)
+ {
+ if(d.steady)
+ began_steady = std::chrono::steady_clock::now();
+ else
+ end_utc = d.to_time_point();
+ }
+ for(;;)
+ {
+ // Get the current path of myself
+ OUTCOME_TRY(currentpath, current_path());
+ // Open myself
+ auto fh = file({}, currentpath, mode_, creation::open_existing, caching_, _flags);
+ if(fh)
+ {
+ if(fh.value().unique_id() == unique_id())
+ return fh;
+ }
+ else
+ {
+ if(fh.error() != std::errc::no_such_file_or_directory)
+ return fh.error();
+ }
+ // Check timeout
+ if(d)
+ {
+ if(d.steady)
+ {
+ if(std::chrono::steady_clock::now() >= (began_steady + std::chrono::nanoseconds(d.nsecs)))
+ return std::errc::timed_out;
+ }
+ else
+ {
+ if(std::chrono::system_clock::now() >= end_utc)
+ return std::errc::timed_out;
+ }
+ }
+ }
}
result<file_handle::extent_type> file_handle::length() const noexcept
diff --git a/include/afio/v2.0/detail/impl/posix/handle.ipp b/include/afio/v2.0/detail/impl/posix/handle.ipp
index 6dff6690..c1e865b7 100644
--- a/include/afio/v2.0/detail/impl/posix/handle.ipp
+++ b/include/afio/v2.0/detail/impl/posix/handle.ipp
@@ -174,59 +174,4 @@ result<void> handle::set_append_only(bool enable) noexcept
return success();
}
-result<void> handle::set_kernel_caching(caching caching) noexcept
-{
- AFIO_LOG_FUNCTION_CALL(this);
- int attribs = fcntl(_v.fd, F_GETFL);
- if(-1 == attribs)
- return {errno, std::system_category()};
- attribs &= ~(O_SYNC | O_DIRECT
-#ifdef O_DSYNC
- | O_DSYNC
-#endif
- );
- switch(_caching)
- {
- case handle::caching::unchanged:
- break;
- case handle::caching::none:
- attribs |= O_SYNC | O_DIRECT;
- if(-1 == fcntl(_v.fd, F_SETFL, attribs))
- return {errno, std::system_category()};
- _v.behaviour |= native_handle_type::disposition::aligned_io;
- break;
- case handle::caching::only_metadata:
- attribs |= O_DIRECT;
- if(-1 == fcntl(_v.fd, F_SETFL, attribs))
- return {errno, std::system_category()};
- _v.behaviour |= native_handle_type::disposition::aligned_io;
- break;
- case handle::caching::reads:
- attribs |= O_SYNC;
- if(-1 == fcntl(_v.fd, F_SETFL, attribs))
- return {errno, std::system_category()};
- _v.behaviour &= ~native_handle_type::disposition::aligned_io;
- break;
- case handle::caching::reads_and_metadata:
-#ifdef O_DSYNC
- attribs |= O_DSYNC;
-#else
- attribs |= O_SYNC;
-#endif
- if(-1 == fcntl(_v.fd, F_SETFL, attribs))
- return {errno, std::system_category()};
- _v.behaviour &= ~native_handle_type::disposition::aligned_io;
- break;
- case handle::caching::all:
- case handle::caching::safety_fsyncs:
- case handle::caching::temporary:
- if(-1 == fcntl(_v.fd, F_SETFL, attribs))
- return {errno, std::system_category()};
- _v.behaviour &= ~native_handle_type::disposition::aligned_io;
- break;
- }
- _caching = caching;
- return success();
-}
-
AFIO_V2_NAMESPACE_END
diff --git a/include/afio/v2.0/detail/impl/posix/mapped_file_handle.ipp b/include/afio/v2.0/detail/impl/posix/mapped_file_handle.ipp
index 0eac991e..b63f3542 100644
--- a/include/afio/v2.0/detail/impl/posix/mapped_file_handle.ipp
+++ b/include/afio/v2.0/detail/impl/posix/mapped_file_handle.ipp
@@ -33,7 +33,7 @@ result<mapped_file_handle::size_type> mapped_file_handle::reserve(size_type rese
OUTCOME_TRY(length, underlying_file_length());
if(length == 0)
{
- return std::errc::invalid_argument;
+ return std::errc::invalid_seek;
}
if(reservation == 0)
{
diff --git a/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp b/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp
index ce8dcfd4..59d4b397 100644
--- a/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp
+++ b/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp
@@ -33,14 +33,6 @@ async_file_handle::io_result<async_file_handle::const_buffers_type> async_file_h
return file_handle::barrier(std::move(reqs), wait_for_device, and_metadata, std::move(d));
}
-result<async_file_handle> async_file_handle::clone(io_service &service) const noexcept
-{
- OUTCOME_TRY(v, clone());
- async_file_handle ret(std::move(v));
- ret._service = &service;
- return std::move(ret);
-}
-
template <class CompletionRoutine, class BuffersType, class IORoutine>
result<async_file_handle::io_state_ptr<CompletionRoutine, BuffersType>> async_file_handle::_begin_io(async_file_handle::operation_t operation, async_file_handle::io_request<BuffersType> reqs, CompletionRoutine &&completion, IORoutine &&ioroutine) noexcept
{
diff --git a/include/afio/v2.0/detail/impl/windows/directory_handle.ipp b/include/afio/v2.0/detail/impl/windows/directory_handle.ipp
index 1f27c85b..6c849dcd 100644
--- a/include/afio/v2.0/detail/impl/windows/directory_handle.ipp
+++ b/include/afio/v2.0/detail/impl/windows/directory_handle.ipp
@@ -31,8 +31,10 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
{
windows_nt_kernel::init();
using namespace windows_nt_kernel;
- // We don't implement unlink on close
- flags &= ~flag::unlink_on_close;
+ if(flags & flag::unlink_on_close)
+ {
+ return std::errc::invalid_argument;
+ }
result<directory_handle> ret(directory_handle(native_handle_type(), 0, 0, _caching, flags));
native_handle_type &nativeh = ret.value()._v;
AFIO_LOG_FUNCTION_CALL(&ret);
@@ -134,27 +136,52 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
ret.value()._flags &= ~flag::disable_safety_unlinks;
}
}
- if(flags & flag::unlink_on_close)
- {
- // Hide this item
- IO_STATUS_BLOCK isb = make_iostatus();
- FILE_BASIC_INFORMATION fbi;
- memset(&fbi, 0, sizeof(fbi));
- fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
- NtSetInformationFile(nativeh.h, &isb, &fbi, sizeof(fbi), FileBasicInformation);
- if(flags & flag::overlapped)
- ntwait(nativeh.h, isb, deadline());
- }
return ret;
}
-result<directory_handle> directory_handle::clone() const noexcept
+result<directory_handle> directory_handle::clone(mode mode_, caching caching_, deadline /* unused */) const noexcept
{
AFIO_LOG_FUNCTION_CALL(this);
- result<directory_handle> ret(directory_handle(native_handle_type(), _devid, _inode, _caching, _flags));
- ret.value()._v.behaviour = _v.behaviour;
- if(!DuplicateHandle(GetCurrentProcess(), _v.h, GetCurrentProcess(), &ret.value()._v.h, 0, false, DUPLICATE_SAME_ACCESS))
- return {GetLastError(), std::system_category()};
+ // Fast path
+ if(mode_ == mode::unchanged && caching_ == caching::unchanged)
+ {
+ result<directory_handle> ret(directory_handle(native_handle_type(), _devid, _inode, _caching, _flags));
+ ret.value()._v.behaviour = _v.behaviour;
+ if(!DuplicateHandle(GetCurrentProcess(), _v.h, GetCurrentProcess(), &ret.value()._v.h, 0, false, DUPLICATE_SAME_ACCESS))
+ return {GetLastError(), std::system_category()};
+ return ret;
+ }
+ // Slow path
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ result<directory_handle> ret(directory_handle(native_handle_type(), _devid, _inode, caching_, _flags));
+ native_handle_type &nativeh = ret.value()._v;
+ nativeh.behaviour |= native_handle_type::disposition::directory;
+ DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ OUTCOME_TRY(access, access_mask_from_handle_mode(nativeh, mode_, _flags));
+ OUTCOME_TRYV(attributes_from_handle_caching_and_flags(nativeh, caching_, _flags));
+ /* It is super important that we remove the DELETE permission for directories as otherwise relative renames
+ will always fail due to an unfortunate design choice by Microsoft.
+ */
+ access &= ~DELETE;
+ OUTCOME_TRY(ntflags, ntflags_from_handle_caching_and_flags(nativeh, caching_, _flags));
+ ntflags |= 0x01 /*FILE_DIRECTORY_FILE*/; // required to open a directory
+ OBJECT_ATTRIBUTES oa;
+ memset(&oa, 0, sizeof(oa));
+ oa.Length = sizeof(OBJECT_ATTRIBUTES);
+ // It is entirely undocumented that this is how you clone a file handle with new privs
+ UNICODE_STRING _path;
+ memset(&_path, 0, sizeof(_path));
+ oa.ObjectName = &_path;
+ oa.RootDirectory = _v.h;
+ IO_STATUS_BLOCK isb = make_iostatus();
+ NTSTATUS ntstat = NtOpenFile(&nativeh.h, access, &oa, &isb, fileshare, ntflags);
+ if(STATUS_PENDING == ntstat)
+ ntstat = ntwait(nativeh.h, isb, deadline());
+ if(ntstat < 0)
+ {
+ return {(int) ntstat, ntkernel_category()};
+ }
return ret;
}
diff --git a/include/afio/v2.0/detail/impl/windows/file_handle.ipp b/include/afio/v2.0/detail/impl/windows/file_handle.ipp
index 1607bb0e..62536aa3 100644
--- a/include/afio/v2.0/detail/impl/windows/file_handle.ipp
+++ b/include/afio/v2.0/detail/impl/windows/file_handle.ipp
@@ -288,15 +288,46 @@ file_handle::io_result<file_handle::const_buffers_type> file_handle::barrier(fil
return io_handle::io_result<const_buffers_type>(std::move(reqs.buffers));
}
-
-result<file_handle> file_handle::clone() const noexcept
+result<file_handle> file_handle::clone(mode mode_, caching caching_, deadline /*unused*/) const noexcept
{
AFIO_LOG_FUNCTION_CALL(this);
- result<file_handle> ret(file_handle(native_handle_type(), _devid, _inode, _caching, _flags));
- ret.value()._service = _service;
- ret.value()._v.behaviour = _v.behaviour;
- if(!DuplicateHandle(GetCurrentProcess(), _v.h, GetCurrentProcess(), &ret.value()._v.h, 0, false, DUPLICATE_SAME_ACCESS))
- return {GetLastError(), std::system_category()};
+ // Fast path
+ if(mode_ == mode::unchanged && caching_ == caching::unchanged)
+ {
+ result<file_handle> ret(file_handle(native_handle_type(), _devid, _inode, caching_, _flags));
+ ret.value()._service = _service;
+ ret.value()._v.behaviour = _v.behaviour;
+ if(!DuplicateHandle(GetCurrentProcess(), _v.h, GetCurrentProcess(), &ret.value()._v.h, 0, false, DUPLICATE_SAME_ACCESS))
+ return {GetLastError(), std::system_category()};
+ return ret;
+ }
+ // Slow path
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ result<file_handle> ret(file_handle(native_handle_type(), _devid, _inode, caching_, _flags));
+ native_handle_type &nativeh = ret.value()._v;
+ nativeh.behaviour |= native_handle_type::disposition::file;
+ DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ OUTCOME_TRY(access, access_mask_from_handle_mode(nativeh, mode_, _flags));
+ OUTCOME_TRYV(attributes_from_handle_caching_and_flags(nativeh, caching_, _flags));
+ OUTCOME_TRY(ntflags, ntflags_from_handle_caching_and_flags(nativeh, caching_, _flags));
+ ntflags |= 0x040 /*FILE_NON_DIRECTORY_FILE*/; // do not open a directory
+ OBJECT_ATTRIBUTES oa;
+ memset(&oa, 0, sizeof(oa));
+ oa.Length = sizeof(OBJECT_ATTRIBUTES);
+ // It is entirely undocumented that this is how you clone a file handle with new privs
+ UNICODE_STRING _path;
+ memset(&_path, 0, sizeof(_path));
+ oa.ObjectName = &_path;
+ oa.RootDirectory = _v.h;
+ IO_STATUS_BLOCK isb = make_iostatus();
+ NTSTATUS ntstat = NtOpenFile(&nativeh.h, access, &oa, &isb, fileshare, ntflags);
+ if(STATUS_PENDING == ntstat)
+ ntstat = ntwait(nativeh.h, isb, deadline());
+ if(ntstat < 0)
+ {
+ return {(int) ntstat, ntkernel_category()};
+ }
return ret;
}
diff --git a/include/afio/v2.0/detail/impl/windows/handle.ipp b/include/afio/v2.0/detail/impl/windows/handle.ipp
index 21f4dcad..ceadb0d0 100644
--- a/include/afio/v2.0/detail/impl/windows/handle.ipp
+++ b/include/afio/v2.0/detail/impl/windows/handle.ipp
@@ -109,26 +109,4 @@ result<void> handle::set_append_only(bool enable) noexcept
return success();
}
-result<void> handle::set_kernel_caching(caching caching) noexcept
-{
- AFIO_LOG_FUNCTION_CALL(this);
- native_handle_type nativeh;
- handle::mode _mode = mode::none;
- if(is_append_only())
- _mode = mode::append;
- else if(is_writable())
- _mode = mode::write;
- else if(is_readable())
- _mode = mode::read;
- OUTCOME_TRY(access, access_mask_from_handle_mode(nativeh, _mode, _flags));
- OUTCOME_TRY(attribs, attributes_from_handle_caching_and_flags(nativeh, caching, _flags));
- nativeh.behaviour |= native_handle_type::disposition::file;
- if(INVALID_HANDLE_VALUE == (nativeh.h = ReOpenFile(_v.h, access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, attribs)))
- return {GetLastError(), std::system_category()};
- _v.swap(nativeh);
- if(!CloseHandle(nativeh.h))
- return {GetLastError(), std::system_category()};
- return success();
-}
-
AFIO_V2_NAMESPACE_END
diff --git a/include/afio/v2.0/directory_handle.hpp b/include/afio/v2.0/directory_handle.hpp
index 8710243a..d13cd2e6 100644
--- a/include/afio/v2.0/directory_handle.hpp
+++ b/include/afio/v2.0/directory_handle.hpp
@@ -218,11 +218,18 @@ public:
return path_handle::close();
}
- /*! Clone this handle (copy constructor is disabled to avoid accidental copying)
+ /*! 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.
*/
- AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<directory_handle> clone() const noexcept;
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<directory_handle> clone(mode _mode = mode::unchanged, caching _caching = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept;
#ifdef _WIN32
AFIO_HEADERS_ONLY_VIRTUAL_SPEC
diff --git a/include/afio/v2.0/file_handle.hpp b/include/afio/v2.0/file_handle.hpp
index 75e9d6c7..03356a24 100644
--- a/include/afio/v2.0/file_handle.hpp
+++ b/include/afio/v2.0/file_handle.hpp
@@ -209,11 +209,19 @@ public:
AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept override;
- /*! Clone this handle (copy constructor is disabled to avoid accidental copying)
+ /*! 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, if not changing the mode, we change caching via `fcntl()`, if
+ changing the mode 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.
*/
- AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<file_handle> clone() const noexcept;
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<file_handle> clone(mode _mode = mode::unchanged, caching _caching = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept;
//! The i/o service this handle is attached to, if any
io_service *service() const noexcept { return _service; }
diff --git a/include/afio/v2.0/handle.hpp b/include/afio/v2.0/handle.hpp
index 49d5a209..47762f6c 100644
--- a/include/afio/v2.0/handle.hpp
+++ b/include/afio/v2.0/handle.hpp
@@ -309,17 +309,6 @@ public:
bool are_writes_durable() const noexcept { return _caching == caching::none || _caching == caching::reads || _caching == caching::reads_and_metadata; }
//! True if issuing safety fsyncs is on
bool are_safety_fsyncs_issued() const noexcept { return !(_flags & flag::disable_safety_fsyncs) && !!(static_cast<int>(_caching) & 1); }
- /*! Changes the kernel cache strategy used by this handle.
- Note most OSs impose severe restrictions on what can be changed and will error out,
- it may be easier to simply create a new handle.
-
- \warning On Windows this reopens the file, it is no slower than
- opening the file fresh but equally it is vastly slower than on POSIX.
-
- \errors Whatever POSIX fcntl() or ReOpenFile() returns.
- \mallocs No memory allocation.
- */
- AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> set_kernel_caching(caching caching) noexcept;
//! The flags this handle was opened with
flag flags() const noexcept { return _flags; }
diff --git a/include/afio/v2.0/mapped_file_handle.hpp b/include/afio/v2.0/mapped_file_handle.hpp
index 49fb304a..e9b1d3ef 100644
--- a/include/afio/v2.0/mapped_file_handle.hpp
+++ b/include/afio/v2.0/mapped_file_handle.hpp
@@ -356,10 +356,15 @@ public:
{
return _mh.barrier(std::move(reqs), wait_for_device, and_metadata, std::move(d));
}
- using file_handle::clone;
- inline result<mapped_file_handle> clone(size_type reservation) const noexcept
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<file_handle> clone(mode _mode = mode::unchanged, caching _caching = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept override
{
- OUTCOME_TRY(fh, clone());
+ OUTCOME_TRY(fh, file_handle::clone(_mode, _caching, d));
+ mapped_file_handle ret(std::move(fh), _reservation);
+ return static_cast<file_handle &&>(ret);
+ }
+ result<mapped_file_handle> clone(size_type reservation, mode _mode = mode::unchanged, caching _caching = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept
+ {
+ OUTCOME_TRY(fh, file_handle::clone(_mode, _caching, d));
return mapped_file_handle(std::move(fh), reservation);
}
//! Return the current maximum permitted extent of the file which is the lesser of the section's length, or the reservation.
diff --git a/test/tests/directory_handle_create_close/runner.cpp b/test/tests/directory_handle_create_close/runner.cpp
index dee37228..1980c0cf 100644
--- a/test/tests/directory_handle_create_close/runner.cpp
+++ b/test/tests/directory_handle_create_close/runner.cpp
@@ -22,8 +22,8 @@ Distributed under the Boost Software License, Version 1.0.
http://www.boost.org/LICENSE_1_0.txt)
*/
-#include "kerneltest/include/kerneltest.hpp"
#include "kernel_directory_handle.cpp.hpp"
+#include "kerneltest/include/kerneltest.hpp"
template <class U> inline void directory_handle_create_close_creation(U &&f)
{
@@ -76,9 +76,6 @@ template <class U> inline void directory_handle_create_close_creation(U &&f)
{ is_a_directory, { directory_handle::mode::write, directory_handle::creation::truncate , directory_handle::flag::none, &entries, &info }, { "non-existing" }, { "non-existing" },{ success() } },
{ is_a_directory, { directory_handle::mode::write, directory_handle::creation::truncate , directory_handle::flag::none, &entries, &info }, { "existing0" }, { "existing0" },{ success() } },
{ is_a_directory, { directory_handle::mode::write, directory_handle::creation::truncate , directory_handle::flag::none, &entries, &info }, { "existing1" }, { "existing1" },{ success() } },
-
- // Does the flag parameter have the expected side effects?
- { success(), { directory_handle::mode::write, directory_handle::creation::open_existing, directory_handle::flag::unlink_on_close, &entries, &info }, { "existing1" }, { "existing1" },{ success() } }
},
precondition::filesystem_setup(),
postcondition::filesystem_comparison_structure(),