| ... | ... | @@ -145,6 +145,8 @@ pub const bin_digest_len = 16; |
| 145 | 145 | pub const hex_digest_len = bin_digest_len * 2; |
| 146 | 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 | 150 | const manifest_file_size_max = 50 * 1024 * 1024; |
| 149 | 151 | |
| 150 | 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 | 154 | /// fastest options right now. |
| 153 | 155 | pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); |
| 154 | 156 | |
| 155 | | /// Initial state, that can be copied. |
| 156 | | pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length); |
| 157 | /// Initial state with random bytes, that can be copied. |
| 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 | 167 | pub const File = struct { |
| 159 | 168 | prefixed_path: ?PrefixedPath, |
| ... | ... | @@ -391,72 +400,29 @@ pub const Manifest = struct { |
| 391 | 400 | @memcpy(manifest_file_path[0..self.hex_digest.len], &self.hex_digest); |
| 392 | 401 | manifest_file_path[hex_digest_len..][0..ext.len].* = ext.*; |
| 393 | 402 | |
| 394 | | if (self.files.items.len == 0) { |
| 395 | | // If there are no file inputs, we check if the manifest file exists instead of |
| 396 | | // comparing the hashes on the files used for the cached item |
| 397 | | while (true) { |
| 398 | | if (self.cache.manifest_dir.openFile(&manifest_file_path, .{ |
| 399 | | .mode = .read_write, |
| 400 | | .lock = .Exclusive, |
| 401 | | .lock_nonblocking = self.want_shared_lock, |
| 402 | | })) |manifest_file| { |
| 403 | | self.manifest_file = manifest_file; |
| 404 | | self.have_exclusive_lock = true; |
| 405 | | break; |
| 406 | | } else |open_err| switch (open_err) { |
| 407 | | error.WouldBlock => { |
| 408 | | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ |
| 409 | | .lock = .Shared, |
| 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; |
| 403 | while (true) { |
| 404 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ |
| 405 | .read = true, |
| 406 | .truncate = false, |
| 407 | .lock = .Exclusive, |
| 408 | .lock_nonblocking = self.want_shared_lock, |
| 409 | })) |manifest_file| { |
| 410 | self.manifest_file = manifest_file; |
| 411 | self.have_exclusive_lock = true; |
| 412 | break; |
| 413 | } else |err| switch (err) { |
| 414 | error.WouldBlock => { |
| 415 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ |
| 416 | .mode = .read_write, |
| 417 | .lock = .Shared, |
| 418 | }); |
| 446 | 419 | break; |
| 447 | | } else |err| switch (err) { |
| 448 | | error.WouldBlock => { |
| 449 | | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ |
| 450 | | .lock = .Shared, |
| 451 | | }); |
| 452 | | break; |
| 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 | | } |
| 420 | }, |
| 421 | // There are no dir components, so you would think that this was |
| 422 | // unreachable, however we have observed on macOS two processes racing |
| 423 | // to do openat() with O_CREAT manifest in ENOENT. |
| 424 | error.FileNotFound => continue, |
| 425 | else => |e| return e, |
| 460 | 426 | } |
| 461 | 427 | } |
| 462 | 428 | |
| ... | ... | @@ -469,6 +435,18 @@ pub const Manifest = struct { |
| 469 | 435 | var any_file_changed = false; |
| 470 | 436 | var line_iter = mem.tokenize(u8, file_contents, "\n"); |
| 471 | 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 | 450 | while (line_iter.next()) |line| { |
| 473 | 451 | defer idx += 1; |
| 474 | 452 | |
| ... | ... | @@ -854,19 +832,13 @@ pub const Manifest = struct { |
| 854 | 832 | defer contents.deinit(); |
| 855 | 833 | |
| 856 | 834 | const writer = contents.writer(); |
| 857 | | var encoded_digest: [hex_digest_len]u8 = undefined; |
| 858 | | |
| 835 | try writer.writeAll(manifest_header ++ "\n"); |
| 859 | 836 | for (self.files.items) |file| { |
| 860 | | _ = fmt.bufPrint( |
| 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", .{ |
| 837 | try writer.print("{d} {d} {d} {} {d} {s}\n", .{ |
| 866 | 838 | file.stat.size, |
| 867 | 839 | file.stat.inode, |
| 868 | 840 | file.stat.mtime, |
| 869 | | &encoded_digest, |
| 841 | fmt.fmtSliceHexLower(&file.bin_digest), |
| 870 | 842 | file.prefixed_path.?.prefix, |
| 871 | 843 | file.prefixed_path.?.sub_path, |
| 872 | 844 | }); |