1

In the output of lsof, does the NAME column always output the resolved absolute pathnames of opened files (resolved in the sense of no symlink, . or .. in the pathname)?

For example, if I cd into some symlink to a directory, and then run a program to open a file under that directory, does lsof only show the resolved absolute pathname of the file?

Can I make lsof output the unresolved absolute pathnames of opened files (unresolved in the sense that directory symlinks should appear in pathnames)?

Thanks.

Tim
  • 101,790

2 Answers2

3

Without options, it should provide pathnames in resolved form. From lsof(8) manual:

An open file may be a regular file, a directory, a block special file, a character special file, an executing text reference, a library, a stream or a network file (Internet socket, NFS file or UNIX domain socket.) A specific file or all the files in a file system may be selected by path.

There can of course be errors, such as pathname too long, in which case pathname won't be resolved, but otherwise lsof relies apparently on a wrapper function around readlink(), which should do the job.

If you do specify a filename as in lsof [options] [--] names, it should be resolved:

names These are path names of specific files to list. Symbolic links are resolved before use. The first name may be separated from the preceding options with the ''--'' option.

Of course, standard rules for resolving filenames apply. Too many levels of symlinks will result in an error. lsof relies on MAXSYMLINKS variable hardcoded in the Linux kernel to max level of 40 symlinks, and for other systems where the variable is undefined - sets that to 32.

If you do specify -b option, filenames won't be resolved:

Third, if the names of your file system directories that lsof obtains from your system's mount table are symbolic links, lsof won't be able to resolve the links. This is because the -b option causes lsof to avoid the kernel readlink(2) function it uses to resolve symbolic links.

1

I don't know if a socket file in use should count as an open file, but in the case of unix domain sockets, lsof will only list as NAME (and is only able to find by) the exact name a socket was bound to, whether that's a relative or absolute path, and with any path artifacts left unchanged:

$ cd /tmp
$ nc -dUl ./././///sock &
[3] 6324
$ ls sock /tmp/sock ./sock
./sock  /tmp/sock  sock
$ lsof sock /tmp/sock ./sock
[nothing]
$ lsof ./././///sock
COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF   NODE NAME
nc      6324  XXX    3u  unix 0xffff9f9faab76000      0t0 175260 ./././///sock type=STREAM

Notice that in the case of a non-abstract unix domain socket, the path the socket was bound or connected to matters only until the kernel has resolved it; it's the [device,inode] tuple which is the real address the socket is bound or connected to:

$ cd /tmp
$ nc -dUl ./././///sock &
[1] 7293
$ mv sock SOCK
$ lsof -aUp $!
COMMAND  PID  USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
nc      7293   xxx    3u  unix 0xffff908d70bb5800      0t0 1137631 ./././///sock type=STREAM
$ lsof ./././///sock
lsof: status error on ./././///sock: No such file or directory
[stupid junk snipped]
$ lsof SOCK
[nothing]
$ nc -U ./././///sock
nc: unix connect failed: No such file or directory
$ nc -U SOCK
[connected ok]

lsof is also not able to find datagram sockets "connected" (ie. with their default send address set) to another socket, is making impossible to distinguish abstract sockets with a @ in their name from those with a NUL byte in their name, etc.

Just in case someone's wondering, all the needed info is available via the sock_diag interface (UDIAG_SHOW_VFS, UDIAG_SHOW_PEER look at the linux/unix_udiag.h header); it's just that lsof is too dumb to make use of it.

Note: the examples are using the nc from the netcat-openbsd package from Debian, which supports Unix sockets.