Linux/ZeroInode
Длинные inode на 32-битной системе
Адаптация системы для поддержки длинных inode (64 бит) на 32-битных системах.
Решаемые задачи
Это важно для обеспечения совместной работы 32-битных систем в 64-битной инфраструктуре, иначе в ближайшее время это станет почти невозможным, что плохо повлияет на работу унаследованных программ.
В настоящий момент с такими файлами не работает даже rpm (на ALT Linux).
Постановка задачи
Сейчас такие файловые системы, как glusterfs и xfs, а также все сетевые файловые системы используют большие inode (номер может зависеть от положения файла на диске или от количества операций по созданию файлов), с которыми не будут работать многие программы, использующие вызов stat. Для работы в некоторых из систем разработаны хаки, сжимающие inode до 32 бит.
Ядро Linux никак не адаптирует длинный inode при вызове обычного stat на 32-битной системе, возвращая ошибку.
Функция stat библиотеки glibc реализована через вызов __xstat, которая в свою очередь либо генерирует системный вызов stat (если структура stat glibc и ядра совпадают), формирующий 32-битную структуру, либо системный вызов stat64, формирующий 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.
При релизе ядра 3.12 Линус Торвальдс упомянул, что проблемы с 32-битными инодами — не приоритетны, и заплатки для них будут только тратить процессорное время на 64-битной системе. (https://lkml.org/lkml/2013/11/3/153).
Что уже сделано
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). Пока такие программы не известны. По хорошему, стоило бы вообще переименовать это поле в структуре, чтобы однозначно выяснить проблемные программы.
Известно, что на mingw st_ino не заполняется: mingw is the only platform where st_ino is always 0<ref>http://erislabs.net/gitweb/?p=gnulib.git;a=commitdiff;h=b5eb8386645bf1bcfafa63c729ff9a86454def87</ref>.
Есть два других варианта:
- Сплющить большой номер (squash), это обычная практика в коде (сетевых) файловых систем.
- Выставлять st_ino не в 0, а в максимально возможное число (все единицы).
Исправления
Для 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 исправления в ядре необязательны.
Чего может коснуться
# define SAME_INODE(Stat_buf_1, Stat_buf_2) \ ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \ && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)
http://www.redhat.com/archives/lvm-devel/2007-July/msg00021.html
В coreutils есть такое:
enum { NOT_AN_INODE_NUMBER = 0 };
coreutils/src/system.h
Другие идеи
Для преодоления подобных проблем, связанных с функцией stat, предлагаются альтернативные варианты решения:
https://sourceware.org/ml/libc-alpha/2013-02/msg00575.html
https://sourceware.org/ml/libc-alpha/2013-02/msg00580.html
Первый заключается в том, что команда stat, несмотря на возникающие ошибки, должна формировать выходную структуру stat и лишь после того, как структура готова, возвращать -1. При таком подходе и структура stat доходит до пользователя в насколько это возможно полном виде, и сообщение об ошибки выводится. Данное решение неприемлемо для нашей задачи, так как одна из целей, преследуемых обнулением Inode - предотвратить возврат кода ошибки.
Второй же вариант чуть более глобален и предполагает исправление самих сборочных правил(включение опции -D_FILE_OFFSET_BITS=64 как умолчательной) и заголовочных файлов, их отход от 32битной инфраструктуры. Иными словами автор предлагает масштабный переход на 64битные версии программ с оставлением поддержки 32битных версий только для уже слинкованных приложений. Данное решение базируется на необходимости пересобирать сами программы и не учитывает текущих насущных требований,
Ссылки
- http://bugs.etersoft.ru/show_bug.cgi?id=8420
- RedHat: NFS + large XFS fs sometimes fails uncached lookups for client when inode number >2^32 on 32-bit computers
<references/>