0x6B6F77616C74
0x6B6F77616C74

Reputation: 2630

How to obtain a file name from the standard FILE structure?

What I want:

 void printFname(FILE * f)
    {    
        char buf[255];
        MagicFunction(f,buf);
        printf("File name: %s",buf);
    }

So, all I need is "MagicFunction", but unfortunatelly I haven't found such ...

Is there any way to implement using an OS library? (windows.h , cocoa.h, posix.h etc.)

Upvotes: 1

Views: 470

Answers (4)

Brian Campbell
Brian Campbell

Reputation: 333284

There is no such function. There may be no filename, or more than one filename that correspond with the FILE *. On Unix, a program can continue to have a reference to a file after it has been renamed or deleted, which could mean that you have a FILE * with no name. Or more hard links may be made to the file, which means a file can have multiple names; which one would you choose? To further confuse things, a file can be temporarily hidden, by mounting a filesystem over a directory containing that file. The file will still be on disk, at its original pathname, but the file will be inaccessible at that path because the mount is obscuring it.

It's also possible that the FILE * never corresponded to a file on the filesystem at all; while they usually do, you can create one from any file descriptor using fdopen(), and that file descriptor may be a pipe, socket, or other file-like object that has never had a path on the disk. In some versions of the C library, you can open a string stream (for instance, fmemopen() in glibc), so the FILE * actually just corresponds to a memory buffer.

If you care about the name, it's best to just keep track of what it was named when you opened the file.

There are some hacky ways to approximate getting the filename; if you're just using this for debugging or informational purposes, then they may be sufficient. Most of these will require operating on the file descriptor rather than the FILE *, as the file descriptor is the lower level way of referring to a file. To get the file descriptor, run fileno() on the FILE *, and remember to check for errors in case there is no file descriptor associated with that FILE *.

On Linux, you can do readlink() on "/proc/self/fd/fileno" where fileno is the file descriptor. That will show you what filename the file had when the file was opened, or a string indicating what other kind of file descriptor it is, like a socket or inotify handle. FreeBSD and NetBSD have Linux emulation layers, which include emulation of Linux-style procfs; you may be able to do this on those if you mount a Linux-compatible procfs, though I don't have them available for testing.

On Mac OS X, you don't have /proc/self/fd. If you don't care about finding the original filename, but some other filename that refers to the file would work (such that you could pass it to another program), you can construct one: /.vol/deviceid/inode. For example, /.vol/234881030/281363. To get those values, run fstat() on the file descriptor, and use st_dev and st_ino on the resulting struct stat.

On Windows, files and the filesystem work quite differently than Unix. Apparently it's possible to map a file back to its name on Windows. As of Windows Vista, you can simply call GetFinalPathNameByHandle(). This takes a HANDLE; to get the HANDLE from the file descriptor, call _get_osfhandle(). Prior to Windows Vista, you need to do a little more work, as described in this article. Note that on Windows fileno() is named _fileno(), though the former may work with a warning.

Going even further into hacky territory, there are a few more techniques that you could use. You could shell out to lsof, or you could extract the code it uses to resolve pathnames. lsof actually looks directly in kernel memory, extracting information from the kernel's name cache. This has several limitations, outlined in the lsof FAQ. And of course, you need root or equivalent privileges to do this, either directly or with an suid/sgid binary.

And finally, for a portable but slow solution for finding one or more filenames matching an open file, you could find the device and inode number using fstat() on the file descriptor, and then recursively traverse the filesystem stat()ing every file, until you find a file with matching device and inode number. Remember the caveats I mention above; you may find no matching files, more than one matching file, and even if you don't find any matching files, the file might still be there, but hidden by a mount point. And of course, there may be race conditions; something may rename the file in such a way that you never see it while traversing the hierarchy.

Upvotes: 8

Jack
Jack

Reputation: 16724

I don't think that there is such function even at windows.h,coca.h or unistd.h. Most probably you write it yourself. Just make a

struct myFile {
FILE *fh;
char *filename;
}

and hold such structures into array of struct myFile and in MagicFunction(f,b) walk on the array looking for the address equal to f.

Upvotes: 0

Chul-Woong Yang
Chul-Woong Yang

Reputation: 1243

Do you fopen() yourself? If then, maintain FILE * to filename hash table yourself.

Otherwise, it's not possible in general.

Upvotes: 0

Jim Buck
Jim Buck

Reputation: 20734

There is no such standard function.

Upvotes: 2

Related Questions