Commit 74aa906
worker: fix two silent output-corruption bugs (absolute symlinks + zero-digest files) (#2346)
* Resolve absolute symlinks in output upload instead of uploading raw targets
When the worker's work directory is populated via DirectoryCache, output
paths can be absolute symlinks pointing into the cache directory
(/var/.../directory_cache/...). The previous output collection code
uploaded these as raw SymlinkInfo with absolute targets that are
meaningless on the Bazel client, causing "No such file or directory"
errors when the client materialised the action result.
Detect absolute symlinks in output paths and resolve them: upload
directory contents as Tree protos and file contents as regular files.
Relative symlinks (intentionally created by the action) are still
preserved as symlinks.
Updates upload_dir_and_symlink_test to use a relative symlink (the
previous /dev/null absolute symlink is now resolved, breaking the old
assertion) and adds a new upload_absolute_symlink_resolves_contents
regression test that verifies an out-of-tree payload reachable via an
absolute symlink lands in CAS as a file.
Ported from upstream
0807153 (PR
#2243).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix zero-digest files missing from worker execution directories
FilesystemStore::get_file_entry_for_digest previously returned a
synthetic FileEntry for zero-digest blobs pointing to a content_path
that never exists on disk (FilesystemStore deliberately never persists
zero-byte files). Downstream worker output-materialisation code that
took the prefetched hardlink path would try to hard_link from this
non-existent source, fail, and either silently produce a missing file
or fall back without enough context to diagnose the failure.
This is the OUTPUT-materialisation companion to PR #2338's DirectoryCache
zero-byte fix, which lives on the input-fetch path. The two changes are
complementary: #2338 covers the directory cache short-circuit; this
covers the FilesystemStore API and the running_actions_manager fallback.
Changes:
- FilesystemStore::get_file_entry_for_digest now returns
Code::NotFound for zero digests instead of a synthetic FileEntry,
forcing callers to materialise empty files via fs::create_file rather
than hard_linking from a phantom source.
- running_actions_manager::download_to_directory adds err_tip context
on the zero-digest fs::create_file / write_all so a failure here is
attributable to the empty-file path.
- Updates the existing get_file_entry_for_zero_digest test to assert
the new NotFound behaviour and renames it to
get_file_entry_for_zero_digest_returns_not_found.
- Adds download_to_directory_zero_digest_empty_file_test that
exercises an empty file declared inside an input directory and
verifies it materialises on disk with zero bytes. This test
intentionally does NOT collide with PR #2338's DirectoryCache
zero-byte test (which validates a different short-circuit path).
Ported from upstream
19d7e20 (PR
#2243).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Close test coverage gaps for ported output-upload fixes
Audit of the two ported commits (e57b675b, b1dc3a1c) flagged two
correctness-relevant gaps. Both are now closed with strict assertions so
the underlying bugs cannot silently regress.
1. upload_absolute_symlink_to_directory_uploads_tree
The original e57b675b regression test covers absolute-symlink-to-file
but not absolute-symlink-to-directory, which exercises an entirely
separate code path (upload_directory + Tree proto serialisation). The
new test creates an out-of-tree directory containing inner.txt,
absolute-symlinks it into the work dir, runs the action, and:
- asserts output_directory_symlinks and output_file_symlinks are
BOTH empty (the symlink must NOT be preserved),
- asserts output_folders contains exactly one entry at the symlink
path, proving Tree-upload happened,
- walks the uploaded Tree, locates inner.txt, fetches its blob from
CAS and verifies the content. This proves the directory was
actually traversed and uploaded — not stubbed.
2. download_to_directory_zero_digest_empty_file_test (strengthened)
Extended the existing single-file test to cover:
- a second sibling zero-digest file (proves the path is not a
single-file fluke),
- a zero-digest file nested inside a subdirectory (proves the
recursive download_to_directory caller also handles NotFound from
get_file_entry_for_digest — not just the top-level invocation),
- strict per-file assertions: is_file, !is_symlink, len == 0, and
a read-back that confirms the file is actually empty rather than
a phantom dirent.
Verdict on the dropped checks:
- Dangling absolute symlink: source returns OutputType::None which is
the correct graceful behaviour, but writing a test for it requires
fabricating a broken symlink mid-action and is comparatively low
value; left uncovered intentionally.
- File mode on zero-digest materialisation: the worker uses
fs::create_file with no explicit mode, so the result follows umask
and would be brittle across CI environments. Length + type
assertions are the stronger invariant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* running_actions_manager_test: use /dev/null in upload_dir_and_symlink_test
Previous iterations of this PR sidestepped `/dev/null` by swapping the
absolute-symlink fixture for either a relative symlink (lost coverage)
or an out-of-tree real empty file (added scaffolding to dodge a corner
case we should just test directly).
Restore the original `ln -s /dev/null empty_sym` fixture. The post-fix
worker code resolves the absolute symlink via `fs::metadata` (follows),
sees a character device (`is_dir() == false`), and falls into the
"upload as file" branch. `upload_file` opens `/dev/null` and reads to
EOF — `/dev/null`'s contract is that reads return 0 bytes immediately
— producing the canonical sha256 empty digest (e3b0c44…) with size 0.
That's well-defined, harmless behavior and a real-world Bazel pattern
(rules sometimes use `ln -sf /dev/null x` to create empty outputs).
Test now locks in this contract directly, no scaffolding required.
Drops the `external_root` / `external_file` setup since it was only
needed to avoid `/dev/null`.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Marcus Eagan <marcuseagan@gmail.com>1 parent a00bf8c commit 74aa906
4 files changed
Lines changed: 631 additions & 51 deletions
File tree
- nativelink-store
- src
- tests
- nativelink-worker
- src
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
749 | 749 | | |
750 | 750 | | |
751 | 751 | | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
752 | 759 | | |
753 | | - | |
754 | | - | |
755 | | - | |
756 | | - | |
757 | | - | |
758 | | - | |
759 | | - | |
760 | | - | |
761 | | - | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
762 | 765 | | |
763 | 766 | | |
764 | 767 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1004 | 1004 | | |
1005 | 1005 | | |
1006 | 1006 | | |
1007 | | - | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
1008 | 1021 | | |
1009 | 1022 | | |
1010 | 1023 | | |
| |||
1020 | 1033 | | |
1021 | 1034 | | |
1022 | 1035 | | |
1023 | | - | |
1024 | | - | |
| 1036 | + | |
| 1037 | + | |
| 1038 | + | |
| 1039 | + | |
| 1040 | + | |
1025 | 1041 | | |
1026 | 1042 | | |
1027 | 1043 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
166 | | - | |
167 | | - | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
168 | 178 | | |
169 | 179 | | |
170 | 180 | | |
| |||
1384 | 1394 | | |
1385 | 1395 | | |
1386 | 1396 | | |
1387 | | - | |
1388 | | - | |
1389 | | - | |
1390 | | - | |
1391 | | - | |
1392 | | - | |
1393 | | - | |
1394 | | - | |
1395 | | - | |
1396 | | - | |
1397 | | - | |
1398 | | - | |
1399 | | - | |
1400 | | - | |
| 1397 | + | |
| 1398 | + | |
| 1399 | + | |
| 1400 | + | |
| 1401 | + | |
| 1402 | + | |
| 1403 | + | |
| 1404 | + | |
| 1405 | + | |
| 1406 | + | |
| 1407 | + | |
| 1408 | + | |
| 1409 | + | |
| 1410 | + | |
| 1411 | + | |
| 1412 | + | |
| 1413 | + | |
| 1414 | + | |
| 1415 | + | |
| 1416 | + | |
| 1417 | + | |
| 1418 | + | |
| 1419 | + | |
| 1420 | + | |
| 1421 | + | |
| 1422 | + | |
| 1423 | + | |
| 1424 | + | |
| 1425 | + | |
| 1426 | + | |
| 1427 | + | |
| 1428 | + | |
| 1429 | + | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
| 1449 | + | |
| 1450 | + | |
| 1451 | + | |
| 1452 | + | |
| 1453 | + | |
| 1454 | + | |
| 1455 | + | |
| 1456 | + | |
| 1457 | + | |
| 1458 | + | |
| 1459 | + | |
| 1460 | + | |
| 1461 | + | |
| 1462 | + | |
| 1463 | + | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
1401 | 1481 | | |
1402 | 1482 | | |
1403 | | - | |
1404 | | - | |
1405 | | - | |
1406 | | - | |
1407 | | - | |
1408 | | - | |
1409 | | - | |
1410 | | - | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
| 1494 | + | |
| 1495 | + | |
| 1496 | + | |
| 1497 | + | |
| 1498 | + | |
| 1499 | + | |
| 1500 | + | |
| 1501 | + | |
| 1502 | + | |
| 1503 | + | |
| 1504 | + | |
| 1505 | + | |
| 1506 | + | |
| 1507 | + | |
| 1508 | + | |
| 1509 | + | |
| 1510 | + | |
| 1511 | + | |
| 1512 | + | |
| 1513 | + | |
1411 | 1514 | | |
1412 | | - | |
1413 | | - | |
1414 | | - | |
1415 | 1515 | | |
1416 | 1516 | | |
1417 | 1517 | | |
| |||
0 commit comments