1const File = @This();
2
3const builtin = @import("builtin");
4const native_os = builtin.os.tag;
5const is_windows = native_os == .windows;
6
7const std = @import("../std.zig");
8const Io = std.Io;
9const assert = std.debug.assert;
10const Dir = std.Io.Dir;
11
12handle: Handle,
13flags: Flags,
14
15pub const Flags = struct {
16 /// * true:
17 /// - windows: opened with MODE.IO.ASYNCHRONOUS
18 /// - POSIX: O_NONBLOCK is set
19 /// * false:
20 /// - windows: opened with SYNCHRONOUS_ALERT or SYNCHRONOUS_NONALERT, or
21 /// not a file.
22 /// - POSIX: O_NONBLOCK is unset
23 nonblocking: bool,
24};
25
26pub const Handle = std.posix.fd_t;
27
28pub const Reader = @import("File/Reader.zig");
29pub const Writer = @import("File/Writer.zig");
30pub const Atomic = @import("File/Atomic.zig");
31/// Memory intended to remain consistent with file contents.
32pub const MemoryMap = @import("File/MemoryMap.zig");
33/// Concurrently read from multiple file streams, eliminating risk of
34/// deadlocking.
35pub const MultiReader = @import("File/MultiReader.zig");
36
37pub const INode = std.posix.ino_t;
38pub const NLink = std.posix.nlink_t;
39pub const Uid = std.posix.uid_t;
40pub const Gid = std.posix.gid_t;
41pub const BlockSize = u32;
42
43pub const Kind = enum {
44 block_device,
45 character_device,
46 directory,
47 named_pipe,
48 sym_link,
49 file,
50 unix_domain_socket,
51 whiteout,
52 door,
53 event_port,
54 unknown,
55};
56
57pub const Stat = struct {
58 /// A number that the system uses to point to the file metadata. This
59 /// number is not guaranteed to be unique across time, as some file
60 /// systems may reuse an inode after its file has been deleted. Some
61 /// systems may change the inode of a file over time.
62 ///
63 /// On Linux, the inode is a structure that stores the metadata, and
64 /// the inode _number_ is what you see here: the index number of the
65 /// inode.
66 ///
67 /// The FileIndex on Windows is similar. It is a number for a file that
68 /// is unique to each filesystem.
69 inode: INode,
70 nlink: NLink,
71 size: u64,
72 permissions: Permissions,
73 kind: Kind,
74 /// Last access time in nanoseconds, relative to UTC 1970-01-01.
75 ///
76 /// Filesystems generally find this value problematic to keep updated since
77 /// it turns read-only file system accesses into file system mutations.
78 /// Some systems report stale values, and some systems explicitly refuse to
79 /// report this value. The latter case is handled by `null`.
80 atime: ?Io.Timestamp,
81 /// Last modification time in nanoseconds, relative to UTC 1970-01-01.
82 mtime: Io.Timestamp,
83 /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.
84 ctime: Io.Timestamp,
85 /// Smallest chunk length in bytes appropriate for optimal I/O. This will
86 /// be set to `1` for operating systems or file systems that do not
87 /// recognize this concept. Not always a power of two.
88 block_size: BlockSize,
89};
90
91pub fn stdout() File {
92 return switch (native_os) {
93 .windows => .{
94 .handle = std.os.windows.peb().ProcessParameters.hStdOutput,
95 .flags = .{ .nonblocking = false },
96 },
97 else => .{
98 .handle = std.posix.STDOUT_FILENO,
99 .flags = .{ .nonblocking = false },
100 },
101 };
102}
103
104pub fn stderr() File {
105 return switch (native_os) {
106 .windows => .{
107 .handle = std.os.windows.peb().ProcessParameters.hStdError,
108 .flags = .{ .nonblocking = false },
109 },
110 else => .{
111 .handle = std.posix.STDERR_FILENO,
112 .flags = .{ .nonblocking = false },
113 },
114 };
115}
116
117pub fn stdin() File {
118 return switch (native_os) {
119 .windows => .{
120 .handle = std.os.windows.peb().ProcessParameters.hStdInput,
121 .flags = .{ .nonblocking = false },
122 },
123 else => .{
124 .handle = std.posix.STDIN_FILENO,
125 .flags = .{ .nonblocking = false },
126 },
127 };
128}
129
130pub const StatError = error{
131 SystemResources,
132 /// In WASI, this error may occur when the file descriptor does
133 /// not hold the required rights to get its filestat information.
134 AccessDenied,
135 PermissionDenied,
136 /// Attempted to stat a non-file stream.
137 Streaming,
138} || Io.Cancelable || Io.UnexpectedError;
139
140/// Returns `Stat` containing basic information about the `File`.
141pub fn stat(file: File, io: Io) StatError!Stat {
142 return io.vtable.fileStat(io.userdata, file);
143}
144
145pub const Lock = enum {
146 none,
147 shared,
148 exclusive,
149};
150
151pub const OpenError = error{
152 PipeBusy,
153 NoDevice,
154 /// On Windows, `\\server` or `\\server\share` was not found.
155 NetworkNotFound,
156 /// On Windows, antivirus software is enabled by default. It can be
157 /// disabled, but Windows Update sometimes ignores the user's preference
158 /// and re-enables it. When enabled, antivirus software on Windows
159 /// intercepts file system operations and makes them significantly slower
160 /// in addition to possibly failing with this error code.
161 AntivirusInterference,
162 /// In WASI, this error may occur when the file descriptor does
163 /// not hold the required rights to open a new resource relative to it.
164 AccessDenied,
165 PermissionDenied,
166 SymLinkLoop,
167 ProcessFdQuotaExceeded,
168 SystemFdQuotaExceeded,
169 /// Either:
170 /// * One of the path components does not exist.
171 /// * Cwd was used, but cwd has been deleted.
172 /// * The path associated with the open directory handle has been deleted.
173 /// * On macOS, multiple processes or threads raced to create the same file
174 /// with `O.EXCL` set to `false`.
175 FileNotFound,
176 /// The path exceeded `max_path_bytes` bytes.
177 /// Insufficient kernel memory was available, or
178 /// the named file is a FIFO and per-user hard limit on
179 /// memory allocation for pipes has been reached.
180 SystemResources,
181 /// The file is too large to be opened. This error is unreachable
182 /// for 64-bit targets, as well as when opening directories.
183 FileTooBig,
184 /// Either:
185 /// * The path refers to a directory and write permissions were requested.
186 /// * The path refers to a directory and `allow_directory` was set to false.
187 IsDir,
188 /// A new path cannot be created because the device has no room for the new file.
189 /// This error is only reachable when the `CREAT` flag is provided.
190 NoSpaceLeft,
191 /// A component used as a directory in the path was not, in fact, a directory, or
192 /// `DIRECTORY` was specified and the path was not a directory.
193 NotDir,
194 /// The path already exists and the `CREAT` and `EXCL` flags were provided.
195 PathAlreadyExists,
196 ReadOnlyFileSystem,
197 DeviceBusy,
198 FileLocksUnsupported,
199 /// One of these three things:
200 /// * pathname refers to an executable image which is currently being
201 /// executed and write access was requested.
202 /// * pathname refers to a file that is currently in use as a swap
203 /// file, and the O_TRUNC flag was specified.
204 /// * pathname refers to a file that is currently being read by the
205 /// kernel (e.g., for module/firmware loading), and write access was
206 /// requested.
207 FileBusy,
208 /// Non-blocking was requested and the operation cannot return immediately.
209 WouldBlock,
210} || Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
211
212pub fn close(file: File, io: Io) void {
213 return io.vtable.fileClose(io.userdata, (&file)[0..1]);
214}
215
216pub fn closeMany(io: Io, files: []const File) void {
217 return io.vtable.fileClose(io.userdata, files);
218}
219
220pub const SyncError = error{
221 InputOutput,
222 NoSpaceLeft,
223 DiskQuota,
224 AccessDenied,
225} || Io.Cancelable || Io.UnexpectedError;
226
227/// Blocks until all pending file contents and metadata modifications for the
228/// file have been synchronized with the underlying filesystem.
229///
230/// This does not ensure that metadata for the directory containing the file
231/// has also reached disk.
232pub fn sync(file: File, io: Io) SyncError!void {
233 return io.vtable.fileSync(io.userdata, file);
234}
235
236/// Test whether the file refers to a terminal (similar to libc "isatty").
237///
238/// See also:
239/// * `enableAnsiEscapeCodes`
240/// * `supportsAnsiEscapeCodes`.
241pub fn isTty(file: File, io: Io) Io.Cancelable!bool {
242 return io.vtable.fileIsTty(io.userdata, file);
243}
244
245pub const EnableAnsiEscapeCodesError = error{
246 NotTerminalDevice,
247} || Io.Cancelable || Io.UnexpectedError;
248
249pub fn enableAnsiEscapeCodes(file: File, io: Io) EnableAnsiEscapeCodesError!void {
250 return io.vtable.fileEnableAnsiEscapeCodes(io.userdata, file);
251}
252
253/// Test whether ANSI escape codes will be treated as such without
254/// attempting to enable support for ANSI escape codes.
255pub fn supportsAnsiEscapeCodes(file: File, io: Io) Io.Cancelable!bool {
256 return io.vtable.fileSupportsAnsiEscapeCodes(io.userdata, file);
257}
258
259pub const SetLengthError = error{
260 FileTooBig,
261 InputOutput,
262 FileBusy,
263 AccessDenied,
264 PermissionDenied,
265 NonResizable,
266} || Io.Cancelable || Io.UnexpectedError;
267
268/// Truncates or expands the file, populating any new data with zeroes.
269///
270/// The file offset after this call is left unchanged.
271///
272/// This function operates on an open file handle. There is not an equivalent
273/// function in `Dir` which operates on paths because generally, such
274/// functionality will introduce Time-Of-Check, Time-Of-Use (TOCTOU) bugs. In
275/// the rare case when those semantics are actually needed, it is reasonable to
276/// open the file with the truncate flag.
277pub fn setLength(file: File, io: Io, new_length: u64) SetLengthError!void {
278 return io.vtable.fileSetLength(io.userdata, file, new_length);
279}
280
281pub const LengthError = StatError;
282
283/// Retrieve the ending byte index of the file.
284///
285/// Sometimes cheaper than `stat` if only the length is needed.
286pub fn length(file: File, io: Io) LengthError!u64 {
287 return io.vtable.fileLength(io.userdata, file);
288}
289
290pub const SetPermissionsError = error{
291 AccessDenied,
292 PermissionDenied,
293 InputOutput,
294 SymLinkLoop,
295 FileNotFound,
296 SystemResources,
297 ReadOnlyFileSystem,
298} || Io.Cancelable || Io.UnexpectedError;
299
300/// Also known as "chmod".
301///
302/// The process must have the correct privileges in order to do this
303/// successfully, or must have the effective user ID matching the owner of the
304/// file.
305pub fn setPermissions(file: File, io: Io, new_permissions: Permissions) SetPermissionsError!void {
306 return io.vtable.fileSetPermissions(io.userdata, file, new_permissions);
307}
308
309pub const SetOwnerError = error{
310 AccessDenied,
311 PermissionDenied,
312 InputOutput,
313 SymLinkLoop,
314 FileNotFound,
315 SystemResources,
316 ReadOnlyFileSystem,
317} || Io.Cancelable || Io.UnexpectedError;
318
319/// Also known as "chown".
320///
321/// The process must have the correct privileges in order to do this
322/// successfully. The group may be changed by the owner of the file to any
323/// group of which the owner is a member. If the owner or group is specified as
324/// `null`, the ID is not changed.
325pub fn setOwner(file: File, io: Io, owner: ?Uid, group: ?Gid) SetOwnerError!void {
326 return io.vtable.fileSetOwner(io.userdata, file, owner, group);
327}
328
329/// Cross-platform representation of permissions on a file.
330///
331/// On POSIX systems this corresponds to "mode" and on Windows this corresponds to "attributes".
332pub const Permissions = std.Options.FilePermissions orelse if (is_windows) enum(std.os.windows.DWORD) {
333 default_file = 0,
334 _,
335
336 pub const default_dir: @This() = .default_file;
337 pub const executable_file: @This() = .default_file;
338 pub const has_executable_bit = false;
339
340 const windows = std.os.windows;
341
342 pub fn toAttributes(self: @This()) windows.FILE.ATTRIBUTE {
343 return @bitCast(@backingInt(self));
344 }
345
346 pub fn readOnly(self: @This()) bool {
347 const attributes = toAttributes(self);
348 return attributes & windows.FILE_ATTRIBUTE_READONLY != 0;
349 }
350
351 pub fn setReadOnly(self: @This(), read_only: bool) @This() {
352 const attributes = toAttributes(self);
353 return @fromBackingInt(@intCast(if (read_only)
354 attributes | windows.FILE_ATTRIBUTE_READONLY
355 else
356 attributes & ~@as(windows.DWORD, windows.FILE_ATTRIBUTE_READONLY)));
357 }
358} else if (std.posix.mode_t != u0) enum(std.posix.mode_t) {
359 /// This is the default mode given to POSIX operating systems for creating
360 /// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first,
361 /// since most people would expect "-rw-r--r--", for example, when using
362 /// the `touch` command, which would correspond to `0o644`. However, POSIX
363 /// libc implementations use `0o666` inside `fopen` and then rely on the
364 /// process-scoped "umask" setting to adjust this number for file creation.
365 default_file = 0o666,
366 /// This is the default mode given to POSIX operating systems for creating
367 /// directories. `0o777` is "-rwxrwxrwx" which is counter-intuitive at first,
368 /// since most people would expect "-rwxr-xr-x", for example, when using
369 /// the `touch` command, which would correspond to `0o755`.
370 default_dir = 0o777,
371 _,
372
373 pub const has_executable_bit = native_os != .wasi;
374
375 pub const executable_file: @This() = .default_dir;
376
377 pub fn toMode(self: @This()) std.posix.mode_t {
378 return @backingInt(self);
379 }
380
381 pub fn fromMode(mode: std.posix.mode_t) @This() {
382 return @fromBackingInt(@intCast(mode));
383 }
384
385 /// Returns `true` if and only if no class has write permissions.
386 pub fn readOnly(self: @This()) bool {
387 const mode = toMode(self);
388 return mode & 0o222 == 0;
389 }
390
391 /// Enables write permission for all classes.
392 pub fn setReadOnly(self: @This(), read_only: bool) @This() {
393 const mode = toMode(self);
394 const o222 = @as(std.posix.mode_t, 0o222);
395 return @fromBackingInt(@intCast(if (read_only) mode & ~o222 else mode | o222));
396 }
397} else enum(u0) {
398 default_file = 0,
399 pub const default_dir: @This() = .default_file;
400 pub const executable_file: @This() = .default_file;
401 pub const has_executable_bit = false;
402};
403
404pub const SetTimestampsError = error{
405 /// times is NULL, or both nsec values are UTIME_NOW, and either:
406 /// * the effective user ID of the caller does not match the owner
407 /// of the file, the caller does not have write access to the
408 /// file, and the caller is not privileged (Linux: does not have
409 /// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability);
410 /// or,
411 /// * the file is marked immutable (see chattr(1)).
412 AccessDenied,
413 /// The caller attempted to change one or both timestamps to a value
414 /// other than the current time, or to change one of the timestamps
415 /// to the current time while leaving the other timestamp unchanged,
416 /// (i.e., times is not NULL, neither nsec field is UTIME_NOW,
417 /// and neither nsec field is UTIME_OMIT) and either:
418 /// * the caller's effective user ID does not match the owner of
419 /// file, and the caller is not privileged (Linux: does not have
420 /// the CAP_FOWNER capability); or,
421 /// * the file is marked append-only or immutable (see chattr(1)).
422 PermissionDenied,
423 ReadOnlyFileSystem,
424} || Io.Cancelable || Io.UnexpectedError;
425
426pub const SetTimestampsOptions = struct {
427 access_timestamp: SetTimestamp = .unchanged,
428 modify_timestamp: SetTimestamp = .unchanged,
429};
430
431pub const SetTimestamp = union(enum) {
432 /// Leave the existing timestamp unmodified.
433 unchanged,
434 /// Set to current time using `Io.Clock.real`.
435 now,
436 /// Set to provided timestamp using `Io.Clock.real`.
437 new: Io.Timestamp,
438
439 /// Convenience for interacting with `Stat`, in which `null` indicates `unchanged`.
440 pub fn init(optional: ?Io.Timestamp) SetTimestamp {
441 return if (optional) |t| .{ .new = t } else .unchanged;
442 }
443};
444
445/// The granularity that ultimately is stored depends on the combination of
446/// operating system and file system. When a value as provided that exceeds
447/// this range, the value is clamped to the maximum.
448pub fn setTimestamps(file: File, io: Io, options: SetTimestampsOptions) SetTimestampsError!void {
449 return io.vtable.fileSetTimestamps(io.userdata, file, options);
450}
451
452/// Sets the accessed and modification timestamps of `file` to the current wall
453/// clock time.
454///
455/// The granularity that ultimately is stored depends on the combination of
456/// operating system and file system.
457pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {
458 return io.vtable.fileSetTimestamps(io.userdata, file, .{
459 .access_timestamp = .now,
460 .modify_timestamp = .now,
461 });
462}
463
464pub const ReadStreamingError = error{EndOfStream} || Reader.Error;
465
466/// May return fewer bytes than buffer space available, including 0.
467/// End-of-stream is indicated by `error.EndOfStream`.
468///
469/// See also:
470/// * `reader`
471pub fn readStreaming(file: File, io: Io, buffer: []const []u8) ReadStreamingError!usize {
472 return (try io.operate(.{ .file_read_streaming = .{
473 .file = file,
474 .data = buffer,
475 } })).file_read_streaming;
476}
477
478pub const ReadPositionalError = error{
479 InputOutput,
480 SystemResources,
481 /// Trying to read a directory file descriptor as if it were a file.
482 IsDir,
483 /// Non-blocking has been enabled, and reading from the file descriptor
484 /// would block.
485 WouldBlock,
486 /// In WASI, this error occurs when the file descriptor does
487 /// not hold the required rights to read from it.
488 AccessDenied,
489 /// Unable to read file due to lock. Depending on the `Io` implementation,
490 /// reading from a locked file may return this error, or may ignore the
491 /// lock.
492 LockViolation,
493 /// This file cannot be read positionally.
494 Unseekable,
495 /// File was not opened with read capability.
496 NotOpenForReading,
497} || Io.Cancelable || Io.UnexpectedError;
498
499/// Returns 0 on stream end or if `buffer` has no space available for data.
500///
501/// See also:
502/// * `reader`
503pub fn readPositional(file: File, io: Io, buffer: []const []u8, offset: u64) ReadPositionalError!usize {
504 return io.vtable.fileReadPositional(io.userdata, file, buffer, offset);
505}
506
507pub const WritePositionalError = error{
508 DiskQuota,
509 FileTooBig,
510 InputOutput,
511 NoSpaceLeft,
512 DeviceBusy,
513 /// File descriptor does not hold the required rights to write to it.
514 AccessDenied,
515 PermissionDenied,
516 /// File is an unconnected socket, or closed its read end.
517 BrokenPipe,
518 /// Insufficient kernel memory to read from in_fd.
519 SystemResources,
520 /// The process cannot access the file because another process has locked
521 /// a portion of the file. Windows-only.
522 LockViolation,
523 /// Non-blocking has been enabled and this operation would block.
524 WouldBlock,
525 /// This error occurs when a device gets disconnected before or mid-flush
526 /// while it's being written to - errno(6): No such device or address.
527 NoDevice,
528 FileBusy,
529 /// This file cannot be written positionally.
530 Unseekable,
531 /// File was not opened with write capability.
532 NotOpenForWriting,
533} || Io.Cancelable || Io.UnexpectedError;
534
535/// See also:
536/// * `writer`
537pub fn writePositional(file: File, io: Io, buffer: []const []const u8, offset: u64) WritePositionalError!usize {
538 return io.vtable.fileWritePositional(io.userdata, file, &.{}, buffer, 1, offset);
539}
540
541/// Equivalent to creating a positional writer, writing `bytes`, and then flushing.
542pub fn writePositionalAll(file: File, io: Io, bytes: []const u8, offset: u64) WritePositionalError!void {
543 var index: usize = 0;
544 while (index < bytes.len)
545 index += try io.vtable.fileWritePositional(io.userdata, file, &.{}, &.{bytes[index..]}, 1, offset + index);
546}
547
548pub const SeekError = error{
549 Unseekable,
550 /// The file descriptor does not hold the required rights to seek on it.
551 AccessDenied,
552} || Io.Cancelable || Io.UnexpectedError;
553
554pub const WriteFilePositionalError = Writer.WriteFileError || error{Unseekable};
555
556/// Defaults to positional reading; falls back to streaming.
557///
558/// Positional is more threadsafe, since the global seek position is not
559/// affected.
560///
561/// See also:
562/// * `readerStreaming`
563pub fn reader(file: File, io: Io, buffer: []u8) Reader {
564 return .init(file, io, buffer);
565}
566
567/// Equivalent to creating a positional reader and reading multiple times to fill `buffer`.
568///
569/// Returns number of bytes read into `buffer`. If less than `buffer.len`, end of file occurred.
570///
571/// See also:
572/// * `reader`
573pub fn readPositionalAll(file: File, io: Io, buffer: []u8, offset: u64) ReadPositionalError!usize {
574 var index: usize = 0;
575 while (index != buffer.len) {
576 const amt = try file.readPositional(io, &.{buffer[index..]}, offset + index);
577 if (amt == 0) break;
578 index += amt;
579 }
580 return index;
581}
582
583/// Positional is more threadsafe, since the global seek position is not
584/// affected, but when such syscalls are not available, preemptively
585/// initializing in streaming mode skips a failed syscall.
586///
587/// See also:
588/// * `reader`
589pub fn readerStreaming(file: File, io: Io, buffer: []u8) Reader {
590 return .initStreaming(file, io, buffer);
591}
592
593/// Defaults to positional reading; falls back to streaming.
594///
595/// Positional is more threadsafe, since the global seek position is not
596/// affected.
597pub fn writer(file: File, io: Io, buffer: []u8) Writer {
598 return .init(file, io, buffer);
599}
600
601/// Positional is more threadsafe, since the global seek position is not
602/// affected, but when such syscalls are not available, preemptively
603/// initializing in streaming mode will skip a failed syscall.
604pub fn writerStreaming(file: File, io: Io, buffer: []u8) Writer {
605 return .initStreaming(file, io, buffer);
606}
607
608/// This is a low-level API that calls the `Io` interface function directly.
609/// For a higher level API, see `writerStreaming`.
610pub fn writeStreaming(file: File, io: Io, header: []const u8, data: []const []const u8, splat: usize) Writer.Error!usize {
611 return (try io.operate(.{ .file_write_streaming = .{
612 .file = file,
613 .header = header,
614 .data = data,
615 .splat = splat,
616 } })).file_write_streaming;
617}
618
619/// Equivalent to creating a streaming writer, writing `bytes`, and then flushing.
620pub fn writeStreamingAll(file: File, io: Io, bytes: []const u8) Writer.Error!void {
621 var index: usize = 0;
622 while (index < bytes.len) {
623 index += try writeStreaming(file, io, &.{}, &.{bytes[index..]}, 1);
624 }
625}
626
627pub const LockError = error{
628 SystemResources,
629 FileLocksUnsupported,
630} || Io.Cancelable || Io.UnexpectedError;
631
632/// Blocks when an incompatible lock is held by another process. A process may
633/// hold only one type of lock (shared or exclusive) on a file. When a process
634/// terminates in any way, the lock is released.
635///
636/// Assumes the file is unlocked.
637pub fn lock(file: File, io: Io, l: Lock) LockError!void {
638 return io.vtable.fileLock(io.userdata, file, l);
639}
640
641/// Assumes the file is locked.
642pub fn unlock(file: File, io: Io) void {
643 return io.vtable.fileUnlock(io.userdata, file);
644}
645
646/// Attempts to obtain a lock, returning `true` if the lock is obtained, and
647/// `false` if there was an existing incompatible lock held. A process may hold
648/// only one type of lock (shared or exclusive) on a file. When a process
649/// terminates in any way, the lock is released.
650///
651/// Assumes the file is unlocked.
652pub fn tryLock(file: File, io: Io, l: Lock) LockError!bool {
653 return io.vtable.fileTryLock(io.userdata, file, l);
654}
655
656pub const DowngradeLockError = Io.Cancelable || Io.UnexpectedError;
657
658/// Assumes the file is already locked in exclusive mode.
659/// Atomically modifies the lock to be in shared mode, without releasing it.
660pub fn downgradeLock(file: File, io: Io) LockError!void {
661 return io.vtable.fileDowngradeLock(io.userdata, file);
662}
663
664pub const RealPathError = error{
665 /// This operating system, file system, or `Io` implementation does not
666 /// support realpath operations.
667 OperationUnsupported,
668 /// The full file system path could not fit into the provided buffer, or
669 /// due to its length could not be obtained via realpath functions no
670 /// matter the buffer size provided.
671 NameTooLong,
672 FileNotFound,
673 AccessDenied,
674 PermissionDenied,
675 NotDir,
676 SymLinkLoop,
677 InputOutput,
678 FileTooBig,
679 IsDir,
680 ProcessFdQuotaExceeded,
681 SystemFdQuotaExceeded,
682 NoDevice,
683 SystemResources,
684 NoSpaceLeft,
685 FileSystem,
686 DeviceBusy,
687 FileBusy,
688 PipeBusy,
689 /// On Windows, `\\server` or `\\server\share` was not found.
690 NetworkNotFound,
691 PathAlreadyExists,
692 /// On Windows, antivirus software is enabled by default. It can be
693 /// disabled, but Windows Update sometimes ignores the user's preference
694 /// and re-enables it. When enabled, antivirus software on Windows
695 /// intercepts file system operations and makes them significantly slower
696 /// in addition to possibly failing with this error code.
697 AntivirusInterference,
698 /// On Windows, the volume does not contain a recognized file system. File
699 /// system drivers might not be loaded, or the volume may be corrupt.
700 UnrecognizedVolume,
701} || Io.Cancelable || Io.UnexpectedError;
702
703/// Obtains the canonicalized absolute path name corresponding to an open file
704/// handle.
705///
706/// This function has limited platform support, and using it can lead to
707/// unnecessary failures and race conditions. It is generally advisable to
708/// avoid this function entirely.
709pub fn realPath(file: File, io: Io, out_buffer: []u8) RealPathError!usize {
710 return io.vtable.fileRealPath(io.userdata, file, out_buffer);
711}
712
713pub const HardLinkOptions = struct {
714 follow_symlinks: bool = false,
715};
716
717pub const HardLinkError = error{
718 AccessDenied,
719 PermissionDenied,
720 DiskQuota,
721 PathAlreadyExists,
722 HardwareFailure,
723 /// Either the OS or the filesystem does not support hard links.
724 OperationUnsupported,
725 SymLinkLoop,
726 LinkQuotaExceeded,
727 FileNotFound,
728 SystemResources,
729 NoSpaceLeft,
730 ReadOnlyFileSystem,
731 CrossDevice,
732 NotDir,
733} || Io.Cancelable || Dir.PathNameError || Io.UnexpectedError;
734
735pub fn hardLink(
736 file: File,
737 io: Io,
738 new_dir: Dir,
739 new_sub_path: []const u8,
740 options: HardLinkOptions,
741) HardLinkError!void {
742 return io.vtable.fileHardLink(io.userdata, file, new_dir, new_sub_path, options);
743}
744
745pub fn createMemoryMap(file: File, io: Io, options: MemoryMap.CreateOptions) MemoryMap.CreateError!MemoryMap {
746 return .create(io, file, options);
747}
748
749test {
750 _ = Reader;
751 _ = Writer;
752 _ = Atomic;
753 _ = MemoryMap;
754}