Linux/InodeConv
Задача
Описание задачи и альтернативный метод решения представлен здесь: http://wiki.etersoft.ru/Linux/ZeroInode
Squashing Inode
Для решения данной проблемы предлагается использование сжатия inode. Похожий подход представлен здесь: https://bugzilla.redhat.com/show_bug.cgi?id=872629. В нашем случае произведены следующие изменения
--- a/ports/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c +++ b/ports/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c @@ -26,10 +26,18 @@ #include <unistd.h> #include <sys/param.h> #include <sys/types.h> +#include <stdio.h> #include <sysdep.h> #include <sys/syscall.h> +static ino_t +squash_dir_inode (ino_t d_ino32, uint64_t d_ino64) +{ + d_ino32 ^= d_ino64 >> (sizeof (uint64_t) - sizeof (ino_t)) * 8; + return d_ino32; +} + /* Pack the dirent64 struct down into 32-bit offset/inode fields, and ensure that no overflow occurs. */ ssize_t @@ -87,10 +95,14 @@ __getdents (int fd, char *buf, size_t nbytes) outp->u.d_ino = d_ino; outp->u.d_off = d_off; - if ((sizeof (outp->u.d_ino) != sizeof (inp->k.d_ino) + if (sizeof (outp->u.d_ino) != sizeof (inp->k.d_ino) && outp->u.d_ino != d_ino) - || (sizeof (outp->u.d_off) != sizeof (inp->k.d_off) - && outp->u.d_off != d_off)) + { + /* Inode overflow. Squash it*/ + outp->u.d_ino = squash_dir_inode (outp->u.d_ino, d_ino); + } + if (sizeof (outp->u.d_off) != sizeof (inp->k.d_off) + && outp->u.d_off != d_off) { /* Overflow. If there was at least one entry before this one, return them without error, otherwise signal overflow. */
--- a/sysdeps/unix/sysv/linux/xstatconv.c +++ b/sysdeps/unix/sysv/linux/xstatconv.c @@ -20,6 +20,7 @@ #include <sys/stat.h> #include <kernel_stat.h> #include <kernel-features.h> +#include <stdio.h> #ifdef STAT_IS_KERNEL_STAT @@ -178,6 +179,14 @@ __xstat64_conv (int vers, struct kernel_stat *kbuf, void *ubuf) #endif } +static ino_t +squash_inode (ino_t ino32, ino64_t ino64) +{ + ino32 ^= ino64 >> (sizeof (ino64_t) - sizeof (ino_t)) * 8; + return ino32; +} + + int __xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf) { @@ -202,8 +211,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 = squash_inode (buf->st_ino, kbuf->st_ino); } } #else @@ -211,8 +219,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 = squash_inode (buf->st_ino, kbuf->st_ino); } #endif buf->st_mode = kbuf->st_mode;
Создание патча
Для создания патча для glibc необходимо руководствоваться следующими документами:
- Документация glibc
- Glibc Wiki - обратить внимание на пункт 5.
- Соглашение по оформлению кода конкретно для glibc
- Соглашение по оформлению кода для GNU
- Стандарт для вносимых изменений
- Manual page
- Багзилла - здесь необходимо создать багу
- Похожая бага
Необходимо оформить патч по приведенным требованиям.
Описание баги на glibc
Здесь составлено описание баги в mail-list для glibc. В настоящее время в сетевых файловых системах используются 64битные и 32битные системы. Проблема в том, что многие программы для проверки существования файла используют функцию stat (), которая возвращает ошибку в 32хбитных системах при переполнении полей структуры stat. Данный патч позволяет решить проблему переполнения поля inode, сжимая его до 32бит. Есть и другие варианты решение: 1. Перекомпиляция программ с флагом сборки -D_FILE_OFFSET_BITS=64. Но этот подход не применим к некоторым старым программам. 2. Обнулять inode. Этот вариант плох тем, что во многих программах есть проверка существования файла по inode: if (st_ino >= 0) ...