| ... | @@ -145,6 +145,8 @@ pub const bin_digest_len = 16; | ... | @@ -145,6 +145,8 @@ pub const bin_digest_len = 16; |
| 145 | pub const hex_digest_len = bin_digest_len * 2; | 145 | pub const hex_digest_len = bin_digest_len * 2; |
| 146 | pub const BinDigest = [bin_digest_len]u8; | 146 | pub const BinDigest = [bin_digest_len]u8; |
| 147 | | 147 | |
| | 148 | /// This is currently just an arbitrary non-empty string that can't match another manifest line. |
| | 149 | const manifest_header = "0"; |
| 148 | const manifest_file_size_max = 50 * 1024 * 1024; | 150 | const manifest_file_size_max = 50 * 1024 * 1024; |
| 149 | | 151 | |
| 150 | /// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it | 152 | /// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it |
| ... | @@ -152,8 +154,15 @@ const manifest_file_size_max = 50 * 1024 * 1024; | ... | @@ -152,8 +154,15 @@ const manifest_file_size_max = 50 * 1024 * 1024; |
| 152 | /// fastest options right now. | 154 | /// fastest options right now. |
| 153 | pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); | 155 | pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); |
| 154 | | 156 | |
| 155 | /// Initial state, that can be copied. | 157 | /// Initial state with random bytes, that can be copied. |
| 156 | pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length); | 158 | /// Refresh this with new random bytes when the manifest |
| | 159 | /// format is modified in a non-backwards-compatible way. |
| | 160 | pub const hasher_init: Hasher = Hasher.init(&[_]u8{ |
| | 161 | 0x33, 0x52, 0xa2, 0x84, |
| | 162 | 0xcf, 0x17, 0x56, 0x57, |
| | 163 | 0x01, 0xbb, 0xcd, 0xe4, |
| | 164 | 0x77, 0xd6, 0xf0, 0x60, |
| | 165 | }); |
| 157 | | 166 | |
| 158 | pub const File = struct { | 167 | pub const File = struct { |
| 159 | prefixed_path: ?PrefixedPath, | 168 | prefixed_path: ?PrefixedPath, |
| ... | @@ -391,72 +400,29 @@ pub const Manifest = struct { | ... | @@ -391,72 +400,29 @@ pub const Manifest = struct { |
| 391 | @memcpy(manifest_file_path[0..self.hex_digest.len], &self.hex_digest); | 400 | @memcpy(manifest_file_path[0..self.hex_digest.len], &self.hex_digest); |
| 392 | manifest_file_path[hex_digest_len..][0..ext.len].* = ext.*; | 401 | manifest_file_path[hex_digest_len..][0..ext.len].* = ext.*; |
| 393 | | 402 | |
| 394 | if (self.files.items.len == 0) { | 403 | while (true) { |
| 395 | // If there are no file inputs, we check if the manifest file exists instead of | 404 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ |
| 396 | // comparing the hashes on the files used for the cached item | 405 | .read = true, |
| 397 | while (true) { | 406 | .truncate = false, |
| 398 | if (self.cache.manifest_dir.openFile(&manifest_file_path, .{ | 407 | .lock = .Exclusive, |
| 399 | .mode = .read_write, | 408 | .lock_nonblocking = self.want_shared_lock, |
| 400 | .lock = .Exclusive, | 409 | })) |manifest_file| { |
| 401 | .lock_nonblocking = self.want_shared_lock, | 410 | self.manifest_file = manifest_file; |
| 402 | })) |manifest_file| { | 411 | self.have_exclusive_lock = true; |
| 403 | self.manifest_file = manifest_file; | 412 | break; |
| 404 | self.have_exclusive_lock = true; | 413 | } else |err| switch (err) { |
| 405 | break; | 414 | error.WouldBlock => { |
| 406 | } else |open_err| switch (open_err) { | 415 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ |
| 407 | error.WouldBlock => { | 416 | .mode = .read_write, |
| 408 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | 417 | .lock = .Shared, |
| 409 | .lock = .Shared, | 418 | }); |
| 410 | }); | | |
| 411 | break; | | |
| 412 | }, | | |
| 413 | error.FileNotFound => { | | |
| 414 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ | | |
| 415 | .read = true, | | |
| 416 | .truncate = false, | | |
| 417 | .lock = .Exclusive, | | |
| 418 | .lock_nonblocking = self.want_shared_lock, | | |
| 419 | })) |manifest_file| { | | |
| 420 | self.manifest_file = manifest_file; | | |
| 421 | self.manifest_dirty = true; | | |
| 422 | self.have_exclusive_lock = true; | | |
| 423 | return false; // cache miss; exclusive lock already held | | |
| 424 | } else |err| switch (err) { | | |
| 425 | // There are no dir components, so you would think | | |
| 426 | // that this was unreachable, however we have | | |
| 427 | // observed on macOS two processes racing to do | | |
| 428 | // openat() with O_CREAT manifest in ENOENT. | | |
| 429 | error.WouldBlock, error.FileNotFound => continue, | | |
| 430 | else => |e| return e, | | |
| 431 | } | | |
| 432 | }, | | |
| 433 | else => |e| return e, | | |
| 434 | } | | |
| 435 | } | | |
| 436 | } else { | | |
| 437 | while (true) { | | |
| 438 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ | | |
| 439 | .read = true, | | |
| 440 | .truncate = false, | | |
| 441 | .lock = .Exclusive, | | |
| 442 | .lock_nonblocking = self.want_shared_lock, | | |
| 443 | })) |manifest_file| { | | |
| 444 | self.manifest_file = manifest_file; | | |
| 445 | self.have_exclusive_lock = true; | | |
| 446 | break; | 419 | break; |
| 447 | } else |err| switch (err) { | 420 | }, |
| 448 | error.WouldBlock => { | 421 | // There are no dir components, so you would think that this was |
| 449 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | 422 | // unreachable, however we have observed on macOS two processes racing |
| 450 | .lock = .Shared, | 423 | // to do openat() with O_CREAT manifest in ENOENT. |
| 451 | }); | 424 | error.FileNotFound => continue, |
| 452 | break; | 425 | else => |e| return e, |
| 453 | }, | | |
| 454 | // There are no dir components, so you would think that this was | | |
| 455 | // unreachable, however we have observed on macOS two processes racing | | |
| 456 | // to do openat() with O_CREAT manifest in ENOENT. | | |
| 457 | error.FileNotFound => continue, | | |
| 458 | else => |e| return e, | | |
| 459 | } | | |
| 460 | } | 426 | } |
| 461 | } | 427 | } |
| 462 | | 428 | |
| ... | @@ -469,6 +435,18 @@ pub const Manifest = struct { | ... | @@ -469,6 +435,18 @@ pub const Manifest = struct { |
| 469 | var any_file_changed = false; | 435 | var any_file_changed = false; |
| 470 | var line_iter = mem.tokenize(u8, file_contents, "\n"); | 436 | var line_iter = mem.tokenize(u8, file_contents, "\n"); |
| 471 | var idx: usize = 0; | 437 | var idx: usize = 0; |
| | 438 | if (if (line_iter.next()) |line| !std.mem.eql(u8, line, manifest_header) else true) { |
| | 439 | self.manifest_dirty = true; |
| | 440 | while (idx < input_file_count) : (idx += 1) { |
| | 441 | const ch_file = &self.files.items[idx]; |
| | 442 | self.populateFileHash(ch_file) catch |err| { |
| | 443 | self.failed_file_index = idx; |
| | 444 | return err; |
| | 445 | }; |
| | 446 | } |
| | 447 | try self.upgradeToExclusiveLock(); |
| | 448 | return false; |
| | 449 | } |
| 472 | while (line_iter.next()) |line| { | 450 | while (line_iter.next()) |line| { |
| 473 | defer idx += 1; | 451 | defer idx += 1; |
| 474 | | 452 | |
| ... | @@ -854,19 +832,13 @@ pub const Manifest = struct { | ... | @@ -854,19 +832,13 @@ pub const Manifest = struct { |
| 854 | defer contents.deinit(); | 832 | defer contents.deinit(); |
| 855 | | 833 | |
| 856 | const writer = contents.writer(); | 834 | const writer = contents.writer(); |
| 857 | var encoded_digest: [hex_digest_len]u8 = undefined; | 835 | try writer.writeAll(manifest_header ++ "\n"); |
| 858 | | | |
| 859 | for (self.files.items) |file| { | 836 | for (self.files.items) |file| { |
| 860 | _ = fmt.bufPrint( | 837 | try writer.print("{d} {d} {d} {} {d} {s}\n", .{ |
| 861 | &encoded_digest, | | |
| 862 | "{s}", | | |
| 863 | .{fmt.fmtSliceHexLower(&file.bin_digest)}, | | |
| 864 | ) catch unreachable; | | |
| 865 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ | | |
| 866 | file.stat.size, | 838 | file.stat.size, |
| 867 | file.stat.inode, | 839 | file.stat.inode, |
| 868 | file.stat.mtime, | 840 | file.stat.mtime, |
| 869 | &encoded_digest, | 841 | fmt.fmtSliceHexLower(&file.bin_digest), |
| 870 | file.prefixed_path.?.prefix, | 842 | file.prefixed_path.?.prefix, |
| 871 | file.prefixed_path.?.sub_path, | 843 | file.prefixed_path.?.sub_path, |
| 872 | }); | 844 | }); |