Domanda

In UNIX if I open a file in append mode like

fd = open("filename", O_APPEND);

then given such a file descriptor one can easily find out what flags it was opened with using fcntl:

fcntl(fd, F_GETFL) & O_APPEND

I know that fcntl is not available on Windows, but I wonder if there is some way to determine this. Windows does clearly support an append mode for example when creating a file with CreateFile and passing in the FILE_APPEND_DATA flag.

But if all I have is a handle to an already opened file I cannot for the life of me find a way to determine what access rights were requested when the file was first opened. This question provides a basic recipe for checking the access rights to a specific file, but that doesn't seem to help. I tried it and even if I open a file with read-only mode it still tells me I have the FILE_APPEND_DATA access to the file, if I were to request it. In other words, this method only tells me what access rights the process has to a particular file (as inherited from the user who started the process). It says nothing about what exact access was requested when the file was opened.

This has nothing to do with how Windows keeps track of whether the file should only be appended to. And it's that latter question that I can't find an answer to anywhere. The closest thing I've found is GetFileInformationByHandleEx but after combing through the docs there's not one file attribute that can be returned through that API that suggests "append mode".

Update: To clarify my question a bit better, this question really just applies to the MS VC runtime library--files opened with the POSIX-like functions _open and written to with fwrite and the like. It appears that the native win32 file handles have no notion of "append mode".

È stato utile?

Soluzione 3

Self-answering, with thanks to @mehrdad and @HansPassant for pointing me in the right direction. Indeed, MSVCRT exports an array of arrays of a structure called ioinfo in which it stores information about each open file handle in the process.

The exact contents of the structure depend on the VC version and some defines, but in general its first two members are defined:

typedef struct {
        intptr_t osfhnd;    /* underlying OS file HANDLE */
        char osfile;        /* attributes of file (e.g., open in text mode?) */
        ....
} ioinfo;

The osfile member is the interesting one--if the file is opened with _O_APPEND the flag called FAPPEND defined as 0x20 is set on this.

I wrote a little utility function in Python based on similar code in CPython's posixmodule that can perform this check: https://gist.github.com/embray/6444262

Altri suggerimenti

For a file that Windows sees as being in "append" mode, this will work:

Use the semi-documented NtQueryInformationFile system call (exported from ntdll.dll) to query for FILE_ACCESS_INFORMATION. That should tell you the access mask that the file was opened with, which should contain FILE_APPEND_DATA.

For a file that the C runtime opens in "append" mode, however, this won't work, because Windows doesn't actually open it in append mode. The way to do this is to somehow translate the file descriptor into the file object, and check the flag there -- but there's no documented way to do this.

You can look at the Visual C++ CRT source code to figure out how to do it... there might be some way to access the array that the descriptor indexes in, but I'm not sure how.
The code in the CRT seems to be _pioinfo(fd)->osfile |= FAPPEND so hopefully that helps.

FileMode.Append is a figment of the .NET Framework team's imagination. It is not a mode that Windows supports. They added it to make the wrapper for the native winapi CreateFile() function (FileStream) easier to use. FileMode is the enum wrapper for its dwCreationDisposition argument. Which does not have a CREATE_APPEND option.

The most relevant code from the FileStream.Init() method is:

   bool seekToEnd = (mode==FileMode.Append);
   // Must use a valid Win32 constant here...
   if (mode == FileMode.Append)
       mode = FileMode.OpenOrCreate;

In other words, FileMode.Append does not match a valid CreateFile() option and has to be mapped. The file will be opened if exists, created if it doesn't. And it takes care of an extra operation, after opening the file, it will seek to the end of the file. Which you'd of course expect when you append to an existing file. Some additional error checking exists, it also makes sure you opened the file for writing.

So that rather shoots a hole in your quest to detect this back. The GetFileInformationByHandleEx() winapi function is available to recover the file state. The documented function for Mehrdad's undocumented native api call. You can get the dwCreationDisposition argument back from FileDispositionInfo to check that it was OpenOrCreate. That is however not unique, it could have also started from the file being opened with FileMode.OpenOrCreate from the client code. Rare, but possible.

What you've lost is the memory of it seeking to the end of the file. You could use the FileStream.Position property but that of course will be affected by any writes in the mean time. If this really matters then you do have to preserve the FileMode value that was used.

(I know this is a very old question from 2013, but I want to share my solution for anyone who want to get file mode [READ, WRITE, APPEND] directly from Windows File Handle)

The NtQueryInformationFile api actually can give you enough information to determine what mode the file is in.

Let's use the following code for demonstration:

IO_STATUS_BLOCK statusBlock;
FILE_ACCESS_INFORMATION accessInfo;

// You have to import this API by yourself using LoadLibary and GetProcAddress
auto status = NtQueryInformationFile(
   yourFileHandle, &statusBlock, &accessInfo,
   sizeof(FILE_ACCESS_INFORMATION), (FILE_INFORMATION_CLASS)8);

auto flags = accessInfo.AccessFlags;

// true if in read mode, otherwise false
auto isRead = (FILE_READ_DATA & flags) != 0;
// true if in write mode, otherwise false
auto isWrite = (FILE_WRITE_DATA & flags) != 0;
// true if in write mode or append mode 
// (I think these 2 modes are mutual exclusive), otherwise false
auto isAppend = (FILE_APPEND_DATA & flags) != 0;

It's actually very simple:

  • If the file was opened using GENERIC_READ (or FILE_READ_DATA), then the FILE_READ_DATA flag is set, otherwise it's not set;
  • If the file was opened using GENERIC_WRITE (or FILE_WRITE_DATA), then the FILE_WRITE_DATA flag is set, otherwise it's not set;
  • If the file was opened using GENERIC_WRITE or FILE_APPEND_DATA, then the FILE_APPEND_DATA flag is set, otherwise it's not set;

Read mode itself will not turn on the FILE_APPEND_DATA flag, and append mode itself will not turn on the FILE_WRITE_DATA flag.

I hope this could help someone else.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top