Resolve Merged-usr Paths Before Every dpkg-query -S Ownership Check - #1951
Conversation
tool_unshadow (host-setup/linux/install-tools.sh) asks dpkg-query -S "$resolved" whether a shadowing file is distro-owned. On a merged-usr host, where /bin and /sbin are symlinks into their /usr counterparts, a path spelled through the symlink (/bin/jq) matches nothing in dpkg's database when a package built after that host's usrmerge records only the canonical /usr/bin/jq spelling, so the ownership guard falls through and --upgrade removes a file dpkg still believes it owns. Adds a shared dpkg_owns_path helper and routes all three dpkg-query -S ownership call sites in the file through it: tool_unshadow's own guard, ripgrep_download_path (an unowned rg download reached through a merged-usr symlink would otherwise misreport as unowned and be removed), and powershell_path_is_unowned, preserving each call site's existing exit-status handling. The helper checks the given path first, since a package that predates a host's usrmerge (common on Debian bookworm and Ubuntu jammy/noble) keeps its file list recorded under the pre-merge /bin or /sbin spelling, and only falls back to a directory-canonicalized spelling when that misses. Only the directory is canonicalized, never the file name: an earlier version of this fix canonicalized the whole path with realpath, which also follows a leaf symlink and made an unowned symlink pointing at a dpkg-owned file (a hand-installed /usr/local/bin/jq -> /usr/bin/jq, say) misreport as owned; a local-strict-review pass caught that regression plus the pre-merge-spelling gap before either reached a pull request. Closes on promotion: #1866 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: ptr727/ProjectTemplate/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughLinux tool setup now uses a shared helper to check whether paths belong to installed packages. If the initial lookup reports “not found,” the helper retries with the directory canonicalized. ChangesLinux package ownership checks
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The package-ownership fallback follows the documented merged-usr behavior, and no material merge-blocking risk was identified. Architecture SummaryArchitecture risk: 🟡 Medium · up to The change affects 1 system. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
Reliability and maintainability
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1951 +/- ##
==========================================
Coverage ? 55.40%
==========================================
Files ? 16
Lines ? 7223
Branches ? 0
==========================================
Hits ? 4002
Misses ? 3221
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The change is narrowly scoped, preserves existing exit-status handling at each call site, and correctly addresses merged-usr ownership lookups without following leaf symlinks.
Review effort: Lite
Findings: None
What changed in this PR
This PR fixes dpkg ownership checks on merged-usr Linux hosts by ensuring dpkg-query -S is asked about a directory-canonicalized spelling (e.g., /usr/bin/...) when the original lookup via a symlinked directory (e.g., /bin/...) returns “not found”, preventing --upgrade from removing dpkg-owned files.
Changes:
- Add
dpkg_owns_pathhelper that checks ownership for the given path first, then retries with the directory canonicalized (preserving dpkg-query exit-status semantics). - Route the three existing
dpkg-query -Sownership checks (tool_unshadow,ripgrep_download_path,powershell_path_is_unowned) through the helper.
| File | Description |
|---|---|
| host-setup/linux/install-tools.sh | Introduces dpkg_owns_path and updates dpkg ownership call sites to handle merged-usr path spellings safely. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@coderabbitai review |
✅ Action performedReview finished.
|
… and Setup Tooling (#2000) ## Summary Promotes develop to main, carrying the 16 pull requests merged to develop since #1943. - **Wait-loop guard (requirement 7):** [#1999](#1999) credits a comparison bound only inside a `[`, `[[`, or `test` invocation, and [#1996](#1996) reconciles the requirement's README count and diagram with its hook. - **Registry:** [#1994](#1994) and [#1976](#1976) record Vantage-Config's line endings and description. - **Scripts and tooling:** - [#1988](#1988) and [#1972](#1972) harden `ruleset_id()`. - [#1974](#1974) and [#1949](#1949) fix `pr_review.py` `reply --match` and `wait`. - [#1969](#1969), [#1964](#1964), [#1954](#1954) and [#1951](#1951) fix host-setup tool shadowing, shims, hook ownership and dpkg ownership checks. - **Gates and audit:** - [#1980](#1980) triages a path collision. - [#1967](#1967) and [#1962](#1962) tighten sha-pin and version-literal checks. - [#1956](#1956) keeps a folded `if:` visible to the interface audit. Closes #1636 Closes #1639 Closes #1948 Closes #1971 Closes #1718 Closes #1934 Closes #1877 Closes #1880 Closes #1865 Closes #1889 Closes #1966 Closes #1906 Closes #1935 Closes #1901 Closes #1905 Closes #1866 Closes #1897 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
tool_unshadow(host-setup/linux/install-tools.sh) asksdpkg-query -S "$resolved"whether ashadowing file is distro-owned. On a merged-usr host, where
/binand/sbinare symlinks intotheir
/usrcounterparts, a path spelled through the symlink (/bin/jq) matches nothing indpkg's database when a package built after that host's usrmerge records only the canonical
/usr/bin/jqspelling, so the ownership guard falls through and--upgraderemoves a file dpkgstill believes it owns.
Adds a shared
dpkg_owns_pathhelper and routes all threedpkg-query -Sownership call sitesin the file through it:
tool_unshadow's own guard,ripgrep_download_path(an unownedrgdownload reached through a merged-usr symlink would otherwise misreport as unowned and be
removed), and
powershell_path_is_unowned, preserving each call site's existing exit-statushandling.
The helper checks the given path first, since a package that predates a host's usrmerge (common
on Debian bookworm and Ubuntu jammy/noble) keeps its file list recorded under the pre-merge
/binor/sbinspelling, and only falls back to a directory-canonicalized spelling when thatmisses. Only the directory is canonicalized, never the file name: an earlier version of this fix
canonicalized the whole path with
realpath, which also follows a leaf symlink and made anunowned symlink pointing at a dpkg-owned file (a hand-installed
/usr/local/bin/jq -> /usr/bin/jq, say) misreport as owned. Two rounds of a local-strict-review pass caught thatregression plus the pre-merge-spelling gap before either reached this pull request; the second
round found no further defect.
Verification
shellcheckandshfmt -dclean on the changed file (also run viascripts/docker_lint.py,along with
cspell).bash -nsyntax check clean.extracting the actual
dpkg_owns_pathfunction from the shipped script and covering: theoriginal
#1866symptom (/bin/lsrecognized as owned via its canonicalized directory), thecanonical path still recognized directly, a genuinely unowned scratch file still reporting
unowned, a nonexistent path reporting unowned rather than erroring, an unowned symlink pointing
at a dpkg-owned file not misreporting as owned (the first-round regression), a
pre-merge-recorded path matched via a stubbed
dpkg-query(the pre-merge-spelling gap), and adpkg-queryexit status of2propagating as2rather than collapsing to "unowned" -- allpass with the fix in place, and reverting to the original code fails the
#1866case asexpected.
first catching the two defects above and the second confirming the fix and finding nothing
further.
Closes on promotion: #1866
🤖 Generated with Claude Code
Summary by CodeRabbit