views:

851

answers:

3

How can one create a new file descriptor from an existing file descriptor such that the new descriptor does not share the same internal file structure/entry in the file table? Specifically attributes such as file offset (and preferably permissions, sharing and modes) should not be shared between the new and old file descriptors.

Under both Windows and Linux, dup() will duplicate the file descriptor, but both descriptors still point to the same file structure in the process' file table. Any seeking on either descriptor will adjust the position for the other descriptors as well.

Note

I've since received answers for both Windows and Linux and adjusted the question a little too often, which has made it difficult for people to answer. I'll adjust my votes and accept the cleanest answer which covers both Windows and Linux. Apologies to all, I'm still new to the SO paradigm. Thanks for the great answers!

A: 

So, I recommend reading up on this a little more. The dup() and related functions serve to create a duplicate value in the file descriptor table pointing to the same entry in the open file table. This is intended to have the same offset. If you call open(), you will create a new entry the open file table.

It doesn't make any sense to create a duplicate of a file descriptor and that new file descriptor have a different offset in the open file table (this seems to contradict what the word "duplicate" means).

I'm not sure what your question is actually. I mean, it isn't the same thing as a duplicate. You could read:

/proc/self/fd/[descriptor]

and get the string that was used to open that file descriptor; bear in mind this may provide some pitfalls, some of which you actually noted in your observation of calling open() again.

Maybe you can explain a little more and I can try to update to help.

BobbyShaftoe
@BobbyShaftoe, you've reiterated my question :PThe behaviour of opening /proc/self/fd/[descriptor] is what I'm after, however a more portable and system-call oriented way of doing this preferable.
Matt Joiner
@Anacrolix, not really. You edited it to add that bit in there. :) Anyway, I'm afraid there is no such animal, certainly not a system call or really portable and reliable.
BobbyShaftoe
@BobbyShaftoe: you might want to observe that the question has been changed to 'Windows only' which is totally unfair to those who answered the question when there was no such qualification.
Jonathan Leffler
@Jonathan Leffler, I see. Yeah, I agree. I've always thought that was a weird aspect of the voting/editing system. You can get many downvotes for an answer that was great for a previous edit.
BobbyShaftoe
my bad guys, please see my note above. all credit to bobby for his excellent linux answer.
Matt Joiner
bobby edit your answer so i can upvote it :\
Matt Joiner
+6  A: 

So basically, what you really want is to be given a file descriptor, and basically open the same file over again, to get a separate position, sharing, mode, etc. And you want to do this on Windows (where the "file descriptor" is basically a foreign object, not something used directly by the OS or the run-time library at all.

Amazingly enough, there is a way to do that, at least with MS VC++. All but two steps of it use only the Win32 API so porting to other compilers/libraries should be fairly reasonable (I think most supply versions of those two functions). Those are for converting a Unix-style file descriptor to a native Win32 file handle, and converting a native Win32 file handle back to a Unix-style file descriptor.

  1. Convert file-descriptor to native file handle with _get_osfhandle()
  2. Get a name for the file with GetFileInformationByHandleEx(FILE_NAME_INFO)1
  3. Use CreateFile to open a new handle to that file
  4. Create a file descriptor for that handle with _open_osfhandle()

Et voilà, we have a new file descriptor referring to the same file, but with its own permissions, position, etc.

Toward the end of your question, you make it sound like you also want the "permissions", but that doesn't seem to make any real sense -- the permissions attach to the file itself, not to how the file is opened, so opening or reopening the file has no effect on the file's permissions. If you really want to know the, you can get it with GetFileInformationByHandle, but be aware that file permissions in Windows are quite a bit different from the (traditional) file permissions in Unix. Unix has owner/group/world permissions on all files, and most systems also have ACLs (though there's more variation in how they work). Windows either has no permissions at all (e.g., files on FAT or FAT32) or else uses ACLs (e.g., files on NTFS), but nothing that's really equivalent to the traditional owner/group/world permissions most people are accustomed to on Unix.

Perhaps you're using "permissions" to refer to whether the file was open for reading, writing, or both. Getting that is considerably uglier than any of the preceding. The problem is that most of it is in the library, not Win32, so there's probably no way to do it that will be even close to portable between compilers. With MS VC++ 9.0 SP1 (not guaranteed for any other compiler) you can do this:

#include <stdio.h>

int get_perms(int fd) {
    int i;
 FILE * base = __iob_func();

    for (i=0; i<_IOB_ENTRIES; i++) 
        if (base[i]._file == fd)
            return base[i]._flag;     // we've found our file
    return 0; // file wasn't found.
}

Since this involved some spelunking, I wrote a quick test to verify that it might actually work:

#ifdef TEST
#include <io.h>

void show_perms(int perms, char const *caption) { 
 printf("File opened for %s\n", caption);
 printf("Read permission = %d\n", (perms & _IOREAD)!=0);
 printf("Write permission = %d\n", (perms & _IOWRT)!=0);
}

int main(int argc, char **argv) { 
 FILE *file1, *file2;
 int perms1, perms2;

 file1=fopen(argv[1], "w");
 perms1 = get_perms(_fileno(file1));
 fclose(file1);

 file2=fopen(argv[1], "r");
 perms2 = get_perms(_fileno(file2));
 fclose(file2);

 show_perms(perms1, "writing");
 show_perms(perms2, "reading");
 return 0;
}
#endif

And the results seem to indicate success:

File opened for writing
Read permission = 0
Write permission = 1
File opened for reading
Read permission = 1
Write permission = 0

You can then test that returned flag against _IOREAD, _IOWRT, and _IORW, which are defined in stdio.h. Despite my previous warnings, I should probably point out that I suspect (though I certainly can't guarantee) that this part of the library is fairly stable, so the real chances of major changes are probably fairly minimal.

In the other direction, however, there's basically no chance at all that it'll work with any other library. It could (but certainly isn't guaranteed to) work with the other compilers that use the MS library, such as Intel, MinGW or Comeau using MS VC++ as its back-end. Of those, I'd say the most likely to work would be Comeau, and the least likely MinGW (but that's only a guess; there's a good chance it won't work with any of them).

  1. Requires the redistributable Win32 FileID API Library
Jerry Coffin
a great answer. i wasn't aware that opening a file prevented it being unlinked or renamed, even if full sharing was enabled. this means that retrieving the file name belong to a handle is perfectly safe, as it cannot change so long as a file descriptor or `HANDLE` remains open to that path.
Matt Joiner
How i can do the same on Linux? thanks
alaamh
@alaamh: have you looked through the other answers? A couple of them talk about how to do the same kinds of things on Linux.
Jerry Coffin
A: 

Why don't you just open the file a second time with open() or CreateFile() on windows? This gives you all freedom of different access rights and separate offset.

This of course has the drawback that you you can not open the file exclusively, but it solves your problem very simply.

RED SOFT ADAIR
Well I'm interested in "reopening" given a file handle and nothing else. On Linux, the original file isn't guaranteed to have moved or been unlinked. On Windows, after some testing it would seem you can't manipulate a filesystem entry as long as someone holds a handle to it.
Matt Joiner