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:...@@ -63,7 +63,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
6363
64 const dirname = path.dirname(new_path) orelse ".";64 const dirname = path.dirname(new_path) orelse ".";
6565
66 var rand_buf: [12]u8 = undefined;66 var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined;
67 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));67 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
68 defer allocator.free(tmp_path);68 defer allocator.free(tmp_path);
69 mem.copy(u8, tmp_path[0..], dirname);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,38 +118,31 @@ pub fn copyFileAbsolute(source_path: []const u8, dest_path: []const u8, args: Co
118/// TODO update this API to avoid a getrandom syscall for every operation.118/// TODO update this API to avoid a getrandom syscall for every operation.
119pub const AtomicFile = struct {119pub const AtomicFile = struct {
120 file: File,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 dest_path: []const u8,123 dest_path: []const u8,
123 file_open: bool,124 file_open: bool,
124 file_exists: bool,125 file_exists: bool,
126 close_dir_on_deinit: bool,
125 dir: Dir,127 dir: Dir,
126128
127 const InitError = File.OpenError;129 const InitError = File.OpenError;
128130
129 /// TODO rename this. Callers should go through Dir API131 const RANDOM_BYTES = 12;
130 pub fn init2(dest_path: []const u8, mode: File.Mode, dir: Dir) InitError!AtomicFile {132 const TMP_PATH_LEN = base64.Base64Encoder.calcSize(RANDOM_BYTES);
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 }
143133
144 tmp_path_buf[tmp_path_len] = 0;134 /// TODO rename this. Callers should go through Dir API
145 const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0];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
147 while (true) {140 while (true) {
148 try crypto.randomBytes(rand_buf[0..]);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);
150143
151 const file = dir.createFileC(144 const file = dir.createFileC(
152 tmp_path_slice,145 &tmp_path_buf,
153 .{ .mode = mode, .exclusive = true },146 .{ .mode = mode, .exclusive = true },
154 ) catch |err| switch (err) {147 ) catch |err| switch (err) {
155 error.PathAlreadyExists => continue,148 error.PathAlreadyExists => continue,
...@@ -162,6 +155,7 @@ pub const AtomicFile = struct {...@@ -162,6 +155,7 @@ pub const AtomicFile = struct {
162 .dest_path = dest_path,155 .dest_path = dest_path,
163 .file_open = true,156 .file_open = true,
164 .file_exists = true,157 .file_exists = true,
158 .close_dir_on_deinit = close_dir_on_deinit,
165 .dir = dir,159 .dir = dir,
166 };160 };
167 }161 }
...@@ -169,7 +163,7 @@ pub const AtomicFile = struct {...@@ -169,7 +163,7 @@ pub const AtomicFile = struct {
169163
170 /// Deprecated. Use `Dir.atomicFile`.164 /// Deprecated. Use `Dir.atomicFile`.
171 pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {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 }
174168
175 /// always call deinit, even after successful finish()169 /// always call deinit, even after successful finish()
...@@ -182,6 +176,9 @@ pub const AtomicFile = struct {...@@ -182,6 +176,9 @@ pub const AtomicFile = struct {
182 self.dir.deleteFileC(&self.tmp_path_buf) catch {};176 self.dir.deleteFileC(&self.tmp_path_buf) catch {};
183 self.file_exists = false;177 self.file_exists = false;
184 }178 }
179 if (self.close_dir_on_deinit) {
180 self.dir.close();
181 }
185 self.* = undefined;182 self.* = undefined;
186 }183 }
187184
...@@ -1281,7 +1278,12 @@ pub const Dir = struct {...@@ -1281,7 +1278,12 @@ pub const Dir = struct {
1281 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.1278 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.
1282 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.1279 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.
1283 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {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};
12871289