1

Is there a way to get the real path (i.e. an absolute path without any '/../', '/./' sequences, symbolic links etc.) from a file handle? I know POSIX has the realpath() function but this doesn't accept a file handle. It uses a file name (string) instead. I'd need a function that accepts an stdio FILE* handle allocated by fopen() or a file descriptor allocated by open() and returns the real path of this file.

Does something like this exist? Or is there any other way to get a fully qualified path from a FILE* handle or POSIX file descriptor?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Andreas
  • 9,245
  • 9
  • 49
  • 97
  • 2
    hmm, i'd doubt that, since a file does not need to have a path or just one path... – kratenko Dec 22 '14 at 20:02
  • All of what you want is part of the filesystem implmentation, not POSIX C. Although you can write it using C. That means how to do it is platform specfic, programs like lsof and fuser are written for a specific OS. IF you want something portable you have a lot of work to do. IF the file still exists and is not something other than a regular file. – jim mcnamara Dec 22 '14 at 20:06
  • 1
    The '`FILE *`' variant would be handled by calling the file descriptor variant with `fileno(fp)`. But since the file descriptor might be for a socket, a deleted file, a plain pipe, or another such nameless 'file', the `frealpath()` function isn't really an option — which is why it isn't provided. Note that even if the system was mandated to keep a record of the name used with `open()` or `creat()`, the name might have been reused by the time you got to try accessing it. At least, you'd have to use `fstat()` and `stat()` and compare inode numbers and holding device numbers. – Jonathan Leffler Dec 22 '14 at 20:06
  • 2
    Any reason you can't just keep track of the filename you used to open the file in the first place and use that? – Dmitri Dec 22 '14 at 20:24

1 Answers1

4

In general, a file may have multiple names (hard links), no names at all (unlinked files), and a file descriptor might be connected to something that's not represented in the filesystem (for example a pipe or a socket).

Under Linux, you can do

snprintf(buf, bufsize, "/proc/self/fd/%d", fd);
rc = readlink(buf, filename, filename_size);

If readlink is successful, and filename is an absolute path (it starts with a slash /), then it might contain a filename (but not necessarily the only one) of the file to which fd is connected — but it might also contain something else (such as the name of the file followed with (deleted)). If filename doesn't start with a slash, then you're out of luck.

As far as I know, there is no portable way of doing something like that.

jch
  • 5,382
  • 22
  • 41
  • 1
    I don't think this answer is entirely correct. It's possible that the result of `readlink` begins with a slash but is not a valid pathname for the open file. This always happens when the file has been deleted; the kernel just adds `" (deleted)"` to the end of the pathname. I don't know any way to distinguish this case from a real name ending in that string. :( – R.. GitHub STOP HELPING ICE Dec 22 '14 at 20:33
  • @R.. Edited. Better now? – jch Dec 22 '14 at 20:48
  • If the file name doesn't start with a slash, you can probably apply `realpath()` to the given name; indeed, to be sure that there are no symlinks etc in the name, you need to use `realpath()`. (At the very least, you can't reliably normalize the name by simply editing it to remove an `/../` or `/./` notations — it can yield completely the wrong pathname if there are symbolic links involved.) The primary gotcha there is if the file was opened when the current working directory was `/some/where` but the process has since changed working directory to `/another/place/altogether`. – Jonathan Leffler Dec 22 '14 at 21:03
  • @JonathanLeffler if it doesn't start with a slash, it's probably of the form "socket:12345". `realpath` is not going to help. – jch Dec 22 '14 at 21:19
  • OK; I guess I need to see what actually happens with the links in `/dev/fd`. If the name is transformed to an absolute name when the file has an absolute name, even when the file is opened with a relative pathname, then applying `realpath()` is not going to sort out a relative name. You'd have to know whether the kernel applies the 'realpath()` transform to the absolute name, too. It might, though presumably it is only done 'on demand', when someone reads the name — otherwise, it is an unnecessary cost for the common case where no-one reads the link while the program runs. – Jonathan Leffler Dec 22 '14 at 21:24