feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.dirCreateFileAtomic
fn dirCreateFileAtomic(
userdata: ?*anyopaque,
dir: Dir,
dest_path: []const u8,
options: Dir.CreateFileAtomicOptions,
) Dir.CreateFileAtomicError!File.Atomic
File
Code
fn dirCreateFileAtomic(
userdata: ?*anyopaque,
dir: Dir,
dest_path: []const u8,
options: Dir.CreateFileAtomicOptions,
) Dir.CreateFileAtomicError!File.Atomic {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const t_io = io(t);
// useless when we have to make up a bogus path name to do the rename()
// anyway.
if (native_os == .linux and !options.replace) tmpfile: {
const flags: posix.O = if (@hasField(posix.O, "TMPFILE")) .{
.ACCMODE = .RDWR,
.TMPFILE = true,
.DIRECTORY = true,
.CLOEXEC = true,
} else if (@hasField(posix.O, "TMPFILE0") and !@hasField(posix.O, "TMPFILE2")) .{
.ACCMODE = .RDWR,
.TMPFILE0 = true,
.TMPFILE1 = true,
.DIRECTORY = true,
.CLOEXEC = true,
} else break :tmpfile;
const dest_dirname = Dir.path.dirname(dest_path);
if (dest_dirname) |dirname| {
// ENOENT, avoiding the ambiguity below.
if (options.make_path) dir.createDirPath(t_io, dirname) catch |err| switch (err) {
error.IsDir,
error.Streaming,
error.DiskQuota,
error.PathAlreadyExists,
error.LinkQuotaExceeded,
error.PipeBusy,
error.FileTooBig,
error.DeviceBusy,
error.FileLocksUnsupported,
error.FileBusy,
=> return error.Unexpected,
else => |e| return e,
};
}
var path_buffer: [posix.PATH_MAX]u8 = undefined;
const sub_path_posix = try pathToPosix(dest_dirname orelse ".", &path_buffer);
const syscall: Syscall = try .start();
while (true) {
const rc = openat_sym(dir.handle, sub_path_posix, flags, options.permissions.toMode());
switch (posix.errno(rc)) {
.SUCCESS => {
syscall.finish();
return .{
.file = .{
.handle = @intCast(rc),
.flags = .{ .nonblocking = false },
},
.file_basename_hex = 0,
.dest_sub_path = dest_path,
.file_open = true,
.file_exists = false,
.close_dir_on_deinit = false,
.dir = dir,
};
},
.INTR => {
try syscall.checkCancel();
continue;
},
.ISDIR, .NOENT, .OPNOTSUPP => {
// does not support O_TMPFILE. Therefore, we must fall
// back to not using O_TMPFILE.
syscall.finish();
break :tmpfile;
},
.INVAL => return syscall.fail(error.BadPathName),
.ACCES => return syscall.fail(error.AccessDenied),
.LOOP => return syscall.fail(error.SymLinkLoop),
.MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
.NAMETOOLONG => return syscall.fail(error.NameTooLong),
.NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
.NODEV => return syscall.fail(error.NoDevice),
.NOMEM => return syscall.fail(error.SystemResources),
.NOSPC => return syscall.fail(error.NoSpaceLeft),
.NOTDIR => return syscall.fail(error.NotDir),
.PERM => return syscall.fail(error.PermissionDenied),
.AGAIN => return syscall.fail(error.WouldBlock),
.NXIO => return syscall.fail(error.NoDevice),
.ILSEQ => return syscall.fail(error.BadPathName),
else => |err| return syscall.unexpectedErrno(err),
}
}
}
if (Dir.path.dirname(dest_path)) |dirname| {
const new_dir = if (options.make_path)
dir.createDirPathOpen(t_io, dirname, .{}) catch |err| switch (err) {
error.IsDir,
error.Streaming,
error.DiskQuota,
error.PathAlreadyExists,
error.LinkQuotaExceeded,
error.PipeBusy,
error.FileTooBig,
error.FileLocksUnsupported,
error.DeviceBusy,
=> return error.Unexpected,
else => |e| return e,
}
else
try dir.openDir(t_io, dirname, .{});
return atomicFileInit(t_io, Dir.path.basename(dest_path), options.permissions, new_dir, true);
}
return atomicFileInit(t_io, dest_path, options.permissions, dir, false);
}