В bug # 8420 достаточно неясно сформулировано, попробую выделить отдельно. Предложение следующее: Поскольку существует непонятное множество программ, использующих stat (некий перечень есть на https://bugzilla.altlinux.org/show_bug.cgi?id=28290#c1), и программы эти никто не стремиться пересобирать с использованием stat64, а некоторые пересобрать и невозможно, предлагаю исправить функцию stat. Сейчас stat (в ядре) производит проверку размера inode, и возвращает ошибку, если он большой. Предлагаю возвращать 0 в этом случае, без ошибки. Программы, которые завязаны на inode, перестанут работать и их нужно будет пересобрать. Остальные же, которые вызывают stat ради проверки наличия файла, а не ради inode (который обычно никому не нужен), спокойно будут работать. Вот замечания на тему: "Достаточно linux/fs/stat.c исходный код посмотреть. Например, там написано, что stat(2) возвращает EOVERFLOW, когда st_ino, st_nlink или st_size недостаточного размера." https://bugzilla.altlinux.org/show_bug.cgi?id=28214#c12 "И не понятно, почему программы падают. Если 64-битный i-node урезать до 32-битного, то упасть еще никто не должен. Ядро отдает номера i-нодов, чтобы юзеру проще было идентифицировать файлы, но назад их не берёт." https://bugzilla.altlinux.org/show_bug.cgi?id=28214#c11 Так что предлагаю подготовить пробный патч, изменяющий stat, и у нас даже будет где его проверить.
Ознакомился с проблемой. Возникшие вопросы и подзадачи: static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * statbuf) - что из себя представляет последний элемент функции, почему аргументов 2. что делают функции вида и является ли первый аргумент системным вызовом ядра SYSCALL_DEFINE2(lstat, const char __user *, filename, struct __old_kernel_stat __user *, statbuf) Планируется вместо сообщений об ошибках после проверки inode просто выводить ноль, как и указано в задании. Но хотелось бы получить представление о первых двух вопросах и описание системного вызова stat.
Исправлен файл stat.c. Теперь, если inode слишком большой длины сообщение об ошибке не возвращается, inode возвращается со значением 0.
Внесенные изменения(/fs/stat.c): @@ -136,7 +136,7 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta tmp.st_dev = old_encode_dev(stat->dev); tmp.st_ino = stat->ino; if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) - return -EOVERFLOW; + tmp.st_ino = 0; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink) @@ -222,7 +222,7 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) tmp.st_dev = encode_dev(stat->dev); tmp.st_ino = stat->ino; if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) - return -EOVERFLOW; + tmp.st_ino = 0; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; if (tmp.st_nlink != stat->nlink)
Написана программа, использующая функцию stat: #include <sys/stat.h> #include <stdio.h> int main(int argc, char* argv[]){ struct stat buf; printf("%s\n", argv[1]); if (stat(argv[1], &buf)>=0) { printf("Inode %ld\n", (long int) buf.st_ino); } else printf("something wrong\n"); return 0;
Интересно, что структура stat64 дополнена полем STAT64_HAS_BROKEN_ST_INO unsigned int __st_ino; По другой версии - из include/uapi/asm/stat.h unsigned long __st_ino; В которую копируется inode без проверки. Уж не знаю, что это за хак такой. Видимо, ради того, что в обычной stat на этом месте находится st_ino (в state64 он перенесён в конец структуры). К нам прямую не относится, потому что нас касается только обычная структура stat. http://lxr.free-electrons.com/ident?i=STAT64_HAS_BROKEN_ST_INO
Собрано ядро c исправленным файлом stat.c, на виртуальной машине с установленным пакетом i586-glibc-core не запускается - при загрузке появляется ошибка initrd: loop: ERROR: /root: Not mounted В планах проверить, запускается ли то же самое, но оригинальное ядро с 32битным glibc
(В ответ на comment #6) > Собрано ядро c исправленным файлом stat.c, на виртуальной машине с > установленным пакетом i586-glibc-core не запускается - при загрузке > появляется ошибка > initrd: loop: ERROR: /root: Not mounted > В планах проверить, запускается ли то же самое, но оригинальное ядро с > 32битным glibc Проблема не в glibc, а в этом новом ядре. Возможно, из initrd пропала поддержка файловой системы или контроллера.
На виртуальной машине было собрано измененное ядро и, с установленной библиотекой i586-glibc-core. Вывод программы: [root@host-35 Ourside]# /home/guest/small_inodes/inode_taker /var/ftp/pvt/Etersoft/monit /var/ftp/pvt/Etersoft/monit something wrong из чего видно, что при обращении к stat в библиотеке glibc, несмотря на исправления в ядре, возвращается ошибка. в планах изучить работу glibc, а именно файлы: /srv/reprofy/Projects/glibc_source/glibc/sysdeps/unix/sysv/linux/xstatconv.c /srv/reprofy/Projects/glibc_source/glibc/sysdeps/unix/sysv/linux/xstat.c /srv/reprofy/Projects/glibc_source/glibc/sysdeps/unix/sysv/linux/i386/xstat.c последний в первую очередь.
для достижения цели была примонтирована файловая система сервера, в файл /etc/fstab была добавлена строчка: cellar:/var/ftp/pvt /var/ftp/pvt nfs nfsvers=3,defaults,nolock 0 0 и выполнены команды # service rpcbind start # mount -a Так же, в планах разобраться с макросом _STAT_VER_KERNEL.
(В ответ на comment #8) > На виртуальной машине было собрано измененное ядро и, с установленной Всё же установлено, а не собрано? > библиотекой i586-glibc-core. Вывод программы: > [root@host-35 Ourside]# /home/guest/small_inodes/inode_taker > /var/ftp/pvt/Etersoft/monit > /var/ftp/pvt/Etersoft/monit > something wrong > > из чего видно, что при обращении к stat в библиотеке glibc, несмотря на > исправления в ядре, возвращается ошибка. Ну особо не видно... примени perror() Важно знать, что за ошибка.
(В ответ на comment #10) > (В ответ на comment #8) > > На виртуальной машине было собрано измененное ядро и, с установленной > Всё же установлено, а не собрано? > Да, установлено.
Действительно, после добавления вывода об ошибке картина немного поменялась: [root@host-35 small_inodes]# ./inode_taker /var/ftp/pub/Etersoft/CIFS@Etersoft/5.4.7/sources/etercifs-5.4.7-alt1.src.rpm /var/ftp/pub/Etersoft/CIFS@Etersoft/5.4.7/sources/etercifs-5.4.7-alt1.src.rpm Unknown error with code = -1 Возвращается ошибка о недопустимости операции, а не о переполнении поля, отведенного под номер Inode. git.eter:/people/reprofy/public/small_inodes.git
Я ошибся и выводил не номер ошибки.
В действительности функция perror() показывает ошибку EOVERFLOW: [reprofy@builder small_inodes]$ ./inode_taker /var/ftp/pub/Etersoft/CIFS@Etersoft/5.4.7/sources/etercifs-5.4.7-alt1.src.rpm /var/ftp/pub/Etersoft/CIFS@Etersoft/5.4.7/sources/etercifs-5.4.7-alt1.src.rpm Error: Value too large for defined data type
Измени вывод inode в случае успеха — там не нужно приведение типа. И чем long int отличается от long?
(В ответ на comment #15) > Измени вывод inode в случае успеха — там не нужно приведение типа. > И чем long int отличается от long? Исправил, просто раньше компилятор выдавал предупреждение о несовпадении формата вывода типа(%ld) и выводимого типа ino_t. Теперь не ругается. По-моему long и long int - одно и то же. Исправил в glibc: @@ -202,8 +202,7 @@ __xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf) if (sizeof (buf->st_ino) != sizeof (kbuf->st_ino) && buf->st_ino != kbuf->st_ino) { - __set_errno (EOVERFLOW); - return -1; + buf->st_ino = 0; } } #else @@ -211,8 +210,7 @@ __xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf) if (sizeof (buf->st_ino) != sizeof (kbuf->st_ino) && buf->st_ino != kbuf->st_ino) { - __set_errno (EOVERFLOW); - return -1; + buf->st_ino = 0; } Собрал и поставил на виртуальную машину с исправленным ядром. Теперь программа при передаче ей файла с длинным inode выводит информацию, что inode равен 0. В планах - проверить влияют ли внесенные ранее исправления в ядро на поведение функции stat.
После установки старого оригинального ядра, без исправления функций, связанных со stat, программа все так же выводит нулевое значение inode.
(В ответ на comment #17) > После установки старого оригинального ядра, без исправления функций, > связанных со stat, программа все так же выводит нулевое значение inode. Какое сочетание glibc и kernel осталось непроверенным?
Все(В ответ на comment #18) > (В ответ на comment #17) > > После установки старого оригинального ядра, без исправления функций, > > связанных со stat, программа все так же выводит нулевое значение inode. > > Какое сочетание glibc и kernel осталось непроверенным? Все сочетания проверены. С оригинальными glibc и kernel проверено еще при сборке на builder.
На 32битной системе с оригинальным ядром и с измененным программа получает сообщение об ошибке при обращении к файлу с длинным inode: [root@host-15 small_inodes]# ./inode_taker /var/ftp/pvt/Etersoft/monit /var/ftp/pvt/Etersoft/monit Error: Value too large for defined data type После установки файла glibc-core с исправленной функцией stat: [root@host-15 small_inodes]# ./inode_taker /var/ftp/pvt/Etersoft/monit /var/ftp/pvt/Etersoft/monit Inode 0 С оригинальным ядром и исправленной glibc: [root@host-15 small_inodes]# ./inode_taker /var/ftp/pvt/Etersoft/monit /var/ftp/pvt/Etersoft/monit Inode 0 Предположение, что функция ядра stat предоставляется альтернативных стандартных библиотек С, у которых нет своей stat. Сам же метод подмены inode на данный момент кажется немного варварским из-за отсутствия предупреждений.
Программа теперь возвращает и численный код ошибки.
(В ответ на comment #21) > Программа теперь возвращает и численный код ошибки. Какой. Можно пример вывода программы? Ты же пишешь о том, что видишь, так позволь тем, кто не видит твоего экрана, получить полное представление о результате, тем более что его не сложно представить.
Обобщая все вышесказанное при обращении длинному inode: Для 64битных систем: с оригинальным ядром и оригинальной i586-glibc - возвращается ошибка, с исправленным ядром и оригинальной i586-glibc - возвращается ошибка, с оригинальным ядром и исправленной i586-glibc - возвращается номер inode=0, с исправленным ядром и исправленной i586-glibc - возвращается номер inode=0. Для 32битных систем: с оригинальным ядром и оригинальной glibc - возвращается ошибка, с исправленным ядром и оригинальной glibc - возвращается ошибка, с оригинальным ядром и исправленной glibc - возвращается номер inode=0, с исправленным ядром и исправленной glibc - возвращается номер inode=0.
(В ответ на comment #22) > (В ответ на comment #21) > > Программа теперь возвращает и численный код ошибки. > Какой. Можно пример вывода программы? Пример для оригинального ядра и оригинальной glibc: [root@host-15 ~]# /home/guest/small_inodes/inode_taker /var/ftp/pvt/Etersoft/monit /var/ftp/pvt/Etersoft/monit Error: Value too large for defined data type Error code = 75
Created attachment 2990 [details] Patch is zeroing long inodes In that patch conversion of long inodes to zero for 32bit systems occurs.
Created attachment 2991 [details] Patch is zeroing long inodes Patch is zeroing long inodes for 32bit systems
Created attachment 2992 [details] Patch is zeroing long inodes Patch is zeroing long inodes for 32bit systems.
Добавлен патч, описание проблемы находится на http://wiki.etersoft.ru/Linux/ZeroInode.
(В ответ на comment #23) > Обобщая все вышесказанное при обращении длинному inode: > Для 64битных систем: > с оригинальным ядром и оригинальной i586-glibc - возвращается ошибка, > с исправленным ядром и оригинальной i586-glibc - возвращается ошибка, > с оригинальным ядром и исправленной i586-glibc - возвращается номер inode=0, > с исправленным ядром и исправленной i586-glibc - возвращается номер inode=0. > Для 32битных систем: > с оригинальным ядром и оригинальной glibc - возвращается ошибка, > с исправленным ядром и оригинальной glibc - возвращается ошибка, > с оригинальным ядром и исправленной glibc - возвращается номер inode=0, > с исправленным ядром и исправленной glibc - возвращается номер inode=0. Исходя из всего этого, можно сделать вывод, что функция stat обрабатывается непосредственно в glibc, а до ядра не доходит. Поэтому необходимо поменять только функцию stat из библиотеки glibc. Если быть более конкретным, то в файле /sysdeps/unix/sysv/linux/xstatconv.c функцию int __xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf), отвечающую за преобразование полученной структуры stat64 файла к структуре stat.
Добавлено описание происходящего при вызове stat: http://wiki.etersoft.ru/Linux/ZeroInode#.D0.9F.D0.BE.D1.81.D1.82.D0.B0.D0.BD.D0.BE.D0.B2.D0.BA.D0.B0_.D0.B7.D0.B0.D0.B4.D0.B0.D1.87.D0.B8
piastry@ настаивает, что стандартной практикой в этом случае является укорачивание inode, как сделано в http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=db8a784af98d392a51fa25bb651bf90a982044e3 Нужно будет в процессе собрать побольше информации, какой вариант какие проблемы даёт.
Собрал glibc-2.17-alt6 в наш репозиторий Sisyphus, поставил для пробы на builder. (Надо было сделать релиз eter6, но сплоховал). С виду всё работает, надо больше эксплуатации. Надо поставить на локальную машину, может какие-то утилиты, близкие к ФС, позапускать...
В итоге, после апробирования было выявлено, что данное изменение glibc пригодно к использованию. Остается невыясненным вопрос - что именно сломается в программах после такого изменения.
В вики добавлены альтернативные варианты решения схожих проблем.
После проведения тестов оказалось, функция readdir тоже требует изменения. Связано с тем, что для получения информации о содержании каталога, она используется свой системный вызов, который так же предоставляет структуру dirent. Изменения необходимо провести в файле: /glibc/ports/sysdeps/sysv/linux/generic/wordsize-32/getdents.c. Планируется разобраться, как именно внести изменения, так как проверка на переполнения отличается от подобной в xstatxonv, выполнить дополнительные проверки.
(В ответ на comment #35) > После проведения тестов оказалось, функция readdir тоже требует изменения. Не знаю, что за тесты имелись в виду, но в первую очередь следовало дополнить существующий тест на inode, чтобы он демонстрировал наличие проблемы. А то не ясно, как осуществлялась проверка, и как проверить потом, что проблемы нет.
На самом деле автоматизированного теста не было, просто была программка, которая выдавала получаемый ей номер inode, передаваемого ей файла или каталога. И с этим она справляется. Просто раньше каталоги в ней не проверялись.
Добавлена программа, которая рекурсивно проверяет коректность отображения информации stat для заданного каталога и его элементов. git.eter:/people/reprofy/public/glibc_tests.git (/small_inodes/inode_test) С задачей своей пока не справляется - в каталогах с длинными inode, где последние обнуляются, не видит подкаталогов и файлов. Завершает работу, если происходит ошибка доступа, хотя теоретически может(и так, вероятно, будет правильней) просматривать другие подкаталоги, к которым доступ открыт и которые лежат рядом. В планах исправить проблему с завершением при ошибке доступа, узнать, почему не видит подкаталоги, у которых возвращается нулевой inode, протестировать, будет ли выдавать ошибки, если длинный айнод не обнулен. Предположение: почему не видит подкаталоги - потому что директория считывается, как набор inode. Попытка использовать вместо readdir readdir64 - не увенчалась успехом - не удалась попытка передать имя из структуры dirent64 - вылезла ошибка о несовместимости типов, хотя тип поля d_name у структур dirent и dirent64 одинаковый.
Тест исправлен, теперь список поддиректорий считывается в dirent64 с помощью readdir64, которую удалось запустить определив макрос _LARGEFILE64_SOURCE. Так же, при ошибке доступа тест не вылетает, программа продолжает просматривать другие файлы и директории. Тест рекурсивно просматривает каталог, задать максимальный уровень вложенности нельзя, поэтому вывод для каталога с небольшим уровнем вложенности: [reprofy@builder small_inodes]$ ./inode_test /var/ftp/pvt/Linux/MCBC/images __USE_LARGEFILE64 DEFINED /var/ftp/pvt/Linux/MCBC/images: ok /var/ftp/pvt/Linux/MCBC/images/TRANS.TBL: ok /var/ftp/pvt/Linux/MCBC/images/drvblock.img: ok /var/ftp/pvt/Linux/MCBC/images/bootnet.img: ok /var/ftp/pvt/Linux/MCBC/images/drvnet.img: ok /var/ftp/pvt/Linux/MCBC/images/pcmciadd.img: ok /var/ftp/pvt/Linux/MCBC/images/boot.img: ok /var/ftp/pvt/Linux/MCBC/images/README: ok /var/ftp/pvt/Linux/MCBC/images/pcmcia.img: ok /var/ftp/pvt/Linux/MCBC/images/pxeboot: ok /var/ftp/pvt/Linux/MCBC/images/pxeboot/initrd.img: ok /var/ftp/pvt/Linux/MCBC/images/pxeboot/README: ok /var/ftp/pvt/Linux/MCBC/images/pxeboot/TRANS.TBL: ok /var/ftp/pvt/Linux/MCBC/images/pxeboot/vmlinuz: ok То же самое на машине без обнуления inode: [root@host-35 guest]# ./inode_test /var/ftp/pvt/Linux/ __USE_LARGEFILE64 DEFINED /var/ftp/pvt/Linux/: Value too large for defined data type
К тому же каталогу: [root@host-35 guest]# ./inode_test /var/ftp/pvt/Linux/MCBC/images __USE_LARGEFILE64 DEFINED /var/ftp/pvt/Linux/MCBC/images: Value too large for defined data type
Попытка сформулировать проблему и выбор решения. Непонятно, рассматривать ли случай, когда inode пользуются, чтобы проверить считалась ли запись: if (st_ino >= 0) ... http://wiki.etersoft.ru/%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5:Linux/ZeroInode