LLFIO v2.00
|
A handle to something with a device and inode number. More...
#include "fs_handle.hpp"
Public Types | |
using | dev_t = uint64_t |
using | ino_t = uint64_t |
using | path_view_type = path_view |
The path view type used by this handle. | |
using | unique_id_type = QUICKCPPLIB_NAMESPACE::integers128::uint128 |
The unique identifier type used by this handle. | |
using | unique_id_type_hasher = QUICKCPPLIB_NAMESPACE::integers128::uint128_hasher |
A hasher for the unique identifier type used by this handle. | |
Public Member Functions | |
fs_handle (const fs_handle &)=delete | |
No copy construction (use clone() ) | |
fs_handle & | operator= (const fs_handle &o)=delete |
No copy assignment. | |
dev_t | st_dev () const noexcept |
Unless flag::disable_safety_unlinks is set, the device id of the file when opened. | |
ino_t | st_ino () const noexcept |
Unless flag::disable_safety_unlinks is set, the inode of the file when opened. When combined with st_dev(), forms a unique identifer on this system. | |
unique_id_type | unique_id () const noexcept |
A unique identifier for this handle across the entire system. Can be used in hash tables etc. | |
virtual result< path_handle > | parent_path_handle (deadline d=std::chrono::seconds(30)) const noexcept |
Obtain a handle to the path currently containing this handle's file entry. | |
template<class... Args> | |
bool | try_parent_path_handle (Args &&... args) noexcept |
template<class... Args, class Rep , class Period > | |
bool | try_parent_path_handle_for (Args &&... args, const std::chrono::duration< Rep, Period > &duration) noexcept |
template<class... Args, class Clock , class Duration > | |
bool | try_parent_path_handle_until (Args &&... args, const std::chrono::time_point< Clock, Duration > &timeout) noexcept |
virtual result< void > | relink (const path_handle &base, path_view_type path, bool atomic_replace=true, deadline d=std::chrono::seconds(30)) noexcept |
Relinks the current path of this open handle to the new path specified. If atomic_replace is true, the relink atomically and silently replaces any item at the new path specified. This operation is both atomic and matching POSIX behaviour even on Microsoft Windows where no Win32 API can match POSIX semantics. | |
template<class... Args> | |
bool | try_relink (Args &&... args) noexcept |
template<class... Args, class Rep , class Period > | |
bool | try_relink_for (Args &&... args, const std::chrono::duration< Rep, Period > &duration) noexcept |
template<class... Args, class Clock , class Duration > | |
bool | try_relink_until (Args &&... args, const std::chrono::time_point< Clock, Duration > &timeout) noexcept |
virtual result< void > | link (const path_handle &base, path_view_type path, deadline d=std::chrono::seconds(30)) noexcept |
Links the inode referred to by this open handle to the path specified. The current path of this open handle is not changed, unless it has no current path due to being unlinked. | |
template<class... Args> | |
bool | try_link (Args &&... args) noexcept |
template<class... Args, class Rep , class Period > | |
bool | try_link_for (Args &&... args, const std::chrono::duration< Rep, Period > &duration) noexcept |
template<class... Args, class Clock , class Duration > | |
bool | try_link_until (Args &&... args, const std::chrono::time_point< Clock, Duration > &timeout) noexcept |
virtual result< void > | unlink (deadline d=std::chrono::seconds(30)) noexcept |
Unlinks the current path of this open handle, causing its entry to immediately disappear from the filing system. | |
template<class... Args> | |
bool | try_unlink (Args &&... args) noexcept |
template<class... Args, class Rep , class Period > | |
bool | try_unlink_for (Args &&... args, const std::chrono::duration< Rep, Period > &duration) noexcept |
template<class... Args, class Clock , class Duration > | |
bool | try_unlink_until (Args &&... args, const std::chrono::time_point< Clock, Duration > &timeout) noexcept |
virtual result< span< path_view_component > > | list_extended_attributes (span< byte > tofill) const noexcept |
Fill the supplied buffer with the names of all extended attributes set on this file or directory, returning a span of path view components. | |
virtual result< span< byte > > | get_extended_attribute (span< byte > tofill, path_view_component name) const noexcept |
Retrieve the value of an extended attribute set on this file or directory. | |
virtual result< void > | set_extended_attribute (path_view_component name, span< const byte > value) noexcept |
Sets the value of an extended attribute on this file or directory. | |
virtual result< void > | remove_extended_attribute (path_view_component) noexcept |
Removes the extended attribute set on this file or directory. | |
result< size_t > | copy_extended_attributes (const fs_handle &src, bool replace_all_local_attributes=false) noexcept |
Copies the extended attributes from one entity to another, optionally replacing all the extended attributes on the destination. | |
Protected Member Functions | |
result< void > | _fetch_inode () const noexcept |
Fill in _devid and _inode from the handle via fstat() | |
virtual const handle & | _get_handle () const noexcept=0 |
virtual result< void > | _replace_handle (handle &&o) noexcept=0 |
constexpr | fs_handle () |
Default constructor. | |
constexpr | fs_handle (dev_t devid, ino_t inode) |
Construct a handle. | |
constexpr | fs_handle (fs_handle &&o) noexcept |
Implicit move construction of fs_handle permitted. | |
fs_handle & | operator= (fs_handle &&o) noexcept |
Move assignment of fs_handle permitted. | |
Protected Attributes | |
dev_t | _devid {0} |
ino_t | _inode {0} |
Friends | |
result< filesystem::path > | to_win32_path (const fs_handle &h, win32_path_namespace mapping) noexcept |
Maps the current path of h into a form suitable for Win32 APIs. Passes through unmodified on POSIX, so you can use this in portable code. | |
A handle to something with a device and inode number.
algorithm::cached_parent_handle_adapter<T>
|
inlineconstexprprotected |
Default constructor.
|
inlineconstexprprotected |
Construct a handle.
|
inlineconstexprprotectednoexcept |
Implicit move construction of fs_handle permitted.
|
inlinenoexcept |
Copies the extended attributes from one entity to another, optionally replacing all the extended attributes on the destination.
This convenience function is implemented using the APIs above, and therefore is racy with respect to concurrent users. If you specifiy an invalid source with replace_all_local_attributes = true
, then this is a convenient way to remove all extended attributes on the local inode.
|
virtualnoexcept |
Retrieve the value of an extended attribute set on this file or directory.
|
virtualnoexcept |
Links the inode referred to by this open handle to the path specified. The current path of this open handle is not changed, unless it has no current path due to being unlinked.
flag::disable_safety_unlinks
is set, this implementation opens a path_handle
to the source containing directory first, then checks before linking that the item about to be hard linked has the same inode as the open file handle. It will retry this matching until success until the deadline given. This should prevent most unmalicious accidental loss of data.base | Base for any relative path. |
path | The relative or absolute new path to hard link to. |
d | The deadline by which the matching of the containing directory to the open handle's inode must succeed, else errc::timed_out will be returned. |
current_path()
via parent_path_handle()
and thus is both expensive and calls malloc many times.
|
virtualnoexcept |
Fill the supplied buffer with the names of all extended attributes set on this file or directory, returning a span of path view components.
Note that this routine is a very thin wrap of listxattr()
on POSIX and NtQueryInformationFile()
on Windows. If the supplied buffer is too small, the syscall typically returns failure rather than do a partial fill. Most implementations do not support more than 64Kb of extended attribute information per inode so maybe 70Kb is a safe default (to account for the return value storage), however properly written code will detect the buffer being too small and will auto-expand it until success.
Move assignment of fs_handle permitted.
|
virtualnoexcept |
Obtain a handle to the path currently containing this handle's file entry.
flag::disable_safety_unlinks
is set, this implementation opens a path_handle
to the source containing directory, then checks if the file entry within has the same inode as the open file handle. It will retry this matching until success until the deadline given.current_path()
and thus is both expensive and calls malloc many times.algorithm::cached_parent_handle_adapter<T>
which overrides this with a zero cost implementation, thus making unlinking and relinking very considerably quicker.
|
virtualnoexcept |
Relinks the current path of this open handle to the new path specified. If atomic_replace
is true, the relink atomically and silently replaces any item at the new path specified. This operation is both atomic and matching POSIX behaviour even on Microsoft Windows where no Win32 API can match POSIX semantics.
Note that if atomic_replace
is false, the operation may be implemented as creating a hard link to the destination (which fails if the destination exists), opening a new file descriptor to the destination, closing the existing file descriptor, replacing the existing file descriptor with the new one (this is to ensure path tracking continues to work), then unlinking the previous link. Thus native_handle()
's value may change. This is not the case on Microsoft Windows nor Linux, both of which provide syscalls capable of refusing to rename if the destination exists.
If the handle refers to a pipe, on Microsoft Windows the base path handle is ignored as there is a single global named pipe namespace. Unless the path fragment begins with \
, the string \??\
is prefixed to the name before passing it to the NT kernel API which performs the rename. This is because \\.\
in Win32 maps onto \??\
in the NT kernel.
flag::disable_safety_unlinks
is set, this implementation opens a path_handle
to the source containing directory first, then checks before relinking that the item about to be relinked has the same inode as the open file handle. It will retry this matching until success until the deadline given. This should prevent most unmalicious accidental loss of data.base | Base for any relative path. |
path | The relative or absolute new path to relink to. |
atomic_replace | Atomically replace the destination if a file entry already is present there. Choosing false for this will fail if a file entry is already present at the destination, and may not be an atomic operation on some platforms (i.e. both the old and new names may be linked to the same inode for a very short period of time). Windows and recent Linuxes are always atomic. |
d | The deadline by which the matching of the containing directory to the open handle's inode must succeed, else errc::timed_out will be returned. |
current_path()
via parent_path_handle()
and thus is both expensive and calls malloc many times. Reimplemented in llfio_v2_xxx::mapped_file_handle.
|
virtualnoexcept |
Removes the extended attribute set on this file or directory.
|
virtualnoexcept |
Sets the value of an extended attribute on this file or directory.
To prevent collision in a globally visible resource, there is a convention whereby you ought to namespace the names of your values as namespace.attribute
e.g. appname.setting
to prevent unintentional collision with other programs. Obviously, do choose a unique appname
if there is any chance another program might use the same namespace name.
On POSIX, there are additional namespacing requirements: before your value name, you need to prefix one of user
or system
, so the actual name you might set would be user.appname.propname
. Windows does not have the user
/system
prefix requirement, but it does no harm to do the exact same on Windows as on POSIX.
The host OS and target filing system choose the limits on value size, and will fail accordingly. Some impose a maximum of 64Kb for all names and values per inode, others have a 4Kb maximum value size, there are lots of combinations. You are probably safest not setting many names, and keep the values short.
This API is implemented as file alternate data streams, rather than the Extended Attributes API as accessed via NtSetEaFile()
and NtQueryEaFile()
(which actually modify the file alternate data stream ::$EA
in any case).
The reason why is that NtSetEaFile()
can only append new records to EA storage. It cannot deallocate any existing EA records, if you try to do so you will get STATUS_EA_CORRUPT_ERROR
. You can append setting the same name to a different value, which can include a null value which then appears as if the name is no longer there. But there is a cap of 64kB for the EA record, and once it is consumed, it is gone forever for that inode.
Obviously that doesn't map at all well onto POSIX extended attributes, where you can set the value of an attribute as frequently as you like. The closest equivalent on Windows is therefore file alternate data streams, even though the attribute's value is then worked with as a whole proper file with all the attendant performance consequences.
As a result, name
must be a valid filename and not contain any characters not permitted in a filename. We use the NT API here, so the character restrictions are far fewer than for the Win32 API e.g. single character names do NOT cause misoperation like on Win32.
|
inlinenoexcept |
Unless flag::disable_safety_unlinks
is set, the device id of the file when opened.
|
inlinenoexcept |
Unless flag::disable_safety_unlinks
is set, the inode of the file when opened. When combined with st_dev(), forms a unique identifer on this system.
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
A unique identifier for this handle across the entire system. Can be used in hash tables etc.
|
virtualnoexcept |
Unlinks the current path of this open handle, causing its entry to immediately disappear from the filing system.
On Windows before Windows 10 1709 unless flag::win_disable_unlink_emulation
is set, this behaviour is simulated by renaming the file to something random and setting its delete-on-last-close flag. Note that Windows may prevent the renaming of a file in use by another process, if so it will NOT be renamed. After the next handle to that file closes, it will become permanently unopenable by anyone else until the last handle is closed, whereupon the entry will be eventually removed by the operating system.
flag::disable_safety_unlinks
is set, this implementation opens a path_handle
to the containing directory first, then checks that the item about to be unlinked has the same inode as the open file handle. It will retry this matching until success until the deadline given. This should prevent most unmalicious accidental loss of data.d | The deadline by which the matching of the containing directory to the open handle's inode must succeed, else errc::timed_out will be returned. |
current_path()
and thus is both expensive and calls malloc many times. On Windows, also calls current_path()
if flag::disable_safety_unlinks
is not set.
|
friend |
Maps the current path of h
into a form suitable for Win32 APIs. Passes through unmodified on POSIX, so you can use this in portable code.
h
, which may have been validated to refer to the exact same inode via .unique_id()
(see below). h | The handle whose .current_path() is to be mapped into a form suitable for Win32 APIs. |
mapping | Which Win32 path namespace to map onto. |
This implementation may need to validate that the mapping of the current path of h
onto the desired Win32 path namespace does indeed refer to the same file:
win32_path_namespace::device
transforms \!!\Device\...
=> \\.\...
and ensures that the mapped file's unique id matches the original, otherwise returning failure.win32_path_namespace::dos
enumerates all the DOS devices on the system and what those map onto within the NT kernel namespace. This mapping is for obvious reasons quite slow.win32_path_namespace::guid_volume
simply fetches the GUID of the volume of the handle, and constructs a valid Win32 path from that.win32_path_namespace::any
means attempt guid_volume
first, and if it fails (e.g. your file is on a network share) then it attempts dos
. This semantic may change in the future, however any path emitted will always be a valid Win32 path.
|
mutableprotected |
|
mutableprotected |