Linux/ZeroInode: различия между версиями

Материал из Etersoft wiki
Перейти к навигацииПерейти к поиску
Строка 15: Строка 15:
Ядро Linux никак не адаптирует длинный inode при вызове обычного stat на 32-битной системе, возвращая ошибку.
Ядро Linux никак не адаптирует длинный inode при вызове обычного stat на 32-битной системе, возвращая ошибку.


В действительности при вызове функции ''stat'' она обрабатывается непосредственно в ''glibc'' и до ядра не доходит. Говоря конкретнее, происходит макроподстановка функцией ''xstat'', в которой в свою очередь происходит либо системный вызов, формирующий 32-битную структуру, если версия ядра и полученной структуры совпадают(32 бита), либо системный вызов, формирующий 64-битную структуру с последующей попыткой преобразования её в 32-битную посредством вызова функции ''int __xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf)''.  Последняя либо успешно преобразует 64-битную струтуру в 32-битную, либо, применительно к inode, возвращает ошибку EOVERFLOW в случае переполнения.
В действительности при вызове функции ''stat'' вызывается соответствующая функция из библиотеки ''glibc'', а не ядра. Говоря конкретнее, происходит макроподстановка ''stat'' функцией ''xstat'', в которой в свою очередь происходит либо системный вызов, формирующий 32-битную структуру, если версия ядра и полученной структуры совпадают(32 бита), либо системный вызов, формирующий 64-битную структуру с последующей попыткой преобразования её в 32-битную посредством вызова функции ''int __xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf)''.  Последняя либо успешно преобразует 64-битную струтуру в 32-битную, либо, применительно к inode, возвращает ошибку EOVERFLOW в случае переполнения.


В рамках этой задачи мы уже исправляли код fuse, в том числе, для RedHat:
В рамках этой задачи мы уже исправляли код fuse, в том числе, для RedHat:

Версия 18:28, 21 октября 2013

Длинные 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 в случае переполнения.

В рамках этой задачи мы уже исправляли код 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)

Ссылки