authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-17 19:08:41-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-17 19:08:41-04:00
log216e14891ea5fa1a88804d9781ef779d448d1220
tree8cfa1b656aaa6b6c01bdba7c5b7aedb5916f5088
parent401eed8153d909eda4146b5a1815dee7130cf1c3

zig build system creates symlinks atomically

* add std.base64 * add std.os.rename * add std.os.atomicSymLink

6 files changed, 266 insertions(+), 11 deletions(-)

CMakeLists.txt+1
......@@ -204,6 +204,7 @@ install(TARGETS zig DESTINATION bin)
204204
205205install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST})
206206
207install(FILES "${CMAKE_SOURCE_DIR}/std/base64.zig" DESTINATION "${ZIG_STD_DEST}")
207208install(FILES "${CMAKE_SOURCE_DIR}/std/buf_map.zig" DESTINATION "${ZIG_STD_DEST}")
208209install(FILES "${CMAKE_SOURCE_DIR}/std/buf_set.zig" DESTINATION "${ZIG_STD_DEST}")
209210install(FILES "${CMAKE_SOURCE_DIR}/std/build.zig" DESTINATION "${ZIG_STD_DEST}")
std/base64.zig created+184
......@@ -0,0 +1,184 @@
1const assert = @import("debug.zig").assert;
2const mem = @import("mem.zig");
3
4pub const standard_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
5
6pub fn encode(dest: []u8, source: []const u8) -> []u8 {
7 return encodeWithAlphabet(dest, source, standard_alphabet);
8}
9
10pub fn decode(dest: []u8, source: []const u8) -> []u8 {
11 return decodeWithAlphabet(dest, source, standard_alphabet);
12}
13
14pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
15 assert(alphabet.len == 65);
16 assert(dest.len >= calcEncodedSize(source.len));
17
18 var i: usize = 0;
19 var out_index: usize = 0;
20 while (i + 2 < source.len; i += 3) {
21 dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
22 out_index += 1;
23
24 dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) |
25 ((source[i + 1] & 0xf0) >> 4)];
26 out_index += 1;
27
28 dest[out_index] = alphabet[((source[i + 1] & 0xf) <<% 2) |
29 ((source[i + 2] & 0xc0) >> 6)];
30 out_index += 1;
31
32 dest[out_index] = alphabet[source[i + 2] & 0x3f];
33 out_index += 1;
34 }
35
36 if (i < source.len) {
37 dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
38 out_index += 1;
39
40 if (i + 1 == source.len) {
41 dest[out_index] = alphabet[(source[i] & 0x3) <<% 4];
42 out_index += 1;
43
44 dest[out_index] = alphabet[64];
45 out_index += 1;
46 } else {
47 dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) |
48 ((source[i + 1] & 0xf0) >> 4)];
49 out_index += 1;
50
51 dest[out_index] = alphabet[(source[i + 1] & 0xf) <<% 2];
52 out_index += 1;
53 }
54
55 dest[out_index] = alphabet[64];
56 out_index += 1;
57 }
58
59 return dest[0...out_index];
60}
61
62pub fn decodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
63 assert(alphabet.len == 65);
64
65 var ascii6 = []u8{64} ** 256;
66 for (alphabet) |c, i| {
67 ascii6[c] = u8(i);
68 }
69
70 return decodeWithAscii6BitMap(dest, source, ascii6[0...], alphabet[64]);
71}
72
73pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8, pad_char: u8) -> []u8 {
74 assert(ascii6.len == 256);
75 assert(dest.len >= calcExactDecodedSizeWithPadChar(source, pad_char));
76
77 var src_index: usize = 0;
78 var dest_index: usize = 0;
79 var in_buf_len: usize = source.len;
80
81 while (in_buf_len > 0 and source[in_buf_len - 1] == pad_char) {
82 in_buf_len -= 1;
83 }
84
85 while (in_buf_len > 4) {
86 dest[dest_index] = ascii6[source[src_index + 0]] <<% 2 |
87 ascii6[source[src_index + 1]] >> 4;
88 dest_index += 1;
89
90 dest[dest_index] = ascii6[source[src_index + 1]] <<% 4 |
91 ascii6[source[src_index + 2]] >> 2;
92 dest_index += 1;
93
94 dest[dest_index] = ascii6[source[src_index + 2]] <<% 6 |
95 ascii6[source[src_index + 3]];
96 dest_index += 1;
97
98 src_index += 4;
99 in_buf_len -= 4;
100 }
101
102 if (in_buf_len > 1) {
103 dest[dest_index] = ascii6[source[src_index + 0]] <<% 2 |
104 ascii6[source[src_index + 1]] >> 4;
105 dest_index += 1;
106 }
107 if (in_buf_len > 2) {
108 dest[dest_index] = ascii6[source[src_index + 1]] <<% 4 |
109 ascii6[source[src_index + 2]] >> 2;
110 dest_index += 1;
111 }
112 if (in_buf_len > 3) {
113 dest[dest_index] = ascii6[source[src_index + 2]] <<% 6 |
114 ascii6[source[src_index + 3]];
115 dest_index += 1;
116 }
117
118 return dest[0...dest_index];
119}
120
121pub fn calcEncodedSize(source_len: usize) -> usize {
122 return (((source_len * 4) / 3 + 3) / 4) * 4;
123}
124
125/// Computes the upper bound of the decoded size based only on the encoded length.
126/// To compute the exact decoded size, see ::calcExactDecodedSize
127pub fn calcMaxDecodedSize(encoded_len: usize) -> usize {
128 return @divExact(encoded_len * 3, 4);
129}
130
131/// Computes the number of decoded bytes there will be. This function must
132/// be given the encoded buffer because there might be padding
133/// bytes at the end ('=' in the standard alphabet)
134pub fn calcExactDecodedSize(encoded: []const u8) -> usize {
135 return calcExactDecodedSizeWithAlphabet(encoded, standard_alphabet);
136}
137
138pub fn calcExactDecodedSizeWithAlphabet(encoded: []const u8, alphabet: []const u8) -> usize {
139 assert(alphabet.len == 65);
140 return calcExactDecodedSizeWithPadChar(encoded, alphabet[64]);
141}
142
143pub fn calcExactDecodedSizeWithPadChar(encoded: []const u8, pad_char: u8) -> usize {
144 var buf_len = encoded.len;
145
146 while (buf_len > 0 and encoded[buf_len - 1] == pad_char) {
147 buf_len -= 1;
148 }
149
150 return (buf_len * 3) / 4;
151}
152
153test "base64" {
154 testBase64();
155 comptime testBase64();
156}
157
158fn testBase64() {
159 testBase64Case("", "");
160 testBase64Case("f", "Zg==");
161 testBase64Case("fo", "Zm8=");
162 testBase64Case("foo", "Zm9v");
163 testBase64Case("foob", "Zm9vYg==");
164 testBase64Case("fooba", "Zm9vYmE=");
165 testBase64Case("foobar", "Zm9vYmFy");
166}
167
168fn testBase64Case(expected_decoded: []const u8, expected_encoded: []const u8) {
169 const calculated_decoded_len = calcExactDecodedSize(expected_encoded);
170 assert(calculated_decoded_len == expected_decoded.len);
171
172 const calculated_encoded_len = calcEncodedSize(expected_decoded.len);
173 assert(calculated_encoded_len == expected_encoded.len);
174
175 var buf: [100]u8 = undefined;
176
177 const actual_decoded = decode(buf[0...], expected_encoded);
178 assert(actual_decoded.len == expected_decoded.len);
179 assert(mem.eql(u8, expected_decoded, actual_decoded));
180
181 const actual_encoded = encode(buf[0...], expected_decoded);
182 assert(actual_encoded.len == expected_encoded.len);
183 assert(mem.eql(u8, expected_encoded, actual_encoded));
184}
std/build.zig+4-8
......@@ -816,11 +816,9 @@ const CLibrary = struct {
816816 builder.spawnChild(cc, cc_args.toSliceConst());
817817
818818 // sym link for libfoo.so.1 to libfoo.so.1.2.3
819 _ = os.deleteFile(builder.allocator, self.major_only_filename);
820 %%os.symLink(builder.allocator, self.out_filename, self.major_only_filename);
819 %%os.atomicSymLink(builder.allocator, self.out_filename, self.major_only_filename);
821820 // sym link for libfoo.so to libfoo.so.1
822 _ = os.deleteFile(builder.allocator, self.name_only_filename);
823 %%os.symLink(builder.allocator, self.major_only_filename, self.name_only_filename);
821 %%os.atomicSymLink(builder.allocator, self.major_only_filename, self.name_only_filename);
824822 }
825823 }
826824
......@@ -1029,10 +1027,8 @@ const InstallCLibraryStep = struct {
10291027
10301028 self.builder.copyFile(self.lib.out_filename, self.dest_file);
10311029 if (!self.lib.static) {
1032 _ = os.deleteFile(self.builder.allocator, self.lib.major_only_filename);
1033 %%os.symLink(self.builder.allocator, self.lib.out_filename, self.lib.major_only_filename);
1034 _ = os.deleteFile(self.builder.allocator, self.lib.name_only_filename);
1035 %%os.symLink(self.builder.allocator, self.lib.major_only_filename, self.lib.name_only_filename);
1030 %%os.atomicSymLink(self.builder.allocator, self.lib.out_filename, self.lib.major_only_filename);
1031 %%os.atomicSymLink(self.builder.allocator, self.lib.major_only_filename, self.lib.name_only_filename);
10361032 }
10371033 }
10381034};
std/index.zig+1
......@@ -1,3 +1,4 @@
1pub const base64 = @import("base64.zig");
12pub const build = @import("build.zig");
23pub const c = @import("c/index.zig");
34pub const cstr = @import("cstr.zig");
std/os/index.zig+72-3
......@@ -26,6 +26,7 @@ const BufMap = @import("../buf_map.zig").BufMap;
2626const cstr = @import("../cstr.zig");
2727
2828const io = @import("../io.zig");
29const base64 = @import("../base64.zig");
2930
3031error Unexpected;
3132error SystemResources;
......@@ -35,9 +36,11 @@ error FileSystem;
3536error IsDir;
3637error FileNotFound;
3738error FileBusy;
38error LinkPathAlreadyExists;
39error PathAlreadyExists;
3940error SymLinkLoop;
4041error ReadOnlyFileSystem;
42error LinkQuotaExceeded;
43error RenameAcrossMountPoints;
4144
4245/// Fills `buf` with random bytes. If linking against libc, this calls the
4346/// appropriate OS-specific library call. Otherwise it uses the zig standard
......@@ -197,7 +200,7 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
197200 if (err > 0) {
198201 return switch (err) {
199202 errno.EBUSY, errno.EINTR => continue,
200 errno.EMFILE => error.SystemResources,
203 errno.EMFILE => error.ProcessFdQuotaExceeded,
201204 errno.EINVAL => unreachable,
202205 else => error.Unexpected,
203206 };
......@@ -406,7 +409,7 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
406409 errno.EFAULT, errno.EINVAL => unreachable,
407410 errno.EACCES, errno.EPERM => error.AccessDenied,
408411 errno.EDQUOT => error.DiskQuota,
409 errno.EEXIST => error.LinkPathAlreadyExists,
412 errno.EEXIST => error.PathAlreadyExists,
410413 errno.EIO => error.FileSystem,
411414 errno.ELOOP => error.SymLinkLoop,
412415 errno.ENAMETOOLONG => error.NameTooLong,
......@@ -419,6 +422,38 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
419422 }
420423}
421424
425// here we replace the standard +/ with -_ so that it can be used in a file name
426const b64_fs_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
427
428pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
429 try (symLink(allocator, existing_path, new_path)) {
430 return;
431 } else |err| {
432 if (err != error.PathAlreadyExists) {
433 return err;
434 }
435 }
436
437 var rand_buf: [12]u8 = undefined;
438 const tmp_path = %return allocator.alloc(u8, new_path.len + base64.calcEncodedSize(rand_buf.len));
439 defer allocator.free(tmp_path);
440 mem.copy(u8, tmp_path[0...], new_path);
441 while (true) {
442 %return getRandomBytes(rand_buf[0...]);
443 _ = base64.encodeWithAlphabet(tmp_path[new_path.len...], rand_buf, b64_fs_alphabet);
444 try (symLink(allocator, existing_path, tmp_path)) {
445 return rename(allocator, tmp_path, new_path);
446 } else |err| {
447 if (err == error.PathAlreadyExists) {
448 continue;
449 } else {
450 return err;
451 }
452 }
453 }
454
455}
456
422457pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void {
423458 const buf = %return allocator.alloc(u8, file_path.len + 1);
424459 defer allocator.free(buf);
......@@ -459,3 +494,37 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
459494 return;
460495 }
461496}
497
498pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) -> %void {
499 const full_buf = %return allocator.alloc(u8, old_path.len + new_path.len + 2);
500 defer allocator.free(full_buf);
501
502 const old_buf = full_buf;
503 mem.copy(u8, old_buf, old_path);
504 old_buf[old_path.len] = 0;
505
506 const new_buf = full_buf[old_path.len + 1...];
507 mem.copy(u8, new_buf, new_path);
508 new_buf[new_path.len] = 0;
509
510 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
511 if (err > 0) {
512 return switch (err) {
513 errno.EACCES, errno.EPERM => error.AccessDenied,
514 errno.EBUSY => error.FileBusy,
515 errno.EDQUOT => error.DiskQuota,
516 errno.EFAULT, errno.EINVAL => unreachable,
517 errno.EISDIR => error.IsDir,
518 errno.ELOOP => error.SymLinkLoop,
519 errno.EMLINK => error.LinkQuotaExceeded,
520 errno.ENAMETOOLONG => error.NameTooLong,
521 errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
522 errno.ENOMEM => error.SystemResources,
523 errno.ENOSPC => error.NoSpaceLeft,
524 errno.EEXIST, errno.ENOTEMPTY => error.PathAlreadyExists,
525 errno.EROFS => error.ReadOnlyFileSystem,
526 errno.EXDEV => error.RenameAcrossMountPoints,
527 else => error.Unexpected,
528 };
529 }
530}
std/os/linux.zig+4
......@@ -311,6 +311,10 @@ pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize {
311311 arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset)
312312}
313313
314pub fn rename(old: &const u8, new: &const u8) -> usize {
315 arch.syscall2(arch.SYS_rename, usize(old), usize(new))
316}
317
314318pub fn open(path: &const u8, flags: usize, perm: usize) -> usize {
315319 arch.syscall3(arch.SYS_open, usize(path), flags, perm)
316320}