Как я могу определить, был ли файл открыт для добавления в Windows?

В UNIX, если я открываю файл в режиме добавления, например

fd = open("filename", O_APPEND);

тогда, имея такой файловый дескриптор, можно легко узнать, с какими флагами он был открыт, используя fcntl:

fcntl(fd, F_GETFL) & O_APPEND

Я знаю, что fcntl недоступен в Windows, но мне интересно, есть ли способ определить это. Windows явно поддерживает режим добавления, например, при создании файла с CreateFile и передаче флага FILE_APPEND_DATA.

Но если все, что у меня есть, это дескриптор уже открытого файла, я не могу найти способ определить, какие права доступа были запрошены при первом открытии файла. В этом вопросе содержится базовый рецепт проверки прав доступа к конкретному файл, но это, похоже, не помогает. Я попробовал, и даже если я открою файл в режиме только для чтения, он все равно скажет мне, что у меня есть FILE_APPEND_DATA доступ к файлу, если я его запрошу. Другими словами, этот метод только сообщает мне, какие права доступа у процесса к конкретному файлу (унаследованные от пользователя, запустившего процесс). Он ничего не говорит о том, какой именно доступ был запрошен при открытии файла.

Это не имеет ничего общего с тем, как Windows отслеживает, нужно ли только добавлять файл. И это последний вопрос, на который я нигде не могу найти ответа. Самое близкое, что я нашел, это GetFileInformationByHandleEx, но после просмотра документации нет ни одного атрибута файла, который можно было бы вернуть через этот API, который предлагает« режим добавления ».

Обновление: чтобы прояснить мой вопрос немного лучше, этот вопрос действительно относится только к библиотеке времени выполнения MS VC - файлам, открываемым с помощью POSIX-подобных функций _open и записываемых с помощью fwrite и т.п. Похоже, что собственные дескрипторы файлов win32 не имеют понятия «режим добавления».


person Iguananaut    schedule 30.08.2013    source источник


Ответы (4)


Для файла, который Windows видит как находящийся в режиме «добавления», это будет работать:

Используйте полудокументированный системный вызов NtQueryInformationFile (экспортированный из ntdll.dll) для запроса FILE_ACCESS_INFORMATION. Это должно сообщить вам маску доступа, с которой был открыт файл, который должен содержать FILE_APPEND_DATA.

Однако для файла, который среда выполнения C открывает в режиме «добавления», это не сработает, поскольку Windows фактически не открывает его в режиме добавления. Способ сделать это - каким-то образом преобразовать файловый дескриптор в файловый объект и проверить там флаг, но документированного способа сделать это нет.

Вы можете посмотреть исходный код Visual C ++ CRT, чтобы понять, как это сделать ... может быть какой-то способ получить доступ к массиву, который индексируется дескриптором, но я не уверен, как это сделать.
Код в CRT кажется _pioinfo(fd)->osfile |= FAPPEND, так что надеюсь, что это поможет.

person user541686    schedule 30.08.2013
comment
Вау, это непонятно. Я не мог найти этого на всех. Я попробую и дам вам знать, сможет ли он сказать мне то, что мне нужно знать. - person Iguananaut; 31.08.2013
comment
@Iguananaut: Да, Microsoft не любит, когда вы используете эти функции, поэтому, к сожалению, это непонятно. Но использовать их практически полностью безопасно, так что вперед, ха-ха. - person user541686; 31.08.2013
comment
Стреляйте - так близко, но так далеко. Независимо от того, открываю ли я файл в режиме «запись» или «добавление», FILE_ACCESS_INFORMATION полностью идентичен (оба бита FILE_WRITE_DATA и FILE_APPEND_DATA включены, но FILE_READ_DATA выключены). Хотя файлы по-прежнему ведут себя по-разному, когда я пишу им ... - person Iguananaut; 31.08.2013
comment
@Iguananaut Какие различия существуют между записью и добавлением? - person user2246674; 31.08.2013
comment
@ user2246674 См. msdn.microsoft.com/en-us/library/z0kc8e3z.aspx Файлы, открытые с помощью _open с флагом _O_APPEND, ищут конец файла перед любой операцией записи согласно POSIX. Это определенно работает в Windows, но после открытия файла нет очевидного способа запросить эту информацию, т.е. как это фактически реализовано в Windows. - person Iguananaut; 31.08.2013
comment
@Iguananaut Но после самого поиска мне интересно, есть ли разница в окнах (даже внутри). В противном случае он может просто сбросить флаг или не отслеживать его. Есть ли способ получить подсказку из текущей позиции поиска? - person user2246674; 31.08.2013
comment
@ user2246674 Нет, режим добавления означает, что указатель файла всегда располагается в конце файла перед операцией записи. Он даже выполняет атомарный поиск и запись, чтобы гарантировать это - он не просто ищет конец файла при открытии файла (на самом деле этого не должно быть - не до записи). Итак, где-то в Windows этот флаг отслеживается, хотя, возможно, таким образом, чтобы он не отображался напрямую на один флаг. Вопрос только в том, где. - person Iguananaut; 31.08.2013
comment
Да, ты прав. Похоже, что _O_APPEND не переводится в FILE_APPEND_DATA во время выполнения, к сожалению. :( Значит, Windows не знает, что этот файл находится в режиме добавления; это отслеживает среда выполнения. - person user541686; 31.08.2013
comment
Я не понимал, что исходный код среды выполнения C доступен, но на самом деле эта функция должна быть уникальной для среды выполнения C и не является функцией Win32 API. Это указывает мне правильное направление. - person Iguananaut; 05.09.2013

FileMode.Append - это плод воображения команды .NET Framework. Это не тот режим, который поддерживает Windows. Они добавили его, чтобы упростить использование оболочки для встроенной функции Winapi CreateFile () (FileStream). FileMode - это оболочка перечисления для его аргумента dwCreationDisposition. У которого нет опции CREATE_APPEND.

Наиболее подходящий код из метода FileStream.Init ():

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

Другими словами, FileMode.Append не соответствует допустимой опции CreateFile () и должен быть сопоставлен. Файл будет открыт, если он существует, или создан, если его нет. И он выполняет дополнительную операцию: после открытия файла он будет искать его до конца. Что вы, конечно, ожидаете, когда добавляете в существующий файл. Существует некоторая дополнительная проверка ошибок, она также гарантирует, что вы открыли файл для записи.

Так что это скорее пробьет дыру в вашем поиске, чтобы обнаружить эту спину. Для восстановления состояния файла доступна функция Winapi GetFileInformationByHandleEx (). Документированная функция для недокументированного вызова собственного API Мехрдада. Вы можете получить аргумент dwCreationDisposition обратно из FileDispositionInfo, чтобы убедиться, что это был OpenOrCreate. Однако это не уникально, он также мог быть запущен из файла, открываемого с помощью FileMode.OpenOrCreate из клиентского кода. Редко, но возможно.

Что вы потеряли, так это воспоминание о том, как он пытается добраться до конца файла. Вы можете использовать свойство FileStream.Position, но оно, конечно, будет зависеть от любых операций записи в то же время. Если это действительно имеет значение, тогда вы действительно должны сохранить используемое значение FileMode.

person Hans Passant    schedule 30.08.2013
comment
Вы, наверное, уже знаете это, но я все равно упомяну ... GetFileInformationByHandleEx задокументирован, но только частично. В нем не упоминается FileAccessInformation как допустимый вариант, но ZwQueryInformationFile упоминает. И GetFileInformationByHandleEx только для Vista +, тогда как ZwQueryInformationFile работает и на XP. - person user541686; 31.08.2013
comment
В CreateFile есть плохо документированный флаг под названием FILE_APPEND_DATA, который используется в примере кода здесь: msdn.microsoft.com/en-us/library/windows/desktop/. Кто знает, почему команда .NET не использовала его, но он есть. - person jsalter; 21.01.2015

Самоответчик, спасибо @mehrdad и @HansPassant за то, что указали мне в правильном направлении. Действительно, MSVCRT экспортирует массив массивов структуры с именем ioinfo, в которой хранится информация о каждом дескрипторе открытого файла в процессе.

Точное содержимое структуры зависит от версии VC и некоторых определений, но в целом определены ее первые два члена:

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

Элемент osfile является интересным - если файл открывается с _O_APPEND, для него устанавливается флаг FAPPEND, определенный как 0x20.

Я написал небольшую служебную функцию на Python на основе аналогичного кода в модуле posix в CPython, который может выполнять эту проверку: https://gist.github.com/embray/6444262

person Iguananaut    schedule 04.09.2013
comment
Кто-нибудь не возражает объяснить, почему это было изменено, даже если оно предоставляет точное решение моей проблемы? - person Iguananaut; 05.09.2013
comment
Функция Python C API _PyVerify_fd как минимум в Python 3 (не проверяла 2.x) также использует __pioinfo для проверки допустимости данного файлового дескриптора. - person jsalter; 22.01.2015
comment
@jsalter Подтверждено, Python 2 также работает с __pioinfo в Modules / posixmodule.c. Мне кажется, что это что-то вроде того, что да, это не общедоступный API, но не похоже, что реализация изменится в ближайшее время, если вообще когда-либо, по крайней мере, для известной версии MSCRT. - person Iguananaut; 22.01.2015

(Я знаю, что это очень старый вопрос из 2013 года, но я хочу поделиться своим решением для всех, кто хочет получить файловый режим [READ, WRITE, APPEND] непосредственно из дескриптора файла Windows)

API NtQueryInformationFile действительно может предоставить вам достаточно информации, чтобы определить, в каком режиме находится файл.

Для демонстрации воспользуемся следующим кодом:

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;

На самом деле это очень просто:

  • Если файл был открыт с использованием GENERIC_READ (или FILE_READ_DATA), тогда установлен флаг FILE_READ_DATA, в противном случае он не установлен;
  • Если файл был открыт с использованием GENERIC_WRITE (или FILE_WRITE_DATA), тогда установлен флаг FILE_WRITE_DATA, в противном случае он не установлен;
  • Если файл был открыт с использованием GENERIC_WRITE или FILE_APPEND_DATA, то установлен флаг FILE_APPEND_DATA, в противном случае он не установлен;

Сам режим чтения не включает флаг FILE_APPEND_DATA, а сам режим добавления не включает флаг FILE_WRITE_DATA.

Я надеюсь, что это может помочь кому-то другому.

person Meigyoku Thmn    schedule 21.08.2020
comment
Отлично, спасибо :) Оглядываясь назад, с тех пор, как я в последний раз написал этот вопрос, я узнал намного больше о том, как работает Cygwin, и, возможно, я смог бы найти это, покопавшись в источниках Cygwin, но я не уверен. В любом случае кажется законным. - person Iguananaut; 22.08.2020