Linux/ZeroInode

Материал из Etersoft wiki
Перейти к: навигация, поиск

Длинные 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[1].

Есть два других варианта:

  1. Сплющить большой номер (squash), это обычная практика в коде (сетевых) файловых систем.
  2. Выставлять 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битных версий только для уже слинкованных приложений. Данное решение базируется на необходимости пересобирать сами программы и не учитывает текущих насущных требований,


Ссылки

  1. http://erislabs.net/gitweb/?p=gnulib.git;a=commitdiff;h=b5eb8386645bf1bcfafa63c729ff9a86454def87