Is there platform independent, non-blocking way to tell if file descriptor is an on-disk file (regular/directory)

Viewed 323

I am looking for type of a file descriptor without the possibility of blocking in the kernel. I am aware I can use fstat(2) but fstat will also get me all sorts of metadata information (access time etc) which may block for an arbitrary amount of time (especially on networked file systems).

EDIT: I am looking for a syscall to do this, spawning a separate process is not acceptable because spawning a process and reading its results is certainly not instant.

The only bit of information I need to know is really if the file descriptor is an on-disk "file" (S_IFREG, S_IFLNK, S_IFDIR) or not. Alternatively, if I could tell if it's a socket (S_IFSOCK), fifo (S_IFIFO), or character device (S_IFCHR) that'd be fine too.

I'm pretty sure any kernel will have this information readily available and I'm interested if that can be surfaced to user-space without blocking.

A portable solution (macOS & Linux at least) would be much appreciated.

Thank you!

2 Answers

On Linux you can look into proc pseudo-filesystem, /proc/<pid>/fd, e.g.:

[max@supernova:/proc/7275/fd] $ ls -l /proc/7275/fd/
total 0
lr-x------ 1 max max 64 Oct 12 16:28 0 -> /dev/null
l-wx------ 1 max max 64 Oct 12 16:28 1 -> 'pipe:[69689]'
lrwx------ 1 max max 64 Oct 12 16:28 10 -> 'socket:[69698]'
l-wx------ 1 max max 64 Oct 12 16:28 100 -> '/home/max/.config/google-chrome/Default/Service Worker/Database/MANIFEST-000001'
lr-x------ 1 max max 64 Oct 12 16:28 101 -> '/home/max/.config/google-chrome/Default/Sync Data/LevelDB/001633.ldb'
l-wx------ 1 max max 64 Oct 12 16:28 102 -> '/home/max/.config/google-chrome/Default/Service Worker/Database/000024.log'
lr-x------ 1 max max 64 Oct 12 16:28 103 -> '/home/max/.config/google-chrome/Default/Service Worker/Database/000022.ldb'
lr-x------ 1 max max 64 Oct 12 16:28 104 -> /opt/google/chrome/nacl_irt_x86_64.nexe
lr-x------ 1 max max 64 Oct 12 16:28 105 -> '/home/max/.config/google-chrome/Default/Service Worker/Database/000005.ldb'
lr-x------ 1 max max 64 Oct 12 16:28 106 -> '/home/max/.config/google-chrome/Default/Service Worker/Database/000025.ldb'
lr-x------ 1 max max 64 Oct 12 16:28 107 -> '/home/max/.config/google-chrome/Default/Service Worker/Database/000019.ldb'
lrwx------ 1 max max 64 Oct 12 16:28 108 -> 'socket:[89401]'
lrwx------ 1 max max 64 Oct 12 16:28 109 -> 'socket:[68628]'
lrwx------ 1 max max 64 Oct 12 16:28 11 -> 'anon_inode:[eventfd]'

You can use the 'lsof' command in non-blocking mode with the arguments '-b'. This argument will cause lsof to avoid kernel functions that might block. For example:

sudo lsof -b | less
COMMAND     PID   TID            USER   FD      TYPE             DEVICE    SIZE/OFF     NODE NAME
systemd       1                  root  cwd       DIR                8,1        4096          2 /
systemd       1                  root  rtd       DIR                8,1        4096          2 /
systemd       1                  root  txt       REG                8,1     1595792      19245 /lib/systemd/systemd

The 'TYPE'will give you the type of file it is. You can pipe this with grep to get information about your file descriptor,

sudo lsof -b | grep <your file descriptor>

Or there are lots of arguments which will allow you to customize the lsof operations.

And for platform independence, these are the platforms on which lsof is supported:-

 Apple Darwin 9 and Mac OS X 10.[567]
 FreeBSD 8.[234], 9.0, 10.0 and 11.0 for AMD64-based systems
 Linux 2.1.72 and above for x86-based systems
 Solaris 9, 10 and 11
Related