Skip to content

Report package database damage and guard package caches during transactions - #3

Merged
debba merged 1 commit into
mainfrom
package-database-health
Aug 26, 2026
Merged

Report package database damage and guard package caches during transactions#3
debba merged 1 commit into
mainfrom
package-database-health

Conversation

@debba

@debba debba commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Why

An interrupted pacman transaction leaves entries in /var/lib/pacman/local with desc, files, and mtree truncated to zero bytes. Pacman still considers the package installed but believes it owns no files, so the next upgrade of that package fails with file conflicts against its own files:

zen-browser-bin: /opt/zen-browser-bin/zen-bin already exists in filesystem

The state is silent until an upgrade hits it, and the error message points at the filesystem rather than at the database, which is the wrong place to look.

The second half of the problem is that emptying a package cache during a transaction is unsafe, and TuxCleaner offered exactly that. A transaction installs archives straight out of ~/.cache/yay, ~/.cache/paru, and the system package cache. Cleaning them mid upgrade can fail the upgrade, and it also removes the archives needed to repair the damage afterwards.

What changes

status reports package database integrity. SystemStatus gains an optional package_database field with the entry count, the damaged entry names, and whether a transaction is running. Only desc counts as evidence: files and mtree are legitimately empty for meta packages such as base and base-devel, which own no files, so keying on those would report healthy systems as broken.

Package database: 1625 entries at /var/lib/pacman/local
  A package transaction is running right now.
  All entries have readable metadata.

Package cache cleanup is refused while a transaction runs. The signal is the presence of /var/lib/pacman/db.lck, which pacman creates for the duration of a transaction and removes afterwards. The guard covers paccache, pacman, apt-get, and dnf actions plus removal of the AUR helper build caches. Cleanups unrelated to package state still run normally.

The apt and dnf lock files are deliberately not used: they are permanent files held with flock, so their existence proves nothing about whether a transaction is in progress. Reporting for dpkg and rpm is left unimplemented rather than guessed at, since those databases are single files and need a different integrity check.

Scope and conventions

  • Distribution-specific paths live in src/distro.rs (package_database_path, package_transaction_lock_path).
  • No new external command is executed, so the executor allowlist is unchanged.
  • SystemStatus gains a field rather than renaming or removing one, so JSON output stays compatible.
  • The lock paths are injectable through Executor::with_transaction_locks so the refusal cases are testable without touching the host.

Tests

Nine new tests covering both the intended behavior and the refusal cases:

  • damaged entries detected, meta packages without files stay healthy, transaction detected from the lock file, missing database is an error rather than a clean bill of health
  • package cache cleanup and build cache removal refused while the lock is held, with the archive proven still present afterwards
  • cleanup allowed once the lock is gone, unrelated cleanups unaffected during a transaction

Full local validation passes:

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets            # 65 passed
sh -n install.sh scripts/*.sh docs/demo/fixtures/bin/*
sh tests/install.sh

Verified against a real system: the new output correctly reported a live pacman -Syu holding the lock, and a clean database after repair.

…ctions

Add a package database integrity check to `status`. An entry whose `desc`
file is missing or empty is reported as damaged, which is what an
interrupted transaction or an unclean shutdown leaves behind: pacman then
knows the package is installed but owns no files, and the next upgrade of
it fails with file conflicts against its own files. `files` and `mtree`
are legitimately empty for meta packages such as `base`, so only `desc`
is treated as evidence of damage.

Refuse package cache cleanup while a package transaction is running. A
transaction installs archives straight out of `~/.cache/yay`,
`~/.cache/paru`, and the system package cache, so emptying them mid
upgrade can fail the upgrade and remove the archives needed to repair it
afterwards. Presence of `/var/lib/pacman/db.lck` is the signal, since
pacman creates it for the duration of a transaction and removes it after.
The apt and dnf lock files are permanent and held with `flock`, so their
existence is not used as a signal.

Cleanups unrelated to package state still run during a transaction.
@debba
debba merged commit 701c526 into main Aug 26, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant