1 of 3

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 -V
  • rpm -Va
  • rpm -Vf
  • rpm -qf
  • rpm -qc
  • rpm -qd
  • rpm -ql
  • rpm -q --scripts
  • rpm2cpio
  • cpio
  • dnf reinstall
  • rpm --setperms
  • rpm --setugids

45 min3 exercises

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”.

PosLetterDiffers in
1Sfile size
2Mmode (permissions and type)
35digest (content)
4Ddevice major/minor
5Lsymlink target
6Uowner
7Ggroup
8Tmodification time
9Pcapabilities

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:

PatternUsually 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 cA 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

DamageRestore withNotes
Modes or owners changedrpm --setperms PKG and rpm --setugids PKGRestores attributes without touching content
A binary or library modified or missingdnf reinstall PKGRe-extracts all files; keeps modified %config(noreplace) files
One file, nothing else disturbedExtract it from the package: rpm2cpio PKG.rpm | cpio -idmv ./pathCopy the file out of the temporary directory
A configuration fileExtract the packaged copy, diff, then copy or mergeReinstall will not restore a modified config
SELinux labels wrongrestorecon -Rv PATHLabels 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

SymptomCauseFix
file ... is not owned by any packageLocally installed fileNot a package problem; check where it came from
dnf reinstall says package not availableRepository lacks that exact versiondnf download PKG-VERSION from another repo, or rpm -Uvh --force the file you have
Reinstall succeeded, config still wrong%config(noreplace) kept your fileExtract and copy the packaged file
Binary works, SELinux denies itLabel wrong, not the contentrestorecon -Rv, ls -Z
rpm -V shows .......T. on many filesTimestamps only (copy/restore)Usually ignorable
Cannot reinstall, no repositoryOfflineBoot 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.

  1. Warm-up

    Decode the verification line

    Without running anything, decode these three lines from rpm -V and say whether you would investigate each:

    S.5....T.  c /etc/chrony.conf
    .M.......    /usr/bin/ping
    missing   d /usr/share/doc/chrony/README
    Check 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 with rpm -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: c config, d doc, l licence, g ghost.

    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
  2. 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 with rpm -V, restore the binary and manual page with the least disturbance, and finish with rpm -V printing nothing. Explain why dnf reinstall is safe here.

    Check your result
    • rpm -V tree first lists the mode change (.M.......) and the missing documentation (missing d), then prints nothing after your repair.
    • ls -l /usr/bin/tree shows -rwxr-xr-x.
    • man tree works again.
    Hint

    dnf reinstall tree re-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 with rpm --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
  3. Challenge

    A reinstall that did not fix the config

    Setup: on a lab VM with chrony installed, 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 chronyd

    chronyc sources shows no sources. A colleague already ran dnf reinstall chrony and 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 final chronyc sources.

    Check your result
    • rpm -V chrony shows S.5....T. c /etc/chrony.conf before the fix (reinstall left the modified config) and nothing after.
    • chronyc sources lists at least one source.
    • You used rpm2cpio/cpio (or dnf download plus 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 with diff. 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