cvd host_bugreport: don't discard the whole archive on a directory entry - #3211
Open
shuowanghsu wants to merge 1 commit into
Open
shuowanghsu wants to merge 1 commit into
shuowanghsu wants to merge 1 commit into
Conversation
`AddFileAt` guarded its input only with `FileExists`, which is a `stat` check and therefore succeeds for anything that exists, including directories. libzip does not read the source until `WritableZip::Finalize`, where reading a directory fails with EISDIR. libzip then calls `zip_source_rollback_write`, which discards the entire archive rather than just the offending entry. `AddNetsimdLogs` triggers exactly that. It listed the netsimd temp directory with a non-recursive `DirectoryContents`, so when netsimd runs with `--pcap` it handed the `pcap/` directory straight to `AddFileAt`. The capture files inside were never collected, and the directory entry destroyed the rest of the bug report on its way out. Require a regular file in `AddFileAt` so a bad entry can only lose itself, and walk the netsimd tree so the captures are actually gathered. The guard tests `S_ISREG` rather than just rejecting directories, because a FIFO or a socket reaches `Finalize` the same way, and opening one there can fail or block indefinitely. `WalkDirectory` is not usable directly for that walk: it reports directories to its callback, and an error returned from the callback aborts the remaining walk. It also cannot be changed to skip directories, because `SymlinkHostPackage`, `WriteFsConfig` and `LinkOrCopyDirectoryContentsRecursively` all depend on receiving them. Add a `WalkDirectoryFiles` sibling instead, which reports only non-directories and declines to descend through symlinked directories so a symlink loop cannot trap the walk. The new `zip_file_test` cases fail against the unpatched library, the archive one with `zip_close` reporting "Read error: Is a directory". Test: bazel test //cuttlefish/host/libs/zip:zip_file_test //cuttlefish/common/libs/utils:files_test Test: bazel build //cuttlefish/host/commands/host_bugreport:cvd_internal_host_bugreport Bug: 562650640
shuowanghsu
force-pushed
the
netsimd-bugreport-eisdir
branch
from
September 22, 2026 00:19
de59a10 to
d2621e2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AddFileAtguarded its input only withFileExists, which is astatcheck and therefore succeeds for anything that exists, including directories. libzip does not read the source untilWritableZip::Finalize, where reading a directory fails withEISDIR. libzip then callszip_source_rollback_write, which discards the entire archive rather than just the offending entry.AddNetsimdLogshits this: it lists the netsimd temp directory with a non-recursiveDirectoryContents, so when netsimd runs with--pcapit hands thepcap/directory straight toAddFileAt. The result is thatcvd host_bugreportproduces an archive containing almost nothing, and the netsimd capture files are never collected either.Against the unpatched library the new test reproduces it exactly:
Changes
AddFileAt. This is the important one — today any caller that passes a directory silently destroys the archive it was building, and does so at finalization time, far from the offending call site. After this change a bad entry can only lose itself. The check isS_ISREGrather than just a directory test, because a FIFO or a socket reachesFinalizethe same way, and opening one there can fail or block indefinitely.pcap/are actually included. Per-entry errors are logged rather than returned, so one bad file cannot abort the rest of the walk.WalkDirectoryFilesnext to the existingWalkDirectoryincommon/libs/utils/files.h, reporting only non-directories. Symlinks are not followed when deciding whether to recurse, so a symlink loop cannot trap the walk.AddFileAtrejects directories and FIFOs, that a rejected entry no longer takes unrelated entries down with it atFinalize, and thatWalkDirectoryFilesrecurses whileWalkDirectorykeeps reporting directories.Notes
WalkDirectoryFilesis a new sibling rather than a change toWalkDirectory, becauseSymlinkHostPackage,WriteFsConfigandLinkOrCopyDirectoryContentsRecursivelyall rely onWalkDirectoryinvoking the callback for directories.WriteFsConfigin particular would silently drop directory entries fromfilesystem_config.txt. Happy to consolidate them differently if you'd prefer.logs/,tombstones/andrecording/are left non-recursive. They are flat in practice, and after change 1 an unexpected subdirectory there is merely logged and skipped rather than fatal. Can extendWalkDirectoryFilesto them if you'd like uniformity.Testing
All new test cases pass with the fix; the
zip_file_testcases fail without it.