Linux/ZeroInode
Длинные inode на 32-битной системе
Адаптация системы для поддержки длинных inode (64 бит) на 32-битных системах.
Решаемые задачи
Это важно для обеспечения совместной работы 32-битных систем в 64-битной инфраструктуре, иначе в ближайшее время это станет почти невозможным, что плохо повлияет на работу унаследованных программ.
В настоящий момент с такими файлами не работает даже rpm (на ALT Linux).
Постановка задачи
Сейчас такие файловые системы, как glusterfs и xfs, а также все сетевые файловые системы используют большие inode (номер может зависеть от положения файла на диске или от количества операций по созданию файлов), с которыми не будут работать многие программы, использующие вызов stat. Для работы в некоторых из систем разработаны хаки, сжимающие inode до 32 бит.
Ядро Linux никак не адаптирует длинный inode при вызове обычного stat на 32-битной системе, возвращая ошибку.
В действительности при вызове функции stat вызывается соответствующая функция из библиотеки glibc, а не ядра. Говоря конкретнее, происходит макроподстановка stat функцией xstat, в которой в свою очередь происходит либо системный вызов, формирующий 32-битную структуру, если версия ядра и полученной структуры совпадают(32 бита), либо системный вызов, формирующий 64-битную структуру с последующей попыткой преобразования её в 32-битную посредством вызова функции int __xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf). Последняя либо успешно преобразует 64-битную структуру в 32-битную, либо, применительно к inode, возвращает ошибку EOVERFLOW в случае переполнения. Задачей видится исправление функции stat, в соответствии с которым она будет возвращать нулевое значение inode, если оригинальное не уместилось в 32-битную структуру.
В рамках этой задачи мы уже исправляли код fuse, в том числе, для RedHat: https://bugzilla.redhat.com/show_bug.cgi?id=872629.
Что уже сделано
apt: Системный вызов stat() для проверки файлов
https://bugzilla.altlinux.org/show_bug.cgi?id=28214
Добавить в verify-elf проверку на использование stat в 32-битных программах
https://bugzilla.altlinux.org/show_bug.cgi?id=28290
rpm: rpmReadSignature failed при inode, выходящем за 32 бита
https://bugzilla.altlinux.org/show_bug.cgi?id=29117
Предложенное решение
Функция stat является часто используемой, поскольку её применяют для определения наличия файла, а так же для узнавания его размера. В большинстве случаев никто не проверяет содержащееся в нём значение inode.
Предлагается простое решение: обнулять inode.
Для тех программ, которым действительно важно значение inode, не избежать пересборки с -D_FILE_OFFSET_BITS=64 (AC_SYS_LARGEFILE в configure.am)
Исправления
Для glibc:
--- a/sysdeps/unix/sysv/linux/xstatconv.c +++ b/sysdeps/unix/sysv/linux/xstatconv.c @@ -202,8 +202,7 @@ __xstat32_conv (int vers, struct stat64 *kbuf, struct stat * 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 * if (sizeof (buf->st_ino) != sizeof (kbuf->st_ino) && buf->st_ino != kbuf->st_ino) { - __set_errno (EOVERFLOW); - return -1; + buf->st_ino = 0; } #endif buf->st_mode = kbuf->st_mode;
Для Linux kernel:
--- a/fs/stat.c +++ b/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)
Стоит отметить, что в случае использования библиотеки glibc исправления в ядре необязательны.