diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2019-09-18 23:30:07 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2019-09-18 23:30:07 +0300 |
commit | fc4e2fb074e3db8ae94d26a42fa70940b7c718b4 (patch) | |
tree | c369bdc2b9b0bb95d7ebafd61fbd77828d86dca9 | |
parent | 987bc3f07753fc2a14f148cd2a800f364a95a187 (diff) |
Implement #34 Rename mode::truncate to truncate_existing, add mode::always_new
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/directory_handle.ipp | 52 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/file_handle.ipp | 30 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/import.hpp | 5 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp | 7 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/directory_handle.ipp | 16 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/file_handle.ipp | 16 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/import.hpp | 5 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp | 10 | ||||
-rw-r--r-- | include/llfio/v2.0/handle.hpp | 195 | ||||
-rw-r--r-- | include/llfio/v2.0/mapped_file_handle.hpp | 3 | ||||
-rw-r--r-- | test/tests/directory_handle_create_close/runner.cpp | 9 | ||||
-rw-r--r-- | test/tests/file_handle_create_close/runner.cpp | 8 | ||||
-rw-r--r-- | test/tests/symlink_handle_create_close/runner.cpp | 4 |
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" }} |