Trace Symlinks and Permissions with namei
You will use namei to see how Linux resolves a pathname, one component at a time. That makes a confusing permission error, broken link or unexpected mount boundary much easier to locate. The examples take about five minutes and need an ordinary user account. They inspect paths only; none of the commands changes files or needs sudo.
The route
Jump straight to the step you need, or tick off Done means at the end.
Checkpoint: confirm the installed command
- Check which version is actually running. The command and its man page can come from different package revisions, so this is worth recording before you compare output with another machine.
namei --version
man namei
On the machine used for this guide, the executable reports util-linux 2.41.3. The installed man page identifies its source as util-linux 2.39.3. The pathname resolution model and options used here are documented by that man page, but formatting can vary between util-linux releases.
Checkpoint: make a harmless path to inspect
- Create a small directory tree containing a directory link and a file link. Keep the temporary directory name in a shell variable so cleanup is precise.
namei_demo=$(mktemp -d /tmp/namei-demo.XXXXXX)
mkdir -p "$namei_demo/real/logs"
printf 'sample\n' > "$namei_demo/real/logs/app.log"
ln -s real "$namei_demo/current"
ln -s logs/app.log "$namei_demo/current-log"
printf '%s\n' "$namei_demo"
The first link makes current/logs/app.log pass through real. The second link points directly to a relative target. Relative symlink targets are interpreted from the directory containing the link, not from your current working directory; that distinction explains many apparently nonsensical failures.
Follow every component
- Ask
nameito resolve the file through the directory link, while displaying modes and owners.
namei -l "$namei_demo/current/logs/app.log"
Output starts with an f: line for the pathname being resolved. Each following line represents a component. A leading d identifies a directory, l a symbolic link, and - a regular file. With -l, the output also includes mode bits, owner and group, with columns aligned for reading.
f: /tmp/namei-demo.XXXXXX/current/logs/app.log
drwxr-xr-x root root /
drwxrwxrwt root root tmp
drwx------ andy dixon namei-demo.XXXXXX
lrwxrwxrwx andy dixon current -> real
drwxr-xr-x andy dixon real
drwxr-xr-x andy dixon logs
-rw-r--r-- andy dixon app.log
The exact temporary directory name and ownership depend on your account. The useful line is usually the first unexpected one: a link whose target is not what you expected, a directory without search permission, or a final component that is not the type you assumed.
Inspect a link without following it
- Use
-nwhen you need to examine the link itself rather than its destination.
namei -n -l "$namei_demo/current-log"
--nosymlinks stops link traversal. This is useful when checking deployment output: it tells you that current-log is a link and shows its stored target, without treating the target's components as the path you asked about. A missing destination is therefore not hidden by a successful-looking resolution.
Without -n, the ordinary command follows links and reports the endpoint. If a link target is missing, namei prints the component it cannot resolve and returns a failure status. Do not confuse that diagnostic with a permission denial: check the component named in the output before changing ownership or modes.
Add context when diagnosing access
- Use the focused display options when the basic trace identifies a suspicious component.
namei -m "$namei_demo/current/logs/app.log"
namei -o "$namei_demo/current/logs/app.log"
namei -x "$namei_demo/current/logs/app.log"
namei -Z "$namei_demo/current/logs/app.log"
-m shows mode bits, -o shows owner and group, and -x marks mountpoint directories with D instead of d. The -Z option asks for the security context; it shows ? where context support is unavailable. Context output is not a substitute for checking the policy that governs access, and no option here changes that policy.
-l is shorthand for -m -o -v: modes, owners and vertical alignment. If you only need one of those details, using the narrower option makes command output easier to scan and easier to capture in a ticket.
Common traps
- A pathname is not just its final filename. Access normally requires search permission on every directory component, so inspect the whole trace.
- The
f:marker means the pathname currently being resolved, not necessarily a regular file. The final type can be a directory, device node, socket or FIFO. - A leading
?marks an error of some kind. Read the message beside the component; do not infer that every error is a mode-bit problem. - Do not add
sudoreflexively. It can make a root-owned path look accessible while hiding the permissions your service account actually has. Use the real account's view when diagnosing a service. - Use one pathname per test while learning the output. The command accepts multiple pathnames, but separate runs make it harder to attribute a failure to the wrong path.
Checkpoint: verify and clean up
- Confirm that the endpoint is the file you created, then remove only the temporary tree.
test -f "$namei_demo/real/logs/app.log" && printf 'endpoint exists\n'
rm -rf -- "$namei_demo"
unset namei_demo
The final command is the only destructive example. It removes the temporary directory created by the guide, not a system path. If you used a different value for the variable, stop and inspect it with printf '%s\n' "$namei_demo" before running the removal. If you are investigating a real path, omit this cleanup block.
Done means
- You can identify every directory, link and endpoint in a pathname trace.
- You know when to use
-n,-m,-o,-x,-Zand-l. - You have checked the component named by an error instead of changing permissions blindly.
- Your temporary test tree is removed, or you deliberately kept it for further inspection.