| ... | @@ -104,30 +104,17 @@ pub fn getRandomBytes(buf: []u8) !void { | ... | @@ -104,30 +104,17 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 104 | Os.linux => while (true) { | 104 | Os.linux => while (true) { |
| 105 | // TODO check libc version and potentially call c.getrandom. | 105 | // TODO check libc version and potentially call c.getrandom. |
| 106 | // See #397 | 106 | // See #397 |
| 107 | const err = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); | 107 | const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); |
| 108 | if (err > 0) { | 108 | switch (errno) { |
| 109 | switch (err) { | 109 | 0 => return, |
| 110 | posix.EINVAL => unreachable, | 110 | posix.EINVAL => unreachable, |
| 111 | posix.EFAULT => unreachable, | 111 | posix.EFAULT => unreachable, |
| 112 | posix.EINTR => continue, | 112 | posix.EINTR => continue, |
| 113 | posix.ENOSYS => { | 113 | posix.ENOSYS => return getRandomBytesDevURandom(buf), |
| 114 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); | 114 | else => return unexpectedErrorPosix(errno), |
| 115 | defer close(fd); | | |
| 116 | | | |
| 117 | try posixRead(fd, buf); | | |
| 118 | return; | | |
| 119 | }, | | |
| 120 | else => return unexpectedErrorPosix(err), | | |
| 121 | } | | |
| 122 | } | 115 | } |
| 123 | return; | | |
| 124 | }, | | |
| 125 | Os.macosx, Os.ios => { | | |
| 126 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); | | |
| 127 | defer close(fd); | | |
| 128 | | | |
| 129 | try posixRead(fd, buf); | | |
| 130 | }, | 116 | }, |
| | 117 | Os.macosx, Os.ios => return getRandomBytesDevURandom(buf), |
| 131 | Os.windows => { | 118 | Os.windows => { |
| 132 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows | 119 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows |
| 133 | // https://github.com/rust-lang-nursery/rand/issues/111 | 120 | // https://github.com/rust-lang-nursery/rand/issues/111 |
| ... | @@ -151,6 +138,22 @@ pub fn getRandomBytes(buf: []u8) !void { | ... | @@ -151,6 +138,22 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 151 | } | 138 | } |
| 152 | } | 139 | } |
| 153 | | 140 | |
| | 141 | fn getRandomBytesDevURandom(buf: []u8) !void { |
| | 142 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); |
| | 143 | defer close(fd); |
| | 144 | |
| | 145 | const stream = &File.openHandle(fd).inStream().stream; |
| | 146 | stream.readNoEof(buf) catch |err| switch (err) { |
| | 147 | error.EndOfStream => unreachable, |
| | 148 | error.OperationAborted => unreachable, |
| | 149 | error.BrokenPipe => unreachable, |
| | 150 | error.Unexpected => return error.Unexpected, |
| | 151 | error.InputOutput => return error.Unexpected, |
| | 152 | error.SystemResources => return error.Unexpected, |
| | 153 | error.IsDir => unreachable, |
| | 154 | }; |
| | 155 | } |
| | 156 | |
| 154 | test "os.getRandomBytes" { | 157 | test "os.getRandomBytes" { |
| 155 | var buf_a: [50]u8 = undefined; | 158 | var buf_a: [50]u8 = undefined; |
| 156 | var buf_b: [50]u8 = undefined; | 159 | var buf_b: [50]u8 = undefined; |
| ... | @@ -235,8 +238,9 @@ pub const PosixReadError = error{ | ... | @@ -235,8 +238,9 @@ pub const PosixReadError = error{ |
| 235 | Unexpected, | 238 | Unexpected, |
| 236 | }; | 239 | }; |
| 237 | | 240 | |
| 238 | /// Calls POSIX read, and keeps trying if it gets interrupted. | 241 | /// Returns the number of bytes that were read, which can be less than |
| 239 | pub fn posixRead(fd: i32, buf: []u8) !void { | 242 | /// buf.len. If 0 bytes were read, that means EOF. |
| | 243 | pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { |
| 240 | // Linux can return EINVAL when read amount is > 0x7ffff000 | 244 | // Linux can return EINVAL when read amount is > 0x7ffff000 |
| 241 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 | 245 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 |
| 242 | const max_buf_len = 0x7ffff000; | 246 | const max_buf_len = 0x7ffff000; |
| ... | @@ -249,7 +253,9 @@ pub fn posixRead(fd: i32, buf: []u8) !void { | ... | @@ -249,7 +253,9 @@ pub fn posixRead(fd: i32, buf: []u8) !void { |
| 249 | switch (err) { | 253 | switch (err) { |
| 250 | 0 => { | 254 | 0 => { |
| 251 | index += rc; | 255 | index += rc; |
| 252 | continue; | 256 | if (rc == want_to_read) continue; |
| | 257 | // Read returned less than buf.len. |
| | 258 | return index; |
| 253 | }, | 259 | }, |
| 254 | posix.EINTR => continue, | 260 | posix.EINTR => continue, |
| 255 | posix.EINVAL => unreachable, | 261 | posix.EINVAL => unreachable, |
| ... | @@ -263,6 +269,7 @@ pub fn posixRead(fd: i32, buf: []u8) !void { | ... | @@ -263,6 +269,7 @@ pub fn posixRead(fd: i32, buf: []u8) !void { |
| 263 | else => return unexpectedErrorPosix(err), | 269 | else => return unexpectedErrorPosix(err), |
| 264 | } | 270 | } |
| 265 | } | 271 | } |
| | 272 | return index; |
| 266 | } | 273 | } |
| 267 | | 274 | |
| 268 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | 275 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. |
| ... | @@ -962,16 +969,16 @@ pub const DeleteFileError = error{ | ... | @@ -962,16 +969,16 @@ pub const DeleteFileError = error{ |
| 962 | | 969 | |
| 963 | pub fn deleteFile(file_path: []const u8) DeleteFileError!void { | 970 | pub fn deleteFile(file_path: []const u8) DeleteFileError!void { |
| 964 | if (builtin.os == Os.windows) { | 971 | if (builtin.os == Os.windows) { |
| 965 | return deleteFileWindows(file_path); | 972 | const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); |
| | 973 | return deleteFileW(&file_path_w); |
| 966 | } else { | 974 | } else { |
| 967 | return deleteFilePosix(file_path); | 975 | const file_path_c = try toPosixPath(file_path); |
| | 976 | return deleteFileC(&file_path_c); |
| 968 | } | 977 | } |
| 969 | } | 978 | } |
| 970 | | 979 | |
| 971 | pub fn deleteFileWindows(file_path: []const u8) !void { | 980 | pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { |
| 972 | const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); | 981 | if (windows.DeleteFileW(file_path) == 0) { |
| 973 | | | |
| 974 | if (windows.DeleteFileW(&file_path_w) == 0) { | | |
| 975 | const err = windows.GetLastError(); | 982 | const err = windows.GetLastError(); |
| 976 | switch (err) { | 983 | switch (err) { |
| 977 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | 984 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, |
| ... | @@ -983,50 +990,49 @@ pub fn deleteFileWindows(file_path: []const u8) !void { | ... | @@ -983,50 +990,49 @@ pub fn deleteFileWindows(file_path: []const u8) !void { |
| 983 | } | 990 | } |
| 984 | } | 991 | } |
| 985 | | 992 | |
| 986 | pub fn deleteFilePosixC(file_path: [*]const u8) !void { | 993 | pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { |
| 987 | const err = posix.getErrno(posix.unlink(file_path)); | 994 | if (is_windows) { |
| 988 | switch (err) { | 995 | const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); |
| 989 | 0 => return, | 996 | return deleteFileW(&file_path_w); |
| 990 | posix.EACCES => return error.AccessDenied, | 997 | } else { |
| 991 | posix.EPERM => return error.AccessDenied, | 998 | const err = posix.getErrno(posix.unlink(file_path)); |
| 992 | posix.EBUSY => return error.FileBusy, | 999 | switch (err) { |
| 993 | posix.EFAULT => unreachable, | 1000 | 0 => return, |
| 994 | posix.EINVAL => unreachable, | 1001 | posix.EACCES => return error.AccessDenied, |
| 995 | posix.EIO => return error.FileSystem, | 1002 | posix.EPERM => return error.AccessDenied, |
| 996 | posix.EISDIR => return error.IsDir, | 1003 | posix.EBUSY => return error.FileBusy, |
| 997 | posix.ELOOP => return error.SymLinkLoop, | 1004 | posix.EFAULT => unreachable, |
| 998 | posix.ENAMETOOLONG => return error.NameTooLong, | 1005 | posix.EINVAL => unreachable, |
| 999 | posix.ENOENT => return error.FileNotFound, | 1006 | posix.EIO => return error.FileSystem, |
| 1000 | posix.ENOTDIR => return error.NotDir, | 1007 | posix.EISDIR => return error.IsDir, |
| 1001 | posix.ENOMEM => return error.SystemResources, | 1008 | posix.ELOOP => return error.SymLinkLoop, |
| 1002 | posix.EROFS => return error.ReadOnlyFileSystem, | 1009 | posix.ENAMETOOLONG => return error.NameTooLong, |
| 1003 | else => return unexpectedErrorPosix(err), | 1010 | posix.ENOENT => return error.FileNotFound, |
| | 1011 | posix.ENOTDIR => return error.NotDir, |
| | 1012 | posix.ENOMEM => return error.SystemResources, |
| | 1013 | posix.EROFS => return error.ReadOnlyFileSystem, |
| | 1014 | else => return unexpectedErrorPosix(err), |
| | 1015 | } |
| 1004 | } | 1016 | } |
| 1005 | } | 1017 | } |
| 1006 | | 1018 | |
| 1007 | pub fn deleteFilePosix(file_path: []const u8) !void { | | |
| 1008 | const file_path_c = try toPosixPath(file_path); | | |
| 1009 | return deleteFilePosixC(&file_path_c); | | |
| 1010 | } | | |
| 1011 | | | |
| 1012 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | 1019 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is |
| 1013 | /// merged and readily available, | 1020 | /// merged and readily available, |
| 1014 | /// there is a possibility of power loss or application termination leaving temporary files present | 1021 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 1015 | /// in the same directory as dest_path. | 1022 | /// in the same directory as dest_path. |
| 1016 | /// Destination file will have the same mode as the source file. | 1023 | /// Destination file will have the same mode as the source file. |
| 1017 | /// TODO investigate if this can work with no allocator | 1024 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { |
| 1018 | pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void { | | |
| 1019 | var in_file = try os.File.openRead(source_path); | 1025 | var in_file = try os.File.openRead(source_path); |
| 1020 | defer in_file.close(); | 1026 | defer in_file.close(); |
| 1021 | | 1027 | |
| 1022 | const mode = try in_file.mode(); | 1028 | const mode = try in_file.mode(); |
| 1023 | | 1029 | |
| 1024 | var atomic_file = try AtomicFile.init(allocator, dest_path, mode); | 1030 | var atomic_file = try AtomicFile.init(dest_path, mode); |
| 1025 | defer atomic_file.deinit(); | 1031 | defer atomic_file.deinit(); |
| 1026 | | 1032 | |
| 1027 | var buf: [page_size]u8 = undefined; | 1033 | var buf: [page_size]u8 = undefined; |
| 1028 | while (true) { | 1034 | while (true) { |
| 1029 | const amt = try in_file.read(buf[0..]); | 1035 | const amt = try in_file.readFull(buf[0..]); |
| 1030 | try atomic_file.file.write(buf[0..amt]); | 1036 | try atomic_file.file.write(buf[0..amt]); |
| 1031 | if (amt != buf.len) { | 1037 | if (amt != buf.len) { |
| 1032 | return atomic_file.finish(); | 1038 | return atomic_file.finish(); |
| ... | @@ -1037,12 +1043,11 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con | ... | @@ -1037,12 +1043,11 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con |
| 1037 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | 1043 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is |
| 1038 | /// merged and readily available, | 1044 | /// merged and readily available, |
| 1039 | /// there is a possibility of power loss or application termination leaving temporary files present | 1045 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 1040 | /// TODO investigate if this can work with no allocator | 1046 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { |
| 1041 | pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | | |
| 1042 | var in_file = try os.File.openRead(source_path); | 1047 | var in_file = try os.File.openRead(source_path); |
| 1043 | defer in_file.close(); | 1048 | defer in_file.close(); |
| 1044 | | 1049 | |
| 1045 | var atomic_file = try AtomicFile.init(allocator, dest_path, mode); | 1050 | var atomic_file = try AtomicFile.init(dest_path, mode); |
| 1046 | defer atomic_file.deinit(); | 1051 | defer atomic_file.deinit(); |
| 1047 | | 1052 | |
| 1048 | var buf: [page_size]u8 = undefined; | 1053 | var buf: [page_size]u8 = undefined; |
| ... | @@ -1056,35 +1061,38 @@ pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: [ | ... | @@ -1056,35 +1061,38 @@ pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: [ |
| 1056 | } | 1061 | } |
| 1057 | | 1062 | |
| 1058 | pub const AtomicFile = struct { | 1063 | pub const AtomicFile = struct { |
| 1059 | /// TODO investigate if we can make this work with no allocator | | |
| 1060 | allocator: *Allocator, | | |
| 1061 | file: os.File, | 1064 | file: os.File, |
| 1062 | tmp_path: []u8, | 1065 | tmp_path_buf: [MAX_PATH_BYTES]u8, |
| 1063 | dest_path: []const u8, | 1066 | dest_path: []const u8, |
| 1064 | finished: bool, | 1067 | finished: bool, |
| 1065 | | 1068 | |
| | 1069 | const InitError = os.File.OpenError; |
| | 1070 | |
| 1066 | /// dest_path must remain valid for the lifetime of AtomicFile | 1071 | /// dest_path must remain valid for the lifetime of AtomicFile |
| 1067 | /// call finish to atomically replace dest_path with contents | 1072 | /// call finish to atomically replace dest_path with contents |
| 1068 | pub fn init(allocator: *Allocator, dest_path: []const u8, mode: File.Mode) !AtomicFile { | 1073 | /// TODO once we have null terminated pointers, use the |
| | 1074 | /// openWriteNoClobberN function |
| | 1075 | pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { |
| 1069 | const dirname = os.path.dirname(dest_path); | 1076 | const dirname = os.path.dirname(dest_path); |
| 1070 | | | |
| 1071 | var rand_buf: [12]u8 = undefined; | 1077 | var rand_buf: [12]u8 = undefined; |
| 1072 | | | |
| 1073 | const dirname_component_len = if (dirname) |d| d.len + 1 else 0; | 1078 | const dirname_component_len = if (dirname) |d| d.len + 1 else 0; |
| 1074 | const tmp_path = try allocator.alloc(u8, dirname_component_len + | 1079 | const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); |
| 1075 | base64.Base64Encoder.calcSize(rand_buf.len)); | 1080 | const tmp_path_len = dirname_component_len + encoded_rand_len; |
| 1076 | errdefer allocator.free(tmp_path); | 1081 | var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 1082 | if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; |
| 1077 | | 1083 | |
| 1078 | if (dirname) |dir| { | 1084 | if (dirname) |dir| { |
| 1079 | mem.copy(u8, tmp_path[0..], dir); | 1085 | mem.copy(u8, tmp_path_buf[0..], dir); |
| 1080 | tmp_path[dir.len] = os.path.sep; | 1086 | tmp_path_buf[dir.len] = os.path.sep; |
| 1081 | } | 1087 | } |
| 1082 | | 1088 | |
| | 1089 | tmp_path_buf[tmp_path_len] = 0; |
| | 1090 | |
| 1083 | while (true) { | 1091 | while (true) { |
| 1084 | try getRandomBytes(rand_buf[0..]); | 1092 | try getRandomBytes(rand_buf[0..]); |
| 1085 | b64_fs_encoder.encode(tmp_path[dirname_component_len..], rand_buf); | 1093 | b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); |
| 1086 | | 1094 | |
| 1087 | const file = os.File.openWriteNoClobber(tmp_path, mode) catch |err| switch (err) { | 1095 | const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) { |
| 1088 | error.PathAlreadyExists => continue, | 1096 | error.PathAlreadyExists => continue, |
| 1089 | // TODO zig should figure out that this error set does not include PathAlreadyExists since | 1097 | // TODO zig should figure out that this error set does not include PathAlreadyExists since |
| 1090 | // it is handled in the above switch | 1098 | // it is handled in the above switch |
| ... | @@ -1092,9 +1100,8 @@ pub const AtomicFile = struct { | ... | @@ -1092,9 +1100,8 @@ pub const AtomicFile = struct { |
| 1092 | }; | 1100 | }; |
| 1093 | | 1101 | |
| 1094 | return AtomicFile{ | 1102 | return AtomicFile{ |
| 1095 | .allocator = allocator, | | |
| 1096 | .file = file, | 1103 | .file = file, |
| 1097 | .tmp_path = tmp_path, | 1104 | .tmp_path_buf = tmp_path_buf, |
| 1098 | .dest_path = dest_path, | 1105 | .dest_path = dest_path, |
| 1099 | .finished = false, | 1106 | .finished = false, |
| 1100 | }; | 1107 | }; |
| ... | @@ -1105,8 +1112,7 @@ pub const AtomicFile = struct { | ... | @@ -1105,8 +1112,7 @@ pub const AtomicFile = struct { |
| 1105 | pub fn deinit(self: *AtomicFile) void { | 1112 | pub fn deinit(self: *AtomicFile) void { |
| 1106 | if (!self.finished) { | 1113 | if (!self.finished) { |
| 1107 | self.file.close(); | 1114 | self.file.close(); |
| 1108 | deleteFile(self.tmp_path) catch {}; | 1115 | deleteFileC(&self.tmp_path_buf) catch {}; |
| 1109 | self.allocator.free(self.tmp_path); | | |
| 1110 | self.finished = true; | 1116 | self.finished = true; |
| 1111 | } | 1117 | } |
| 1112 | } | 1118 | } |
| ... | @@ -1114,15 +1120,25 @@ pub const AtomicFile = struct { | ... | @@ -1114,15 +1120,25 @@ pub const AtomicFile = struct { |
| 1114 | pub fn finish(self: *AtomicFile) !void { | 1120 | pub fn finish(self: *AtomicFile) !void { |
| 1115 | assert(!self.finished); | 1121 | assert(!self.finished); |
| 1116 | self.file.close(); | 1122 | self.file.close(); |
| 1117 | try rename(self.tmp_path, self.dest_path); | | |
| 1118 | self.allocator.free(self.tmp_path); | | |
| 1119 | self.finished = true; | 1123 | self.finished = true; |
| | 1124 | if (is_posix) { |
| | 1125 | const dest_path_c = try toPosixPath(self.dest_path); |
| | 1126 | return renameC(&self.tmp_path_buf, &dest_path_c); |
| | 1127 | } else if (is_windows) { |
| | 1128 | const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path); |
| | 1129 | const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); |
| | 1130 | return renameW(&tmp_path_w, &dest_path_w); |
| | 1131 | } else { |
| | 1132 | @compileError("Unsupported OS"); |
| | 1133 | } |
| 1120 | } | 1134 | } |
| 1121 | }; | 1135 | }; |
| 1122 | | 1136 | |
| 1123 | pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { | 1137 | pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { |
| 1124 | if (is_windows) { | 1138 | if (is_windows) { |
| 1125 | @compileError("TODO implement for windows"); | 1139 | const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); |
| | 1140 | const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); |
| | 1141 | return renameW(&old_path_w, &new_path_w); |
| 1126 | } else { | 1142 | } else { |
| 1127 | const err = posix.getErrno(posix.rename(old_path, new_path)); | 1143 | const err = posix.getErrno(posix.rename(old_path, new_path)); |
| 1128 | switch (err) { | 1144 | switch (err) { |
| ... | @@ -1150,17 +1166,21 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { | ... | @@ -1150,17 +1166,21 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { |
| 1150 | } | 1166 | } |
| 1151 | } | 1167 | } |
| 1152 | | 1168 | |
| | 1169 | pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void { |
| | 1170 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; |
| | 1171 | if (windows.MoveFileExW(old_path, new_path, flags) == 0) { |
| | 1172 | const err = windows.GetLastError(); |
| | 1173 | switch (err) { |
| | 1174 | else => return unexpectedErrorWindows(err), |
| | 1175 | } |
| | 1176 | } |
| | 1177 | } |
| | 1178 | |
| 1153 | pub fn rename(old_path: []const u8, new_path: []const u8) !void { | 1179 | pub fn rename(old_path: []const u8, new_path: []const u8) !void { |
| 1154 | if (is_windows) { | 1180 | if (is_windows) { |
| 1155 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; | | |
| 1156 | const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); | 1181 | const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); |
| 1157 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | 1182 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); |
| 1158 | if (windows.MoveFileExW(&old_path_w, &new_path_w, flags) == 0) { | 1183 | return renameW(&old_path_w, &new_path_w); |
| 1159 | const err = windows.GetLastError(); | | |
| 1160 | switch (err) { | | |
| 1161 | else => return unexpectedErrorWindows(err), | | |
| 1162 | } | | |
| 1163 | } | | |
| 1164 | } else { | 1184 | } else { |
| 1165 | const old_path_c = try toPosixPath(old_path); | 1185 | const old_path_c = try toPosixPath(old_path); |
| 1166 | const new_path_c = try toPosixPath(new_path); | 1186 | const new_path_c = try toPosixPath(new_path); |