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>2018-07-24 22:29:21 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2018-07-24 22:29:21 +0300
commit964d174c1c44d4810789b198bbf07271cebbe8c8 (patch)
treedc3d0ebb672e919caa2f9d2b8fb5654d230ae604 /include
parent084b3eb8aaae639e9a63e97245386fd78b8d8413 (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')
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/algorithm/shared_fs_mutex/atomic_append.hpp19
-rw-r--r--include/llfio/v2.0/config.hpp2
-rw-r--r--include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp12
-rw-r--r--include/llfio/v2.0/detail/impl/posix/file_handle.ipp10
-rw-r--r--include/llfio/v2.0/detail/impl/posix/io_handle.ipp27
-rw-r--r--include/llfio/v2.0/detail/impl/posix/map_handle.ipp76
-rw-r--r--include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp10
-rw-r--r--include/llfio/v2.0/detail/impl/windows/directory_handle.ipp46
-rw-r--r--include/llfio/v2.0/detail/impl/windows/file_handle.ipp55
-rw-r--r--include/llfio/v2.0/detail/impl/windows/fs_handle.ipp5
-rw-r--r--include/llfio/v2.0/detail/impl/windows/import.hpp63
-rw-r--r--include/llfio/v2.0/detail/impl/windows/io_handle.ipp10
-rw-r--r--include/llfio/v2.0/detail/impl/windows/map_handle.ipp60
-rw-r--r--include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp283
-rw-r--r--include/llfio/v2.0/io_handle.hpp89
-rw-r--r--include/llfio/v2.0/llfio.hpp1
-rw-r--r--include/llfio/v2.0/map_handle.hpp8
-rw-r--r--include/llfio/v2.0/path_view.hpp204
-rw-r--r--include/llfio/v2.0/symlink_handle.hpp417
-rw-r--r--include/llfio/v2.0/utils.hpp3
21 files changed, 1129 insertions, 277 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp
index 5470bae0..dfdb3db0 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 2b1b67be8b3f2bd0bf563fee070c12b95c3d1460
-#define LLFIO_PREVIOUS_COMMIT_DATE "2018-07-12 17:59:46 +00:00"
-#define LLFIO_PREVIOUS_COMMIT_UNIQUE 2b1b67be
+#define LLFIO_PREVIOUS_COMMIT_REF 084b3eb8aaae639e9a63e97245386fd78b8d8413
+#define LLFIO_PREVIOUS_COMMIT_DATE "2018-07-13 19:20:14 +00:00"
+#define LLFIO_PREVIOUS_COMMIT_UNIQUE 084b3eb8
diff --git a/include/llfio/v2.0/algorithm/shared_fs_mutex/atomic_append.hpp b/include/llfio/v2.0/algorithm/shared_fs_mutex/atomic_append.hpp
index b285cef5..39d08564 100644
--- a/include/llfio/v2.0/algorithm/shared_fs_mutex/atomic_append.hpp
+++ b/include/llfio/v2.0/algorithm/shared_fs_mutex/atomic_append.hpp
@@ -139,9 +139,9 @@ namespace algorithm
do
{
OUTCOME_TRY(_, _h.read(0, {{reinterpret_cast<byte *>(&_header), 48}}));
- if(_[0].data != reinterpret_cast<byte *>(&_header))
+ if(_[0].data() != reinterpret_cast<byte *>(&_header))
{
- memcpy(&_header, _[0].data, _[0].len);
+ memcpy(&_header, _[0].data(), _[0].size());
}
if(_skip_hashing)
{
@@ -307,7 +307,8 @@ namespace algorithm
std::terminate();
}
const atomic_append_detail::lock_request *record, *lastrecord;
- for(record = reinterpret_cast<const atomic_append_detail::lock_request *>(readoutcome.value()[0].data), lastrecord = reinterpret_cast<const atomic_append_detail::lock_request *>(readoutcome.value()[0].data + readoutcome.value()[0].len); record < lastrecord && record->hash != lock_request.hash; ++record)
+ for(record = reinterpret_cast<const atomic_append_detail::lock_request *>(readoutcome.value()[0].data()), lastrecord = reinterpret_cast<const atomic_append_detail::lock_request *>(readoutcome.value()[0].data() + readoutcome.value()[0].size()); record < lastrecord && record->hash != lock_request.hash;
+ ++record)
{
my_lock_request_offset += sizeof(atomic_append_detail::lock_request);
}
@@ -361,9 +362,9 @@ namespace algorithm
assert(record_offset >= start_offset);
assert(record_offset - start_offset <= sizeof(_buffer));
OUTCOME_TRY(batchread, _h.read(start_offset, {{_buffer, (size_t)(record_offset - start_offset) + sizeof(atomic_append_detail::lock_request)}}));
- assert(batchread[0].len == record_offset - start_offset + sizeof(atomic_append_detail::lock_request));
- const atomic_append_detail::lock_request *record = reinterpret_cast<atomic_append_detail::lock_request *>(batchread[0].data + batchread[0].len - sizeof(atomic_append_detail::lock_request));
- const atomic_append_detail::lock_request *firstrecord = reinterpret_cast<atomic_append_detail::lock_request *>(batchread[0].data);
+ assert(batchread[0].size() == record_offset - start_offset + sizeof(atomic_append_detail::lock_request));
+ const atomic_append_detail::lock_request *record = reinterpret_cast<atomic_append_detail::lock_request *>(batchread[0].data() + batchread[0].size() - sizeof(atomic_append_detail::lock_request));
+ const atomic_append_detail::lock_request *firstrecord = reinterpret_cast<atomic_append_detail::lock_request *>(batchread[0].data());
// Skip all completed lock requests or not mentioning any of my entities
for(; record >= firstrecord; record_offset -= sizeof(atomic_append_detail::lock_request), --record)
@@ -510,12 +511,12 @@ namespace algorithm
}
const auto &bytesread = bytesread_.value();
// If read was partial, we are done after this round
- if(bytesread[0].len < sizeof(_buffer))
+ if(bytesread[0].size() < sizeof(_buffer))
{
done = true;
}
- const auto *record = reinterpret_cast<const atomic_append_detail::lock_request *>(bytesread[0].data);
- const auto *lastrecord = reinterpret_cast<const atomic_append_detail::lock_request *>(bytesread[0].data + bytesread[0].len);
+ const auto *record = reinterpret_cast<const atomic_append_detail::lock_request *>(bytesread[0].data());
+ const auto *lastrecord = reinterpret_cast<const atomic_append_detail::lock_request *>(bytesread[0].data() + bytesread[0].size());
for(; record < lastrecord; ++record)
{
if(!record->hash && (record->unique_id == 0u))
diff --git a/include/llfio/v2.0/config.hpp b/include/llfio/v2.0/config.hpp
index f105f3b6..d1280639 100644
--- a/include/llfio/v2.0/config.hpp
+++ b/include/llfio/v2.0/config.hpp
@@ -27,7 +27,7 @@ Distributed under the Boost Software License, Version 1.0.
//#include <iostream>
//#define LLFIO_LOG_TO_OSTREAM std::cerr
-//#define LLFIO_LOGGING_LEVEL 6
+#define LLFIO_LOGGING_LEVEL 1
//#define LLFIO_DISABLE_PATHS_IN_FAILURE_INFO
//! \file config.hpp Configures a compiler environment for LLFIO header and source code
diff --git a/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp b/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp
index b24dff98..4a3a8652 100644
--- a/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp
@@ -111,7 +111,7 @@ template <class BuffersType, class IORoutine> result<async_file_handle::io_state
LLFIO_LOG_FATAL(0, "file_handle::io_state::operator() called with invalid index");
std::terminate();
}
- result.value()[idx].len = bytes_transferred;
+ result.value()[idx] = {result.value()[idx].data(), (size_type) bytes_transferred};
}
}
this->parent->service()->_work_done();
@@ -228,15 +228,15 @@ template <class BuffersType, class IORoutine> result<async_file_handle::io_state
if(_v.requires_aligned_io())
{
assert((offset & 511) == 0);
- assert(((uintptr_t) out[n].data & 511) == 0);
- assert((out[n].len & 511) == 0);
+ assert(((uintptr_t) out[n].data() & 511) == 0);
+ assert((out[n].size() & 511) == 0);
}
#endif
struct aiocb *aiocb = state->aiocbs + n;
aiocb->aio_fildes = _v.fd;
aiocb->aio_offset = offset;
- aiocb->aio_buf = reinterpret_cast<void *>(const_cast<byte *>(out[n].data));
- aiocb->aio_nbytes = out[n].len;
+ aiocb->aio_buf = reinterpret_cast<void *>(const_cast<byte *>(out[n].data()));
+ aiocb->aio_nbytes = out[n].size();
aiocb->aio_sigevent.sigev_notify = SIGEV_NONE;
aiocb->aio_sigevent.sigev_value.sival_ptr = reinterpret_cast<void *>(state);
switch(operation)
@@ -259,7 +259,7 @@ template <class BuffersType, class IORoutine> result<async_file_handle::io_state
#else
#error todo
#endif
- offset += out[n].len;
+ offset += out[n].size();
++state->items_to_go;
}
int ret = 0;
diff --git a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
index 3ae2dce0..7972f16b 100644
--- a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
@@ -145,7 +145,7 @@ file_handle::io_result<file_handle::const_buffers_type> file_handle::barrier(fil
// empty buffers means bytes = 0 which means sync entire file
for(const auto &req : reqs.buffers)
{
- bytes += req.len;
+ bytes += req.size();
}
unsigned flags = SYNC_FILE_RANGE_WRITE; // start writing all dirty pages in range now
if(wait_for_device)
@@ -464,7 +464,7 @@ result<file_handle::extent_type> file_handle::zero(file_handle::extent_type offs
auto *buffer = static_cast<byte *>(alloca(bytes));
memset(buffer, 0, bytes);
OUTCOME_TRY(written, write(offset, {{buffer, bytes}}, d));
- return written[0].len;
+ return written[0].size();
}
try
{
@@ -477,9 +477,9 @@ result<file_handle::extent_type> file_handle::zero(file_handle::extent_type offs
{
auto towrite = (bytes < blocksize) ? bytes : blocksize;
OUTCOME_TRY(written, write(offset, {{buffer, towrite}}, d));
- offset += written[0].len;
- bytes -= written[0].len;
- ret += written[0].len;
+ offset += written[0].size();
+ bytes -= written[0].size();
+ ret += written[0].size();
}
return ret;
}
diff --git a/include/llfio/v2.0/detail/impl/posix/io_handle.ipp b/include/llfio/v2.0/detail/impl/posix/io_handle.ipp
index 1b67de56..d2e5b82a 100644
--- a/include/llfio/v2.0/detail/impl/posix/io_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/io_handle.ipp
@@ -34,6 +34,13 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_BEGIN
+constexpr inline void _check_iovec_match()
+{
+ static_assert(sizeof(io_handle::buffer_type) == sizeof(iovec), "buffer_type and struct iovec do not match in size");
+ static_assert(offsetof(io_handle::buffer_type, _data) == offsetof(iovec, iov_base), "buffer_type and struct iovec do not have same offset of data member");
+ static_assert(offsetof(io_handle::buffer_type, _len) == offsetof(iovec, iov_len), "buffer_type and struct iovec do not have same offset of len member");
+}
+
size_t io_handle::max_buffers() const noexcept
{
static size_t v;
@@ -76,7 +83,6 @@ io_handle::io_result<io_handle::buffers_type> io_handle::read(io_handle::io_requ
iov[n].iov_len = reqs.buffers[n].len;
}
#else
- static_assert(sizeof(buffer_type) == sizeof(iovec), "buffer_type and struct iovec do not match");
auto *iov = reinterpret_cast<struct iovec *>(reqs.buffers.data());
#endif
#ifndef NDEBUG
@@ -107,18 +113,18 @@ io_handle::io_result<io_handle::buffers_type> io_handle::read(io_handle::io_requ
}
for(auto &buffer : reqs.buffers)
{
- if(buffer.len >= static_cast<size_t>(bytesread))
+ if(buffer.size() >= static_cast<size_t>(bytesread))
{
- bytesread -= buffer.len;
+ bytesread -= buffer.size();
}
else if(bytesread > 0)
{
- buffer.len = bytesread;
+ buffer = {buffer.data(), (size_type) bytesread};
bytesread = 0;
}
else
{
- buffer.len = 0;
+ buffer = {buffer.data(), 0};
}
}
return {reqs.buffers};
@@ -143,7 +149,6 @@ io_handle::io_result<io_handle::const_buffers_type> io_handle::write(io_handle::
iov[n].iov_len = reqs.buffers[n].len;
}
#else
- static_assert(sizeof(buffer_type) == sizeof(iovec), "buffer_type and struct iovec do not match");
auto *iov = reinterpret_cast<struct iovec *>(reqs.buffers.data());
#endif
#ifndef NDEBUG
@@ -174,18 +179,18 @@ io_handle::io_result<io_handle::const_buffers_type> io_handle::write(io_handle::
}
for(auto &buffer : reqs.buffers)
{
- if(buffer.len >= static_cast<size_t>(byteswritten))
+ if(buffer.size() >= static_cast<size_t>(byteswritten))
{
- byteswritten -= buffer.len;
+ byteswritten -= buffer.size();
}
else if(byteswritten > 0)
{
- buffer.len = byteswritten;
+ buffer = {buffer.data(), (size_type) byteswritten};
byteswritten = 0;
}
else
{
- buffer.len = 0;
+ buffer = {buffer.data(), 0};
}
}
return {reqs.buffers};
@@ -257,8 +262,6 @@ result<io_handle::extent_guard> io_handle::lock(io_handle::extent_type offset, i
{
return errc::timed_out;
}
-
-
return posix_error();
}
return extent_guard(this, offset, bytes, exclusive);
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 8531033d..60e4ec14 100644
--- a/include/llfio/v2.0/detail/impl/posix/map_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/map_handle.ipp
@@ -189,11 +189,11 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::barrier(map_ha
// Check for overflow
for(const auto &req : reqs.buffers)
{
- if(bytes + req.len < bytes)
+ if(bytes + req.size() < bytes)
{
return errc::value_too_large;
}
- bytes += req.len;
+ bytes += req.size();
}
// If empty, do the whole file
if(reqs.buffers.empty())
@@ -204,7 +204,7 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::barrier(map_ha
if(!and_metadata && is_nvram())
{
auto synced = barrier({addr, bytes});
- if(synced.len >= bytes)
+ if(synced.size() >= bytes)
{
return {reqs.buffers};
}
@@ -426,17 +426,17 @@ result<map_handle::size_type> map_handle::truncate(size_type newsize, bool permi
result<map_handle::buffer_type> map_handle::commit(buffer_type region, section_handle::flag flag) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
// Set permissions on the pages
region = utils::round_to_page_size(region);
- extent_type offset = _offset + (region.data - _addr);
- size_type bytes = region.len;
- OUTCOME_TRYV(do_mmap(_v, region.data, MAP_FIXED, _section, bytes, offset, flag));
+ extent_type offset = _offset + (region.data() - _addr);
+ size_type bytes = region.size();
+ OUTCOME_TRYV(do_mmap(_v, region.data(), MAP_FIXED, _section, bytes, offset, flag));
// Tell the kernel we will be using these pages soon
- if(-1 == ::madvise(region.data, region.len, MADV_WILLNEED))
+ if(-1 == ::madvise(region.data(), region.size(), MADV_WILLNEED))
{
return posix_error();
}
@@ -446,51 +446,51 @@ result<map_handle::buffer_type> map_handle::commit(buffer_type region, section_h
result<map_handle::buffer_type> map_handle::decommit(buffer_type region) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
region = utils::round_to_page_size(region);
// Tell the kernel to kick these pages into storage
- if(-1 == ::madvise(region.data, region.len, MADV_DONTNEED))
+ if(-1 == ::madvise(region.data(), region.size(), MADV_DONTNEED))
{
return posix_error();
}
// Set permissions on the pages to no access
- extent_type offset = _offset + (region.data - _addr);
- size_type bytes = region.len;
- OUTCOME_TRYV(do_mmap(_v, region.data, MAP_FIXED, _section, bytes, offset, section_handle::flag::none));
+ extent_type offset = _offset + (region.data() - _addr);
+ size_type bytes = region.size();
+ OUTCOME_TRYV(do_mmap(_v, region.data(), MAP_FIXED, _section, bytes, offset, section_handle::flag::none));
return region;
}
result<void> map_handle::zero_memory(buffer_type region) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
#ifdef MADV_REMOVE
- buffer_type page_region{utils::round_up_to_page_size(region.data), utils::round_down_to_page_size(region.len)};
+ buffer_type page_region{utils::round_up_to_page_size(region.data()), utils::round_down_to_page_size(region.size())};
// Zero contents and punch a hole in any backing storage
- if((page_region.len != 0u) && -1 != ::madvise(page_region.data, page_region.len, MADV_REMOVE))
+ if((page_region.size() != 0u) && -1 != ::madvise(page_region.data(), page_region.size(), MADV_REMOVE))
{
- memset(region.data, 0, page_region.data - region.data);
- memset(page_region.data + page_region.len, 0, (region.data + region.len) - (page_region.data + page_region.len));
+ memset(region.data(), 0, page_region.data() - region.data());
+ memset(page_region.data() + page_region.size(), 0, (region.data() + region.size()) - (page_region.data() + page_region.size()));
return success();
}
#endif
//! Only Linux implements syscall zero(), and it's covered by MADV_REMOVE already
- memset(region.data, 0, region.len);
+ memset(region.data(), 0, region.size());
return success();
}
result<span<map_handle::buffer_type>> map_handle::prefetch(span<buffer_type> regions) noexcept
{
LLFIO_LOG_FUNCTION_CALL(0);
- for(const auto &region : regions)
+ for(auto &region : regions)
{
- if(-1 == ::madvise(region.data, region.len, MADV_WILLNEED))
+ if(-1 == ::madvise(region.data(), region.size(), MADV_WILLNEED))
{
return posix_error();
}
@@ -502,25 +502,25 @@ result<map_handle::buffer_type> map_handle::do_not_store(buffer_type region) noe
{
LLFIO_LOG_FUNCTION_CALL(0);
region = utils::round_to_page_size(region);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
#ifdef MADV_FREE
// Lightweight unset of dirty bit for these pages. Needs FreeBSD or very recent Linux.
- if(-1 != ::madvise(region.data, region.len, MADV_FREE))
+ if(-1 != ::madvise(region.data(), region.size(), MADV_FREE))
return region;
#endif
#ifdef MADV_REMOVE
// This is rather heavy weight in that it also punches a hole in any backing storage
// but it works on Linux for donkey's years
- if(-1 != ::madvise(region.data, region.len, MADV_REMOVE))
+ if(-1 != ::madvise(region.data(), region.size(), MADV_REMOVE))
{
return region;
}
#endif
// No support on this platform
- region.len = 0;
+ region = {region.data(), 0};
return region;
}
@@ -533,17 +533,17 @@ map_handle::io_result<map_handle::buffers_type> map_handle::read(io_request<buff
{
if(togo != 0u)
{
- req.data = addr;
- if(req.len > togo)
+ req = {addr, req.size()};
+ if(req.size() > togo)
{
- req.len = togo;
+ req = {req.data(), togo};
}
- addr += req.len;
- togo -= req.len;
+ addr += req.size();
+ togo -= req.size();
}
else
{
- req.len = 0;
+ req = {req.data(), 0};
}
}
return reqs.buffers;
@@ -560,18 +560,18 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::write(io_reque
{
if(togo != 0u)
{
- if(req.len > togo)
+ if(req.size() > togo)
{
- req.len = togo;
+ req = {req.data(), togo};
}
- memcpy(addr, req.data, req.len);
- req.data = addr;
- addr += req.len;
- togo -= req.len;
+ memcpy(addr, req.data(), req.size());
+ req = {addr, req.size()};
+ addr += req.size();
+ togo -= req.size();
}
else
{
- req.len = 0;
+ req = {req.data(), 0};
}
}
return false;
diff --git a/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp b/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp
index 927de39a..4efb577b 100644
--- a/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp
@@ -70,7 +70,7 @@ template <class BuffersType, class IORoutine> result<async_file_handle::io_state
LLFIO_LOG_FATAL(0, "async_file_handle::io_state::operator() called with invalid index");
std::terminate();
}
- result.value()[idx].len = bytes_transferred;
+ result.value()[idx] = {result.value()[idx].data(), (size_t) bytes_transferred};
}
}
this->parent->service()->_work_done();
@@ -177,16 +177,16 @@ template <class BuffersType, class IORoutine> result<async_file_handle::io_state
}
// Use the unused hEvent member to pass through the state
ol->hEvent = reinterpret_cast<HANDLE>(state);
- offset += out[n].len;
+ offset += out[n].size();
++state->items_to_go;
#ifndef NDEBUG
if(_v.requires_aligned_io())
{
- assert((reinterpret_cast<uintptr_t>(out[n].data) & 511) == 0);
- assert((out[n].len & 511) == 0);
+ assert((reinterpret_cast<uintptr_t>(out[n].data()) & 511) == 0);
+ assert((out[n].size() & 511) == 0);
}
#endif
- if(!ioroutine(_v.h, const_cast<byte *>(out[n].data), static_cast<DWORD>(out[n].len), ol, handle_completion::Do))
+ if(!ioroutine(_v.h, const_cast<byte *>(out[n].data()), static_cast<DWORD>(out[n].size()), ol, handle_completion::Do))
{
--state->items_to_go;
state->result.write = win32_error();
diff --git a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
index abdd5a1e..158bbc7f 100644
--- a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
@@ -138,50 +138,8 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
result<directory_handle> directory_handle::clone(mode mode_, caching caching_, deadline /* unused */) const noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- // 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, 0, DUPLICATE_SAME_ACCESS) == 0)
- {
- return win32_error();
- }
- 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 ntkernel_error(ntstat);
- }
+ result<directory_handle> ret(directory_handle(native_handle_type(), _devid, _inode, _caching, _flags));
+ OUTCOME_TRY(do_clone_handle(ret.value()._v, _v, mode_, caching_, _flags, true));
return ret;
}
diff --git a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
index 885bd39c..82c855ae 100644
--- a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
@@ -163,19 +163,6 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
#endif
}
}
- if(flags & flag::unlink_on_first_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());
- }
- }
if(_creation == creation::truncate && ret.value().are_safety_fsyncs_issued())
{
FlushFileBuffers(nativeh.h);
@@ -342,47 +329,9 @@ file_handle::io_result<file_handle::const_buffers_type> file_handle::barrier(fil
result<file_handle> file_handle::clone(mode mode_, caching caching_, deadline /*unused*/) const noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- // 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, 0, DUPLICATE_SAME_ACCESS) == 0)
- {
- return win32_error();
- }
- 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 ntkernel_error(ntstat);
- }
+ ret.value()._service = _service;
+ OUTCOME_TRY(do_clone_handle(ret.value()._v, _v, mode_, caching_, _flags));
return ret;
}
diff --git a/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp b/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp
index 0060d1d1..2b440326 100644
--- a/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp
@@ -200,7 +200,10 @@ result<void> fs_handle::unlink(deadline d) noexcept
oa.ObjectName = &_path;
oa.RootDirectory = h.native_handle().h;
IO_STATUS_BLOCK isb = make_iostatus();
- NTSTATUS ntstat = NtOpenFile(&duph, SYNCHRONIZE | DELETE, &oa, &isb, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0x20 /*FILE_SYNCHRONOUS_IO_NONALERT*/);
+ DWORD ntflags = 0x20 /*FILE_SYNCHRONOUS_IO_NONALERT*/;
+ if(h.is_symlink())
+ ntflags |= 0x00200000 /*FILE_OPEN_REPARSE_POINT*/;
+ NTSTATUS ntstat = NtOpenFile(&duph, SYNCHRONIZE | DELETE, &oa, &isb, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, ntflags);
if(ntstat < 0)
{
return ntkernel_error(ntstat);
diff --git a/include/llfio/v2.0/detail/impl/windows/import.hpp b/include/llfio/v2.0/detail/impl/windows/import.hpp
index 20da757a..0c739dbd 100644
--- a/include/llfio/v2.0/detail/impl/windows/import.hpp
+++ b/include/llfio/v2.0/detail/impl/windows/import.hpp
@@ -958,7 +958,7 @@ inline HANDLE get_thread_local_waitable_timer()
- sleep_interval: Set to the number of steady milliseconds until the sleep must end
- sleep_object: Set to a primed deadline timer HANDLE which will signal when the system clock reaches the deadline
*/
-#define LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d) \
+#define LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d) \
std::chrono::steady_clock::time_point began_steady; \
\
std::chrono::system_clock::time_point end_utc; \
@@ -977,7 +977,7 @@ DWORD sleep_interval = INFINITE;
\
HANDLE sleep_object = nullptr;
-#define LLFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d) \
+#define LLFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d) \
\
if(d) \
\
@@ -1001,7 +1001,7 @@ if(d)
\
}
-#define LLFIO_WIN_DEADLINE_TO_TIMEOUT(type, d) \
+#define LLFIO_WIN_DEADLINE_TO_TIMEOUT(type, d) \
\
if(d) \
\
@@ -1025,7 +1025,7 @@ if(d)
- sleep_interval: Set to the number of steady milliseconds until the sleep must end
- sleep_object: Set to a primed deadline timer HANDLE which will signal when the system clock reaches the deadline
*/
-#define LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d) \
+#define LLFIO_WIN_DEADLINE_TO_SLEEP_INIT(d) \
std::chrono::steady_clock::time_point began_steady; \
\
std::chrono::system_clock::time_point end_utc; \
@@ -1050,7 +1050,7 @@ if(d)
\
}
-#define LLFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d) \
+#define LLFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d) \
if((d) && (d).steady) \
{ \
std::chrono::nanoseconds ns = std::chrono::duration_cast<std::chrono::nanoseconds>((began_steady + std::chrono::nanoseconds((d).nsecs)) - std::chrono::steady_clock::now()); \
@@ -1060,7 +1060,7 @@ if(d)
_timeout.QuadPart = ns.count() / -100; \
}
-#define LLFIO_WIN_DEADLINE_TO_PARTIAL_DEADLINE(nd, d) \
+#define LLFIO_WIN_DEADLINE_TO_PARTIAL_DEADLINE(nd, d) \
if(d) \
{ \
if((d).steady) \
@@ -1075,7 +1075,7 @@ if(d)
(nd) = (d); \
}
-#define LLFIO_WIN_DEADLINE_TO_TIMEOUT(d) \
+#define LLFIO_WIN_DEADLINE_TO_TIMEOUT(d) \
\
if(d) \
\
@@ -1296,6 +1296,55 @@ inline result<DWORD> ntflags_from_handle_caching_and_flags(native_handle_type &n
return ntflags;
}
+inline result<void> do_clone_handle(native_handle_type &dest, const native_handle_type &src, handle::mode mode_, handle::caching caching_, handle::flag _flags, bool isdir = false) noexcept
+{
+ // Fast path
+ if(mode_ == handle::mode::unchanged && caching_ == handle::caching::unchanged)
+ {
+ dest.behaviour = src.behaviour;
+ if(DuplicateHandle(GetCurrentProcess(), dest.h, GetCurrentProcess(), const_cast<HANDLE *>(&src.h), 0, 0, DUPLICATE_SAME_ACCESS) == 0)
+ {
+ return win32_error();
+ }
+ return success();
+ }
+ // Slow path
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ dest.behaviour |= src.behaviour & ~127U; // propagate type of handle only
+ DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ OUTCOME_TRY(access, access_mask_from_handle_mode(dest, mode_, _flags));
+ OUTCOME_TRYV(attributes_from_handle_caching_and_flags(dest, caching_, _flags));
+ if(isdir)
+ {
+ /* 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(dest, 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 = src.h;
+ IO_STATUS_BLOCK isb = make_iostatus();
+ NTSTATUS ntstat = NtOpenFile(&dest.h, access, &oa, &isb, fileshare, ntflags);
+ if(STATUS_PENDING == ntstat)
+ {
+ ntstat = ntwait(dest.h, isb, deadline());
+ }
+ if(ntstat < 0)
+ {
+ return ntkernel_error(ntstat);
+ }
+ return success();
+}
+
/* Our own custom CreateFileW() implementation.
The Win32 CreateFileW() implementation is unfortunately slow. It also, very annoyingly,
diff --git a/include/llfio/v2.0/detail/impl/windows/io_handle.ipp b/include/llfio/v2.0/detail/impl/windows/io_handle.ipp
index 239e917e..1c61ea2d 100644
--- a/include/llfio/v2.0/detail/impl/windows/io_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/io_handle.ipp
@@ -84,15 +84,15 @@ template <class BuffersType, class Syscall> inline io_handle::io_result<BuffersT
#ifndef NDEBUG
if(nativeh.requires_aligned_io())
{
- assert(((uintptr_t) req.data & 511) == 0);
- assert((req.len & 511) == 0);
+ assert(((uintptr_t) req.data() & 511) == 0);
+ assert((req.size() & 511) == 0);
}
#endif
- if(!syscall(nativeh.h, req.data, static_cast<DWORD>(req.len), &transferred, &ol) && ERROR_IO_PENDING != GetLastError())
+ if(!syscall(nativeh.h, req.data(), static_cast<DWORD>(req.size()), &transferred, &ol) && ERROR_IO_PENDING != GetLastError())
{
return win32_error();
}
- reqs.offset += req.len;
+ reqs.offset += req.size();
}
// If handle is overlapped, wait for completion of each i/o.
if(nativeh.is_overlapped())
@@ -116,7 +116,7 @@ template <class BuffersType, class Syscall> inline io_handle::io_result<BuffersT
{
return ntkernel_error(static_cast<NTSTATUS>(ols[n].Internal));
}
- reqs.buffers[n].len = ols[n].InternalHigh;
+ reqs.buffers[n] = {reqs.buffers[n].data(), ols[n].InternalHigh};
}
return io_handle::io_result<BuffersType>(std::move(reqs.buffers));
}
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 f650976f..ca54e008 100644
--- a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp
@@ -441,11 +441,11 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::barrier(map_ha
// Check for overflow
for(const auto &req : reqs.buffers)
{
- if(bytes + req.len < bytes)
+ if(bytes + req.size() < bytes)
{
return errc::value_too_large;
}
- bytes += req.len;
+ bytes += req.size();
}
// bytes = 0 means flush entire mapping
if(bytes == 0)
@@ -456,7 +456,7 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::barrier(map_ha
if(!and_metadata && is_nvram())
{
auto synced = barrier({addr, bytes});
- if(synced.len >= bytes)
+ if(synced.size() >= bytes)
{
return {reqs.buffers};
}
@@ -646,14 +646,14 @@ result<map_handle::size_type> map_handle::truncate(size_type newsize, bool /* un
result<map_handle::buffer_type> map_handle::commit(buffer_type region, section_handle::flag flag) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
DWORD prot = 0;
if(flag == section_handle::flag::none)
{
- OUTCOME_TRYV(win32_maps_apply(region.data, region.len, [](byte *addr, size_t bytes) -> result<void> {
+ OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), [](byte *addr, size_t bytes) -> result<void> {
DWORD _ = 0;
if(VirtualProtect(addr, bytes, PAGE_NOACCESS, &_) == 0)
{
@@ -680,7 +680,7 @@ result<map_handle::buffer_type> map_handle::commit(buffer_type region, section_h
prot = PAGE_EXECUTE;
}
region = utils::round_to_page_size(region);
- OUTCOME_TRYV(win32_maps_apply(region.data, region.len, [prot](byte *addr, size_t bytes) -> result<void> {
+ OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), [prot](byte *addr, size_t bytes) -> result<void> {
if(VirtualAlloc(addr, bytes, MEM_COMMIT, prot) == nullptr)
{
return win32_error();
@@ -693,12 +693,12 @@ result<map_handle::buffer_type> map_handle::commit(buffer_type region, section_h
result<map_handle::buffer_type> map_handle::decommit(buffer_type region) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
region = utils::round_to_page_size(region);
- OUTCOME_TRYV(win32_maps_apply(region.data, region.len, [](byte *addr, size_t bytes) -> result<void> {
+ OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), [](byte *addr, size_t bytes) -> result<void> {
if(VirtualFree(addr, bytes, MEM_DECOMMIT) == 0)
{
return win32_error();
@@ -713,18 +713,18 @@ result<void> map_handle::zero_memory(buffer_type region) noexcept
windows_nt_kernel::init();
using namespace windows_nt_kernel;
LLFIO_LOG_FUNCTION_CALL(this);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
// Alas, zero() will not work on mapped views on Windows :(, so memset to zero and call discard if available
- memset(region.data, 0, region.len);
- if((DiscardVirtualMemory_ != nullptr) && region.len >= utils::page_size())
+ memset(region.data(), 0, region.size());
+ if((DiscardVirtualMemory_ != nullptr) && region.size() >= utils::page_size())
{
region = utils::round_to_page_size(region);
- if(region.len > 0)
+ if(region.size() > 0)
{
- OUTCOME_TRYV(win32_maps_apply(region.data, region.len, [](byte *addr, size_t bytes) -> result<void> {
+ OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), [](byte *addr, size_t bytes) -> result<void> {
if(DiscardVirtualMemory_(addr, bytes) == 0)
{
return win32_error();
@@ -759,7 +759,7 @@ result<map_handle::buffer_type> map_handle::do_not_store(buffer_type region) noe
using namespace windows_nt_kernel;
LLFIO_LOG_FUNCTION_CALL(0);
region = utils::round_to_page_size(region);
- if(region.data == nullptr)
+ if(region.data() == nullptr)
{
return errc::invalid_argument;
}
@@ -769,7 +769,7 @@ result<map_handle::buffer_type> map_handle::do_not_store(buffer_type region) noe
// Win8's DiscardVirtualMemory is much faster if it's available
if(DiscardVirtualMemory_ != nullptr)
{
- OUTCOME_TRYV(win32_maps_apply(region.data, region.len, [](byte *addr, size_t bytes) -> result<void> {
+ OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), [](byte *addr, size_t bytes) -> result<void> {
if(DiscardVirtualMemory_(addr, bytes) == 0)
{
return win32_error();
@@ -779,7 +779,7 @@ result<map_handle::buffer_type> map_handle::do_not_store(buffer_type region) noe
return region;
}
// Else MEM_RESET will do
- OUTCOME_TRYV(win32_maps_apply(region.data, region.len, [](byte *addr, size_t bytes) -> result<void> {
+ OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), [](byte *addr, size_t bytes) -> result<void> {
if(VirtualAlloc(addr, bytes, MEM_RESET, 0) == nullptr)
{
return win32_error();
@@ -789,7 +789,7 @@ result<map_handle::buffer_type> map_handle::do_not_store(buffer_type region) noe
return region;
}
// We did nothing
- region.len = 0;
+ region = {region.data(), 0};
return region;
}
@@ -802,17 +802,17 @@ map_handle::io_result<map_handle::buffers_type> map_handle::read(io_request<buff
{
if(togo != 0u)
{
- req.data = addr;
- if(req.len > togo)
+ req = {addr, req.size()};
+ if(req.size() > togo)
{
- req.len = togo;
+ req = {req.data(), togo};
}
- addr += req.len;
- togo -= req.len;
+ addr += req.size();
+ togo -= req.size();
}
else
{
- req.len = 0;
+ req = {req.data(), 0};
}
}
return reqs.buffers;
@@ -829,18 +829,18 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::write(io_reque
{
if(togo != 0u)
{
- if(req.len > togo)
+ if(req.size() > togo)
{
- req.len = togo;
+ req = {req.data(), togo};
}
- memcpy(addr, req.data, req.len);
- req.data = addr;
- addr += req.len;
- togo -= req.len;
+ memcpy(addr, req.data(), req.size());
+ req = {addr, req.size()};
+ addr += req.size();
+ togo -= req.size();
}
else
{
- req.len = 0;
+ req = {req.data(), 0};
}
}
return false;
diff --git a/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
new file mode 100644
index 00000000..f9138f78
--- /dev/null
+++ b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
@@ -0,0 +1,283 @@
+/* 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)
+*/
+
+#include "../../../symlink_handle.hpp"
+#include "import.hpp"
+
+LLFIO_V2_NAMESPACE_BEGIN
+
+result<symlink_handle> symlink_handle::clone(mode mode_, deadline /*unused*/) const noexcept
+{
+ LLFIO_LOG_FUNCTION_CALL(this);
+ result<symlink_handle> ret(symlink_handle(native_handle_type(), _devid, _inode, _flags));
+ OUTCOME_TRY(do_clone_handle(ret.value()._v, _v, mode_, caching::all, _flags));
+ return ret;
+}
+
+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
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ result<symlink_handle> ret(symlink_handle(native_handle_type(), 0, 0, flags));
+ native_handle_type &nativeh = ret.value()._v;
+ LLFIO_LOG_FUNCTION_CALL(&ret);
+ nativeh.behaviour |= native_handle_type::disposition::symlink;
+ DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+ if(_mode == mode::append || _creation == creation::truncate)
+ {
+ return errc::function_not_supported;
+ }
+ OUTCOME_TRY(access, access_mask_from_handle_mode(nativeh, _mode, flags));
+ OUTCOME_TRY(attribs, attributes_from_handle_caching_and_flags(nativeh, caching::all, flags));
+ nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable
+ if(base.is_valid() || path.is_ntpath())
+ {
+ DWORD creatdisp = 0x00000001 /*FILE_OPEN*/;
+ switch(_creation)
+ {
+ case creation::open_existing:
+ break;
+ case creation::only_if_not_exist:
+ creatdisp = 0x00000002 /*FILE_CREATE*/;
+ break;
+ case creation::if_needed:
+ creatdisp = 0x00000003 /*FILE_OPEN_IF*/;
+ break;
+ case creation::truncate:
+ creatdisp = 0x00000004 /*FILE_OVERWRITE*/;
+ break;
+ }
+
+ attribs &= 0x00ffffff; // the real attributes only, not the win32 flags
+ OUTCOME_TRY(ntflags, ntflags_from_handle_caching_and_flags(nativeh, caching::all, flags));
+ ntflags |= 0x4000 /*FILE_OPEN_FOR_BACKUP_INTENT*/ | 0x00200000 /*FILE_OPEN_REPARSE_POINT*/;
+ ntflags |= 0x040 /*FILE_NON_DIRECTORY_FILE*/; // do not open a directory
+ IO_STATUS_BLOCK isb = make_iostatus();
+
+ path_view::c_str zpath(path, true);
+ UNICODE_STRING _path{};
+ _path.Buffer = const_cast<wchar_t *>(zpath.buffer);
+ _path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
+ if(zpath.length >= 4 && _path.Buffer[0] == '\\' && _path.Buffer[1] == '!' && _path.Buffer[2] == '!' && _path.Buffer[3] == '\\')
+ {
+ _path.Buffer += 3;
+ _path.Length -= 3 * sizeof(wchar_t);
+ _path.MaximumLength -= 3 * sizeof(wchar_t);
+ }
+
+ OBJECT_ATTRIBUTES oa{};
+ memset(&oa, 0, sizeof(oa));
+ oa.Length = sizeof(OBJECT_ATTRIBUTES);
+ oa.ObjectName = &_path;
+ oa.RootDirectory = base.is_valid() ? base.native_handle().h : nullptr;
+ oa.Attributes = 0x40 /*OBJ_CASE_INSENSITIVE*/;
+ // if(!!(flags & file_flags::int_opening_link))
+ // oa.Attributes|=0x100/*OBJ_OPENLINK*/;
+
+ LARGE_INTEGER AllocationSize{};
+ memset(&AllocationSize, 0, sizeof(AllocationSize));
+ NTSTATUS ntstat = NtCreateFile(&nativeh.h, access, &oa, &isb, &AllocationSize, attribs, fileshare, creatdisp, ntflags, nullptr, 0);
+ if(STATUS_PENDING == ntstat)
+ {
+ ntstat = ntwait(nativeh.h, isb, deadline());
+ }
+ if(ntstat < 0)
+ {
+ return ntkernel_error(ntstat);
+ }
+ }
+ else
+ {
+ DWORD creation = OPEN_EXISTING;
+ switch(_creation)
+ {
+ case creation::open_existing:
+ break;
+ case creation::only_if_not_exist:
+ creation = CREATE_NEW;
+ break;
+ case creation::if_needed:
+ creation = OPEN_ALWAYS;
+ break;
+ case creation::truncate:
+ creation = TRUNCATE_EXISTING;
+ break;
+ }
+ // required to open a symlink
+ attribs |= FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
+ path_view::c_str zpath(path, false);
+ if(INVALID_HANDLE_VALUE == (nativeh.h = CreateFileW_(zpath.buffer, access, fileshare, nullptr, creation, attribs, nullptr))) // NOLINT
+ {
+ DWORD errcode = GetLastError();
+ // assert(false);
+ return win32_error(errcode);
+ }
+ }
+ return ret;
+}
+
+result<symlink_handle::buffers_type> symlink_handle::read(symlink_handle::io_request<symlink_handle::buffers_type> req) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ LLFIO_LOG_FUNCTION_CALL(this);
+ using windows_nt_kernel::REPARSE_DATA_BUFFER;
+ symlink_handle::buffers_type tofill;
+ if(req.kernelbuffer.empty())
+ {
+ // Let's assume the average symbolic link will be 256 characters long.
+ size_t toallocate = (sizeof(FILE_ID_FULL_DIR_INFORMATION) + 256 * sizeof(wchar_t));
+ auto *mem = new(std::nothrow) char[toallocate];
+ if(mem == nullptr)
+ {
+ return errc::not_enough_memory;
+ }
+ tofill._kernel_buffer = std::unique_ptr<char[]>(mem);
+ tofill._kernel_buffer_size = toallocate;
+ }
+ REPARSE_DATA_BUFFER *rpd;
+ size_t bytes;
+ for(;;)
+ {
+ rpd = req.kernelbuffer.empty() ? reinterpret_cast<REPARSE_DATA_BUFFER *>(tofill._kernel_buffer.get()) : reinterpret_cast<REPARSE_DATA_BUFFER *>(req.kernelbuffer.data());
+ bytes = req.kernelbuffer.empty() ? static_cast<ULONG>(tofill._kernel_buffer_size) : static_cast<ULONG>(req.kernelbuffer.size());
+ DWORD written = 0;
+ if(!DeviceIoControl(_v.h, FSCTL_GET_REPARSE_POINT, NULL, 0, rpd, (DWORD) bytes, &written, NULL))
+ {
+ DWORD errcode = GetLastError();
+ if(req.kernelbuffer.empty() && (errcode == ERROR_INSUFFICIENT_BUFFER || errcode == ERROR_MORE_DATA))
+ {
+ tofill._kernel_buffer.reset();
+ size_t toallocate = tofill._kernel_buffer_size * 2;
+ auto *mem = new(std::nothrow) char[toallocate];
+ if(mem == nullptr)
+ {
+ return errc::not_enough_memory;
+ }
+ tofill._kernel_buffer = std::unique_ptr<char[]>(mem);
+ tofill._kernel_buffer_size = toallocate;
+ continue;
+ }
+ return win32_error(errcode);
+ }
+ switch(rpd->ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ tofill._link = path_view(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.SubstituteNameOffset / sizeof(rpd->MountPointReparseBuffer.PathBuffer[0]), rpd->MountPointReparseBuffer.SubstituteNameLength / sizeof(rpd->MountPointReparseBuffer.PathBuffer[0]));
+ tofill._type = symlink_type::win_junction;
+ return std::move(tofill);
+ case IO_REPARSE_TAG_SYMLINK:
+ tofill._link = path_view(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(rpd->SymbolicLinkReparseBuffer.PathBuffer[0]), rpd->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(rpd->SymbolicLinkReparseBuffer.PathBuffer[0]));
+ tofill._type = symlink_type::symbolic;
+ return std::move(tofill);
+ }
+ return errc::protocol_not_supported;
+ }
+}
+
+result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle::io_request<symlink_handle::const_buffers_type> req) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ LLFIO_LOG_FUNCTION_CALL(this);
+ using windows_nt_kernel::REPARSE_DATA_BUFFER;
+ size_t destpathbytes = req.buffers.path().native_size() * sizeof(wchar_t);
+ size_t buffersize = sizeof(REPARSE_DATA_BUFFER) + destpathbytes * 2 + 256;
+ if(buffersize < req.kernelbuffer.size())
+ {
+ return errc::not_enough_memory;
+ }
+ const size_t headerlen = offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
+ auto *buffer = req.kernelbuffer.empty() ? alloca(buffersize) : req.kernelbuffer.data();
+ memset(buffer, 0, sizeof(REPARSE_DATA_BUFFER));
+ auto *rpd = (REPARSE_DATA_BUFFER *) buffer;
+ path_view::c_str zpath(req.buffers.path(), true);
+ switch(req.buffers.type())
+ {
+ case symlink_type::none:
+ return errc::invalid_argument;
+ case symlink_type::symbolic:
+ {
+ const size_t reparsebufferheaderlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - headerlen;
+ rpd->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+ if(zpath.length >= 4 && zpath.buffer[0] == '\\' && zpath.buffer[1] == '!' && zpath.buffer[2] == '!' && zpath.buffer[3] == '\\')
+ {
+ memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer, zpath.buffer + 3, destpathbytes - 6 + sizeof(wchar_t));
+ rpd->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+ rpd->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes - 6;
+ rpd->SymbolicLinkReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes - 6 + sizeof(wchar_t));
+ rpd->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT) destpathbytes - 6;
+ memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer + 3, rpd->SymbolicLinkReparseBuffer.PrintNameLength - 6 + sizeof(wchar_t));
+ }
+ else
+ {
+ memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer, zpath.buffer, destpathbytes + sizeof(wchar_t));
+ rpd->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+ rpd->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes;
+ rpd->SymbolicLinkReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes + sizeof(wchar_t));
+ rpd->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT) destpathbytes;
+ memcpy(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer, rpd->SymbolicLinkReparseBuffer.PrintNameLength + sizeof(wchar_t));
+ }
+ rpd->SymbolicLinkReparseBuffer.Flags = req.buffers.path().is_relative() ? 0x1 /*SYMLINK_FLAG_RELATIVE*/ : 0;
+ rpd->ReparseDataLength = (USHORT)(rpd->SymbolicLinkReparseBuffer.SubstituteNameLength + rpd->SymbolicLinkReparseBuffer.PrintNameLength + 2 * sizeof(wchar_t) + reparsebufferheaderlen);
+ break;
+ }
+ case symlink_type::win_wsl:
+ // TODO FIXME
+ abort();
+ case symlink_type::win_junction:
+ {
+ const size_t reparsebufferheaderlen = offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - headerlen;
+ rpd->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ if(zpath.length >= 4 && zpath.buffer[0] == '\\' && zpath.buffer[1] == '!' && zpath.buffer[2] == '!' && zpath.buffer[3] == '\\')
+ {
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer, zpath.buffer + 3, destpathbytes - 6 + sizeof(wchar_t));
+ rpd->MountPointReparseBuffer.SubstituteNameOffset = 0;
+ rpd->MountPointReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes - 6;
+ rpd->MountPointReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes - 6 + sizeof(wchar_t));
+ rpd->MountPointReparseBuffer.PrintNameLength = (USHORT) destpathbytes - 6;
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer + 3, rpd->MountPointReparseBuffer.PrintNameLength - 6 + sizeof(wchar_t));
+ }
+ else
+ {
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer, zpath.buffer, destpathbytes + sizeof(wchar_t));
+ rpd->MountPointReparseBuffer.SubstituteNameOffset = 0;
+ rpd->MountPointReparseBuffer.SubstituteNameLength = (USHORT) destpathbytes;
+ rpd->MountPointReparseBuffer.PrintNameOffset = (USHORT)(destpathbytes + sizeof(wchar_t));
+ rpd->MountPointReparseBuffer.PrintNameLength = (USHORT) destpathbytes;
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t), zpath.buffer, rpd->MountPointReparseBuffer.PrintNameLength + sizeof(wchar_t));
+ }
+ rpd->ReparseDataLength = (USHORT)(rpd->MountPointReparseBuffer.SubstituteNameLength + rpd->MountPointReparseBuffer.PrintNameLength + 2 * sizeof(wchar_t) + reparsebufferheaderlen);
+ break;
+ }
+ }
+ DWORD bytesout = 0;
+ if(DeviceIoControl(_v.h, FSCTL_SET_REPARSE_POINT, rpd, (DWORD)(rpd->ReparseDataLength + headerlen), NULL, 0, &bytesout, NULL) == 0)
+ {
+ return win32_error();
+ }
+ return success(std::move(req.buffers));
+}
+
+LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/io_handle.hpp b/include/llfio/v2.0/io_handle.hpp
index 58716368..55aa1f21 100644
--- a/include/llfio/v2.0/io_handle.hpp
+++ b/include/llfio/v2.0/io_handle.hpp
@@ -51,10 +51,13 @@ public:
using flag = handle::flag;
//! The scatter buffer type used by this handle. Guaranteed to be `TrivialType` and `StandardLayoutType`.
+ //! Try to make address and length 64 byte, or ideally, `page_size()` aligned where possible.
struct buffer_type
{
//! Type of the pointer to memory.
using pointer = byte *;
+ //! Type of the pointer to memory.
+ using const_pointer = const byte *;
//! Type of the iterator to memory.
using iterator = byte *;
//! Type of the iterator to memory.
@@ -62,29 +65,50 @@ public:
//! Type of the length of memory.
using size_type = size_t;
- //! Pointer to memory to be filled by a read. Try to make this 64 byte, or ideally, `page_size()` aligned where possible.
- pointer data;
- //! The number of bytes to fill into this address. Try to make this a 64 byte multiple, or ideally, a whole multiple of `page_size()`.
- size_type len;
+ //! Default constructor
+ buffer_type() = default;
+ //! Constructor
+ constexpr buffer_type(pointer data, size_type len) noexcept : _data(data), _len(len) {}
+ buffer_type(const buffer_type &) = default;
+ buffer_type(buffer_type &&) = default;
+ buffer_type &operator=(const buffer_type &) = default;
+ buffer_type &operator=(buffer_type &&) = default;
+ ~buffer_type() = default;
+
+ // Emulation of this being a span<byte> in the TS
+
+ //! Returns the address of the bytes for this buffer
+ constexpr pointer data() noexcept { return _data; }
+ //! Returns the address of the bytes for this buffer
+ constexpr const_pointer data() const noexcept { return _data; }
+ //! Returns the number of bytes in this buffer
+ constexpr size_type size() const noexcept { return _len; }
//! Returns an iterator to the beginning of the buffer
- constexpr iterator begin() { return data; }
+ constexpr iterator begin() noexcept { return _data; }
//! Returns an iterator to the beginning of the buffer
- constexpr const_iterator begin() const { return data; }
+ constexpr const_iterator begin() const noexcept { return _data; }
//! Returns an iterator to the beginning of the buffer
- constexpr const_iterator cbegin() const { return data; }
+ constexpr const_iterator cbegin() const noexcept { return _data; }
//! Returns an iterator to after the end of the buffer
- constexpr iterator end() { return data + len; }
+ constexpr iterator end() noexcept { return _data + _len; }
//! Returns an iterator to after the end of the buffer
- constexpr const_iterator end() const { return data + len; }
+ constexpr const_iterator end() const noexcept { return _data + _len; }
//! Returns an iterator to after the end of the buffer
- constexpr const_iterator cend() const { return data + len; }
+ constexpr const_iterator cend() const noexcept { return _data + _len; }
+ private:
+ friend constexpr inline void _check_iovec_match();
+ pointer _data;
+ size_type _len;
};
//! The gather buffer type used by this handle. Guaranteed to be `TrivialType` and `StandardLayoutType`.
+ //! Try to make address and length 64 byte, or ideally, `page_size()` aligned where possible.
struct const_buffer_type
{
//! Type of the pointer to memory.
using pointer = const byte *;
+ //! Type of the pointer to memory.
+ using const_pointer = const byte *;
//! Type of the iterator to memory.
using iterator = const byte *;
//! Type of the iterator to memory.
@@ -92,23 +116,40 @@ public:
//! Type of the length of memory.
using size_type = size_t;
- //! Pointer to memory to be written. Try to make this 64 byte, or ideally, `page_size()` aligned where possible.
- pointer data;
- //! The number of bytes to write from this address. Try to make this a 64 byte multiple, or ideally, a whole multiple of `page_size()`.
- size_type len;
+ //! Default constructor
+ const_buffer_type() = default;
+ //! Constructor
+ constexpr const_buffer_type(pointer data, size_type len) noexcept : _data(data), _len(len) {}
+ const_buffer_type(const const_buffer_type &) = default;
+ const_buffer_type(const_buffer_type &&) = default;
+ const_buffer_type &operator=(const const_buffer_type &) = default;
+ const_buffer_type &operator=(const_buffer_type &&) = default;
+ ~const_buffer_type() = default;
+
+ // Emulation of this being a span<byte> in the TS
+
+ //! Returns the address of the bytes for this buffer
+ constexpr pointer data() noexcept { return _data; }
+ //! Returns the address of the bytes for this buffer
+ constexpr const_pointer data() const noexcept { return _data; }
+ //! Returns the number of bytes in this buffer
+ constexpr size_type size() const noexcept { return _len; }
//! Returns an iterator to the beginning of the buffer
- constexpr iterator begin() { return data; }
+ constexpr iterator begin() noexcept { return _data; }
//! Returns an iterator to the beginning of the buffer
- constexpr const_iterator begin() const { return data; }
+ constexpr const_iterator begin() const noexcept { return _data; }
//! Returns an iterator to the beginning of the buffer
- constexpr const_iterator cbegin() const { return data; }
+ constexpr const_iterator cbegin() const noexcept { return _data; }
//! Returns an iterator to after the end of the buffer
- constexpr iterator end() { return data + len; }
+ constexpr iterator end() noexcept { return _data + _len; }
//! Returns an iterator to after the end of the buffer
- constexpr const_iterator end() const { return data + len; }
+ constexpr const_iterator end() const noexcept { return _data + _len; }
//! Returns an iterator to after the end of the buffer
- constexpr const_iterator cend() const { return data + len; }
+ constexpr const_iterator cend() const noexcept { return _data + _len; }
+ private:
+ pointer _data;
+ size_type _len;
};
#ifndef NDEBUG
static_assert(std::is_trivial<buffer_type>::value, "buffer_type is not a trivial type!");
@@ -459,11 +500,11 @@ public:
size_t bytes = 0;
for(auto &i : reqs.buffers)
{
- if(bytes + i.len < bytes)
+ if(bytes + i.size() < bytes)
{
return errc::value_too_large;
}
- bytes += i.len;
+ bytes += i.size();
}
return lock(reqs.offset, bytes, false, d);
}
@@ -473,11 +514,11 @@ public:
size_t bytes = 0;
for(auto &i : reqs.buffers)
{
- if(bytes + i.len < bytes)
+ if(bytes + i.size() < bytes)
{
return errc::value_too_large;
}
- bytes += i.len;
+ bytes += i.size();
}
return lock(reqs.offset, bytes, true, d);
}
diff --git a/include/llfio/v2.0/llfio.hpp b/include/llfio/v2.0/llfio.hpp
index 4fee0f54..59984dca 100644
--- a/include/llfio/v2.0/llfio.hpp
+++ b/include/llfio/v2.0/llfio.hpp
@@ -73,6 +73,7 @@ import LLFIO_MODULE_NAME;
#ifndef LLFIO_LEAN_AND_MEAN
#include "storage_profile.hpp"
#endif
+#include "symlink_handle.hpp"
#include "algorithm/cached_parent_handle_adapter.hpp"
#include "algorithm/shared_fs_mutex/atomic_append.hpp"
diff --git a/include/llfio/v2.0/map_handle.hpp b/include/llfio/v2.0/map_handle.hpp
index d3a8c2df..60deb440 100644
--- a/include/llfio/v2.0/map_handle.hpp
+++ b/include/llfio/v2.0/map_handle.hpp
@@ -362,15 +362,15 @@ public:
LLFIO_MAKE_FREE_FUNCTION
static const_buffer_type barrier(const_buffer_type req, bool evict = false) noexcept
{
- const_buffer_type ret{(const_buffer_type::pointer)(((uintptr_t) req.data) & 31), 0};
- ret.len = req.data + req.len - ret.data;
- for(const_buffer_type::pointer addr = ret.data; addr < ret.data + ret.len; addr += 32)
+ auto *tp = (const_buffer_type::pointer)(((uintptr_t) req.data()) & 31);
+ const_buffer_type ret{tp, (size_t)(req.data() + req.size() - tp)};
+ for(const_buffer_type::pointer addr = ret.data(); addr < ret.data() + ret.size(); addr += 32)
{
// Slightly UB ...
auto *p = reinterpret_cast<const persistent<byte> *>(addr);
if(memory_flush_none == p->flush(evict ? memory_flush_evict : memory_flush_retain))
{
- req.len = 0;
+ ret = {tp, 0};
break;
}
}
diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp
index 23ec86a5..73faec7a 100644
--- a/include/llfio/v2.0/path_view.hpp
+++ b/include/llfio/v2.0/path_view.hpp
@@ -122,16 +122,6 @@ maximum compatibility you should still use the Win32 API.
class LLFIO_DECL path_view
{
public:
- //! Character type
- using value_type = char;
- //! Pointer type
- using pointer = char *;
- //! Const pointer type
- using const_pointer = const char *;
- //! Reference type
- using reference = char &;
- //! Const reference type
- using const_reference = const char &;
// const_iterator
// iterator
// reverse_iterator
@@ -141,6 +131,9 @@ public:
//! Difference type
using difference_type = std::ptrdiff_t;
+ //! The preferred separator type
+ static constexpr auto preferred_separator = filesystem::path::preferred_separator;
+
private:
static constexpr auto _npos = string_view::npos;
#ifdef _WIN32
@@ -167,6 +160,16 @@ private:
} _state;
template <class U> constexpr auto _invoke(U &&f) noexcept { return !_state._utf16.empty() ? f(_state._utf16) : f(_state._utf8); }
template <class U> constexpr auto _invoke(U &&f) const noexcept { return !_state._utf16.empty() ? f(_state._utf16) : f(_state._utf8); }
+ constexpr auto _find_first_sep(size_t startidx = 0) const noexcept
+ {
+ // wchar paths must use backslashes
+ if(!_state._utf16.empty())
+ {
+ return _state._utf16.find('\\', startidx);
+ }
+ // char paths can use either
+ return _state._utf8.find_first_of("/\\", startidx);
+ }
constexpr auto _find_last_sep() const noexcept
{
// wchar paths must use backslashes
@@ -191,7 +194,8 @@ private:
} _state;
template <class U> constexpr auto _invoke(U &&f) noexcept { return f(_state._utf8); }
template <class U> constexpr auto _invoke(U &&f) const noexcept { return f(_state._utf8); }
- constexpr auto _find_last_sep() const noexcept { return _state._utf8.rfind(filesystem::path::preferred_separator); }
+ constexpr auto _find_first_sep(size_t startidx = 0) const noexcept { return _state._utf8.find(preferred_separator, startidx); }
+ constexpr auto _find_last_sep() const noexcept { return _state._utf8.rfind(preferred_separator); }
#endif
public:
//! Constructs an empty path view
@@ -252,16 +256,38 @@ public:
{
return _invoke([](const auto &v) { return v.empty(); });
}
- constexpr bool has_root_path() const noexcept;
- constexpr bool has_root_name() const noexcept;
- constexpr bool has_root_directory() const noexcept;
- constexpr bool has_relative_path() const noexcept;
- constexpr bool has_parent_path() const noexcept;
- constexpr bool has_filename() const noexcept;
- constexpr bool has_stem() const noexcept;
- constexpr bool has_extension() const noexcept;
- constexpr bool is_absolute() const noexcept;
- constexpr bool is_relative() const noexcept;
+ constexpr bool has_root_path() const noexcept { return !root_path().empty(); }
+ constexpr bool has_root_name() const noexcept { return !root_name().empty(); }
+ constexpr bool has_root_directory() const noexcept { return !root_directory().empty(); }
+ constexpr bool has_relative_path() const noexcept { return !relative_path().empty(); }
+ constexpr bool has_parent_path() const noexcept { return !parent_path().empty(); }
+ constexpr bool has_filename() const noexcept { return !filename().empty(); }
+ constexpr bool has_stem() const noexcept { return !stem().empty(); }
+ constexpr bool has_extension() const noexcept { return !extension().empty(); }
+ constexpr bool is_absolute() const noexcept
+ {
+ auto sep_idx = _find_first_sep();
+ if(_npos == sep_idx)
+ {
+ return false;
+ }
+#ifdef _WIN32
+ if(is_ntpath())
+ return true;
+ return _invoke([sep_idx](const auto &v) {
+ if(sep_idx == 0)
+ {
+ if(v[sep_idx + 1] == preferred_separator) // double separator at front
+ return true;
+ }
+ auto colon_idx = v.find(':');
+ return colon_idx < sep_idx; // colon before first separator
+ });
+#else
+ return sep_idx == 0;
+#endif
+ }
+ constexpr bool is_relative() const noexcept { return !is_absolute(); }
// True if the path view contains any of the characters `*`, `?`, (POSIX only: `[` or `]`).
constexpr bool contains_glob() const noexcept
{
@@ -342,11 +368,109 @@ public:
{
return _invoke([](const auto &v) { return v.size(); });
}
- constexpr path_view root_name() const noexcept;
- constexpr path_view root_directory() const noexcept;
- constexpr path_view root_path() const noexcept;
- constexpr path_view relative_path() const noexcept;
- constexpr path_view parent_path() const noexcept;
+ //! Returns a view of the root name part of this view e.g. C:
+ constexpr path_view root_name() const noexcept
+ {
+ auto sep_idx = _find_first_sep();
+ if(_npos == sep_idx)
+ {
+ return path_view();
+ }
+ return _invoke([sep_idx](const auto &v) { return path_view(v.data(), sep_idx); });
+ }
+ //! Returns a view of the root directory, if there is one e.g. /
+ constexpr path_view root_directory() const noexcept
+ {
+ auto sep_idx = _find_first_sep();
+ if(_npos == sep_idx)
+ {
+ return path_view();
+ }
+ return _invoke([sep_idx](const auto &v) {
+#ifdef _WIN32
+ auto colon_idx = v.find(':');
+ if(colon_idx < sep_idx)
+ {
+ return path_view(v.data() + sep_idx, 1);
+ }
+#endif
+ if(sep_idx == 0)
+ {
+ return path_view(v.data(), 1);
+ }
+ return path_view();
+ });
+ }
+ //! Returns, if any, a view of the root path part of this view e.g. C:/
+ constexpr path_view root_path() const noexcept
+ {
+ auto sep_idx = _find_first_sep();
+ if(_npos == sep_idx)
+ {
+ return path_view();
+ }
+ return _invoke([this, sep_idx](const auto &v) {
+#ifdef _WIN32
+ if(is_ntpath())
+ {
+ return path_view(v.data() + 3, 1);
+ }
+ // Special case \\.\ and \\?\ to match filesystem::path
+ if(v.size() >= 4 && sep_idx == 0 && v[1] == '\\' && (v[2] == '.' || v[2] == '?') && v[3] == '\\')
+ {
+ return path_view(v.data() + 0, 4);
+ }
+ auto colon_idx = v.find(':');
+ if(colon_idx < sep_idx)
+ {
+ return path_view(v.data(), sep_idx + 1);
+ }
+#endif
+ if(sep_idx == 0)
+ {
+ return path_view(v.data(), 1);
+ }
+ return path_view();
+ });
+ }
+ //! Returns a view of everything after the root path
+ constexpr path_view relative_path() const noexcept
+ {
+ auto sep_idx = _find_first_sep();
+ if(_npos == sep_idx)
+ {
+ return *this;
+ }
+ return _invoke([this, sep_idx](const auto &v) {
+#ifdef _WIN32
+ // Special case \\.\ and \\?\ to match filesystem::path
+ if(v.size() >= 4 && sep_idx == 0 && v[1] == '\\' && (v[2] == '.' || v[2] == '?') && v[3] == '\\')
+ {
+ return path_view(v.data() + 4, v.size() - 4);
+ }
+ auto colon_idx = v.find(':');
+ if(colon_idx < sep_idx)
+ {
+ return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1);
+ }
+#endif
+ if(sep_idx == 0)
+ {
+ return path_view(v.data() + 1, v.size() - 1);
+ }
+ return path_view(v.data(), v.size());
+ });
+ }
+ //! Returns a view of the everything apart from the filename part of this view
+ constexpr path_view parent_path() const noexcept
+ {
+ auto sep_idx = _find_last_sep();
+ if(_npos == sep_idx)
+ {
+ return path_view();
+ }
+ return _invoke([sep_idx](const auto &v) { return path_view(v.data(), sep_idx); });
+ }
//! Returns a view of the filename part of this view.
constexpr path_view filename() const noexcept
{
@@ -357,8 +481,32 @@ public:
}
return _invoke([sep_idx](const auto &v) { return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1); });
}
- constexpr path_view stem() const noexcept;
- constexpr path_view extension() const noexcept;
+ //! Returns a view of the filename without any file extension
+ constexpr path_view stem() const noexcept
+ {
+ auto sep_idx = _find_last_sep();
+ return _invoke([sep_idx](const auto &v) {
+ auto dot_idx = v.rfind('.');
+ if(_npos == dot_idx || (_npos != sep_idx && dot_idx < sep_idx) || dot_idx == sep_idx + 1 || (dot_idx == sep_idx + 2 && v[dot_idx - 1] == '.'))
+ {
+ return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1);
+ }
+ return path_view(v.data() + sep_idx + 1, dot_idx - sep_idx - 1);
+ });
+ }
+ //! Returns a view of the file extension part of this view
+ constexpr path_view extension() const noexcept
+ {
+ auto sep_idx = _find_last_sep();
+ return _invoke([sep_idx](const auto &v) {
+ auto dot_idx = v.rfind('.');
+ if(_npos == dot_idx || (_npos != sep_idx && dot_idx < sep_idx) || dot_idx == sep_idx + 1 || (dot_idx == sep_idx + 2 && v[dot_idx - 1] == '.'))
+ {
+ return path_view();
+ }
+ return path_view(v.data() + dot_idx, v.size() - dot_idx);
+ });
+ }
//! Return the path view as a path.
filesystem::path path() const
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
diff --git a/include/llfio/v2.0/utils.hpp b/include/llfio/v2.0/utils.hpp
index 8d731016..0de1930e 100644
--- a/include/llfio/v2.0/utils.hpp
+++ b/include/llfio/v2.0/utils.hpp
@@ -68,8 +68,7 @@ namespace utils
template <class T> inline T round_to_page_size(T i) noexcept
{
const size_t pagesize = page_size();
- i.data = reinterpret_cast<byte *>((LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<uintptr_t>(i.data)) & ~(pagesize - 1));
- i.len = (i.len + pagesize - 1) & ~(pagesize - 1);
+ i = {reinterpret_cast<byte *>((LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<uintptr_t>(i.data())) & ~(pagesize - 1)), (i.size() + pagesize - 1) & ~(pagesize - 1)};
return i;
}