authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-26 20:32:06-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-03-26 20:32:06-04:00
logaec4967f36d16cfee43529bd341300f5f77af34a
tree6e4fc6faa7989a494b417b9584d20a97f07e0916
parent463b90b977ba4817f6cf90a85e8609092f53dfd2
parentcf4cbea88e2a2e56f71d721e9c686ab68dd19658
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4814 from gereeter/reduced-path-max

In AtomicFile, work relative to the destination's parent directory

1 files changed, 24 insertions(+), 22 deletions(-)

lib/std/fs.zig+24-22
......@@ -63,7 +63,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
6363
6464 const dirname = path.dirname(new_path) orelse ".";
6565
66 var rand_buf: [12]u8 = undefined;
66 var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined;
6767 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
6868 defer allocator.free(tmp_path);
6969 mem.copy(u8, tmp_path[0..], dirname);
......@@ -118,38 +118,31 @@ pub fn copyFileAbsolute(source_path: []const u8, dest_path: []const u8, args: Co
118118/// TODO update this API to avoid a getrandom syscall for every operation.
119119pub const AtomicFile = struct {
120120 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,
122123 dest_path: []const u8,
123124 file_open: bool,
124125 file_exists: bool,
126 close_dir_on_deinit: bool,
125127 dir: Dir,
126128
127129 const InitError = File.OpenError;
128130
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);
143133
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;
146139
147140 while (true) {
148141 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);
150143
151144 const file = dir.createFileC(
152 tmp_path_slice,
145 &tmp_path_buf,
153146 .{ .mode = mode, .exclusive = true },
154147 ) catch |err| switch (err) {
155148 error.PathAlreadyExists => continue,
......@@ -162,6 +155,7 @@ pub const AtomicFile = struct {
162155 .dest_path = dest_path,
163156 .file_open = true,
164157 .file_exists = true,
158 .close_dir_on_deinit = close_dir_on_deinit,
165159 .dir = dir,
166160 };
167161 }
......@@ -169,7 +163,7 @@ pub const AtomicFile = struct {
169163
170164 /// Deprecated. Use `Dir.atomicFile`.
171165 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 });
173167 }
174168
175169 /// always call deinit, even after successful finish()
......@@ -182,6 +176,9 @@ pub const AtomicFile = struct {
182176 self.dir.deleteFileC(&self.tmp_path_buf) catch {};
183177 self.file_exists = false;
184178 }
179 if (self.close_dir_on_deinit) {
180 self.dir.close();
181 }
185182 self.* = undefined;
186183 }
187184
......@@ -1281,7 +1278,12 @@ pub const Dir = struct {
12811278 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.
12821279 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.
12831280 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 }
12851287 }
12861288};
12871289