Resolve Package Paths Safely with dpkg-realpath
You will finish with a repeatable way to canonicalise package paths against a staging or alternate installation root. 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. The checks are read-only, so they do not require 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 pathname, but it is not 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 run and record its version before relying on version-specific behaviour:
$ command -v dpkg-realpath
/usr/bin/dpkg-realpath
$ dpkg-realpath --version
Debian dpkg-realpath version 1.22.6.
The command was introduced in dpkg 1.20.1. The --zero option was added in dpkg 1.20.6. Older systems may not have the command or that option, so check rather than assuming that a script can use them everywhere.
Checkpoint: if command -v finds nothing, install or select the dpkg package supplied by your distribution. Do not replace the command with an unverified local script in a maintainer workflow.
2. Resolve a normal system path
Give the command one pathname. It prints an absolute canonical pathname followed by a newline:
$ dpkg-realpath /usr/bin/../bin/sh
/usr/bin/dash
Canonicalisation removes the .. component and follows the symbolic link from /usr/bin/sh to the shell installed here. Your result can differ if that link points to another shell. The command does not change the link or the target.
A pathname is required. A missing pathname is not a useful way to ask for the current directory, so keep the input explicit in scripts. Use --help to inspect the accepted options without changing anything.
3. Resolve inside a staging root
Create or choose a root directory that contains the filesystem you are examining. The following example assumes an image is mounted at /srv/image and wants the path /usr/bin/sh inside that image:
$ dpkg-realpath --root /srv/image /usr/bin/sh
/usr/bin/dash
The output is relative to /srv/image, so it is /usr/bin/dash, not /srv/image/usr/bin/dash. This is the form dpkg helpers can pass to other operations that already understand the installation root.
For a harmless local test, make a small tree under /tmp and add 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. That result is a useful sanity check: the tool is resolving the path in the image, not merely normalising characters in the caller's filesystem.
Warning: do not prepend the root to the pathname as well. This is wrong:
$ dpkg-realpath --root /srv/image /srv/image/usr/bin/sh
/srv/image/usr/bin/sh
It asks the tool to resolve a path named /srv/image/usr/bin/sh inside the image. The root would effectively be duplicated in the lookup. Give it the path as seen from the image root.
4. Select the root with DPKG_ROOT
Set DPKG_ROOT when the root is part of the surrounding dpkg-aware environment and you do not want to repeat the option:
$ DPKG_ROOT=/srv/image dpkg-realpath /usr/bin/sh
/usr/bin/dash
The environment variable is used only when neither --root nor --instdir is specified. An explicit option therefore 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
Both --root and --instdir set the same base directory for this command. Use the spelling that matches the surrounding dpkg workflow. If no option or DPKG_ROOT is present, the base is /.
5. Make output safe for pipelines
Normal output ends with a newline. When a pathname may contain a newline, request a NUL terminator and pass 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
The final byte is 00. Do not pipe this form into a tool that expects newline-delimited text, because it will receive no newline. --zero affects only the output terminator; it does not alter path resolution.
6. Diagnose a surprising result
First print the exact root and input your script is using. The most common mistake is passing a host path where an image-relative path is required:
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 that the intended file exists. The installed command can canonicalise a path lexically even when the final object is absent, so use an explicit existence check when 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"
Keep the existence check separate from the command's output. The returned path is root-relative; prefix it with the same trusted root only when inspecting the host-side image. Do not use an untrusted root value in a privileged command without validating where it points.
Done means
dpkg-realpath --versionidentified the installed dpkg release.- The input path was written relative to the selected filesystem root, without that root being repeated.
--root,--instdirorDPKG_ROOTselected the intended base directory.- Scripts use
--zeroonly with consumers that expect NUL-delimited output. - Existence was checked separately when a real file or link target was required.