mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
zig build system creates symlinks atomically
* add std.base64 * add std.os.rename * add std.os.atomicSymLink
This commit is contained in:
parent
401eed8153
commit
216e14891e
@ -204,6 +204,7 @@ install(TARGETS zig DESTINATION bin)
|
||||
|
||||
install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST})
|
||||
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/base64.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/buf_map.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/buf_set.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/build.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
|
184
std/base64.zig
Normal file
184
std/base64.zig
Normal file
@ -0,0 +1,184 @@
|
||||
const assert = @import("debug.zig").assert;
|
||||
const mem = @import("mem.zig");
|
||||
|
||||
pub const standard_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
pub fn encode(dest: []u8, source: []const u8) -> []u8 {
|
||||
return encodeWithAlphabet(dest, source, standard_alphabet);
|
||||
}
|
||||
|
||||
pub fn decode(dest: []u8, source: []const u8) -> []u8 {
|
||||
return decodeWithAlphabet(dest, source, standard_alphabet);
|
||||
}
|
||||
|
||||
pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
|
||||
assert(alphabet.len == 65);
|
||||
assert(dest.len >= calcEncodedSize(source.len));
|
||||
|
||||
var i: usize = 0;
|
||||
var out_index: usize = 0;
|
||||
while (i + 2 < source.len; i += 3) {
|
||||
dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
|
||||
out_index += 1;
|
||||
|
||||
dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) |
|
||||
((source[i + 1] & 0xf0) >> 4)];
|
||||
out_index += 1;
|
||||
|
||||
dest[out_index] = alphabet[((source[i + 1] & 0xf) <<% 2) |
|
||||
((source[i + 2] & 0xc0) >> 6)];
|
||||
out_index += 1;
|
||||
|
||||
dest[out_index] = alphabet[source[i + 2] & 0x3f];
|
||||
out_index += 1;
|
||||
}
|
||||
|
||||
if (i < source.len) {
|
||||
dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
|
||||
out_index += 1;
|
||||
|
||||
if (i + 1 == source.len) {
|
||||
dest[out_index] = alphabet[(source[i] & 0x3) <<% 4];
|
||||
out_index += 1;
|
||||
|
||||
dest[out_index] = alphabet[64];
|
||||
out_index += 1;
|
||||
} else {
|
||||
dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) |
|
||||
((source[i + 1] & 0xf0) >> 4)];
|
||||
out_index += 1;
|
||||
|
||||
dest[out_index] = alphabet[(source[i + 1] & 0xf) <<% 2];
|
||||
out_index += 1;
|
||||
}
|
||||
|
||||
dest[out_index] = alphabet[64];
|
||||
out_index += 1;
|
||||
}
|
||||
|
||||
return dest[0...out_index];
|
||||
}
|
||||
|
||||
pub fn decodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
|
||||
assert(alphabet.len == 65);
|
||||
|
||||
var ascii6 = []u8{64} ** 256;
|
||||
for (alphabet) |c, i| {
|
||||
ascii6[c] = u8(i);
|
||||
}
|
||||
|
||||
return decodeWithAscii6BitMap(dest, source, ascii6[0...], alphabet[64]);
|
||||
}
|
||||
|
||||
pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8, pad_char: u8) -> []u8 {
|
||||
assert(ascii6.len == 256);
|
||||
assert(dest.len >= calcExactDecodedSizeWithPadChar(source, pad_char));
|
||||
|
||||
var src_index: usize = 0;
|
||||
var dest_index: usize = 0;
|
||||
var in_buf_len: usize = source.len;
|
||||
|
||||
while (in_buf_len > 0 and source[in_buf_len - 1] == pad_char) {
|
||||
in_buf_len -= 1;
|
||||
}
|
||||
|
||||
while (in_buf_len > 4) {
|
||||
dest[dest_index] = ascii6[source[src_index + 0]] <<% 2 |
|
||||
ascii6[source[src_index + 1]] >> 4;
|
||||
dest_index += 1;
|
||||
|
||||
dest[dest_index] = ascii6[source[src_index + 1]] <<% 4 |
|
||||
ascii6[source[src_index + 2]] >> 2;
|
||||
dest_index += 1;
|
||||
|
||||
dest[dest_index] = ascii6[source[src_index + 2]] <<% 6 |
|
||||
ascii6[source[src_index + 3]];
|
||||
dest_index += 1;
|
||||
|
||||
src_index += 4;
|
||||
in_buf_len -= 4;
|
||||
}
|
||||
|
||||
if (in_buf_len > 1) {
|
||||
dest[dest_index] = ascii6[source[src_index + 0]] <<% 2 |
|
||||
ascii6[source[src_index + 1]] >> 4;
|
||||
dest_index += 1;
|
||||
}
|
||||
if (in_buf_len > 2) {
|
||||
dest[dest_index] = ascii6[source[src_index + 1]] <<% 4 |
|
||||
ascii6[source[src_index + 2]] >> 2;
|
||||
dest_index += 1;
|
||||
}
|
||||
if (in_buf_len > 3) {
|
||||
dest[dest_index] = ascii6[source[src_index + 2]] <<% 6 |
|
||||
ascii6[source[src_index + 3]];
|
||||
dest_index += 1;
|
||||
}
|
||||
|
||||
return dest[0...dest_index];
|
||||
}
|
||||
|
||||
pub fn calcEncodedSize(source_len: usize) -> usize {
|
||||
return (((source_len * 4) / 3 + 3) / 4) * 4;
|
||||
}
|
||||
|
||||
/// Computes the upper bound of the decoded size based only on the encoded length.
|
||||
/// To compute the exact decoded size, see ::calcExactDecodedSize
|
||||
pub fn calcMaxDecodedSize(encoded_len: usize) -> usize {
|
||||
return @divExact(encoded_len * 3, 4);
|
||||
}
|
||||
|
||||
/// Computes the number of decoded bytes there will be. This function must
|
||||
/// be given the encoded buffer because there might be padding
|
||||
/// bytes at the end ('=' in the standard alphabet)
|
||||
pub fn calcExactDecodedSize(encoded: []const u8) -> usize {
|
||||
return calcExactDecodedSizeWithAlphabet(encoded, standard_alphabet);
|
||||
}
|
||||
|
||||
pub fn calcExactDecodedSizeWithAlphabet(encoded: []const u8, alphabet: []const u8) -> usize {
|
||||
assert(alphabet.len == 65);
|
||||
return calcExactDecodedSizeWithPadChar(encoded, alphabet[64]);
|
||||
}
|
||||
|
||||
pub fn calcExactDecodedSizeWithPadChar(encoded: []const u8, pad_char: u8) -> usize {
|
||||
var buf_len = encoded.len;
|
||||
|
||||
while (buf_len > 0 and encoded[buf_len - 1] == pad_char) {
|
||||
buf_len -= 1;
|
||||
}
|
||||
|
||||
return (buf_len * 3) / 4;
|
||||
}
|
||||
|
||||
test "base64" {
|
||||
testBase64();
|
||||
comptime testBase64();
|
||||
}
|
||||
|
||||
fn testBase64() {
|
||||
testBase64Case("", "");
|
||||
testBase64Case("f", "Zg==");
|
||||
testBase64Case("fo", "Zm8=");
|
||||
testBase64Case("foo", "Zm9v");
|
||||
testBase64Case("foob", "Zm9vYg==");
|
||||
testBase64Case("fooba", "Zm9vYmE=");
|
||||
testBase64Case("foobar", "Zm9vYmFy");
|
||||
}
|
||||
|
||||
fn testBase64Case(expected_decoded: []const u8, expected_encoded: []const u8) {
|
||||
const calculated_decoded_len = calcExactDecodedSize(expected_encoded);
|
||||
assert(calculated_decoded_len == expected_decoded.len);
|
||||
|
||||
const calculated_encoded_len = calcEncodedSize(expected_decoded.len);
|
||||
assert(calculated_encoded_len == expected_encoded.len);
|
||||
|
||||
var buf: [100]u8 = undefined;
|
||||
|
||||
const actual_decoded = decode(buf[0...], expected_encoded);
|
||||
assert(actual_decoded.len == expected_decoded.len);
|
||||
assert(mem.eql(u8, expected_decoded, actual_decoded));
|
||||
|
||||
const actual_encoded = encode(buf[0...], expected_decoded);
|
||||
assert(actual_encoded.len == expected_encoded.len);
|
||||
assert(mem.eql(u8, expected_encoded, actual_encoded));
|
||||
}
|
@ -816,11 +816,9 @@ const CLibrary = struct {
|
||||
builder.spawnChild(cc, cc_args.toSliceConst());
|
||||
|
||||
// sym link for libfoo.so.1 to libfoo.so.1.2.3
|
||||
_ = os.deleteFile(builder.allocator, self.major_only_filename);
|
||||
%%os.symLink(builder.allocator, self.out_filename, self.major_only_filename);
|
||||
%%os.atomicSymLink(builder.allocator, self.out_filename, self.major_only_filename);
|
||||
// sym link for libfoo.so to libfoo.so.1
|
||||
_ = os.deleteFile(builder.allocator, self.name_only_filename);
|
||||
%%os.symLink(builder.allocator, self.major_only_filename, self.name_only_filename);
|
||||
%%os.atomicSymLink(builder.allocator, self.major_only_filename, self.name_only_filename);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1029,10 +1027,8 @@ const InstallCLibraryStep = struct {
|
||||
|
||||
self.builder.copyFile(self.lib.out_filename, self.dest_file);
|
||||
if (!self.lib.static) {
|
||||
_ = os.deleteFile(self.builder.allocator, self.lib.major_only_filename);
|
||||
%%os.symLink(self.builder.allocator, self.lib.out_filename, self.lib.major_only_filename);
|
||||
_ = os.deleteFile(self.builder.allocator, self.lib.name_only_filename);
|
||||
%%os.symLink(self.builder.allocator, self.lib.major_only_filename, self.lib.name_only_filename);
|
||||
%%os.atomicSymLink(self.builder.allocator, self.lib.out_filename, self.lib.major_only_filename);
|
||||
%%os.atomicSymLink(self.builder.allocator, self.lib.major_only_filename, self.lib.name_only_filename);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub const base64 = @import("base64.zig");
|
||||
pub const build = @import("build.zig");
|
||||
pub const c = @import("c/index.zig");
|
||||
pub const cstr = @import("cstr.zig");
|
||||
|
@ -26,6 +26,7 @@ const BufMap = @import("../buf_map.zig").BufMap;
|
||||
const cstr = @import("../cstr.zig");
|
||||
|
||||
const io = @import("../io.zig");
|
||||
const base64 = @import("../base64.zig");
|
||||
|
||||
error Unexpected;
|
||||
error SystemResources;
|
||||
@ -35,9 +36,11 @@ error FileSystem;
|
||||
error IsDir;
|
||||
error FileNotFound;
|
||||
error FileBusy;
|
||||
error LinkPathAlreadyExists;
|
||||
error PathAlreadyExists;
|
||||
error SymLinkLoop;
|
||||
error ReadOnlyFileSystem;
|
||||
error LinkQuotaExceeded;
|
||||
error RenameAcrossMountPoints;
|
||||
|
||||
/// Fills `buf` with random bytes. If linking against libc, this calls the
|
||||
/// 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 {
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
errno.EBUSY, errno.EINTR => continue,
|
||||
errno.EMFILE => error.SystemResources,
|
||||
errno.EMFILE => error.ProcessFdQuotaExceeded,
|
||||
errno.EINVAL => unreachable,
|
||||
else => error.Unexpected,
|
||||
};
|
||||
@ -406,7 +409,7 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
|
||||
errno.EFAULT, errno.EINVAL => unreachable,
|
||||
errno.EACCES, errno.EPERM => error.AccessDenied,
|
||||
errno.EDQUOT => error.DiskQuota,
|
||||
errno.EEXIST => error.LinkPathAlreadyExists,
|
||||
errno.EEXIST => error.PathAlreadyExists,
|
||||
errno.EIO => error.FileSystem,
|
||||
errno.ELOOP => error.SymLinkLoop,
|
||||
errno.ENAMETOOLONG => error.NameTooLong,
|
||||
@ -419,6 +422,38 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
|
||||
}
|
||||
}
|
||||
|
||||
// here we replace the standard +/ with -_ so that it can be used in a file name
|
||||
const b64_fs_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
|
||||
|
||||
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
|
||||
try (symLink(allocator, existing_path, new_path)) {
|
||||
return;
|
||||
} else |err| {
|
||||
if (err != error.PathAlreadyExists) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
var rand_buf: [12]u8 = undefined;
|
||||
const tmp_path = %return allocator.alloc(u8, new_path.len + base64.calcEncodedSize(rand_buf.len));
|
||||
defer allocator.free(tmp_path);
|
||||
mem.copy(u8, tmp_path[0...], new_path);
|
||||
while (true) {
|
||||
%return getRandomBytes(rand_buf[0...]);
|
||||
_ = base64.encodeWithAlphabet(tmp_path[new_path.len...], rand_buf, b64_fs_alphabet);
|
||||
try (symLink(allocator, existing_path, tmp_path)) {
|
||||
return rename(allocator, tmp_path, new_path);
|
||||
} else |err| {
|
||||
if (err == error.PathAlreadyExists) {
|
||||
continue;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void {
|
||||
const buf = %return allocator.alloc(u8, file_path.len + 1);
|
||||
defer allocator.free(buf);
|
||||
@ -459,3 +494,37 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) -> %void {
|
||||
const full_buf = %return allocator.alloc(u8, old_path.len + new_path.len + 2);
|
||||
defer allocator.free(full_buf);
|
||||
|
||||
const old_buf = full_buf;
|
||||
mem.copy(u8, old_buf, old_path);
|
||||
old_buf[old_path.len] = 0;
|
||||
|
||||
const new_buf = full_buf[old_path.len + 1...];
|
||||
mem.copy(u8, new_buf, new_path);
|
||||
new_buf[new_path.len] = 0;
|
||||
|
||||
const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
errno.EACCES, errno.EPERM => error.AccessDenied,
|
||||
errno.EBUSY => error.FileBusy,
|
||||
errno.EDQUOT => error.DiskQuota,
|
||||
errno.EFAULT, errno.EINVAL => unreachable,
|
||||
errno.EISDIR => error.IsDir,
|
||||
errno.ELOOP => error.SymLinkLoop,
|
||||
errno.EMLINK => error.LinkQuotaExceeded,
|
||||
errno.ENAMETOOLONG => error.NameTooLong,
|
||||
errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
|
||||
errno.ENOMEM => error.SystemResources,
|
||||
errno.ENOSPC => error.NoSpaceLeft,
|
||||
errno.EEXIST, errno.ENOTEMPTY => error.PathAlreadyExists,
|
||||
errno.EROFS => error.ReadOnlyFileSystem,
|
||||
errno.EXDEV => error.RenameAcrossMountPoints,
|
||||
else => error.Unexpected,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +311,10 @@ pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize {
|
||||
arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset)
|
||||
}
|
||||
|
||||
pub fn rename(old: &const u8, new: &const u8) -> usize {
|
||||
arch.syscall2(arch.SYS_rename, usize(old), usize(new))
|
||||
}
|
||||
|
||||
pub fn open(path: &const u8, flags: usize, perm: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_open, usize(path), flags, perm)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user