Linux и Large Files

А вот представим, что у меня есть библиотека, собранная с -D_FILE_OFFSET_BITS=64
Все fopen/fseek/fread/fclose там делаются внутри, снаружи только имена файлов прилетают.

А потом я к ней линкую приложение, компилированное без этого флага, просто gcc -c

Вопрос: оно будет работать? Или возможны моментики?

Нет, я не ленивый и конечно попробую, но у меня этих линуксов под рукой штуки две и с новыми ядрами, а что будет со старыми ядрами/glibc/whatever?

P.S. Назначение LARGEFILE_SOURCE/LARGEFILE64_SOURCE так и не понял, вроде бы для позиционирования за 2 гигабайта и чтения мелкими кусками достаточно _FILE_OFFSET_BITS

P.P.S. Единственная система, где вообще не надо париться - FreeBSD :) Какой, оказывается, кусок жизни с этой LFS прошел мимо меня...

Comments

Вся проблема не в линуксе, а в gcc/libc. Не спозиционируешься, и даже append'ом не запишешь файл за 2 гигабайта, если не стоит O_LARGEFILE при открытии. Как поставить этот флаг при открытии файла на с++ через его потоки я так и не понял, простого пути нет, сложный нафиг не нужен. В итоге, открывал обычным open(2), потом через fdopen уже буферезированный вывод. По дефолту O_LARGEFILE не ставится ни с какими флагами сборки.

Тут вот с версиями glibc есть явная чехарда, я ее и хочу прояснить:
У меня в LibRaw используется fopen/fseek/fread/fclose

До недавнего времени я считал, что RAW больше двух гигабайт не бывает и меня все это не парило. Как выяснилось, бывают (с цифровых кинокамер). Имею багрепорт, в двух словах "на Ubuntu работает, на Mac OS X - нет".

Ну и понятно почему на макоси нет - там fseek - 32-битный, fseeko - off_t. Но на Убунте то работает, вероятно в (новых?) glibc сходу все прозрачно.
Но я решил, что уж раз чинить, то чинить все сразу и винды и старый линукс. Про fdopen - интересное замечание, учту.

Я ещё раз подумал. Возможно, что мои воспоминания о проблеме ещё связаны с 32-битностью архитектуры когда мне это было нужно. Может на 64-битной off_t по дефолту 64 бита, и файлы сразу большие, поэтому на убунте работает?

О, про 64 бита - отличный вопрос, я не подумал. Скорее да, заработало именно поэтому.

У меня в 10-й SuSE хватило только _FILE_OFFSET_BITS=64 и больше ничего не надо. Т.е.
fopen(..)
fwrite(3 байта)
fseeko(f,5000000000,SEEK_CUR)
fwrite(3 байта)
Честно образует файл (с дыркой) на 5000000006 байт.

Ядро 2.6.18, glibc 2.5
При этом fopen честно переопределено в fopen64, наверное потому и работает без O_LARGEFILE

Так это, -D - это установка макроопределения препроцессора. Уже на этапе собственно компиляции, от этих -D ничего не остается.

Вернее, остануся более другие определения __off_t и lseek64 там где в коде было написано lseek (ну и прочие функции - соответственно), что легко можно проверить посредством gcc -E,

Соответственно скомпилированный объектник будет содержать ссылки на библиотечные функции с 64-битными смещениями, с чем его не линкуй. Другое дело ежели где-то в интерфейсе библиотеки торчат парметры или функции с типом off_t. Вот тут можно на грабли наступить, если разные файлы слинкованы с разными значениями этих макросов.

А все три варианта макросов с точки зрения системных хедеров glibc - практически инварианты. Все равно они все сводятся к внутреннему макросу
__USE_LARGEFILE64.

У меня в коде написано fopen/fseek/fread (и в одном месте fgets).
Что вместо fseek нужен fseeko - я уже понял.
Про offset_bits тоже понял.
Конфликта по именам - не будет, как я понимаю.

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

Кстати, а нахрена так сложно сделано то? Кто мешал починить внутри, а не выносить все эти lseek64 наружу? В чем фишка, в бинарной совместимости?

Алекс, ну возьми ты stdio.h с ближайшего линукса и почитай. Там все на простом и понятном языке C написано. Даже в исходники libc лезть не придется.

А на вопрос "нахрена так сделано" ответ очевиден - динамическая линковка + требование чтобы старые бинарники работали с новой libc.
Если представить себе как ложатся в стек параметы тех же lseek/lseek64 (да еще во всех вариантах - bigendian/littlendian, процессор ест невыровненные данные/посылает SIGBUS) станет ясно, что единственный способ обеспечить совместимый ABI, это сохранить старую точку входа в динамическую библиотеку со старой семантикой, а рядом - завести новую с другим именем.

Я то почитаю (почитал уже), но у меня все довольно новое. Опасаюся я, что в старых может быть все иначе, потому и спрашиваю про старые.

Впрочем, наверное действительно надо разбираться по мере поступления, работает на 10-й Suse - и отлично, если не работает на старой, там и разберемся.
Интересно, почему у этого англичанина все просто работает, ведь не должно бы.

А по совместимости - я бы сохранил старую точку входа, назвав ее old_lseek, а всю компиляцию всех новых сорцов заворачивал бы в new_cool_64bit_lseek без всяких флагов.

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

Ответственно заявляю: _было_ нельзя. (т.е. я продолжаю страшилку). Может с повсеместным x86_64 стало лучше, не смотрел. Я проблему решал после того как перестал писаться лог-файл после 2гб, который открывался через fopen (..,"a") и писался fprintf-ом. Решение для плюсовых потоков мне показалось трудно осмыслить :) Они мне вообще не очевидны, не люблю и не использую, кроме sstream внутри макросов.

С 64 битами проблемы просто нету, совсем.

Блин, вот не было печали с этими кинокамерами.

Это в теории
на практике всё ровно наборот. <img src="http://l-stat.livejournal.com/img/talk/md01_alien.gif" alt=":-P">

под рукой линукса нету, есть только "где не надо париться" ;)

но по уму там под этими #ifdef должны разные функции libc вызываться и/или делаться разные syscall. Поэтому если файл реально не больше 2 гб, то проблем быть не должно.

С файлами реально не больше 2Gb никаких проблем нету и так, все работает.

Ко мне киношники пристали, у них большие файлы (и хрен возьмешь на тест, тоже проблема). Причем, пристали со словами, что на линуксе ("свежая убунту") работает (!), а на маке - нет. Почему не работает на маке - я уже понял, нужен fseeko. На Линуксе тоже он нужен.

А вот как у них получилось, что на линуксе работает - вообще не могу понять.

Разгадал - работает на 64 битах без всяких лишних ударов в бубен.

а на маках разве не 64 бита?

По дефолту, кстати, нет.
Но gcc -m64 - да, собирает 64 бита.

Ни одного DVD нет? :-)

Я не понял шутки.....

На всякий случай поясню сам - у них RAW-файлы по 16 гигов....

А тебе нужен какой-то конкретный RAW или любое видео?

D:\HDTV\rips\HellBoy2.iso>dir
12/09/2008 06:02 PM 32 HELLBOY2_D1.dvd
12/09/2008 06:51 PM 44,836,323,328 HELLBOY2_D1.iso
Виндовс, правда.

Не, ну файлов на больше 2Gb я и сам наделаю. И все эти seek/read проверю, конечно.

Но для полноценного тестирования нужен, конечно же, RAW-файл, который жрет LibRaw как родной. Хотя я уже получил отмашку "ура, заработало", но сам поразвлекаться не могу.

Только не в плюсах!
В старом добром си вы можете слинковать определение func(char) в одном объектнике с использованием func(int) в другом на платформе, где sizeof(int)>sizeof(char) и вам за это ничего не будет... на линкове.

Re: Только не в плюсах!
фишка в том, что в FreeBSD off_t - 8-байтный.

Правда я в любом случае слегка погорячился: переделать на fseeko (и соотв. функцию на виндах) в любом случае надо. А вот какие-то прыжки с флагами препроцессора на компиляции - нужны только Linux _даже_ при использовании fseeko

CONFIG_RESOURCES_64BIT
У меня Linux dememax-laptop 2.6.29-gentoo-r5 #3 SMP Wed Jul 29 11:38:42 MSD 2009 i686 Intel(R) Core(TM)2 CPU T5600 @ 1.83GHz GenuineIntel GNU/Linux

Я себе гонфигурил CONFIG_RESOURCES_64BIT в ядре.
Ща взглянул, у меня вообще никакого упоминания в .config нет.
http://cateee.net/lkddb/web-lkddb/RESOURCES_64BIT.html

Re: CONFIG_RESOURCES_64BIT
Почитайте сперва что эта опция делает

Re: CONFIG_RESOURCES_64BIT
Так ведь нет предела человеческому заблуждению.
Вот и я, почему-то всегда думал, что эта опция виляет в том числе на такие типы, как off_t.

Re: на компиляции - нужны только Linux _даже_ при использова
Подтверждаю, если у меня не передать -D_FILE_OFFSET_BITS=64, то тип off_t будет 4 байта.
Но стоит передать - получается 8 байт. Это касается gcc *.c и g++ *.cpp.

Единственно чего я не понял, то почему включения заголовка stdio.h недостаточно для объявления типа off_t, нужно обязательно ещё sys/types.h, иначе не знает такого имени компилятор.

Re: на компиляции - нужны только Linux _даже_ при использова
И что характерно, действительно разные показывает nm данные:
U fseeko64@@GLIBC_2.1 - для случая с -D_FILE_OFFSET_BITS=64
и
U fseeko@@GLIBC_2.1 - для случая без
Вай-вай-вай, нужны эти дефайны указывать явно для случая 64 на платформе 32. :-(

Re: на компиляции - нужны только Linux _даже_ при использова
<i>Единственно чего я не понял, то почему включения заголовка stdio.h недостаточно для объявления типа off_t, нужно обязательно ещё sys/types.h, иначе не знает такого имени компилятор.</i>

Это исторически так сделано, оставь их

Народ, не программировал кучу лет, а надо сделать, чтобы tcopy на 32-битной системе копировала с ленты на диск файлы более 2Gb.
Исходник для Солярисе есть:
http://src.opensolaris.org/source/xref/netvirt/usr/src/cmd/tcopy/tcopy.c
Я со своей невысокой колокольни так понял, что либо надо тупо заменить open() на open64(), либо в open() вставить флаг O_LARGEFILE.
А c off_t надо что-то делать? В исходнике оно уже почему-то off64_t?
Всё так просто или ещё что-то?
А как дальше компилировать?
gcc -c -D_LARGEFILE_SOURCE tcopy.c
Или не так?
Ваще туплю.

А о какой системе речь?

На более-менее современном линуксе хватает -D_FILE_OFFSET_BITS=64 и сразу наступает почти полное счастье.
Для полноты его нужна еще замена fseek/ftell на fseeko/ftello (и для дескрипторного интерфейса вроде аналогично).

Что будет при попытке писать одним вызовом больше двух гигов - не знаю, но скорее всего ничего хорошего.

32-битный Solaris 8 или в крайнем случае 32-битный CentOs 5.2.

А как писать не одним вызовом?
Вот у меня лента, на которой 10-гигабайтный файл, я пишу tcopy и естественно хочу, чтобы сразу весь файл переписался. То есть такое невозможно?

Ну вот в центосе должно хватить _FILE_OFFSET_BITS=64 а про солярку просто не знаю (8-я версия - это что-то очень старое....?)

Что касается вызовов read, то я посмотрел в исходник, там все хорошо, читает-пишет буфер разумного размера.