The Ur Project

Documenting the craft of stacking things so they remain stacked. For a very long time.

pkg-repair(1)

NAME

pkg-repair - restore an installed package's drifted files from its payload

SYNOPSIS

pkg repair [options] name

DESCRIPTION

pkg-verify(1) tells you a file isn't what the package installed. This puts it back.

Repair runs the same per-file check as verify, and for every subpackage that has drifted, it lays the package's payload down again. The payload is the exact bytes the manifest describes, so afterwards every file matches its manifest again.

name is a bare name, which repairs every installed subpackage of it, or name/subpkg for just one.

A package that verifies clean is left alone:

gzip verifies clean; nothing to repair

What gets rewritten?

The whole payload of each drifted subpackage, not just the files that failed. Every file is checked against its sha256 as it's written, then renamed into place. The clean files come out identical to what was there, so you won't notice, but they are rewritten. Subpackages that verified clean aren't touched at all.

A file that couldn't be read is a different matter. Verify has no verdict on it, so repair has no business overwriting it. It warns, and leaves it:

warning: cannot read /etc/secret/a.conf; not repaired

Where do the bytes come from?

From the repository the package came from, so that has to be reachable, either on local disk or over the network. A payload that pkg-stage-update(1) already fetched is used from the stage, without going back to the repository.

What it isn't for

Repair restores what the manifest says, and nothing else. It doesn't resolve anything, it doesn't upgrade, and it never changes the installed version. It's the answer to "something modified these files". It isn't the answer to "this package is broken", which is pkg-upgrade(1), or a remove and an install.

Racing another transaction

Repair takes the same lock on the root that an install takes, after any confirmation prompt. If another transaction replaced one of the drifted subpackages between the scan and the lock, laying the old payload down would be drift. So it refuses, restores nothing, and asks you to run it again:

error: repair: gzip/bin: changed by another transaction since the scan; nothing restored, re-run

And if a file still fails after it was restored, say on a read-only filesystem, that's an error too. It isn't reported as repaired.

OPTIONS

--root DIR
The filesystem root the package's files are restored under. Same as pkg verify --root. Defaults to /.
--dry-run
Report which files would be restored, and write nothing.

All global options apply; see pkg(1).

OUTPUT

gzip/bin: restored 2 file(s) /usr/bin/zcat /usr/share/man/man1/gzip.1 repaired 2 file(s)

The count is the files that had drifted, not every file the payload rewrote. Under --dry-run:

gzip/bin: would restore 2 file(s) /usr/bin/zcat /usr/share/man/man1/gzip.1 would repair 2 file(s) (dry run; nothing written)

CONFIRMATION

Repair changes the system. At the default confirm level of destructive it doesn't prompt. At all it does. See pkg.toml(5).

EXAMPLES

The usual sequence

Look, look closer, fix, and look again:

pkg verify gzip --quiet pkg repair gzip --dry-run pkg repair gzip pkg verify gzip --quiet

One subpackage

pkg repair gzip/bin

After an accidental delete

rm /usr/bin/gunzip pkg verify gzip --quiet pkg repair gzip

A mounted image

pkg --state-root /mnt/image/var/lib/pkg \ repair coreutils --root /mnt/image

Everything that drifted

pkg --json list | jq -r '.packages[].name' | sort -u \ | while read -r p; do pkg --json verify "$p" | jq -e '.failed > 0' >/dev/null \ && pkg --yes repair "$p" done

Look before you repair

Repair overwrites. When the drift might be deliberate, look first:

pkg verify openssh --quiet pkg repair openssh --dry-run

A config file you meant to change shows up as drift, and repair will happily put it back. That's what you asked for.

Scripting

pkg --json repair gzip --dry-run | jq '.repaired' pkg --json --yes repair gzip | jq '.repaired[].package'

Watching what it found

pkg -v repair coreutils --dry-run pkg -vvv repair coreutils --dry-run 2>&1 | grep drifted

EXIT STATUS

0 on success, including when there was nothing to do. 1 when the name isn't installed, a payload can't be fetched, another transaction got in between, or a file still fails after it was restored.

SEE ALSO

pkg(1), pkg-verify(1), pkg-query(1), pkg-upgrade(1), pkg-install(1), pkg-stage-update(1), pkg.toml(5), pkg-state(5), pkg-repository-layout(7)