Resolve Package Paths Safely with dpkg-realpath
dpkg-realpath canonicalises a path against a staging or alternate installation root. It sounds dull, until a build script quietly prefixes that root twice and everything breaks. The examples use dpkg-realpath from dpkg 1.22.6ubuntu6.6, installed on this machine, and take about ten minutes. You need a shell and a pathname to inspect; every check here is read-only, so nothing needs elevated privileges.
The route
Jump straight to the step you need, or tick off Done means at the end.
The awkward detail is also the useful one: the root directory is used while resolving the path, but it is never printed as a prefix. Pass /etc/example, not /srv/image/etc/example, when /srv/image is the root of the image.
1. Check the installed command
Confirm which executable will actually run, and record its version before you rely on anything version-specific:
$ command -v dpkg-realpath
/usr/bin/dpkg-realpath
$ dpkg-realpath --version
Debian dpkg-realpath version 1.22.6.
The command arrived in dpkg 1.20.1, and --zero followed in dpkg 1.20.6. Older systems may have neither, so check rather than assume a script can use them everywhere it runs.
Checkpoint
If command -v finds nothing, install or select the dpkg package your distribution ships. Do not swap in an unverified local script inside a maintainer workflow.
2. Resolve a normal system path
Give the command one pathname and it prints an absolute canonical pathname, followed by a newline:
$ dpkg-realpath /usr/bin/../bin/sh
/usr/bin/dash
Canonicalisation drops the .. component and follows the symbolic link from /usr/bin/sh to whichever shell is installed here. Your result can differ if that link points somewhere else. The command never changes the link or its target, it only reports.
A pathname is required; a missing one is not a sanctioned way to ask for the current directory, so keep the input explicit in any script. --help lists the accepted options without changing anything.
3. Resolve inside a staging root
Create or choose a root directory holding the filesystem you are examining. Here an image is mounted at /srv/image, and the goal is the path /usr/bin/sh inside it:
$ dpkg-realpath --root /srv/image /usr/bin/sh
/usr/bin/dash
The output is relative to /srv/image, so it reads /usr/bin/dash, not /srv/image/usr/bin/dash. That is the exact form other dpkg helpers expect when they already understand the installation root.
For a harmless local test, build a small tree under /tmp with a relative symbolic link:
$ mkdir -p /tmp/dpkg-image/etc /tmp/dpkg-image/var/lib
$ printf '%s\n' data > /tmp/dpkg-image/etc/target
$ ln -s ../etc/target /tmp/dpkg-image/var/lib/link
$ dpkg-realpath --root /tmp/dpkg-image /var/lib/link
/var/etc/target
The link target is relative to /var/lib, so ../etc/target resolves to /var/etc/target. A useful sanity check: the tool is resolving the path inside the image, not simply tidying up characters against the caller's own filesystem.
Warning
Do not prepend the root to the pathname as well. This is the wrong move:
$ dpkg-realpath --root /srv/image /srv/image/usr/bin/sh
/srv/image/usr/bin/sh
That asks the tool to resolve a path literally named /srv/image/usr/bin/sh inside the image, effectively duplicating the root in the lookup. Give it the path exactly as seen from the image root.
4. Select the root with DPKG_ROOT
Set DPKG_ROOT when the root belongs to the surrounding dpkg-aware environment and you would rather not repeat the option every time:
$ DPKG_ROOT=/srv/image dpkg-realpath /usr/bin/sh
/usr/bin/dash
The environment variable is only consulted when neither --root nor --instdir is given, so an explicit option always wins, which makes a command-line override easy to audit:
$ DPKG_ROOT=/wrong-image dpkg-realpath --root /srv/image /usr/bin/sh
/usr/bin/dash
--root and --instdir set the same base directory for this command; pick the spelling that matches the surrounding dpkg workflow. With neither option nor DPKG_ROOT present, the base is /.
5. Make the output safe for pipelines
Normal output ends with a newline. When a pathname might contain one, ask for a NUL terminator and hand the result to a consumer that understands NUL-delimited records:
$ dpkg-realpath --zero --root /srv/image /usr/bin/sh | od -An -t x1
2f 75 73 72 2f 62 69 6e 2f 64 61 73 68 00
That final byte is 00. Do not pipe this form into a tool expecting newline-delimited text, because it gets no newline at all. --zero only changes the output terminator; it never touches how the path resolves.
6. Diagnose a surprising result
Print the exact root and input your script is using first. The most common mistake is handing it a host path where the tool wants an image-relative one:
root=/srv/image
path=/usr/bin/sh
printf 'root=%s path=%s\n' "$root" "$path"
dpkg-realpath --root "$root" "$path"
status=$?
printf 'status=%s\n' "$status"
A successful status is not proof the file exists: the command can canonicalise a path lexically even when the target is absent, so add an explicit existence check whenever that matters:
resolved=$(dpkg-realpath --root "$root" "$path") || exit $?
test -e "$root$resolved" || {
printf 'not present in root: %s\n' "$root$resolved" >&2
exit 1
}
printf '%s\n' "$resolved"
Security boundary
Keep the existence check separate from the command's output. The returned path is root-relative; only prefix it with the same trusted root when inspecting the host side of that image, and never feed an untrusted root value into a privileged command without validating where it actually points.
Done means
- Version confirmed.
dpkg-realpath --versionidentified the installed dpkg release. - Path written relative to the root, once. Not repeated inside the pathname itself.
- Root selected on purpose. Via
--root,--instdirorDPKG_ROOT, deliberately. --zeroonly reaches consumers built for it. NUL-delimited output, NUL-aware reader.- Existence checked separately, whenever a real file or link target actually mattered.