| ... | ... | @@ -63,7 +63,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 63 | 63 | |
| 64 | 64 | const dirname = path.dirname(new_path) orelse "."; |
| 65 | 65 | |
| 66 | | var rand_buf: [12]u8 = undefined; |
| 66 | var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined; |
| 67 | 67 | const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); |
| 68 | 68 | defer allocator.free(tmp_path); |
| 69 | 69 | mem.copy(u8, tmp_path[0..], dirname); |
| ... | ... | @@ -118,38 +118,31 @@ pub fn copyFileAbsolute(source_path: []const u8, dest_path: []const u8, args: Co |
| 118 | 118 | /// TODO update this API to avoid a getrandom syscall for every operation. |
| 119 | 119 | pub const AtomicFile = struct { |
| 120 | 120 | file: File, |
| 121 | | tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8, |
| 121 | // TODO either replace this with rand_buf or use []u16 on Windows |
| 122 | tmp_path_buf: [TMP_PATH_LEN:0]u8, |
| 122 | 123 | dest_path: []const u8, |
| 123 | 124 | file_open: bool, |
| 124 | 125 | file_exists: bool, |
| 126 | close_dir_on_deinit: bool, |
| 125 | 127 | dir: Dir, |
| 126 | 128 | |
| 127 | 129 | const InitError = File.OpenError; |
| 128 | 130 | |
| 129 | | /// TODO rename this. Callers should go through Dir API |
| 130 | | pub fn init2(dest_path: []const u8, mode: File.Mode, dir: Dir) InitError!AtomicFile { |
| 131 | | const dirname = path.dirname(dest_path); |
| 132 | | var rand_buf: [12]u8 = undefined; |
| 133 | | const dirname_component_len = if (dirname) |d| d.len + 1 else 0; |
| 134 | | const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); |
| 135 | | const tmp_path_len = dirname_component_len + encoded_rand_len; |
| 136 | | var tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined; |
| 137 | | if (tmp_path_len > tmp_path_buf.len) return error.NameTooLong; |
| 138 | | |
| 139 | | if (dirname) |dn| { |
| 140 | | mem.copy(u8, tmp_path_buf[0..], dn); |
| 141 | | tmp_path_buf[dn.len] = path.sep; |
| 142 | | } |
| 131 | const RANDOM_BYTES = 12; |
| 132 | const TMP_PATH_LEN = base64.Base64Encoder.calcSize(RANDOM_BYTES); |
| 143 | 133 | |
| 144 | | tmp_path_buf[tmp_path_len] = 0; |
| 145 | | const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0]; |
| 134 | /// TODO rename this. Callers should go through Dir API |
| 135 | pub fn init2(dest_path: []const u8, mode: File.Mode, dir: Dir, close_dir_on_deinit: bool) InitError!AtomicFile { |
| 136 | var rand_buf: [RANDOM_BYTES]u8 = undefined; |
| 137 | var tmp_path_buf: [TMP_PATH_LEN:0]u8 = undefined; |
| 138 | tmp_path_buf[base64.Base64Encoder.calcSize(RANDOM_BYTES)] = 0; |
| 146 | 139 | |
| 147 | 140 | while (true) { |
| 148 | 141 | try crypto.randomBytes(rand_buf[0..]); |
| 149 | | base64_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf); |
| 142 | base64_encoder.encode(&tmp_path_buf, &rand_buf); |
| 150 | 143 | |
| 151 | 144 | const file = dir.createFileC( |
| 152 | | tmp_path_slice, |
| 145 | &tmp_path_buf, |
| 153 | 146 | .{ .mode = mode, .exclusive = true }, |
| 154 | 147 | ) catch |err| switch (err) { |
| 155 | 148 | error.PathAlreadyExists => continue, |
| ... | ... | @@ -162,6 +155,7 @@ pub const AtomicFile = struct { |
| 162 | 155 | .dest_path = dest_path, |
| 163 | 156 | .file_open = true, |
| 164 | 157 | .file_exists = true, |
| 158 | .close_dir_on_deinit = close_dir_on_deinit, |
| 165 | 159 | .dir = dir, |
| 166 | 160 | }; |
| 167 | 161 | } |
| ... | ... | @@ -169,7 +163,7 @@ pub const AtomicFile = struct { |
| 169 | 163 | |
| 170 | 164 | /// Deprecated. Use `Dir.atomicFile`. |
| 171 | 165 | pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { |
| 172 | | return init2(dest_path, mode, cwd()); |
| 166 | return cwd().atomicFile(dest_path, .{ .mode = mode }); |
| 173 | 167 | } |
| 174 | 168 | |
| 175 | 169 | /// always call deinit, even after successful finish() |
| ... | ... | @@ -182,6 +176,9 @@ pub const AtomicFile = struct { |
| 182 | 176 | self.dir.deleteFileC(&self.tmp_path_buf) catch {}; |
| 183 | 177 | self.file_exists = false; |
| 184 | 178 | } |
| 179 | if (self.close_dir_on_deinit) { |
| 180 | self.dir.close(); |
| 181 | } |
| 185 | 182 | self.* = undefined; |
| 186 | 183 | } |
| 187 | 184 | |
| ... | ... | @@ -1281,7 +1278,12 @@ pub const Dir = struct { |
| 1281 | 1278 | /// `dest_path` must remain valid for the lifetime of `AtomicFile`. |
| 1282 | 1279 | /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents. |
| 1283 | 1280 | pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile { |
| 1284 | | return AtomicFile.init2(dest_path, options.mode, self); |
| 1281 | if (path.dirname(dest_path)) |dirname| { |
| 1282 | const dir = try self.openDir(dirname, .{}); |
| 1283 | return AtomicFile.init2(path.basename(dest_path), options.mode, dir, true); |
| 1284 | } else { |
| 1285 | return AtomicFile.init2(dest_path, options.mode, self, false); |
| 1286 | } |
| 1285 | 1287 | } |
| 1286 | 1288 | }; |
| 1287 | 1289 | |