tags:

views:

2303

answers:

6

Is it possible to get the filename of a file descriptor in C?

+3  A: 

Impossible. A file descriptor may have multiple names in the filesystem, or it may have no name at all.

Edit: Assuming you are talking about a plain old POSIX system, without any OS-specific APIs, since you didn't specify an OS.

Tyler McHenry
talking about linux
adk
then my answer applies. Linux has no facilities to do this. Linux (POSIX) file descriptors don't necessarily refer to files, and even if they do they refer to inodes, not file names. A descriptor can point to a deleted file (which therefore has no name, this is a common way of making temp files) or it may point to an inode with multiple names (hard links).
Tyler McHenry
lsof seems to do it.
Duck
Try taking a look at the lsof source code. :) That's what I did when I had this same question myself a while back. lsof works on black magic and sacrificial goats - you cannot hope to duplicate its behavior. To be more specific, lsof is tightly coupled with the linux kernel, and doesn't do what it does by means of any API that is available to user-land code.
Tyler McHenry
Linux has a non-portable proc API for this. There are indeed limitations, but saying it's impossible is just plain false.
bdonlan
@Tyler - lsof runs in userspace. Therefore, there is an API for whatever it does available to userland code :)
bdonlan
@Tyler OP is running on linux. And lsof runs fine on a number of unix variants including sun, hpux, etc
Duck
@Duck, the portability there is probably why lsof's source has so much black magic; each UNIX variant does it differently. The linux proc interfaces aren't too bad, really, alebit rather sparsely documented.
bdonlan
@bdonlan That and it has so many options it can make any sane person weep.
Duck
@bdonlan I know it runs in userspace, but that does not mean there is a well-defined, stable API for what it does. I'm not saying that you _can't_ do what lsof does (obviously you can), but that you can't really do it without committing to doing and maintaining all of the same ridiculous things that lsof does to remain semi-portable.
Tyler McHenry
@Tyler, the linux proc api is stable. It isn't portable, but if all you care about is Linux, the kernel people are very careful about not breaking apps that depend on it.
bdonlan
I see a lot of mention of reading the lsof source as if all will be revealed. Read the lsof source...you will enjoy the 'dialects' folder.
sixlettervariables
+2  A: 

In Windows, with GetFileInformationByHandleEx, passing FileNameInfo, you can retrieve the file name.

Martin v. Löwis
as much as i hate windows, the windows equivalent is always nice to have
Matt Joiner
+1  A: 

You can use fstat() to get the file's inode by struct stat. Then, using readdir() you can compare the inode you found with those that exist (struct dirent) in a directory (assuming that you know the directory, otherwise you'll have to search the whole filesystem) and find the corresponding file name. Nasty?

Petros
+7  A: 

As Tyler points out, there's no way to do what you require "directly and reliably", since a given FD may correspond to 0 filenames (in various cases) or > 1 (multiple "hard links" is how the latter situation is generally described). If you do still need the functionality with all the limitations (on speed AND on the possibility of getting 0, 2, ... results rather than 1), here's how you can do it: first, fstat the FD -- this tells you, in the resulting struct stat, what device the file lives on, how many hard links it has, whether it's a special file, etc. This may already answer your question -- e.g. if 0 hard links you will KNOW there is in fact no corresponding filename on disk.

If the stats give you hope, then you have to "walk the tree" of directories on the relevant device until you find all the hard links (or just the first one, if you don't need more than one and any one will do). For that purpose, you use readdir (and opendir &c of course) recursively opening subdirectories until you find in a struct dirent thus received the same inode number you had in the original struct stat (at which time if you want the whole path, rather than just the name, you'll need to walk the chain of directories backwards to reconstruct it).

If this general approach is acceptable, but you need more detailed C code, let us know, it won't be hard to write (though I'd rather not write it if it's useless, i.e. you cannot withstand the inevitably slow performance or the possibility of getting != 1 result for the purposes of your application;-).

Alex Martelli
+5  A: 

Before writing this off as impossible I suggest you look at the source code of the lsof command.

There may be restrictions but lsof seems capable of determining the file descriptor and file name. This information exists in the /proc filesystem so it should be possible to get at from your program.

Duck
+13  A: 

You can use readlink() on /proc/self/fd/(file descriptor). This will give you the name of the file as it was when it was opened - however, if the file was moved or deleted since then, it may no longer be accurate (although Linux can track renames in some cases). To verify, stat the filename given and fstat the fd you have, and make sure st_dev and st_ino are the same.

Of course, not all file descriptors refer to files, and for those you'll see some odd text strings, such as 'pipe:[1538488]'. Since all of the real filenames will be absolute paths, you can determine which these are easily enough. Further, as others have noted, files can have multiple hardlinks pointing to them - this will only report the one it was opened with. If you want to find all names for a given file, you'll just have to traverse the entire filesystem.

bdonlan
worked like a charm. thanks!
Sebastian Good
interesting warning about this. On ext4, the same st_ino number will be used with alarming regularity. I tracked down a bug in a program I was working with to a file descriptor being stale because it did a stat on the filename and an fstat on the descriptor and thought they were the same. In fact the file had been rewritten and renamed over the top twice.
Bron Gondwana