mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
587 lines
21 KiB
Zig
587 lines
21 KiB
Zig
const File = @This();
|
|
|
|
const builtin = @import("builtin");
|
|
const native_os = builtin.os.tag;
|
|
const is_windows = native_os == .windows;
|
|
|
|
const std = @import("../std.zig");
|
|
const Io = std.Io;
|
|
const assert = std.debug.assert;
|
|
const Dir = std.Io.Dir;
|
|
|
|
handle: Handle,
|
|
|
|
pub const Reader = @import("File/Reader.zig");
|
|
pub const Writer = @import("File/Writer.zig");
|
|
pub const Atomic = @import("File/Atomic.zig");
|
|
|
|
pub const Handle = std.posix.fd_t;
|
|
pub const INode = std.posix.ino_t;
|
|
pub const Uid = std.posix.uid_t;
|
|
pub const Gid = std.posix.gid_t;
|
|
|
|
pub const Kind = enum {
|
|
block_device,
|
|
character_device,
|
|
directory,
|
|
named_pipe,
|
|
sym_link,
|
|
file,
|
|
unix_domain_socket,
|
|
whiteout,
|
|
door,
|
|
event_port,
|
|
unknown,
|
|
};
|
|
|
|
pub const Stat = struct {
|
|
/// A number that the system uses to point to the file metadata. This
|
|
/// number is not guaranteed to be unique across time, as some file
|
|
/// systems may reuse an inode after its file has been deleted. Some
|
|
/// systems may change the inode of a file over time.
|
|
///
|
|
/// On Linux, the inode is a structure that stores the metadata, and
|
|
/// the inode _number_ is what you see here: the index number of the
|
|
/// inode.
|
|
///
|
|
/// The FileIndex on Windows is similar. It is a number for a file that
|
|
/// is unique to each filesystem.
|
|
inode: INode,
|
|
size: u64,
|
|
permissions: Permissions,
|
|
kind: Kind,
|
|
/// Last access time in nanoseconds, relative to UTC 1970-01-01.
|
|
atime: Io.Timestamp,
|
|
/// Last modification time in nanoseconds, relative to UTC 1970-01-01.
|
|
mtime: Io.Timestamp,
|
|
/// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.
|
|
ctime: Io.Timestamp,
|
|
};
|
|
|
|
pub fn stdout() File {
|
|
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdOutput else std.posix.STDOUT_FILENO };
|
|
}
|
|
|
|
pub fn stderr() File {
|
|
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdError else std.posix.STDERR_FILENO };
|
|
}
|
|
|
|
pub fn stdin() File {
|
|
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdInput else std.posix.STDIN_FILENO };
|
|
}
|
|
|
|
pub const StatError = error{
|
|
SystemResources,
|
|
/// In WASI, this error may occur when the file descriptor does
|
|
/// not hold the required rights to get its filestat information.
|
|
AccessDenied,
|
|
PermissionDenied,
|
|
/// Attempted to stat a non-file stream.
|
|
Streaming,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Returns `Stat` containing basic information about the `File`.
|
|
pub fn stat(file: File, io: Io) StatError!Stat {
|
|
return io.vtable.fileStat(io.userdata, file);
|
|
}
|
|
|
|
pub const OpenMode = enum {
|
|
read_only,
|
|
write_only,
|
|
read_write,
|
|
};
|
|
|
|
pub const Lock = enum {
|
|
none,
|
|
shared,
|
|
exclusive,
|
|
};
|
|
|
|
pub const OpenFlags = struct {
|
|
mode: OpenMode = .read_only,
|
|
|
|
/// Open the file with an advisory lock to coordinate with other processes
|
|
/// accessing it at the same time. An exclusive lock will prevent other
|
|
/// processes from acquiring a lock. A shared lock will prevent other
|
|
/// processes from acquiring a exclusive lock, but does not prevent
|
|
/// other process from getting their own shared locks.
|
|
///
|
|
/// The lock is advisory, except on Linux in very specific circumstances[1].
|
|
/// This means that a process that does not respect the locking API can still get access
|
|
/// to the file, despite the lock.
|
|
///
|
|
/// On these operating systems, the lock is acquired atomically with
|
|
/// opening the file:
|
|
/// * Darwin
|
|
/// * DragonFlyBSD
|
|
/// * FreeBSD
|
|
/// * Haiku
|
|
/// * NetBSD
|
|
/// * OpenBSD
|
|
/// On these operating systems, the lock is acquired via a separate syscall
|
|
/// after opening the file:
|
|
/// * Linux
|
|
/// * Windows
|
|
///
|
|
/// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
|
|
lock: Lock = .none,
|
|
|
|
/// Sets whether or not to wait until the file is locked to return. If set to true,
|
|
/// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
|
|
/// is available to proceed.
|
|
lock_nonblocking: bool = false,
|
|
|
|
/// Set this to allow the opened file to automatically become the
|
|
/// controlling TTY for the current process.
|
|
allow_ctty: bool = false,
|
|
|
|
follow_symlinks: bool = true,
|
|
|
|
pub fn isRead(self: OpenFlags) bool {
|
|
return self.mode != .write_only;
|
|
}
|
|
|
|
pub fn isWrite(self: OpenFlags) bool {
|
|
return self.mode != .read_only;
|
|
}
|
|
};
|
|
|
|
pub const CreateFlags = struct {
|
|
/// Whether the file will be created with read access.
|
|
read: bool = false,
|
|
|
|
/// If the file already exists, and is a regular file, and the access
|
|
/// mode allows writing, it will be truncated to length 0.
|
|
truncate: bool = true,
|
|
|
|
/// Ensures that this open call creates the file, otherwise causes
|
|
/// `error.PathAlreadyExists` to be returned.
|
|
exclusive: bool = false,
|
|
|
|
/// Open the file with an advisory lock to coordinate with other processes
|
|
/// accessing it at the same time. An exclusive lock will prevent other
|
|
/// processes from acquiring a lock. A shared lock will prevent other
|
|
/// processes from acquiring a exclusive lock, but does not prevent
|
|
/// other process from getting their own shared locks.
|
|
///
|
|
/// The lock is advisory, except on Linux in very specific circumstances[1].
|
|
/// This means that a process that does not respect the locking API can still get access
|
|
/// to the file, despite the lock.
|
|
///
|
|
/// On these operating systems, the lock is acquired atomically with
|
|
/// opening the file:
|
|
/// * Darwin
|
|
/// * DragonFlyBSD
|
|
/// * FreeBSD
|
|
/// * Haiku
|
|
/// * NetBSD
|
|
/// * OpenBSD
|
|
/// On these operating systems, the lock is acquired via a separate syscall
|
|
/// after opening the file:
|
|
/// * Linux
|
|
/// * Windows
|
|
///
|
|
/// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
|
|
lock: Lock = .none,
|
|
|
|
/// Sets whether or not to wait until the file is locked to return. If set to true,
|
|
/// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
|
|
/// is available to proceed.
|
|
lock_nonblocking: bool = false,
|
|
|
|
permissions: Permissions = .default_file,
|
|
};
|
|
|
|
pub const OpenError = error{
|
|
SharingViolation,
|
|
PipeBusy,
|
|
NoDevice,
|
|
/// On Windows, `\\server` or `\\server\share` was not found.
|
|
NetworkNotFound,
|
|
/// On Windows, antivirus software is enabled by default. It can be
|
|
/// disabled, but Windows Update sometimes ignores the user's preference
|
|
/// and re-enables it. When enabled, antivirus software on Windows
|
|
/// intercepts file system operations and makes them significantly slower
|
|
/// in addition to possibly failing with this error code.
|
|
AntivirusInterference,
|
|
/// In WASI, this error may occur when the file descriptor does
|
|
/// not hold the required rights to open a new resource relative to it.
|
|
AccessDenied,
|
|
PermissionDenied,
|
|
SymLinkLoop,
|
|
ProcessFdQuotaExceeded,
|
|
SystemFdQuotaExceeded,
|
|
/// Either:
|
|
/// * One of the path components does not exist.
|
|
/// * Cwd was used, but cwd has been deleted.
|
|
/// * The path associated with the open directory handle has been deleted.
|
|
/// * On macOS, multiple processes or threads raced to create the same file
|
|
/// with `O.EXCL` set to `false`.
|
|
FileNotFound,
|
|
/// The path exceeded `max_path_bytes` bytes.
|
|
/// Insufficient kernel memory was available, or
|
|
/// the named file is a FIFO and per-user hard limit on
|
|
/// memory allocation for pipes has been reached.
|
|
SystemResources,
|
|
/// The file is too large to be opened. This error is unreachable
|
|
/// for 64-bit targets, as well as when opening directories.
|
|
FileTooBig,
|
|
/// The path refers to directory but the `DIRECTORY` flag was not provided.
|
|
IsDir,
|
|
/// A new path cannot be created because the device has no room for the new file.
|
|
/// This error is only reachable when the `CREAT` flag is provided.
|
|
NoSpaceLeft,
|
|
/// A component used as a directory in the path was not, in fact, a directory, or
|
|
/// `DIRECTORY` was specified and the path was not a directory.
|
|
NotDir,
|
|
/// The path already exists and the `CREAT` and `EXCL` flags were provided.
|
|
PathAlreadyExists,
|
|
DeviceBusy,
|
|
FileLocksUnsupported,
|
|
/// One of these three things:
|
|
/// * pathname refers to an executable image which is currently being
|
|
/// executed and write access was requested.
|
|
/// * pathname refers to a file that is currently in use as a swap
|
|
/// file, and the O_TRUNC flag was specified.
|
|
/// * pathname refers to a file that is currently being read by the
|
|
/// kernel (e.g., for module/firmware loading), and write access was
|
|
/// requested.
|
|
FileBusy,
|
|
/// Non-blocking was requested and the operation cannot return immediately.
|
|
WouldBlock,
|
|
} || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
pub fn close(file: File, io: Io) void {
|
|
return io.vtable.fileClose(io.userdata, file);
|
|
}
|
|
|
|
pub const SyncError = error{
|
|
InputOutput,
|
|
NoSpaceLeft,
|
|
DiskQuota,
|
|
AccessDenied,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Blocks until all pending file contents and metadata modifications for the
|
|
/// file have been synchronized with the underlying filesystem.
|
|
///
|
|
/// This does not ensure that metadata for the directory containing the file
|
|
/// has also reached disk.
|
|
pub fn sync(file: File, io: Io) SyncError!void {
|
|
return io.vtable.fileSync(io.userdata, file);
|
|
}
|
|
|
|
/// Test whether the file refers to a terminal.
|
|
///
|
|
/// See also:
|
|
/// * `getOrEnableAnsiEscapeSupport`
|
|
/// * `supportsAnsiEscapeCodes`.
|
|
pub fn isTty(file: File, io: Io) bool {
|
|
return io.vtable.fileIsTty(io.userdata, file);
|
|
}
|
|
|
|
pub const EnableAnsiEscapeCodesError = error{} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
pub fn enableAnsiEscapeCodes(file: File, io: Io) EnableAnsiEscapeCodesError {
|
|
return io.vtable.fileEnableAnsiEscapeCodes(io.userdata, file);
|
|
}
|
|
|
|
/// Test whether ANSI escape codes will be treated as such without
|
|
/// attempting to enable support for ANSI escape codes.
|
|
pub fn supportsAnsiEscapeCodes(file: File, io: Io) Io.Cancelable!bool {
|
|
return io.vtable.fileSupportsAnsiEscapeCodes(io.userdata, file);
|
|
}
|
|
|
|
pub const SetLengthError = error{
|
|
FileTooBig,
|
|
InputOutput,
|
|
FileBusy,
|
|
AccessDenied,
|
|
PermissionDenied,
|
|
NonResizable,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Truncates or expands the file, populating any new data with zeroes.
|
|
///
|
|
/// The file offset after this call is left unchanged.
|
|
pub fn setLength(file: File, io: Io, new_length: u64) SetLengthError!void {
|
|
return io.vtable.fileSetLength(io.userdata, file, new_length);
|
|
}
|
|
|
|
pub const LengthError = StatError;
|
|
|
|
/// Retrieve the ending byte index of the file.
|
|
///
|
|
/// Sometimes cheaper than `stat` if only the length is needed.
|
|
pub fn length(file: File, io: Io) LengthError!u64 {
|
|
return io.vtable.fileLength(io.userdata, file);
|
|
}
|
|
|
|
pub const SetPermissionsError = error{
|
|
AccessDenied,
|
|
PermissionDenied,
|
|
InputOutput,
|
|
SymLinkLoop,
|
|
FileNotFound,
|
|
SystemResources,
|
|
ReadOnlyFileSystem,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Also known as "chmod".
|
|
///
|
|
/// The process must have the correct privileges in order to do this
|
|
/// successfully, or must have the effective user ID matching the owner of the
|
|
/// file.
|
|
pub fn setPermissions(file: File, io: Io, new_permissions: Permissions) SetPermissionsError!void {
|
|
return io.vtable.fileSetPermissions(io.userdata, file, new_permissions);
|
|
}
|
|
|
|
pub const SetOwnerError = error{
|
|
AccessDenied,
|
|
PermissionDenied,
|
|
InputOutput,
|
|
SymLinkLoop,
|
|
FileNotFound,
|
|
SystemResources,
|
|
ReadOnlyFileSystem,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Also known as "chown".
|
|
///
|
|
/// The process must have the correct privileges in order to do this
|
|
/// successfully. The group may be changed by the owner of the file to any
|
|
/// group of which the owner is a member. If the owner or group is specified as
|
|
/// `null`, the ID is not changed.
|
|
pub fn setOwner(file: File, io: Io, owner: ?Uid, group: ?Gid) SetOwnerError!void {
|
|
return io.vtable.fileSetOwner(io.userdata, file, owner, group);
|
|
}
|
|
|
|
/// Cross-platform representation of permissions on a file.
|
|
///
|
|
/// On POSIX systems this corresponds to "mode" and on Windows this corresponds to "attributes".
|
|
///
|
|
/// Overridable via `std.options`.
|
|
pub const Permissions = std.options.FilePermissions orelse if (is_windows) enum(std.os.windows.DWORD) {
|
|
default_file = 0,
|
|
_,
|
|
|
|
pub const default_dir: @This() = .default_file;
|
|
pub const has_executable_bit = false;
|
|
|
|
const windows = std.os.windows;
|
|
|
|
pub fn toAttributes(self: @This()) windows.DWORD {
|
|
return @intFromEnum(self);
|
|
}
|
|
|
|
pub fn readOnly(self: @This()) bool {
|
|
const attributes = toAttributes(self);
|
|
return attributes & windows.FILE_ATTRIBUTE_READONLY != 0;
|
|
}
|
|
|
|
pub fn setReadOnly(self: @This(), read_only: bool) @This() {
|
|
const attributes = toAttributes(self);
|
|
return @enumFromInt(if (read_only)
|
|
attributes | windows.FILE_ATTRIBUTE_READONLY
|
|
else
|
|
attributes & ~@as(windows.DWORD, windows.FILE_ATTRIBUTE_READONLY));
|
|
}
|
|
} else if (std.posix.mode_t != u0) enum(std.posix.mode_t) {
|
|
/// This is the default mode given to POSIX operating systems for creating
|
|
/// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first,
|
|
/// since most people would expect "-rw-r--r--", for example, when using
|
|
/// the `touch` command, which would correspond to `0o644`. However, POSIX
|
|
/// libc implementations use `0o666` inside `fopen` and then rely on the
|
|
/// process-scoped "umask" setting to adjust this number for file creation.
|
|
default_file = 0o666,
|
|
default_dir = 0o755,
|
|
_,
|
|
|
|
pub const has_executable_bit = true;
|
|
|
|
pub fn toMode(self: @This()) std.posix.mode_t {
|
|
return @intFromEnum(self);
|
|
}
|
|
|
|
/// Returns `true` if and only if no class has write permissions.
|
|
pub fn readOnly(self: @This()) bool {
|
|
const mode = toMode(self);
|
|
return mode & 0o222 == 0;
|
|
}
|
|
|
|
/// Enables write permission for all classes.
|
|
pub fn setReadOnly(self: @This(), read_only: bool) @This() {
|
|
const mode = toMode(self);
|
|
const o222 = @as(std.posix.mode_t, 0o222);
|
|
return @enumFromInt(if (read_only) mode & ~o222 else mode | o222);
|
|
}
|
|
} else enum(u0) {
|
|
default_file = 0,
|
|
pub const default_dir: @This() = .default_file;
|
|
pub const has_executable_bit = false;
|
|
};
|
|
|
|
pub const SetTimestampsError = error{
|
|
/// times is NULL, or both nsec values are UTIME_NOW, and either:
|
|
/// * the effective user ID of the caller does not match the owner
|
|
/// of the file, the caller does not have write access to the
|
|
/// file, and the caller is not privileged (Linux: does not have
|
|
/// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability);
|
|
/// or,
|
|
/// * the file is marked immutable (see chattr(1)).
|
|
AccessDenied,
|
|
/// The caller attempted to change one or both timestamps to a value
|
|
/// other than the current time, or to change one of the timestamps
|
|
/// to the current time while leaving the other timestamp unchanged,
|
|
/// (i.e., times is not NULL, neither nsec field is UTIME_NOW,
|
|
/// and neither nsec field is UTIME_OMIT) and either:
|
|
/// * the caller's effective user ID does not match the owner of
|
|
/// file, and the caller is not privileged (Linux: does not have
|
|
/// the CAP_FOWNER capability); or,
|
|
/// * the file is marked append-only or immutable (see chattr(1)).
|
|
PermissionDenied,
|
|
ReadOnlyFileSystem,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// The granularity that ultimately is stored depends on the combination of
|
|
/// operating system and file system. When a value as provided that exceeds
|
|
/// this range, the value is clamped to the maximum.
|
|
pub fn setTimestamps(
|
|
file: File,
|
|
io: Io,
|
|
last_accessed: Io.Timestamp,
|
|
last_modified: Io.Timestamp,
|
|
) SetTimestampsError!void {
|
|
return io.vtable.fileUpdateTimes(io.userdata, file, last_accessed, last_modified);
|
|
}
|
|
|
|
/// Sets the accessed and modification timestamps of `file` to the current wall
|
|
/// clock time.
|
|
///
|
|
/// The granularity that ultimately is stored depends on the combination of
|
|
/// operating system and file system.
|
|
pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {
|
|
return io.vtable.fileSetTimestampsNow(io.userdata, file);
|
|
}
|
|
|
|
pub const OpenSelfExeError = OpenError || std.fs.SelfExePathError || LockError;
|
|
|
|
pub fn openSelfExe(io: Io, flags: OpenFlags) OpenSelfExeError!File {
|
|
return io.vtable.openSelfExe(io.userdata, flags);
|
|
}
|
|
|
|
pub const ReadPositionalError = Reader.Error || error{Unseekable};
|
|
|
|
pub fn readPositional(file: File, io: Io, buffer: [][]u8, offset: u64) ReadPositionalError!usize {
|
|
return io.vtable.fileReadPositional(io.userdata, file, buffer, offset);
|
|
}
|
|
|
|
pub const WritePositionalError = Writer.Error || error{Unseekable};
|
|
|
|
pub fn writePositional(file: File, io: Io, buffer: [][]const u8, offset: u64) WritePositionalError!usize {
|
|
return io.vtable.fileWritePositional(io.userdata, file, buffer, offset);
|
|
}
|
|
|
|
pub const WriteFileStreamingError = error{
|
|
/// `out_fd` is an unconnected socket, or out_fd closed its read end.
|
|
BrokenPipe,
|
|
/// Descriptor is not valid or locked, or an mmap(2)-like operation is not available for in_fd.
|
|
UnsupportedOperation,
|
|
/// Nonblocking I/O has been selected but the write would block.
|
|
WouldBlock,
|
|
/// Unspecified error while reading from in_fd.
|
|
InputOutput,
|
|
/// Insufficient kernel memory to read from in_fd.
|
|
SystemResources,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Opens a file for reading or writing, without attempting to create a new
|
|
/// file, based on an absolute path.
|
|
///
|
|
/// Returns an open resource to be released with `close`.
|
|
///
|
|
/// Asserts that the path is absolute. See `Dir.openFile` for a function that
|
|
/// operates on both absolute and relative paths.
|
|
///
|
|
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
|
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
|
|
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
|
|
pub fn openAbsolute(io: Io, absolute_path: []const u8, flags: OpenFlags) OpenError!File {
|
|
assert(std.fs.path.isAbsolute(absolute_path));
|
|
return Io.Dir.cwd().openFile(io, absolute_path, flags);
|
|
}
|
|
|
|
pub const SeekError = error{
|
|
Unseekable,
|
|
/// The file descriptor does not hold the required rights to seek on it.
|
|
AccessDenied,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
pub const WriteFilePositionalError = Writer.WriteFileError || error{Unseekable};
|
|
|
|
/// Defaults to positional reading; falls back to streaming.
|
|
///
|
|
/// Positional is more threadsafe, since the global seek position is not
|
|
/// affected.
|
|
pub fn reader(file: File, io: Io, buffer: []u8) Reader {
|
|
return .init(file, io, buffer);
|
|
}
|
|
|
|
/// Positional is more threadsafe, since the global seek position is not
|
|
/// affected, but when such syscalls are not available, preemptively
|
|
/// initializing in streaming mode skips a failed syscall.
|
|
pub fn readerStreaming(file: File, io: Io, buffer: []u8) Reader {
|
|
return .initStreaming(file, io, buffer);
|
|
}
|
|
|
|
/// Defaults to positional reading; falls back to streaming.
|
|
///
|
|
/// Positional is more threadsafe, since the global seek position is not
|
|
/// affected.
|
|
pub fn writer(file: File, io: Io, buffer: []u8) Writer {
|
|
return .init(file, io, buffer);
|
|
}
|
|
|
|
/// Positional is more threadsafe, since the global seek position is not
|
|
/// affected, but when such syscalls are not available, preemptively
|
|
/// initializing in streaming mode will skip a failed syscall.
|
|
pub fn writerStreaming(file: File, io: Io, buffer: []u8) Writer {
|
|
return .initStreaming(file, io, buffer);
|
|
}
|
|
|
|
pub const LockError = error{
|
|
SystemResources,
|
|
FileLocksUnsupported,
|
|
} || Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Blocks when an incompatible lock is held by another process. A process may
|
|
/// hold only one type of lock (shared or exclusive) on a file. When a process
|
|
/// terminates in any way, the lock is released.
|
|
///
|
|
/// Assumes the file is unlocked.
|
|
pub fn lock(file: File, io: Io, l: Lock) LockError!void {
|
|
return io.vtable.fileLock(io.userdata, file, l);
|
|
}
|
|
|
|
/// Assumes the file is locked.
|
|
pub fn unlock(file: File, io: Io) void {
|
|
return io.vtable.fileUnlock(io.userdata, file);
|
|
}
|
|
|
|
/// Attempts to obtain a lock, returning `true` if the lock is obtained, and
|
|
/// `false` if there was an existing incompatible lock held. A process may hold
|
|
/// only one type of lock (shared or exclusive) on a file. When a process
|
|
/// terminates in any way, the lock is released.
|
|
///
|
|
/// Assumes the file is unlocked.
|
|
pub fn tryLock(file: File, io: Io, l: Lock) LockError!bool {
|
|
return io.vtable.fileTryLock(io.userdata, file, l);
|
|
}
|
|
|
|
pub const DowngradeLockError = Io.Cancelable || Io.UnexpectedError;
|
|
|
|
/// Assumes the file is already locked in exclusive mode.
|
|
/// Atomically modifies the lock to be in shared mode, without releasing it.
|
|
pub fn downgradeLock(file: File, io: Io) LockError!void {
|
|
return io.vtable.fileDowngradeLock(io.userdata, file);
|
|
}
|