| author | |
| committer | |
| log | 216e14891ea5fa1a88804d9781ef779d448d1220 |
| tree | 8cfa1b656aaa6b6c01bdba7c5b7aedb5916f5088 |
| parent | 401eed8153d909eda4146b5a1815dee7130cf1c3 |
* add std.base64
* add std.os.rename
* add std.os.atomicSymLink6 files changed, 266 insertions(+), 11 deletions(-)
CMakeLists.txt+1| ... | @@ -204,6 +204,7 @@ install(TARGETS zig DESTINATION bin) | ... | @@ -204,6 +204,7 @@ install(TARGETS zig DESTINATION bin) |
| 204 | 204 | ||
| 205 | install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST}) | 205 | install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST}) |
| 206 | 206 | ||
| 207 | install(FILES "${CMAKE_SOURCE_DIR}/std/base64.zig" DESTINATION "${ZIG_STD_DEST}") | ||
| 207 | install(FILES "${CMAKE_SOURCE_DIR}/std/buf_map.zig" DESTINATION "${ZIG_STD_DEST}") | 208 | install(FILES "${CMAKE_SOURCE_DIR}/std/buf_map.zig" DESTINATION "${ZIG_STD_DEST}") |
| 208 | install(FILES "${CMAKE_SOURCE_DIR}/std/buf_set.zig" DESTINATION "${ZIG_STD_DEST}") | 209 | install(FILES "${CMAKE_SOURCE_DIR}/std/buf_set.zig" DESTINATION "${ZIG_STD_DEST}") |
| 209 | install(FILES "${CMAKE_SOURCE_DIR}/std/build.zig" DESTINATION "${ZIG_STD_DEST}") | 210 | install(FILES "${CMAKE_SOURCE_DIR}/std/build.zig" DESTINATION "${ZIG_STD_DEST}") |
std/base64.zig created+184| ... | @@ -0,0 +1,184 @@ | ||
| 1 | const assert = @import("debug.zig").assert; | ||
| 2 | const mem = @import("mem.zig"); | ||
| 3 | |||
| 4 | pub const standard_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | ||
| 5 | |||
| 6 | pub fn encode(dest: []u8, source: []const u8) -> []u8 { | ||
| 7 | return encodeWithAlphabet(dest, source, standard_alphabet); | ||
| 8 | } | ||
| 9 | |||
| 10 | pub fn decode(dest: []u8, source: []const u8) -> []u8 { | ||
| 11 | return decodeWithAlphabet(dest, source, standard_alphabet); | ||
| 12 | } | ||
| 13 | |||
| 14 | pub 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 | |||
| 62 | pub 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 | |||
| 73 | pub 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 | |||
| 121 | pub 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 | ||
| 127 | pub 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) | ||
| 134 | pub fn calcExactDecodedSize(encoded: []const u8) -> usize { | ||
| 135 | return calcExactDecodedSizeWithAlphabet(encoded, standard_alphabet); | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn calcExactDecodedSizeWithAlphabet(encoded: []const u8, alphabet: []const u8) -> usize { | ||
| 139 | assert(alphabet.len == 65); | ||
| 140 | return calcExactDecodedSizeWithPadChar(encoded, alphabet[64]); | ||
| 141 | } | ||
| 142 | |||
| 143 | pub 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 | |||
| 153 | test "base64" { | ||
| 154 | testBase64(); | ||
| 155 | comptime testBase64(); | ||
| 156 | } | ||
| 157 | |||
| 158 | fn 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 | |||
| 168 | fn 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 { | ... | @@ -816,11 +816,9 @@ const CLibrary = struct { |
| 816 | builder.spawnChild(cc, cc_args.toSliceConst()); | 816 | builder.spawnChild(cc, cc_args.toSliceConst()); |
| 817 | 817 | ||
| 818 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 | 818 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 |
| 819 | _ = os.deleteFile(builder.allocator, self.major_only_filename); | 819 | %%os.atomicSymLink(builder.allocator, self.out_filename, self.major_only_filename); |
| 820 | %%os.symLink(builder.allocator, self.out_filename, self.major_only_filename); | ||
| 821 | // sym link for libfoo.so to libfoo.so.1 | 820 | // sym link for libfoo.so to libfoo.so.1 |
| 822 | _ = os.deleteFile(builder.allocator, self.name_only_filename); | 821 | %%os.atomicSymLink(builder.allocator, self.major_only_filename, self.name_only_filename); |
| 823 | %%os.symLink(builder.allocator, self.major_only_filename, self.name_only_filename); | ||
| 824 | } | 822 | } |
| 825 | } | 823 | } |
| 826 | 824 | ||
| ... | @@ -1029,10 +1027,8 @@ const InstallCLibraryStep = struct { | ... | @@ -1029,10 +1027,8 @@ const InstallCLibraryStep = struct { |
| 1029 | 1027 | ||
| 1030 | self.builder.copyFile(self.lib.out_filename, self.dest_file); | 1028 | self.builder.copyFile(self.lib.out_filename, self.dest_file); |
| 1031 | if (!self.lib.static) { | 1029 | if (!self.lib.static) { |
| 1032 | _ = os.deleteFile(self.builder.allocator, self.lib.major_only_filename); | 1030 | %%os.atomicSymLink(self.builder.allocator, self.lib.out_filename, self.lib.major_only_filename); |
| 1033 | %%os.symLink(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); |
| 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); | ||
| 1036 | } | 1032 | } |
| 1037 | } | 1033 | } |
| 1038 | }; | 1034 | }; |
std/index.zig+1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | pub const base64 = @import("base64.zig"); | ||
| 1 | pub const build = @import("build.zig"); | 2 | pub const build = @import("build.zig"); |
| 2 | pub const c = @import("c/index.zig"); | 3 | pub const c = @import("c/index.zig"); |
| 3 | pub const cstr = @import("cstr.zig"); | 4 | pub const cstr = @import("cstr.zig"); |
std/os/index.zig+72-3| ... | @@ -26,6 +26,7 @@ const BufMap = @import("../buf_map.zig").BufMap; | ... | @@ -26,6 +26,7 @@ const BufMap = @import("../buf_map.zig").BufMap; |
| 26 | const cstr = @import("../cstr.zig"); | 26 | const cstr = @import("../cstr.zig"); |
| 27 | 27 | ||
| 28 | const io = @import("../io.zig"); | 28 | const io = @import("../io.zig"); |
| 29 | const base64 = @import("../base64.zig"); | ||
| 29 | 30 | ||
| 30 | error Unexpected; | 31 | error Unexpected; |
| 31 | error SystemResources; | 32 | error SystemResources; |
| ... | @@ -35,9 +36,11 @@ error FileSystem; | ... | @@ -35,9 +36,11 @@ error FileSystem; |
| 35 | error IsDir; | 36 | error IsDir; |
| 36 | error FileNotFound; | 37 | error FileNotFound; |
| 37 | error FileBusy; | 38 | error FileBusy; |
| 38 | error LinkPathAlreadyExists; | 39 | error PathAlreadyExists; |
| 39 | error SymLinkLoop; | 40 | error SymLinkLoop; |
| 40 | error ReadOnlyFileSystem; | 41 | error ReadOnlyFileSystem; |
| 42 | error LinkQuotaExceeded; | ||
| 43 | error RenameAcrossMountPoints; | ||
| 41 | 44 | ||
| 42 | /// Fills `buf` with random bytes. If linking against libc, this calls the | 45 | /// Fills `buf` with random bytes. If linking against libc, this calls the |
| 43 | /// appropriate OS-specific library call. Otherwise it uses the zig standard | 46 | /// 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 { | ... | @@ -197,7 +200,7 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void { |
| 197 | if (err > 0) { | 200 | if (err > 0) { |
| 198 | return switch (err) { | 201 | return switch (err) { |
| 199 | errno.EBUSY, errno.EINTR => continue, | 202 | errno.EBUSY, errno.EINTR => continue, |
| 200 | errno.EMFILE => error.SystemResources, | 203 | errno.EMFILE => error.ProcessFdQuotaExceeded, |
| 201 | errno.EINVAL => unreachable, | 204 | errno.EINVAL => unreachable, |
| 202 | else => error.Unexpected, | 205 | else => error.Unexpected, |
| 203 | }; | 206 | }; |
| ... | @@ -406,7 +409,7 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con | ... | @@ -406,7 +409,7 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con |
| 406 | errno.EFAULT, errno.EINVAL => unreachable, | 409 | errno.EFAULT, errno.EINVAL => unreachable, |
| 407 | errno.EACCES, errno.EPERM => error.AccessDenied, | 410 | errno.EACCES, errno.EPERM => error.AccessDenied, |
| 408 | errno.EDQUOT => error.DiskQuota, | 411 | errno.EDQUOT => error.DiskQuota, |
| 409 | errno.EEXIST => error.LinkPathAlreadyExists, | 412 | errno.EEXIST => error.PathAlreadyExists, |
| 410 | errno.EIO => error.FileSystem, | 413 | errno.EIO => error.FileSystem, |
| 411 | errno.ELOOP => error.SymLinkLoop, | 414 | errno.ELOOP => error.SymLinkLoop, |
| 412 | errno.ENAMETOOLONG => error.NameTooLong, | 415 | errno.ENAMETOOLONG => error.NameTooLong, |
| ... | @@ -419,6 +422,38 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con | ... | @@ -419,6 +422,38 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con |
| 419 | } | 422 | } |
| 420 | } | 423 | } |
| 421 | 424 | ||
| 425 | // here we replace the standard +/ with -_ so that it can be used in a file name | ||
| 426 | const b64_fs_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="; | ||
| 427 | |||
| 428 | pub 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 | |||
| 422 | pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void { | 457 | pub fn deleteFile(allocator: &Allocator, file_path: []const u8) -> %void { |
| 423 | const buf = %return allocator.alloc(u8, file_path.len + 1); | 458 | const buf = %return allocator.alloc(u8, file_path.len + 1); |
| 424 | defer allocator.free(buf); | 459 | defer allocator.free(buf); |
| ... | @@ -459,3 +494,37 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con | ... | @@ -459,3 +494,37 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con |
| 459 | return; | 494 | return; |
| 460 | } | 495 | } |
| 461 | } | 496 | } |
| 497 | |||
| 498 | pub 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 { | ... | @@ -311,6 +311,10 @@ pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize { |
| 311 | arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset) | 311 | arch.syscall4(arch.SYS_pwrite, usize(fd), usize(buf), count, offset) |
| 312 | } | 312 | } |
| 313 | 313 | ||
| 314 | pub fn rename(old: &const u8, new: &const u8) -> usize { | ||
| 315 | arch.syscall2(arch.SYS_rename, usize(old), usize(new)) | ||
| 316 | } | ||
| 317 | |||
| 314 | pub fn open(path: &const u8, flags: usize, perm: usize) -> usize { | 318 | pub fn open(path: &const u8, flags: usize, perm: usize) -> usize { |
| 315 | arch.syscall3(arch.SYS_open, usize(path), flags, perm) | 319 | arch.syscall3(arch.SYS_open, usize(path), flags, perm) |
| 316 | } | 320 | } |