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>2019-09-18 23:30:07 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2019-09-18 23:30:07 +0300
commitfc4e2fb074e3db8ae94d26a42fa70940b7c718b4 (patch)
treec369bdc2b9b0bb95d7ebafd61fbd77828d86dca9
parent987bc3f07753fc2a14f148cd2a800f364a95a187 (diff)
Implement #34 Rename mode::truncate to truncate_existing, add mode::always_new
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/directory_handle.ipp52
-rw-r--r--include/llfio/v2.0/detail/impl/posix/file_handle.ipp30
-rw-r--r--include/llfio/v2.0/detail/impl/posix/import.hpp5
-rw-r--r--include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp7
-rw-r--r--include/llfio/v2.0/detail/impl/windows/directory_handle.ipp16
-rw-r--r--include/llfio/v2.0/detail/impl/windows/file_handle.ipp16
-rw-r--r--include/llfio/v2.0/detail/impl/windows/import.hpp5
-rw-r--r--include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp10
-rw-r--r--include/llfio/v2.0/handle.hpp195
-rw-r--r--include/llfio/v2.0/mapped_file_handle.hpp3
-rw-r--r--test/tests/directory_handle_create_close/runner.cpp9
-rw-r--r--test/tests/file_handle_create_close/runner.cpp8
-rw-r--r--test/tests/symlink_handle_create_close/runner.cpp4
14 files changed, 241 insertions, 125 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp
index 883113b3..5ccb6d89 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 efc3c6d35cf9b877bf16ba996f5d4e67b4be6354
-#define LLFIO_PREVIOUS_COMMIT_DATE "2019-09-12 13:03:47 +00:00"
-#define LLFIO_PREVIOUS_COMMIT_UNIQUE efc3c6d3
+#define LLFIO_PREVIOUS_COMMIT_REF 987bc3f07753fc2a14f148cd2a800f364a95a187
+#define LLFIO_PREVIOUS_COMMIT_DATE "2019-09-16 19:44:53 +00:00"
+#define LLFIO_PREVIOUS_COMMIT_UNIQUE 987bc3f0
diff --git a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
index 61169c8f..f1cca812 100644
--- a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
@@ -67,7 +67,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
_mode = mode::read;
}
// Also trying to truncate a directory returns EISDIR
- if(_creation == creation::truncate)
+ if(_creation == creation::truncate_existing)
{
return errc::is_a_directory;
}
@@ -85,9 +85,34 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
path = ".";
}
path_view::c_str zpath(path);
+ auto rename_random_dir_over_existing_dir = [_mode, _caching, flags](const path_handle &base, path_view_type path) -> result<directory_handle> {
+ // Take a path handle to the directory containing the file
+ auto path_parent = path.parent_path();
+ path_handle dirh;
+ if(base.is_valid() && path_parent.empty())
+ {
+ OUTCOME_TRY(dh, base.clone());
+ dirh = std::move(dh);
+ }
+ else if(!path_parent.empty())
+ {
+ OUTCOME_TRY(dh, path_handle::path(base, path_parent));
+ dirh = std::move(dh);
+ }
+ // Create a randomly named directory, and rename it over
+ OUTCOME_TRY(rfh, random_directory(dirh, _mode, _caching, flags));
+ auto r = rfh.relink(dirh, path.filename());
+ if(r)
+ {
+ return std::move(rfh);
+ }
+ // If failed to rename, remove
+ (void) rfh.unlink();
+ return r.error();
+ };
if(base.is_valid())
{
- if(_creation == creation::if_needed || _creation == creation::only_if_not_exist)
+ if(_creation == creation::if_needed || _creation == creation::only_if_not_exist || _creation == creation::always_new)
{
if(-1 == ::mkdirat(base.native_handle().fd, zpath.buffer, 0x1f8 /*770*/))
{
@@ -95,6 +120,16 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
{
return posix_error();
}
+ if(_creation == creation::always_new)
+ {
+ auto r = rename_random_dir_over_existing_dir(base, path);
+ if(!r)
+ {
+ return r.error();
+ }
+ ret = std::move(r).value();
+ goto opened;
+ }
}
attribs &= ~(O_CREAT | O_EXCL);
}
@@ -102,7 +137,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
}
else
{
- if(_creation == creation::if_needed || _creation == creation::only_if_not_exist)
+ if(_creation == creation::if_needed || _creation == creation::only_if_not_exist || _creation == creation::always_new)
{
if(-1 == ::mkdir(zpath.buffer, 0x1f8 /*770*/))
{
@@ -110,6 +145,16 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
{
return posix_error();
}
+ if(_creation == creation::always_new)
+ {
+ auto r = rename_random_dir_over_existing_dir(base, path);
+ if(!r)
+ {
+ return r.error();
+ }
+ ret = std::move(r).value();
+ goto opened;
+ }
}
attribs &= ~(O_CREAT | O_EXCL);
}
@@ -119,6 +164,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
{
return posix_error();
}
+opened:
if(!(flags & flag::disable_safety_unlinks))
{
if(!ret.value()._fetch_inode())
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 a6b7613f..97d99bb0 100644
--- a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
@@ -46,6 +46,32 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
}
if(-1 == nativeh.fd)
{
+ if((mode::write == _mode || mode::append == _mode) && creation::always_new == _creation && EEXIST == errno)
+ {
+ // Take a path handle to the directory containing the file
+ auto path_parent = path.parent_path();
+ path_handle dirh;
+ if(base.is_valid() && path_parent.empty())
+ {
+ OUTCOME_TRY(dh, base.clone());
+ dirh = std::move(dh);
+ }
+ else if(!path_parent.empty())
+ {
+ OUTCOME_TRY(dh, path_handle::path(base, path_parent));
+ dirh = std::move(dh);
+ }
+ // Create a randomly named file, and rename it over
+ OUTCOME_TRY(rfh, random_file(dirh, _mode, _caching, flags));
+ auto r = rfh.relink(dirh, path.filename());
+ if(r)
+ {
+ return std::move(rfh);
+ }
+ // If failed to rename, remove
+ (void) rfh.unlink();
+ return r.error();
+ }
return posix_error();
}
if((flags & flag::disable_prefetching) || (flags & flag::maximum_prefetching))
@@ -64,7 +90,7 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
}
#endif
}
- if(_creation == creation::truncate && ret.value().are_safety_barriers_issued())
+ if(_creation == creation::truncate_existing && ret.value().are_safety_barriers_issued())
{
fsync(nativeh.fd);
}
@@ -217,7 +243,7 @@ result<file_handle> file_handle::clone(mode mode_, caching caching_, deadline d)
#ifdef O_DSYNC
| O_DSYNC
#endif
- );
+ );
switch(caching_)
{
case caching::unchanged:
diff --git a/include/llfio/v2.0/detail/impl/posix/import.hpp b/include/llfio/v2.0/detail/impl/posix/import.hpp
index 0ff3248c..d7e10dcb 100644
--- a/include/llfio/v2.0/detail/impl/posix/import.hpp
+++ b/include/llfio/v2.0/detail/impl/posix/import.hpp
@@ -70,9 +70,12 @@ inline result<int> attribs_from_handle_mode_caching_and_flags(native_handle_type
case handle::creation::if_needed:
attribs |= O_CREAT;
break;
- case handle::creation::truncate:
+ case handle::creation::truncate_existing:
attribs |= O_TRUNC;
break;
+ case handle::creation::always_new:
+ attribs |= O_CREAT | O_EXCL;
+ break;
}
switch(_caching)
{
diff --git a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
index 116ac4f5..1efe4da6 100644
--- a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
@@ -286,7 +286,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
native_handle_type &nativeh = ret.value()._v;
LLFIO_LOG_FUNCTION_CALL(&ret);
nativeh.behaviour |= native_handle_type::disposition::symlink;
- if(_mode == mode::append || _creation == creation::truncate)
+ if(_mode == mode::append || _creation == creation::truncate_existing)
{
return errc::function_not_supported;
}
@@ -344,7 +344,8 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
}
case creation::only_if_not_exist:
case creation::if_needed:
- case creation::truncate:
+ case creation::truncate_existing:
+ case creation::always_new:
{
// Create an empty symlink, ignoring any file exists errors, unless only_if_not_exist
auto r = ret.value()._create_symlink(dirh, leafname,
@@ -353,7 +354,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
#else
"",
#endif
- std::chrono::seconds(10), false);
+ std::chrono::seconds(10), creation::always_new == _creation);
if(!r)
{
if(_creation == creation::only_if_not_exist || r.error() != errc::file_exists)
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 e8bbeaee..e7994ddd 100644
--- a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
@@ -65,9 +65,12 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
case creation::if_needed:
creatdisp = 0x00000003 /*FILE_OPEN_IF*/;
break;
- case creation::truncate:
+ case creation::truncate_existing:
creatdisp = 0x00000004 /*FILE_OVERWRITE*/;
break;
+ case creation::always_new:
+ creatdisp = 0x00000000 /*FILE_SUPERSEDE*/;
+ break;
}
attribs &= 0x00ffffff; // the real attributes only, not the win32 flags
@@ -117,7 +120,8 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
case creation::if_needed:
need_to_set_case_sensitive = (isb.Information == 2 /*FILE_CREATED*/);
break;
- case creation::truncate:
+ case creation::truncate_existing:
+ case creation::always_new:
need_to_set_case_sensitive = true;
break;
}
@@ -135,9 +139,12 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
case creation::if_needed:
creation = OPEN_ALWAYS;
break;
- case creation::truncate:
+ case creation::truncate_existing:
creation = TRUNCATE_EXISTING;
break;
+ case creation::always_new:
+ creation = CREATE_ALWAYS;
+ break;
}
attribs |= FILE_FLAG_BACKUP_SEMANTICS; // required to open a directory
path_view::c_str zpath(path, false);
@@ -158,7 +165,8 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
case creation::if_needed:
need_to_set_case_sensitive = (GetLastError() != ERROR_ALREADY_EXISTS); // FIXME: doesn't appear to detect when new directory not created
break;
- case creation::truncate:
+ case creation::truncate_existing:
+ case creation::always_new:
need_to_set_case_sensitive = true;
break;
}
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 9802f25c..3a2d4f62 100644
--- a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
@@ -52,9 +52,12 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
case creation::if_needed:
creatdisp = 0x00000003 /*FILE_OPEN_IF*/;
break;
- case creation::truncate:
+ case creation::truncate_existing:
creatdisp = 0x00000004 /*FILE_OVERWRITE*/;
break;
+ case creation::always_new:
+ creatdisp = 0x00000000 /*FILE_SUPERSEDE*/;
+ break;
}
attribs &= 0x00ffffff; // the real attributes only, not the win32 flags
@@ -104,7 +107,8 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
case creation::if_needed:
need_to_set_sparse = (isb.Information == 2 /*FILE_CREATED*/);
break;
- case creation::truncate:
+ case creation::truncate_existing:
+ case creation::always_new:
need_to_set_sparse = true;
break;
}
@@ -122,9 +126,12 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
case creation::if_needed:
creation = OPEN_ALWAYS;
break;
- case creation::truncate:
+ case creation::truncate_existing:
creation = TRUNCATE_EXISTING;
break;
+ case creation::always_new:
+ creation = CREATE_ALWAYS;
+ break;
}
path_view::c_str zpath(path, false);
if(INVALID_HANDLE_VALUE == (nativeh.h = CreateFileW_(zpath.buffer, access, fileshare, nullptr, creation, attribs, nullptr))) // NOLINT
@@ -144,7 +151,8 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
case creation::if_needed:
need_to_set_sparse = (GetLastError() != ERROR_ALREADY_EXISTS); // new inode created
break;
- case creation::truncate:
+ case creation::truncate_existing:
+ case creation::always_new:
need_to_set_sparse = true;
break;
}
diff --git a/include/llfio/v2.0/detail/impl/windows/import.hpp b/include/llfio/v2.0/detail/impl/windows/import.hpp
index f00be78f..7a85ee4a 100644
--- a/include/llfio/v2.0/detail/impl/windows/import.hpp
+++ b/include/llfio/v2.0/detail/impl/windows/import.hpp
@@ -1365,6 +1365,9 @@ which means our file open routines return EACCES, which is useless to us for det
when we are trying to open files being deleted. We therefore reimplement CreateFileW()
with a non-broken version.
+Another bug fix is that CREATE_ALWAYS is mapped to FILE_OPEN_IF on Win32, which does
+not replace the inode if it already exists. We map it correctly to FILE_SUPERSEDE.
+
This edition does pretty much the same as the Win32 edition, minus support for file
templates and lpFileName being anything but a file path.
*/
@@ -1388,7 +1391,7 @@ inline HANDLE CreateFileW_(_In_ LPCTSTR lpFileName, _In_ DWORD dwDesiredAccess,
dwCreationDisposition = FILE_CREATE;
break;
case CREATE_ALWAYS:
- dwCreationDisposition = FILE_OVERWRITE_IF;
+ dwCreationDisposition = FILE_SUPERSEDE; // WARNING: Win32 uses FILE_OVERWRITE_IF;
break;
case OPEN_EXISTING:
dwCreationDisposition = FILE_OPEN;
diff --git a/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
index 343cccc7..f0b91efa 100644
--- a/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
@@ -64,9 +64,12 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
case creation::if_needed:
creatdisp = 0x00000003 /*FILE_OPEN_IF*/;
break;
- case creation::truncate:
+ case creation::truncate_existing:
creatdisp = 0x00000004 /*FILE_OVERWRITE*/;
break;
+ case creation::always_new:
+ creatdisp = 0x00000000 /*FILE_SUPERSEDE*/;
+ break;
}
attribs &= 0x00ffffff; // the real attributes only, not the win32 flags
@@ -120,9 +123,12 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
case creation::if_needed:
creation = OPEN_ALWAYS;
break;
- case creation::truncate:
+ case creation::truncate_existing:
creation = TRUNCATE_EXISTING;
break;
+ case creation::always_new:
+ creation = CREATE_ALWAYS;
+ break;
}
// required to open a symlink
attribs |= FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
diff --git a/include/llfio/v2.0/handle.hpp b/include/llfio/v2.0/handle.hpp
index dbf55873..26b8ff22 100644
--- a/include/llfio/v2.0/handle.hpp
+++ b/include/llfio/v2.0/handle.hpp
@@ -77,7 +77,8 @@ public:
open_existing = 0, //!< Filesystem entry must already exist
only_if_not_exist, //!< Filesystem entry must NOT exist, and is atomically created by the success of this operation
if_needed, //!< If filesystem entry exists that is used, else one is created
- truncate //!< Filesystem entry must already exist. It is atomically truncated on open, leaving creation date and unique identifier unmodified.
+ truncate_existing, //!< Filesystem entry must already exist. It is atomically truncated on open, leaving creation date and unique identifier unmodified.
+ always_new //!< If filesystem entry exists, it is atomically replaced with a new inode, else a new entry is created.
// NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!!
};
//! What i/o on the handle may complete immediately due to kernel caching
@@ -94,94 +95,92 @@ public:
// NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!!
};
//! Bitwise flags which can be specified
- QUICKCPPLIB_BITFIELD_BEGIN(flag)
- {
- none = 0, //!< No flags
- /*! Unlinks the file on handle close. On POSIX, this simply unlinks whatever is pointed
- to by `path()` upon the call of `close()` if and only if the inode matches. On Windows,
- if you are on Windows 10 1709 or later, exactly the same thing occurs. If on previous
- editions of Windows, the file entry does not disappears but becomes unavailable for
- anyone else to open with an `errc::resource_unavailable_try_again` error return. Because this is confusing, unless the
- `win_disable_unlink_emulation` flag is also specified, this POSIX behaviour is
- somewhat emulated by LLFIO on older Windows by renaming the file to a random name on `close()`
- causing it to appear to have been unlinked immediately.
- */
- unlink_on_first_close = 1U << 0U,
-
- /*! Some kernel caching modes have unhelpfully inconsistent behaviours
- in getting your data onto storage, so by default unless this flag is
- specified LLFIO adds extra fsyncs to the following operations for the
- caching modes specified below:
- * truncation of file length either explicitly or during file open.
- * closing of the handle either explicitly or in the destructor.
-
- Additionally on Linux only to prevent loss of file metadata:
- * On the parent directory whenever a file might have been created.
- * On the parent directory on file close.
-
- This only occurs for these kernel caching modes:
- * caching::none
- * caching::reads
- * caching::reads_and_metadata
- * caching::safety_barriers
- */
- disable_safety_barriers = 1U << 2U,
- /*! `file_handle::unlink()` could accidentally delete the wrong file if someone has
- renamed the open file handle since the time it was opened. To prevent this occuring,
- where the OS doesn't provide race free unlink-by-open-handle we compare the inode of
- the path we are about to unlink with that of the open handle before unlinking.
- \warning This does not prevent races where in between the time of checking the inode
- and executing the unlink a third party changes the item about to be unlinked. Only
- operating systems with a true race-free unlink syscall are race free.
- */
- disable_safety_unlinks = 1U << 3U,
- /*! Ask the OS to disable prefetching of data. This can improve random
- i/o performance.
- */
- disable_prefetching = 1U << 4U,
- /*! Ask the OS to maximise prefetching of data, possibly prefetching the entire file
- into kernel cache. This can improve sequential i/o performance.
- */
- maximum_prefetching = 1U << 5U,
-
- win_disable_unlink_emulation = 1U << 24U, //!< See the documentation for `unlink_on_first_close`
- /*! Microsoft Windows NTFS, having been created in the late 1980s, did not originally
- implement extents-based storage and thus could only represent sparse files via
- efficient compression of intermediate zeros. With NTFS v3.0 (Microsoft Windows 2000),
- a proper extents-based on-storage representation was added, thus allowing only 64Kb
- extent chunks written to be stored irrespective of whatever the maximum file extent
- was set to.
-
- For various historical reasons, extents-based storage is disabled by default in newly
- created files on NTFS, unlike in almost every other major filing system. You have to
- explicitly "opt in" to extents-based storage.
-
- As extents-based storage is nearly cost free on NTFS, LLFIO by default opts in to
- extents-based storage for any empty file it creates. If you don't want this, you
- can specify this flag to prevent that happening.
- */
- win_disable_sparse_file_creation = 1U << 25U,
- /*! Filesystems tend to be embarrassingly parallel for operations performed to different
- inodes. Where LLFIO performs i/o to multiple inodes at a time, it will use OpenMP or
- the Parallelism or Concurrency standard library extensions to usually complete the
- operation in constant rather than linear time. If you don't want this default, you can
- disable default using this flag.
- */
- disable_parallelism = 1U << 26U,
- /*! Microsoft Windows NTFS has the option, when creating a directory, to set whether
- leafname lookup will be case sensitive. This is the only way of getting exact POSIX
- semantics on Windows without resorting to editing the system registry, however it also
- affects all code doing lookups within that directory, so we must default it to off.
- */
- win_create_case_sensitive_directory = 1U << 27U,
-
- // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!!
-
- overlapped = 1U << 28U, //!< On Windows, create any new handles with OVERLAPPED semantics
- byte_lock_insanity = 1U << 29U, //!< Using insane POSIX byte range locks
- anonymous_inode = 1U << 30U //!< This is an inode created with no representation on the filing system
- }
- QUICKCPPLIB_BITFIELD_END(flag);
+ QUICKCPPLIB_BITFIELD_BEGIN(flag){
+ none = 0, //!< No flags
+ /*! Unlinks the file on handle close. On POSIX, this simply unlinks whatever is pointed
+ to by `path()` upon the call of `close()` if and only if the inode matches. On Windows,
+ if you are on Windows 10 1709 or later, exactly the same thing occurs. If on previous
+ editions of Windows, the file entry does not disappears but becomes unavailable for
+ anyone else to open with an `errc::resource_unavailable_try_again` error return. Because this is confusing, unless the
+ `win_disable_unlink_emulation` flag is also specified, this POSIX behaviour is
+ somewhat emulated by LLFIO on older Windows by renaming the file to a random name on `close()`
+ causing it to appear to have been unlinked immediately.
+ */
+ unlink_on_first_close = 1U << 0U,
+
+ /*! Some kernel caching modes have unhelpfully inconsistent behaviours
+ in getting your data onto storage, so by default unless this flag is
+ specified LLFIO adds extra fsyncs to the following operations for the
+ caching modes specified below:
+ * truncation of file length either explicitly or during file open.
+ * closing of the handle either explicitly or in the destructor.
+
+ Additionally on Linux only to prevent loss of file metadata:
+ * On the parent directory whenever a file might have been created.
+ * On the parent directory on file close.
+
+ This only occurs for these kernel caching modes:
+ * caching::none
+ * caching::reads
+ * caching::reads_and_metadata
+ * caching::safety_barriers
+ */
+ disable_safety_barriers = 1U << 2U,
+ /*! `file_handle::unlink()` could accidentally delete the wrong file if someone has
+ renamed the open file handle since the time it was opened. To prevent this occuring,
+ where the OS doesn't provide race free unlink-by-open-handle we compare the inode of
+ the path we are about to unlink with that of the open handle before unlinking.
+ \warning This does not prevent races where in between the time of checking the inode
+ and executing the unlink a third party changes the item about to be unlinked. Only
+ operating systems with a true race-free unlink syscall are race free.
+ */
+ disable_safety_unlinks = 1U << 3U,
+ /*! Ask the OS to disable prefetching of data. This can improve random
+ i/o performance.
+ */
+ disable_prefetching = 1U << 4U,
+ /*! Ask the OS to maximise prefetching of data, possibly prefetching the entire file
+ into kernel cache. This can improve sequential i/o performance.
+ */
+ maximum_prefetching = 1U << 5U,
+
+ win_disable_unlink_emulation = 1U << 24U, //!< See the documentation for `unlink_on_first_close`
+ /*! Microsoft Windows NTFS, having been created in the late 1980s, did not originally
+ implement extents-based storage and thus could only represent sparse files via
+ efficient compression of intermediate zeros. With NTFS v3.0 (Microsoft Windows 2000),
+ a proper extents-based on-storage representation was added, thus allowing only 64Kb
+ extent chunks written to be stored irrespective of whatever the maximum file extent
+ was set to.
+
+ For various historical reasons, extents-based storage is disabled by default in newly
+ created files on NTFS, unlike in almost every other major filing system. You have to
+ explicitly "opt in" to extents-based storage.
+
+ As extents-based storage is nearly cost free on NTFS, LLFIO by default opts in to
+ extents-based storage for any empty file it creates. If you don't want this, you
+ can specify this flag to prevent that happening.
+ */
+ win_disable_sparse_file_creation = 1U << 25U,
+ /*! Filesystems tend to be embarrassingly parallel for operations performed to different
+ inodes. Where LLFIO performs i/o to multiple inodes at a time, it will use OpenMP or
+ the Parallelism or Concurrency standard library extensions to usually complete the
+ operation in constant rather than linear time. If you don't want this default, you can
+ disable default using this flag.
+ */
+ disable_parallelism = 1U << 26U,
+ /*! Microsoft Windows NTFS has the option, when creating a directory, to set whether
+ leafname lookup will be case sensitive. This is the only way of getting exact POSIX
+ semantics on Windows without resorting to editing the system registry, however it also
+ affects all code doing lookups within that directory, so we must default it to off.
+ */
+ win_create_case_sensitive_directory = 1U << 27U,
+
+ // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!!
+
+ overlapped = 1U << 28U, //!< On Windows, create any new handles with OVERLAPPED semantics
+ byte_lock_insanity = 1U << 29U, //!< Using insane POSIX byte range locks
+ anonymous_inode = 1U << 30U //!< This is an inode created with no representation on the filing system
+ } QUICKCPPLIB_BITFIELD_END(flag);
protected:
caching _caching{caching::none};
@@ -192,14 +191,22 @@ public:
//! Default constructor
constexpr handle() {} // NOLINT
//! Construct a handle from a supplied native handle
- explicit constexpr handle(native_handle_type h, caching caching = caching::none, flag flags = flag::none) noexcept : _caching(caching), _flags(flags), _v(std::move(h)) {}
+ explicit constexpr handle(native_handle_type h, caching caching = caching::none, flag flags = flag::none) noexcept
+ : _caching(caching)
+ , _flags(flags)
+ , _v(std::move(h))
+ {
+ }
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~handle();
//! No copy construction (use clone())
handle(const handle &) = delete;
//! No copy assignment
handle &operator=(const handle &o) = delete;
//! Move the handle.
- constexpr handle(handle &&o) noexcept : _caching(o._caching), _flags(o._flags), _v(std::move(o._v))
+ constexpr handle(handle &&o) noexcept
+ : _caching(o._caching)
+ , _flags(o._flags)
+ , _v(std::move(o._v))
{
o._caching = caching::none;
o._flags = flag::none;
@@ -350,7 +357,7 @@ inline std::ostream &operator<<(std::ostream &s, const handle::mode &v)
}
inline std::ostream &operator<<(std::ostream &s, const handle::creation &v)
{
- static constexpr const char *values[] = {"open_existing", "only_if_not_exist", "if_needed", "truncate"};
+ static constexpr const char *values[] = {"open_existing", "only_if_not_exist", "if_needed", "truncate_existing", "always_new"};
if(static_cast<size_t>(v) >= sizeof(values) / sizeof(values[0]) || (values[static_cast<size_t>(v)] == nullptr)) // NOLINT
{
return s << "llfio::handle::creation::<unknown>";
@@ -484,7 +491,7 @@ namespace detail
#endif
}
}
-}
+} // namespace detail
#endif
#if LLFIO_EXPERIMENTAL_STATUS_CODE
@@ -563,7 +570,7 @@ namespace detail
(void) buffer;
LLFIO_LOG_INFO(inst->native_handle()._init, buffer);
}
-}
+} // namespace detail
// BEGIN make_free_functions.py
//! Swap with another instance
diff --git a/include/llfio/v2.0/mapped_file_handle.hpp b/include/llfio/v2.0/mapped_file_handle.hpp
index 758321c6..969ce864 100644
--- a/include/llfio/v2.0/mapped_file_handle.hpp
+++ b/include/llfio/v2.0/mapped_file_handle.hpp
@@ -202,7 +202,8 @@ public:
return {std::move(mfh)};
}
case creation::only_if_not_exist:
- case creation::truncate:
+ case creation::truncate_existing:
+ case creation::always_new:
{
// Don't attempt mapping now as file will be empty
mapped_file_handle mfh(std::move(fh));
diff --git a/test/tests/directory_handle_create_close/runner.cpp b/test/tests/directory_handle_create_close/runner.cpp
index 47043c08..41c7c905 100644
--- a/test/tests/directory_handle_create_close/runner.cpp
+++ b/test/tests/directory_handle_create_close/runner.cpp
@@ -54,6 +54,7 @@ template <class U> inline void directory_handle_create_close_creation(U &&f)
static const il_result<void> no_such_file_or_directory = LLFIO_V2_NAMESPACE::errc::no_such_file_or_directory;
static const il_result<void> file_exists = LLFIO_V2_NAMESPACE::errc::file_exists;
static const il_result<void> is_a_directory = LLFIO_V2_NAMESPACE::errc::is_a_directory;
+ static const il_result<void> directory_not_empty = LLFIO_V2_NAMESPACE::errc::directory_not_empty;
static const il_result<void> permission_denied = LLFIO_V2_NAMESPACE::errc::permission_denied;
// clang-format off
@@ -94,9 +95,11 @@ template <class U> inline void directory_handle_create_close_creation(U &&f)
{ file_exists, { directory_handle::mode::write, directory_handle::creation::only_if_not_exist, directory_handle::flag::none, &entries, &info }, { "existing0" }, { "existing0" },{ success() } },
{ success(), { directory_handle::mode::write, directory_handle::creation::if_needed , directory_handle::flag::none, &entries, &info }, { "non-existing" }, { "existing0" },{ success() } },
{ success(), { directory_handle::mode::write, directory_handle::creation::if_needed , directory_handle::flag::none, &entries, &info }, { "existing1" }, { "existing1" },{ success() } },
- { 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() } }
+ { is_a_directory, { directory_handle::mode::write, directory_handle::creation::truncate_existing, directory_handle::flag::none, &entries, &info }, { "non-existing" }, { "non-existing" },{ success() } },
+ { is_a_directory, { directory_handle::mode::write, directory_handle::creation::truncate_existing, directory_handle::flag::none, &entries, &info }, { "existing0" }, { "existing0" },{ success() } },
+ { is_a_directory, { directory_handle::mode::write, directory_handle::creation::truncate_existing, directory_handle::flag::none, &entries, &info }, { "existing1" }, { "existing1" },{ success() } },
+ { success(), { directory_handle::mode::write, directory_handle::creation::always_new , directory_handle::flag::none, &entries, &info }, { "non-existing" }, { "existing0" },{ success() } },
+ { directory_not_empty, { directory_handle::mode::write, directory_handle::creation::always_new , directory_handle::flag::none, &entries, &info }, { "existing1" }, { "existing1" },{ success() } }
},
precondition::filesystem_setup(),
postcondition::filesystem_comparison_structure(),
diff --git a/test/tests/file_handle_create_close/runner.cpp b/test/tests/file_handle_create_close/runner.cpp
index d2ec684d..2cb83784 100644
--- a/test/tests/file_handle_create_close/runner.cpp
+++ b/test/tests/file_handle_create_close/runner.cpp
@@ -74,9 +74,11 @@ template <class U> inline void file_handle_create_close_creation(U &&f)
{ file_exists, { file_handle::mode::write, file_handle::creation::only_if_not_exist, file_handle::flag::none }, { "existing0" }, { "existing0" }},
{ success(), { file_handle::mode::write, file_handle::creation::if_needed , file_handle::flag::none }, { "non-existing" }, { "existing0" }},
{ success(), { file_handle::mode::write, file_handle::creation::if_needed , file_handle::flag::none }, { "existing1" }, { "existing1" }},
- { no_such_file_or_directory, { file_handle::mode::write, file_handle::creation::truncate , file_handle::flag::none }, { "non-existing" }, { "non-existing" }},
- { success(), { file_handle::mode::write, file_handle::creation::truncate , file_handle::flag::none }, { "existing0" }, { "existing0" }},
- { success(), { file_handle::mode::write, file_handle::creation::truncate , file_handle::flag::none }, { "existing1" }, { "existing0" }},
+ { no_such_file_or_directory, { file_handle::mode::write, file_handle::creation::truncate_existing, file_handle::flag::none }, { "non-existing" }, { "non-existing" }},
+ { success(), { file_handle::mode::write, file_handle::creation::truncate_existing, file_handle::flag::none }, { "existing0" }, { "existing0" }},
+ { success(), { file_handle::mode::write, file_handle::creation::truncate_existing, file_handle::flag::none }, { "existing1" }, { "existing0" }},
+ { success(), { file_handle::mode::write, file_handle::creation::always_new , file_handle::flag::none }, { "non-existing" }, { "existing0" }},
+ { success(), { file_handle::mode::write, file_handle::creation::always_new , file_handle::flag::none }, { "existing1" }, { "existing0" }},
// Does the flag parameter have the expected side effects?
{ success(), { file_handle::mode::write, file_handle::creation::open_existing, file_handle::flag::unlink_on_first_close }, { "existing1" }, { "non-existing" }}
diff --git a/test/tests/symlink_handle_create_close/runner.cpp b/test/tests/symlink_handle_create_close/runner.cpp
index 1e8e72e0..376b4063 100644
--- a/test/tests/symlink_handle_create_close/runner.cpp
+++ b/test/tests/symlink_handle_create_close/runner.cpp
@@ -63,7 +63,9 @@ template <class U> inline void symlink_handle_create_close_creation(U &&f)
{ file_exists, { symlink_handle::mode::write, symlink_handle::creation::only_if_not_exist, symlink_handle::flag::none }, { "existing1" }, { "existing1" }},
{ success(), { symlink_handle::mode::write, symlink_handle::creation::if_needed , symlink_handle::flag::none }, { "existing0" }, { "existing1" }},
{ success(), { symlink_handle::mode::write, symlink_handle::creation::if_needed , symlink_handle::flag::none }, { "existing1" }, { "existing1" }},
- { function_not_supported, { symlink_handle::mode::write, symlink_handle::creation::truncate , symlink_handle::flag::none }, { "existing1" }, { "existing1" }},
+ { function_not_supported, { symlink_handle::mode::write, symlink_handle::creation::truncate_existing, symlink_handle::flag::none }, { "existing1" }, { "existing1" }},
+ { success(), { symlink_handle::mode::write, symlink_handle::creation::always_new , symlink_handle::flag::none }, { "existing0" }, { "existing1" }},
+ { success(), { symlink_handle::mode::write, symlink_handle::creation::always_new , symlink_handle::flag::none }, { "existing1" }, { "existing1" }},
// Does the flag parameter have the expected side effects?
{ success(), { symlink_handle::mode::write, symlink_handle::creation::open_existing, symlink_handle::flag::unlink_on_first_close }, { "existing1" }, { "existing0" }}