Summary: | Сделать обнуление длинных inode в 32-битных функциях | ||
---|---|---|---|
Product: | [Внутреннее (Etersoft)] Отдел серверных решений | Reporter: | Vitaly Lipatov <lav> |
Component: | Общее | Assignee: | Денис Обрезков <reprofy> |
Status: | ASSIGNED --- | QA Contact: | Vitaly Lipatov <lav> |
Severity: | minor | ||
Priority: | P4 | CC: | piastry, sin |
Version: | не указана | ||
Target Milestone: | --- | ||
Hardware: | PC | ||
OS: | Linux | ||
Whiteboard: | |||
Заявки RT: | Связано с: | ||
Дата напоминания: | |||
Bug Depends on: | 9670 | ||
Bug Blocks: | |||
Attachments: |
Patch is zeroing long inodes
Patch is zeroing long inodes Patch is zeroing long inodes |
Description
Vitaly Lipatov
2013-10-02 18:50:22 MSK
Ознакомился с проблемой. Возникшие вопросы и подзадачи: 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 |