diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-03-13 21:06:07 -0400 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-03-13 21:22:08 -0400 |
| commit | 66e76a0209586000a78fe896071e73202a80b81f (patch) | |
| tree | 15b80516b0aaddd2e114205e41ee0cda49c32030 /lib/std/os.zig | |
| parent | eb4d313dbc406b37f6bfdd98988c88c3b8ed542e (diff) | |
| download | zig-66e76a0209586000a78fe896071e73202a80b81f.tar.gz zig-66e76a0209586000a78fe896071e73202a80b81f.zip | |
zig build system: correctly handle multiple output artifacts
Previously the zig build system incorrectly assumed that the only build
artifact was a binary. Now, when you enable the cache, only the output
dir is printed to stdout, and the zig build system iterates over the
files in that directory, copying them to the output directory.
To support this change:
* Add `std.os.renameat`, `std.os.renameatZ`, and `std.os.renameatW`.
* Fix `std.os.linux.renameat` not compiling due to typos.
* Deprecate `std.fs.updateFile` and `std.fs.updateFileMode`.
* Add `std.fs.Dir.updateFile`, which supports using open directory
handles for both the source and destination paths, as well as an
options parameter which allows overriding the mode.
* Update `std.fs.AtomicFile` to support operating based on an open
directory handle. Instead of `std.fs.AtomicFile.init`, use
`std.fs.Dir.atomicFile`.
* `std.fs.AtomicFile` deinit() better handles the situation when the
rename fails but the temporary file still exists, by still
attempting to remove the temporary file.
* `std.fs.Dir.openFileWindows` is moved to `std.os.windows.OpenFileW`.
* `std.os.RenameError` gains the error codes `NoDevice`,
`SharingViolation`, and `PipeBusy` which have been observed from
Windows.
Closes #4733
Diffstat (limited to 'lib/std/os.zig')
| -rw-r--r-- | lib/std/os.zig | 114 |
1 files changed, 111 insertions, 3 deletions
diff --git a/lib/std/os.zig b/lib/std/os.zig index ed99c48021..0186e1b0bf 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -461,13 +461,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { ); switch (rc) { - .SUCCESS => {}, + .SUCCESS => return, .INVALID_HANDLE => unreachable, // Handle not open for writing .ACCESS_DENIED => return error.CannotTruncate, else => return windows.unexpectedStatus(rc), } - - return; } while (true) { @@ -852,6 +850,7 @@ pub const OpenError = error{ /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `openC`. +/// TODO support windows pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { const file_path_c = try toPosixPath(file_path); return openC(&file_path_c, flags, perm); @@ -859,6 +858,7 @@ pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `open`. +/// TODO support windows pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t { while (true) { const rc = system.open(file_path, flags, perm); @@ -892,6 +892,7 @@ pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t { /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. /// See also `openatC`. +/// TODO support windows pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t { const file_path_c = try toPosixPath(file_path); return openatC(dir_fd, &file_path_c, flags, mode); @@ -900,6 +901,7 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. /// See also `openat`. +/// TODO support windows pub fn openatC(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t { while (true) { const rc = system.openat(dir_fd, file_path, flags, mode); @@ -1527,6 +1529,9 @@ const RenameError = error{ RenameAcrossMountPoints, InvalidUtf8, BadPathName, + NoDevice, + SharingViolation, + PipeBusy, } || UnexpectedError; /// Change the name or location of a file. @@ -1580,6 +1585,108 @@ pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!v return windows.MoveFileExW(old_path, new_path, flags); } +/// Change the name or location of a file based on an open directory handle. +pub fn renameat( + old_dir_fd: fd_t, + old_path: []const u8, + new_dir_fd: fd_t, + new_path: []const u8, +) RenameError!void { + if (builtin.os.tag == .windows) { + const old_path_w = try windows.sliceToPrefixedFileW(old_path); + const new_path_w = try windows.sliceToPrefixedFileW(new_path); + return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE); + } else { + const old_path_c = try toPosixPath(old_path); + const new_path_c = try toPosixPath(new_path); + return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c); + } +} + +/// Same as `renameat` except the parameters are null-terminated byte arrays. +pub fn renameatZ( + old_dir_fd: fd_t, + old_path: [*:0]const u8, + new_dir_fd: fd_t, + new_path: [*:0]const u8, +) RenameError!void { + if (builtin.os.tag == .windows) { + const old_path_w = try windows.cStrToPrefixedFileW(old_path); + const new_path_w = try windows.cStrToPrefixedFileW(new_path); + return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE); + } + + switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { + 0 => return, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EBUSY => return error.FileBusy, + EDQUOT => return error.DiskQuota, + EFAULT => unreachable, + EINVAL => unreachable, + EISDIR => return error.IsDir, + ELOOP => return error.SymLinkLoop, + EMLINK => return error.LinkQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + EEXIST => return error.PathAlreadyExists, + ENOTEMPTY => return error.PathAlreadyExists, + EROFS => return error.ReadOnlyFileSystem, + EXDEV => return error.RenameAcrossMountPoints, + else => |err| return unexpectedErrno(err), + } +} + +/// Same as `renameat` except the parameters are null-terminated UTF16LE encoded byte arrays. +/// Assumes target is Windows. +/// TODO these args can actually be slices when using ntdll. audit the rest of the W functions too. +pub fn renameatW( + old_dir_fd: fd_t, + old_path: [*:0]const u16, + new_dir_fd: fd_t, + new_path_w: [*:0]const u16, + ReplaceIfExists: windows.BOOLEAN, +) RenameError!void { + const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE; + const src_fd = try windows.OpenFileW(old_dir_fd, old_path, null, access_mask, windows.FILE_OPEN); + defer windows.CloseHandle(src_fd); + + const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); + var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined; + const new_path = mem.span(new_path_w); + const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path.len * 2; + if (struct_len > struct_buf_len) return error.NameTooLong; + + const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf); + + rename_info.* = .{ + .ReplaceIfExists = ReplaceIfExists, + .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(new_path_w)) null else new_dir_fd, + .FileNameLength = @intCast(u32, new_path.len * 2), // already checked error.NameTooLong + .FileName = undefined, + }; + std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path.len], new_path); + + var io_status_block: windows.IO_STATUS_BLOCK = undefined; + + const rc = windows.ntdll.NtSetInformationFile( + src_fd, + &io_status_block, + rename_info, + @intCast(u32, struct_len), // already checked for error.NameTooLong + .FileRenameInformation, + ); + + switch (rc) { + .SUCCESS => return, + .INVALID_HANDLE => unreachable, + else => return windows.unexpectedStatus(rc), + } +} + pub const MakeDirError = error{ AccessDenied, DiskQuota, @@ -3125,6 +3232,7 @@ pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP } /// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded. +/// TODO use ntdll for better semantics pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { const h_file = try windows.CreateFileW( pathname, |
