Package and RPM Database RepairLesson 1 of 3
Verifying and repairing installed packages
Read rpm -V results, find which package owns a file, and restore damaged files without losing local configuration.
rpm -Vrpm -Varpm -Vfrpm -qfrpm -qcrpm -qdrpm -qlrpm -q --scriptsrpm2cpiocpiodnf reinstallrpm --setpermsrpm --setugids
After this lesson you can
- Run rpm -V and read each position of the result string and the file-type letter.
- Decide for each reported file whether it is expected (a local configuration change) or damage.
- Restore a binary, a document and a configuration file, using reinstall or extraction from the package as appropriate.
The idea
Every installed package leaves a record in the RPM database: each file it owns, with its expected size, mode, owner, digest (a SHA-256 hash on current releases) and modification time. Verification compares that record with the files on disk. It does not tell you why something changed, but it tells you what changed, which is the first question in most troubleshooting.
rpm -V PACKAGE # verify one package
rpm -Va # verify everything (slow, run it when idle)
rpm -Vf /path # verify the package that owns this file
No output means everything matches. Output means differences.
Reading a result line
SM5DLUGTP c /path/to/file
The nine characters are fixed positions; a dot means “matches”, a letter means “differs”.
| Pos | Letter | Differs in |
|---|---|---|
| 1 | S | file size |
| 2 | M | mode (permissions and type) |
| 3 | 5 | digest (content) |
| 4 | D | device major/minor |
| 5 | L | symlink target |
| 6 | U | owner |
| 7 | G | group |
| 8 | T | modification time |
| 9 | P | capabilities |
An optional tenth column adds . or ? for SELinux and other attributes depending on the RPM version. Then the file-type letter: c configuration, d documentation, l licence, g ghost, r readme, blank for an ordinary file. The word missing replaces the nine characters when the file does not exist.
How to judge a line:
| Pattern | Usually means |
|---|---|
S.5....T. c /etc/... | A configuration file edited on purpose |
.M....... /usr/bin/... | Mode changed: chmod, or an installer script; compare with rpm -qlv |
SM5....T. /usr/bin/... | Binary replaced or modified: a manually copied update, damage, or tampering |
missing d /usr/share/doc/... | Documentation removed; harmless to function |
.......T. /usr/lib/... | Only the timestamp: touched or restored, usually harmless |
..5...... /etc/... with no c | A non-config file whose content changed: investigate |
Configuration (c) differences are expected and should be documented, not “repaired”. Everything else needs an explanation.
Which package owns it
rpm -qf /usr/bin/labapp # owning package
rpm -ql labapp # all files; add -v for modes
rpm -qc labapp # configuration files
rpm -qd labapp # documentation files
rpm -q --scripts labapp # install/remove scriptlets (what the package does when it runs)
A file that belongs to no package (file ... is not owned by any package) is something an administrator or a script put there. That is not an error by itself, but it is outside what verification covers.
Repair options
| Damage | Restore with | Notes |
|---|---|---|
| Modes or owners changed | rpm --setperms PKG and rpm --setugids PKG | Restores attributes without touching content |
| A binary or library modified or missing | dnf reinstall PKG | Re-extracts all files; keeps modified %config(noreplace) files |
| One file, nothing else disturbed | Extract it from the package: rpm2cpio PKG.rpm | cpio -idmv ./path | Copy the file out of the temporary directory |
| A configuration file | Extract the packaged copy, diff, then copy or merge | Reinstall will not restore a modified config |
| SELinux labels wrong | restorecon -Rv PATH | Labels are not part of rpm’s file digest checks the same way |
dnf download PKG fetches the .rpm without installing it, from the same repositories.
Try it
This lesson’s outputs are real, from rpm 4.18 on a package built for the test (labapp, with a binary, a %config(noreplace) file and a document).
Verification after damage
Three changes were made: a line appended to the config, the binary replaced and its mode changed, and the README deleted.
rpm -V labapp; echo "rc=$?"
S.5...... c /etc/labapp/labapp.conf
SM5...... /usr/bin/labapp
missing d /usr/share/doc/labapp/README
rc=1
The exit code is 1 when something differs, which makes rpm -V usable in scripts. Reading the three lines: the config differs in size and digest (an edit), the binary differs in size, mode and content (replaced), and the doc is missing. Note that the config line has the c type letter and the other two do not (doc has d).
Ask the database
rpm -qf /usr/bin/labapp
rpm -qc labapp
rpm -qd labapp
labapp-1.0-1.noarch
/etc/labapp/labapp.conf
/usr/share/doc/labapp/README
Pull one pristine file out of the package
mkdir ex && cd ex
rpm2cpio ../labapp-1.0-1.noarch.rpm | cpio -idm
ls -l usr/bin/labapp
cat usr/bin/labapp
-rwxr-xr-x 1 root root 26 Nov 4 17:23 usr/bin/labapp
#!/bin/sh
echo labapp 1.0
This gives you the original file and its mode, without installing anything. Use it to diff a config or to restore a single file.
Reinstall
rpm -Uvh --force labapp-1.0-1.noarch.rpm
rpm -V labapp; echo "rc=$?"
labapp-1.0-1 ########################################
Cleaning up / removing...
labapp-1.0-1 ########################################
rc=0
(On RHEL you use dnf reinstall labapp, which does the same through the repositories.) The binary and the README came back. A test where only the config had been modified, then reinstalled, produced this:
cat /etc/labapp/labapp.conf
port=8080
mode=prod
mode=debug
The appended mode=debug is still there: the reinstall kept the modified configuration file. That is the noreplace protection doing its job, and it is exactly why a reinstall does not “reset” a configuration. To get the packaged version, extract it (above) and copy it over, or look for a .rpmnew file next to the original (ls /etc/labapp/) when a newer package has different defaults.
When repair does not work
| Symptom | Cause | Fix |
|---|---|---|
file ... is not owned by any package | Locally installed file | Not a package problem; check where it came from |
dnf reinstall says package not available | Repository lacks that exact version | dnf download PKG-VERSION from another repo, or rpm -Uvh --force the file you have |
| Reinstall succeeded, config still wrong | %config(noreplace) kept your file | Extract and copy the packaged file |
| Binary works, SELinux denies it | Label wrong, not the content | restorecon -Rv, ls -Z |
rpm -V shows .......T. on many files | Timestamps only (copy/restore) | Usually ignorable |
| Cannot reinstall, no repository | Offline | Boot installation media or use the local repository from the next lesson |
Going further
Beyond the exam. rpm -Va is a point-in-time comparison against a database on the same machine. If an attacker can write to /var/lib/rpm, the comparison can be made to say anything; AIDE with a baseline kept elsewhere (see the file integrity lesson) covers that case. Use rpm -V for accidents, AIDE for intrusion.
Practice
0 of 3 done
Do these on your own system. They get harder. Check each result, then tick it off.
- Warm-up
Decode the verification line
Without running anything, decode these three lines from
rpm -Vand say whether you would investigate each:S.5....T. c /etc/chrony.conf .M....... /usr/bin/ping missing d /usr/share/doc/chrony/READMECheck your result
- Line 1: size, digest (MD5/SHA) and mtime differ on a config file (
c): normal if you edited it; investigate if you did not. - Line 2: mode differs on a non-config binary: suspicious (a permission change, possibly
chmod); check withrpm -qlv. - Line 3: a documentation file (
d) is missing: harmless to function, restore if you need it.
Hint
The first nine characters are fixed positions: S size, M mode, 5 digest, D device, L link, U user, G group, T mtime, P capabilities. A dot means the attribute matches. The letter after them is the file type:
cconfig,ddoc,llicence,gghost.Solution
S 5 T on a config file -> expected after editing M only on a binary -> mode changed; compare with rpm -qlv missing + d -> doc file removed; low impact - Line 1: size, digest (MD5/SHA) and mtime differ on a config file (
- Practice
Damage three files and restore them
On a lab system pick a small package you can afford to break, for example
tree(sudo dnf install -y tree). Change its binary's mode, delete its manual page, and append a line to a configuration file if it has one. Show the damage withrpm -V, restore the binary and manual page with the least disturbance, and finish withrpm -Vprinting nothing. Explain whydnf reinstallis safe here.Check your result
rpm -V treefirst lists the mode change (.M.......) and the missing documentation (missing d), then prints nothing after your repair.ls -l /usr/bin/treeshows-rwxr-xr-x.man treeworks again.
Hint
dnf reinstall treere-extracts every file from the package, which restores modes and missing files. Files marked as configuration (%config(noreplace)) that you have modified are kept; a pure permission change can also be fixed withrpm --setperms tree.Solution
sudo dnf install -y tree sudo chmod 700 /usr/bin/tree sudo rm /usr/share/man/man1/tree.1* rpm -V tree sudo dnf reinstall -y tree rpm -V tree; echo "rc=$?" ls -l /usr/bin/tree - Challenge
A reinstall that did not fix the config
Setup: on a lab VM with
chronyinstalled, break the configuration file:sudo cp -a /etc/chrony.conf /root/chrony.conf.mine sudo sed -i '/^pool\|^server/d' /etc/chrony.conf sudo systemctl restart chronydchronyc sourcesshows no sources. A colleague already randnf reinstall chronyand says the problem is still there. Find out why, restore the packaged default of that one file without touching others, compare it with your backup, and bring time sync back. Show the finalchronyc sources.Check your result
rpm -V chronyshowsS.5....T. c /etc/chrony.confbefore the fix (reinstall left the modified config) and nothing after.chronyc sourceslists at least one source.- You used
rpm2cpio/cpio(ordnf downloadplus extraction) to get a pristine copy.
Hint
Files marked
%config(noreplace)are protected: when you changed them, the package manager keeps your version (and may write the packaged one as.rpmnew). To restore the original, extract it from the package and copy it over, or compare withdiff. Look for/etc/chrony.conf.rpmnew.Solution
rpm -V chrony ls /etc/chrony.conf* cd /tmp && dnf download chrony rpm2cpio chrony-*.rpm | cpio -idmv ./etc/chrony.conf diff /tmp/etc/chrony.conf /etc/chrony.conf sudo cp -p /tmp/etc/chrony.conf /etc/chrony.conf sudo restorecon -v /etc/chrony.conf sudo systemctl restart chronyd chronyc sources