Дубликат файлового дескриптора с собственным файловым смещением

Как можно создать новый дескриптор файла из существующего дескриптора файла, чтобы новый дескриптор не использовал одну и ту же внутреннюю файловую структуру/запись в таблице файлов? В частности, такие атрибуты, как смещение файла (и, желательно, разрешения, совместное использование и режимы), не должны совместно использоваться новым и старым файловыми дескрипторами.

Как в Windows, так и в Linux dup() будет дублировать дескриптор файла, но оба дескриптора по-прежнему указывают на одну и ту же файловую структуру в файловой таблице процесса. Любой поиск по любому из дескрипторов будет корректировать положение и для других дескрипторов.

Примечание

С тех пор я получил ответы как для Windows, так и для Linux, и слишком часто корректировал вопрос, из-за чего людям было трудно ответить. Я изменю свои голоса и приму самый чистый ответ, который охватывает как Windows , так и Linux. Извиняюсь перед всеми, я все еще новичок в парадигме SO. Спасибо за отличные ответы!


person Matt Joiner    schedule 21.10.2009    source источник


Ответы (3)


Итак, в основном, что вам действительно нужно, так это получить файловый дескриптор и, по сути, снова открыть тот же файл, чтобы получить отдельную позицию, совместное использование, режим и т. д. И вы хотите сделать это в Windows (где «файловый дескриптор " в основном является посторонним объектом, а не чем-то, что используется напрямую ОС или библиотекой времени выполнения вообще.

Удивительно, но есть способ сделать это, по крайней мере, с помощью MS VC++. Все, кроме двух шагов, используют только Win32 API, поэтому перенос на другие компиляторы/библиотеки должен быть достаточно разумным (я думаю, что большинство версий этих двух функций). Они предназначены для преобразования дескриптора файла в стиле Unix в собственный дескриптор файла Win32 и преобразования собственного дескриптора файла Win32 обратно в дескриптор файла в стиле Unix.

  1. Преобразование дескриптора файла в собственный дескриптор файла с помощью _get_osfhandle()
  2. Получите имя файла с помощью GetFileInformationByHandleEx(FILE_NAME_INFO)1
  3. Используйте CreateFile, чтобы открыть новый дескриптор этого файла.
  4. Создайте файловый дескриптор для этого дескриптора с помощью _open_osfhandle().

Вуаля, у нас есть новый файловый дескриптор, ссылающийся на тот же файл, но с собственными разрешениями, положением и т. д.

Ближе к концу вашего вопроса вы говорите, что вам также нужны «разрешения», но это, похоже, не имеет никакого реального смысла — разрешения прикрепляются к самому файлу, а не к тому, как файл открывается, поэтому открытие или повторное открытие файла не влияет на права доступа к файлу. Если вы действительно хотите знать, вы можете получить его с помощью GetFileInformationByHandle, но имейте в виду, что права доступа к файлам в Windows немного отличаются от (традиционных) прав доступа к файлам в Unix. Unix имеет разрешения владельца/группы/мира для всех файлов, и в большинстве систем также есть ACL (хотя есть больше различий в том, как они работают). Windows либо вообще не имеет разрешений (например, файлы в FAT или FAT32), либо использует ACL (например, файлы в NTFS), но ничего, что действительно эквивалентно традиционным разрешениям владельца/группы/мира, к которым привыкло большинство людей в Unix.

Возможно, вы используете «разрешения», чтобы указать, был ли файл открыт для чтения, записи или того и другого. Получение этого значительно уродливее любого из предыдущих. Проблема в том, что большая часть этого находится в библиотеке, а не в Win32, поэтому, вероятно, нет способа сделать это, который был бы даже близок к переносимости между компиляторами. С MS VC++ 9.0 SP1 (не гарантируется для любого другого компилятора) вы можете сделать это:

#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.
}

Поскольку для этого потребовалось немного спелеологии, я написал быстрый тест, чтобы убедиться, что он действительно работает:

#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

И результаты, кажется, указывают на успех:

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

Затем вы можете проверить возвращенный флаг на соответствие _IOREAD, _IOWRT и _IORW, которые определены в stdio.h. Несмотря на мои предыдущие предупреждения, я, вероятно, должен указать, что я подозреваю (хотя я, конечно, не могу гарантировать), что эта часть библиотеки довольно стабильна, поэтому реальные шансы на серьезные изменения, вероятно, довольно минимальны.

С другой стороны, однако, совсем нет никаких шансов, что он будет работать с какой-либо другой библиотекой. Он может (но точно не гарантируется) работать с другими компиляторами, использующими библиотеку MS, такими как Intel, MinGW или Comeau, использующими MS VC++ в качестве серверной части. Из них я бы сказал, что с наибольшей вероятностью будет работать Comeau, а с наименьшей - MinGW (но это только предположение; есть большая вероятность, что это не сработает ни с одним из них).

  1. Требуется распространяемая библиотека Win32 FileID API
person Jerry Coffin    schedule 27.10.2009
comment
отличный ответ. я не знал, что открытие файла предотвратило его отмену или переименование, даже если был включен полный общий доступ. это означает, что получение имени файла, принадлежащего дескриптору, совершенно безопасно, поскольку оно не может измениться, пока дескриптор файла или HANDLE остается открытым для этого пути. - person Matt Joiner; 27.10.2009
comment
@alaamh: ты просмотрел другие ответы? Пара из них рассказывает о том, как делать то же самое в Linux. - person Jerry Coffin; 06.05.2010

Так что рекомендую почитать об этом немного подробнее. dup() и связанные с ней функции служат для создания повторяющегося значения в таблице файловых дескрипторов, указывающего на ту же запись в открытой файловой таблице. Это предназначено для того же смещения. Если вы вызовете open(), вы создадите новую запись в таблице открытых файлов.

Нет смысла создавать дубликат файлового дескриптора, и этот новый файловый дескриптор имеет другое смещение в таблице открытых файлов (похоже, это противоречит тому, что означает слово «дубликат»).

Я не уверен, что ваш вопрос на самом деле. Я имею в виду, это не то же самое, что дубликат. Вы могли прочитать:

/proc/self/fd/[descriptor]

и получить строку, которая использовалась для открытия этого файлового дескриптора; имейте в виду, что это может привести к некоторым ловушкам, некоторые из которых вы действительно заметили в своем наблюдении за повторным вызовом open().

Может быть, вы можете объяснить немного больше, и я могу попытаться обновить, чтобы помочь.

person BobbyShaftoe    schedule 21.10.2009
comment
@BobbyShaftoe, вы повторили мой вопрос: P Поведение при открытии /proc/self/fd/[descriptor] - это то, что мне нужно, однако более переносимый и ориентированный на системные вызовы способ сделать это предпочтительнее. - person Matt Joiner; 21.10.2009
comment
@ Анакроликс, не совсем так. Вы отредактировали его, чтобы добавить туда этот бит. :) Да и вообще, боюсь нет такого зверька, уж точно не системный вызов или реально портативный и надежный. - person BobbyShaftoe; 21.10.2009
comment
@BobbyShaftoe: возможно, вы захотите заметить, что вопрос был изменен на «Только для Windows», что совершенно несправедливо по отношению к тем, кто ответил на вопрос, когда такой квалификации не было. - person Jonathan Leffler; 27.10.2009
comment
@ Джонатан Леффлер, понятно. Да я согласен. Я всегда думал, что это странный аспект системы голосования/редактирования. Вы можете получить много отрицательных голосов за ответ, который был отличным для предыдущего редактирования. - person BobbyShaftoe; 27.10.2009
comment
мои плохие парни, пожалуйста, смотрите мою заметку выше. вся заслуга Бобби за его отличный ответ по Linux. - person Matt Joiner; 27.10.2009

Почему бы вам просто не открыть файл во второй раз с помощью open() или CreateFile() в Windows? Это дает вам полную свободу различных прав доступа и отдельного смещения.

Это, конечно, имеет тот недостаток, что вы не можете открыть файл исключительно, но это решает вашу проблему очень просто.

person RED SOFT ADAIR    schedule 29.10.2009
comment
Ну, я заинтересован в повторном открытии с учетом дескриптора файла и ничего больше. В Linux нет гарантии, что исходный файл был перемещен или отсоединен. В Windows после некоторого тестирования может показаться, что вы не можете манипулировать записью файловой системы, пока кто-то держит ее дескриптор. - person Matt Joiner; 29.10.2009