Bug 9552

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
В 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, и у нас даже будет где его проверить.
Comment 1 Денис Обрезков 2013-10-04 18:36:37 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.
Comment 2 Денис Обрезков 2013-10-05 18:18:34 MSK
Исправлен файл stat.c. Теперь, если inode слишком большой длины сообщение об ошибке не возвращается, inode возвращается со значением 0.
Comment 3 Денис Обрезков 2013-10-05 19:52:29 MSK
Внесенные изменения(/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)
Comment 4 Денис Обрезков 2013-10-05 19:53:18 MSK
Написана программа, использующая функцию 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;
Comment 5 Vitaly Lipatov 2013-10-05 20:34:06 MSK
Интересно, что структура 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
Comment 6 Денис Обрезков 2013-10-07 20:50:58 MSK
Собрано ядро c исправленным файлом stat.c, на виртуальной машине с установленным пакетом i586-glibc-core не запускается - при загрузке появляется ошибка
initrd: loop: ERROR: /root: Not mounted
В планах проверить, запускается ли то же самое, но оригинальное ядро с 32битным glibc
Comment 7 Vitaly Lipatov 2013-10-08 15:06:01 MSK
(В ответ на comment #6)
> Собрано ядро c исправленным файлом stat.c, на виртуальной машине с
> установленным пакетом i586-glibc-core не запускается - при загрузке
> появляется ошибка
> initrd: loop: ERROR: /root: Not mounted
> В планах проверить, запускается ли то же самое, но оригинальное ядро с
> 32битным glibc
Проблема не в glibc, а в этом новом ядре. Возможно, из initrd пропала поддержка файловой системы или контроллера.
Comment 8 Денис Обрезков 2013-10-14 20:47:20 MSK
На виртуальной машине было собрано измененное ядро и, с установленной библиотекой 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

последний в первую очередь.
Comment 9 Денис Обрезков 2013-10-14 20:51:45 MSK
для достижения цели была примонтирована файловая система сервера, в файл /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 10 Vitaly Lipatov 2013-10-14 21:09:16 MSK
(В ответ на 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 11 Денис Обрезков 2013-10-16 14:47:36 MSK
(В ответ на comment #10)
> (В ответ на comment #8)
> > На виртуальной машине было собрано измененное ядро и, с установленной
> Всё же установлено, а не собрано?
> 
Да, установлено.
Comment 12 Денис Обрезков 2013-10-16 15:53:43 MSK
Действительно, после добавления вывода об ошибке картина немного поменялась:
[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
Comment 13 Денис Обрезков 2013-10-16 16:04:13 MSK
Я ошибся и выводил не номер ошибки.
Comment 14 Денис Обрезков 2013-10-16 16:20:59 MSK
В действительности функция 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
Comment 15 Vitaly Lipatov 2013-10-16 17:07:42 MSK
Измени вывод inode в случае успеха — там не нужно приведение типа.
И чем long int отличается от long?
Comment 16 Денис Обрезков 2013-10-16 20:05:33 MSK
(В ответ на 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.
Comment 17 Денис Обрезков 2013-10-16 20:47:41 MSK
После установки старого оригинального ядра, без исправления функций, связанных со stat, программа все так же выводит нулевое значение inode.
Comment 18 Vitaly Lipatov 2013-10-17 18:15:50 MSK
(В ответ на comment #17)
> После установки старого оригинального ядра, без исправления функций,
> связанных со stat, программа все так же выводит нулевое значение inode.

Какое сочетание glibc и kernel осталось непроверенным?
Comment 19 Денис Обрезков 2013-10-18 16:09:02 MSK
Все(В ответ на comment #18)
> (В ответ на comment #17)
> > После установки старого оригинального ядра, без исправления функций,
> > связанных со stat, программа все так же выводит нулевое значение inode.
> 
> Какое сочетание glibc и kernel осталось непроверенным?

Все сочетания проверены. С оригинальными glibc и kernel проверено еще при сборке на builder.
Comment 20 Денис Обрезков 2013-10-19 18:32:19 MSK
На 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 Денис Обрезков 2013-10-19 19:15:01 MSK
Программа теперь возвращает и численный код ошибки.
Comment 22 Vitaly Lipatov 2013-10-19 19:38:30 MSK
(В ответ на comment #21)
> Программа теперь возвращает и численный код ошибки.
Какой. Можно пример вывода программы?

Ты же пишешь о том, что видишь, так позволь тем, кто не видит твоего экрана, получить полное представление о результате, тем более что его не сложно представить.
Comment 23 Денис Обрезков 2013-10-19 19:58:00 MSK
Обобщая все вышесказанное при обращении длинному inode:
Для 64битных систем:
с оригинальным ядром и оригинальной i586-glibc - возвращается ошибка,
с исправленным ядром и оригинальной i586-glibc - возвращается ошибка,
с оригинальным ядром и исправленной i586-glibc - возвращается номер inode=0,
с исправленным ядром и исправленной i586-glibc - возвращается номер inode=0.
Для 32битных систем:
с оригинальным ядром и оригинальной glibc - возвращается ошибка,
с исправленным ядром и оригинальной glibc - возвращается ошибка,
с оригинальным ядром и исправленной glibc - возвращается номер inode=0,
с исправленным ядром и исправленной glibc - возвращается номер inode=0.
Comment 24 Денис Обрезков 2013-10-19 19:59:15 MSK
(В ответ на 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
Comment 25 Денис Обрезков 2013-10-21 15:33:37 MSK
Created attachment 2990 [details]
Patch is zeroing long inodes

In that patch conversion of long inodes to zero for 32bit systems occurs.
Comment 26 Денис Обрезков 2013-10-21 15:45:32 MSK
Created attachment 2991 [details]
Patch is zeroing long inodes

Patch is zeroing long inodes for 32bit systems
Comment 27 Денис Обрезков 2013-10-21 15:58:00 MSK
Created attachment 2992 [details]
Patch is zeroing long inodes

Patch is zeroing long inodes for 32bit systems.
Comment 28 Денис Обрезков 2013-10-21 16:00:30 MSK
Добавлен патч, описание проблемы находится на http://wiki.etersoft.ru/Linux/ZeroInode.
Comment 29 Денис Обрезков 2013-10-21 17:27:24 MSK
(В ответ на 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.
Comment 30 Денис Обрезков 2013-10-21 18:57:30 MSK
Добавлено описание происходящего при вызове 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
Comment 31 Vitaly Lipatov 2013-10-21 18:58:04 MSK
piastry@ настаивает, что стандартной практикой в этом случае является укорачивание inode, как сделано в
http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=db8a784af98d392a51fa25bb651bf90a982044e3

Нужно будет в процессе собрать побольше информации, какой вариант какие проблемы даёт.
Comment 32 Vitaly Lipatov 2013-10-21 19:29:30 MSK
Собрал glibc-2.17-alt6 в наш репозиторий Sisyphus, поставил для пробы на builder. (Надо было сделать релиз eter6, но сплоховал).
С виду всё работает, надо больше эксплуатации.
Надо поставить на локальную машину, может какие-то утилиты, близкие к ФС, позапускать...
Comment 33 Денис Обрезков 2013-10-21 20:50:29 MSK
В итоге, после апробирования было выявлено, что данное изменение glibc пригодно к использованию. Остается невыясненным вопрос - что именно сломается в программах после такого изменения.
Comment 34 Денис Обрезков 2013-10-23 20:10:31 MSK
В вики добавлены альтернативные варианты решения схожих проблем.
Comment 35 Денис Обрезков 2013-11-13 20:17:56 MSK
После проведения тестов оказалось, функция readdir тоже требует изменения. Связано с тем, что для получения информации о содержании каталога, она используется свой системный вызов, который так же предоставляет структуру dirent.
Изменения необходимо провести в файле:
/glibc/ports/sysdeps/sysv/linux/generic/wordsize-32/getdents.c.

Планируется разобраться, как именно внести изменения, так как проверка на переполнения отличается от подобной в xstatxonv, выполнить дополнительные проверки.
Comment 36 Vitaly Lipatov 2013-11-13 21:07:49 MSK
(В ответ на comment #35)
> После проведения тестов оказалось, функция readdir тоже требует изменения.
Не знаю, что за тесты имелись в виду, но в первую очередь следовало дополнить существующий тест на inode, чтобы он демонстрировал наличие проблемы. А то не ясно, как осуществлялась проверка, и как проверить потом, что проблемы нет.
Comment 37 Денис Обрезков 2013-11-29 19:02:28 MSK
На самом деле автоматизированного теста не было, просто была программка, которая выдавала получаемый ей номер inode, передаваемого ей файла или каталога. И с этим она справляется. Просто раньше каталоги в ней не проверялись.
Comment 38 Денис Обрезков 2013-11-30 20:48:04 MSK
Добавлена программа, которая рекурсивно проверяет коректность отображения информации stat для заданного каталога и его элементов.
git.eter:/people/reprofy/public/glibc_tests.git
(/small_inodes/inode_test)

С задачей своей пока не справляется - в каталогах с длинными inode, где последние обнуляются, не видит подкаталогов и файлов.
Завершает работу, если происходит ошибка доступа, хотя теоретически может(и так, вероятно, будет правильней) просматривать другие подкаталоги, к которым доступ открыт и которые лежат рядом.
В планах исправить проблему с завершением при ошибке доступа, узнать, почему не видит подкаталоги, у которых возвращается нулевой inode, протестировать, будет ли выдавать ошибки, если длинный айнод не обнулен.
Предположение: почему не видит подкаталоги - потому что директория считывается, как набор inode. Попытка использовать вместо readdir readdir64 - не увенчалась успехом - не удалась попытка передать имя из структуры dirent64 - вылезла ошибка о несовместимости типов, хотя тип поля d_name у структур dirent и dirent64 одинаковый.
Comment 39 Денис Обрезков 2013-12-02 17:56:08 MSK
Тест исправлен, теперь список поддиректорий считывается в 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
Comment 40 Денис Обрезков 2013-12-02 17:57:56 MSK
К тому же каталогу:
[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
Comment 41 Денис Обрезков 2013-12-06 19:48:07 MSK
Попытка сформулировать проблему и выбор решения. Непонятно, рассматривать ли случай, когда 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