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

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

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

Примечание

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

Ответы

Ответ 1

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

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

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

Et voilà, у нас есть новый файловый дескриптор, относящийся к тому же файлу, но с его собственными разрешениями, позицией и т.д.

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

Возможно, вы используете "разрешения", чтобы узнать, открыт ли файл для чтения, записи или того и другого. Получение этого значительно уродливее, чем любое из предыдущих. Проблема в том, что большинство из них находится в библиотеке, а не в Win32, поэтому, вероятно, нет способа сделать это, что будет даже близко к переносимым между компиляторами. С MS VС++ 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.
}

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

#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 VС++ в качестве своего исходного кода. Из них я бы сказал, что наиболее вероятно, что это будет "Комо", и наименее вероятный MinGW (но это только предположение, там хороший шанс, что он не сработает ни с одним из них).

Ответ 2

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

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

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

/proc/self/fd/[descriptor]

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

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

Ответ 3

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

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