| Summary: | Увеличить ограничение на длину названия файла в Linux | ||
|---|---|---|---|
| Product: | LINUX@Etersoft | Reporter: | Vitaly Lipatov <lav> |
| Component: | Общее | Assignee: | Денис Обрезков <reprofy> |
| Status: | DEFERRED --- | QA Contact: | |
| Severity: | minor | ||
| Priority: | P4 | CC: | ITProf13, lav, piastry, reprofy |
| Version: | не указана | ||
| Target Milestone: | выпуск 1.0 | ||
| Hardware: | PC | ||
| OS: | All | ||
| Whiteboard: | |||
| Заявки RT: | Связано с: | ||
| Дата напоминания: | |||
| Bug Depends on: | 9614, 9615 | ||
| Bug Blocks: | |||
|
Description
Vitaly Lipatov
2013-04-24 02:30:51 MSK
Немного исследовал проблему. Действительно большинство линуксовых файловых систем (ext*, xfs, btrfs) имеют ограничение в 255 символов на длину имени файла. Исключением в данном случае является только reiserfs. Можно попробовать конечно пропатчить ядро и использовать эту фс, но возможность использования только для одной фс выглядит малопривелкательной. Ссылка на обсуждение в linux-cifs-client@: https://lists.samba.org/archive/linux-cifs-client/2006-March/001230.html NFS - лимит 255 символов. CIFS - 260 символов UTF-16. Описание структуры ext4 на диске. https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout Надо изучить и понять ограничения, нужно ли создавать файловую систему с расширенным ограничением, или всё упирается в то, что длина имени файла хранится в одном байте... Если я правильно понял, то изначально в файловой системе EXT использовалось ограничение на длину имени файла в 255 символов, а размер самого поля имени был 2 байта:
4 /*
5 * The ext filesystem constants/structures
6 */
7
8 #define EXT_NAME_LEN 255
55 struct ext_dir_entry {
56 unsigned long inode;
57 unsigned short rec_len;
58 unsigned short name_len;
59 char name[EXT_NAME_LEN];
60 };
В новых же системах, начиная с ext2 длину файла намеренно уменьшили до 1 байта, а младшие байт отвели под флаги определения типа файла.
Linux /fs/ext4/ext4.h:
1576 #define EXT4_NAME_LEN 255
1577
1578 struct ext4_dir_entry {
1579 __le32 inode; /* Inode number */
1580 __le16 rec_len; /* Directory entry length */
1581 __le16 name_len; /* Name length */
1582 char name[EXT4_NAME_LEN]; /* File name */
1583 };
1584
1585 /*
1586 * The new version of the directory entry. Since EXT4 structures are
1587 * stored in intel byte order, and the name_len field could never be
1588 * bigger than 255 chars, it's safe to reclaim the extra byte for the
1589 * file_type field.
1590 */
1591 struct ext4_dir_entry_2 {
1592 __le32 inode; /* Inode number */
1593 __le16 rec_len; /* Directory entry length */
1594 __u8 name_len; /* Name length */
1595 __u8 file_type;
1596 char name[EXT4_NAME_LEN]; /* File name */
1597 };
Но первая структура, как видно, осталась, может можно пользоваться как-то ею, потому что других ограничений кроме однобайтности поля имени файла в самой файловой системе не видно.
(В ответ на comment #4) > Если я правильно понял, то изначально в файловой системе EXT использовалось > ограничение на длину имени файла в 255 символов, а размер самого поля имени > был 2 байта: ... > В новых же системах, начиная с ext2 длину файла намеренно уменьшили до 1 > байта, а младшие байт отвели под флаги определения типа файла. О, это очень интересно. > 1576 #define EXT4_NAME_LEN 255 > 1577 > 1578 struct ext4_dir_entry { > 1579 __le32 inode; /* Inode number */ > 1580 __le16 rec_len; /* Directory entry length */ > 1581 __le16 name_len; /* Name length */ > 1582 char name[EXT4_NAME_LEN]; /* File name */ ... > 1586 * The new version of the directory entry. Since EXT4 structures are > 1587 * stored in intel byte order, and the name_len field could never be > 1588 * bigger than 255 chars, it's safe to reclaim the extra byte for the > 1589 * file_type field. Мне кажется, это было очень плохой идеей. Неплохо бы поискать, как они до этого додумались. Почему просто не расширили структуру. > 1591 struct ext4_dir_entry_2 { > 1592 __le32 inode; /* Inode number */ > 1593 __le16 rec_len; /* Directory entry length */ > 1594 __u8 name_len; /* Name length */ > 1595 __u8 file_type; > 1596 char name[EXT4_NAME_LEN]; /* File name */ > 1597 }; > > Но первая структура, как видно, осталась, может можно пользоваться как-то > ею, потому что других ограничений кроме однобайтности поля имени файла в > самой файловой системе не видно. Нет, думаю у нас должна появиться ext4_dir_entry_3. Но это структура хранения где — на диске или в памяти? Может быть будет проще подойти со стороны утилиты mkfs.ext4, которая создаёт файловую систему. Если она это делает самостоятельно, без ядра, то можно начать модификацию с неё — видимо, добавив флаг, включающий режим длинных имён. (В ответ на comment #1) > Немного исследовал проблему. Действительно большинство линуксовых файловых > систем (ext*, xfs, btrfs) имеют ограничение в 255 символов на длину имени > файла. Исключением в данном случае является только reiserfs. Можно > попробовать конечно пропатчить ядро и использовать эту фс, но возможность > использования только для одной фс выглядит малопривелкательной. Про ReiserFS на википедии пометка Максимальная длина имени файла 4032 байт, limited to 255 by Linux VFS Но при этом в описании структуры имени файла в VFS отводится 2 байта http://www.win.tue.nl/~aeb/linux/lk/lk-8.html : struct qstr { const unsigned char *name; unsigned int len; unsigned int hash; }; (В ответ на comment #6) ... > Про ReiserFS на википедии пометка > > Максимальная длина имени файла > > 4032 байт, limited to 255 by Linux VFS > > Но при этом в описании структуры имени файла в VFS отводится 2 байта Возможно, это ссылка 10-летней давности. reiserfs не очень интересна, потому что не развивается. > http://www.win.tue.nl/~aeb/linux/lk/lk-8.html : > > struct qstr { > const unsigned char *name; > unsigned int len; > unsigned int hash; > }; В C-библиотеке есть dirent http://www.gnu.org/software/libc/manual/html_node/Directory-Entries.html И кошмар потом будет с ней. Правда в документации написано про unsigned char d_namlen а на деле структура такие поля имеет: struct dirent { __ino_t d_ino; __off_t d_off; unsigned short int d_reclen; unsigned char d_type; char d_name[256];<-><------>/* We must not include limits.h! */ }; Ну и глупые , что не должны ключать limits.h Формулировка проблемы на русском и обходной путь для решения (к сожалению, он не может работать, но можно сделать, чтобы он работал) http://bb.comp-house.ru/comp-house.repo/wiki/linux-long-filenames Описание проблемы с MacFUSE, признание, что на Маке такая же проблема, и расчёт, что для представления 255 корейских букв нужно 2295 байт: https://groups.google.com/forum/#!topic/macfuse/g9uB-PVoJ2o Видимо, корейцам, японцам и китайцам проблема ещё более актуальна, чем русским. Есть интересная идея, которая позволит не переделывать файловые системы, предлагая тормозной вариант: реализовать укорачивание файлов через fuse. Ничего готового я не нашёл (если не считать перекодирования в 8-битную кодировку, как описано выше), но есть много похожего: http://sourceforge.net/apps/mediawiki/fuse/index.php?title=DatabaseFileSystems Нужно придумать, как поставленная проблема будет называться на английском. Проблема в том, что Long Filenames принято называть имена, которая придумала MS для расширения DOS-имён 8.3 в длинные. (В ответ на comment #8) > Формулировка проблемы на русском и обходной путь для решения (к сожалению, > он не может работать, но можно сделать, чтобы он работал) > http://bb.comp-house.ru/comp-house.repo/wiki/linux-long-filenames > Кажется, что подход, основанный на создании ФС в userspace - наиболее простое решение и наименее проблемное. > > Есть интересная идея, которая позволит не переделывать файловые системы, > предлагая тормозной вариант: > реализовать укорачивание файлов через fuse. > Ничего готового я не нашёл (если не считать перекодирования в 8-битную > кодировку, как описано выше), но есть много похожего: > http://sourceforge.net/apps/mediawiki/fuse/index. > php?title=DatabaseFileSystems Не совсем понятно каким именно образом будет укорачиваться это имя. Предлжение по названию проблемы: Short filename's length in Linux. Или по названию баги: Increasing filename length in Linux Имена будут отображаться, как есть, а при обращении к ним, модуль ядра FUSE будет перехватывать эти обращения и приводить имена к формату, понятному vfs(<=255 byte)? (В ответ на comment #10) > Предлжение по названию проблемы: Я подумал, что надо попробовать предложить термин VLFN - Very Long FileNames Это будет как бы продолжением темы LFN (Long FileNames), с которой Microsoft когда-то сделала прорыв. (В ответ на comment #10) > Предлжение по названию проблемы: Я подумал, что надо попробовать предложить термин VLFN - Very Long FileNames Это будет как бы продолжением темы LFN (Long FileNames), с которой Microsoft когда-то сделала прорыв. (В ответ на comment #7) > (В ответ на comment #6) > В C-библиотеке есть dirent > http://www.gnu.org/software/libc/manual/html_node/Directory-Entries.html > > И кошмар потом будет с ней. Правда в документации написано про unsigned char > d_namlen > а на деле структура такие поля имеет: > > struct dirent > { > __ino_t d_ino; > __off_t d_off; > unsigned short int d_reclen; > unsigned char d_type; > char d_name[256];<-><------>/* We must not include limits.h! */ > }; > Ну и глупые , что не должны ключать limits.h оказывается , что там в структуре dirent нет длины имени файла, d_reclen - длина самой записи Написана небольшая программа, которая пытается создать файл, с указанным в ней именем, а затем проверяет был ли файл создан и, если да, выводит его имя и длину имени. Затем выводит некоторые статические ограничения на длину файла из limits.h. В планах разобраться с динамическими ограничениями, управляемыми функциями sysconf, pathconf, fpathconf. Посмотреть , что будет, если длина файла будет занимать больше 255 байт. (В ответ на comment #15) > Написана небольшая программа, которая пытается создать файл, с указанным в > ней именем, а затем проверяет был ли файл создан и, если да, выводит его имя > и длину имени. Ну выводить имя стоит в любом случае. И быть готовым к тому, что в следующей версии нужно будет опытным путём (уменьшая длину имени, пока не создастся файл) определить реальное ограничение. > В планах разобраться с динамическими ограничениями, управляемыми функциями * получаемыми через функции… В программу добавлен вывод функции fpathconf, которая выводит ограничения( в нашем случае NAME_MAX и PATH_MAX) для конкретного файла, по его дескриптору В программу добавлен вывод функции pathconf, которая выводит ограничения( в нашем случае NAME_MAX и PATH_MAX) для конкретного файла, по его имени. Функция sysconf подобной информации не предоставляет и на данный момент отсутствует. Добавлена функция генерации длинных имен файлов. При создании файлов длинной >255 символов происходит ошибка. (В ответ на comment #18) > В программу добавлен вывод функции pathconf, которая выводит ограничения( в > нашем случае NAME_MAX и PATH_MAX) для конкретного файла, по его имени. Этот текст уже был :) Потренируйся с Git http://wiki.etersoft.ru/GitTask и публикуй свой репозиторий Добавлена функция поиска наибольшей доступной длины имени, с которым можно создать файл. Кажется правильным попытаться сначала создать файл с длинным именем - посмотреть, какие ограничения накладывает сама функция open() из glibc и убрать их. Там в различных местах (ядра и glibc) может быть вбито число 255, без использования каких-либо констант. Это стоит признать неправильным, и заменить числа на соответствующие константы, возможно, для начала, с нестандартным названием, раз уж они не могут по каким-то соображениям (которые хорошо бы выяснить) использовать стандартные константы. Ограничения на длину имени создаваемого файла в линукс задается числом :
struct dirent
{
#ifndef __USE_FILE_OFFSET64
__ino_t d_ino;
__off_t d_off;
#else
__ino64_t d_ino;
__off64_t d_off;
#endif
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256]; /* We must not include limits.h! */
};
Видимо, связано это с тем, что в файле glibc/posix/glob.c определяется переменная NAME_MAX следующим образом:
/* NAME_MAX is usually defined in <dirent.h> or <limits.h>. */
#include <limits.h>
#ifndef NAME_MAX
# define NAME_MAX (sizeof (((struct dirent *) 0)->d_name))
#endif
Почему не используется какая-либо константа в самом файле dirent.h, почему нет ее определения в файле limits.h библиотеки glibc - непонятно. Найти, где конкретно реализован вызов open, чтобы увидеть там проверку соответствия длины файл, пока не получилось
В планах - исправить в glibc значения констант, добавить функциональность, при которой при создании файлов будут выводиться диагностические сообщения. Далее тоже самое сделать с vfs. Все это попробовать запустить на системе с reiserfs или подобной, где нет ограничения на длину имени файла 255 байт. нашел код функции open(), сложен для понимания:
__fortify_function int
open (const char *__path, int __oflag, ...)
{
if (__va_arg_pack_len () > 1)
__open_too_many_args ();
if (__builtin_constant_p (__oflag))
{
if ((__oflag & O_CREAT) != 0 && __va_arg_pack_len () < 1)
{
__open_missing_mode ();
return __open_2 (__path, __oflag);
}
return __open_alias (__path, __oflag, __va_arg_pack ());
}
if (__va_arg_pack_len () < 1)
return __open_2 (__path, __oflag);
return __open_alias (__path, __oflag, __va_arg_pack ());
При этом:
extern int __REDIRECT (__open_alias, (const char *__path, int __oflag, ...),
open) __nonnull ((1));
а,
# define __REDIRECT(name, proto, alias) name proto __asm__ (__ASMNAME (#alias))
и это говорит о том, что вызывается ассемблерная вставка open.
возможно, я не прав.
Было выяснено утилитой strace, что ошибку возвращает системный вызов ядра open():
open("a1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456", O_WRONLY|O_CREAT|O_TRUNC, 0401) = -1 ENAMETOOLONG (File name too long)
Чтобы выявить функцию, вызывающую данную ошибку при создании файла, рядом с каждой отсылкой идентификатора ошибки был поставлен вывод сообщения ядра по типу:
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2499,8 +2499,10 @@ EXPORT_SYMBOL_GPL(d_materialise_unique);
static int prepend(char **buffer, int *buflen, const char *str, int namelen)
{
*buflen -= namelen;
- if (*buflen < 0)
+ if (*buflen < 0) {
+ printk("dcache.c error prepend()\n");
return -ENAMETOOLONG;
+ }
*buffer -= namelen;
memcpy(*buffer, str, namelen);
return 0;
вопрос: нужен ли в printk() параметр по типу KERN_INFO. Так же необходимо сделать так, чтобы в каждом измененном файле был подключен linux/kernel.h
Планируется протестировать ядро на виртуальной машине.
На данный момент, после изучения кода ядра, складывается ощущение, что практически все зависит от заданного ограничения NAME_MAX или аналогичных ограничений конкретных файловых систем.
(В ответ на comment #26) ... > На данный момент, после изучения кода ядра, складывается ощущение, что > практически все зависит от заданного ограничения NAME_MAX или аналогичных > ограничений конкретных файловых систем. Ну вообще информация о NAME_MAX дана в исходных данных задачи, это то, с чего нужно было начать — изменить константу и скомпилировать ядро. Дальше уже смотреть, что где вылезет. Собственно, с NTFS или reiserfs ничего вылезти и не должно. Было поставлено ядро с выводом диагностических сообщений, которые показали, что выводится диагностическое сообщение соответствующее ext4, #dmesg: [ 721.248824] EXT_4_ERR Программа, создающая файлы, была изменена, теперь она так же создает длинный файл(>255 байт) русских символов. При запуске программы из разделов ntfs и raiserfs было выявлено, что в файловой системе ntfs файл с длинным русским именем создается, в противоположность этому в reiserfs - нет. На данный момент можно предположить, что основным ограничением является ограничение на длину со стороны файловой системы. Роль в ограничения со стороны vfs и glibc не ясна. В планах просмотреть, как работают функции, оперирующие именами. Из все этого следует, что необходимо создать тест, который будет проверять функции, оперирующие с именами, исправить нужные функции в glibc, выбрать файловую систему, в которой будет проще всего сделать изменения и провести их. В связи с задачей совмещения нескольких репозиториев в один, написан скрипт для перемещения всех файлов в подпапку с перезаписью путей к файлам. основная идея взята отсюда: http://ahitrin.github.io/work/2012/07/16/%D0%A1%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B5%D0%B2-%D0%B2-git/ скрипт: current_dir=${PWD##*/} temp_dir=tmpdir git filter-branch --tree-filter 'mkdir ../tmpdir; mv ./* ../tmpdir; mv ../tmpdir ./$current_dir/; mv ./$current_dir/tmpdir ./$current_dir/$current_dir' master перед запуском необходимо перейти в директорию репозитория. Далее планируется действовать по аналогии со статьей по ссылке. Тестовая программа теперь в репозитории: git.eter:/people/reprofy/public/glibc_tests.git Были изучены системы jfs и btrfs, в которых были найдены соответствующие ограничения, краткое описание практического сравнения уже здесь есть: http://wiki.etersoft.ru/Comparison_of_file_systems В этих двух системах были изменены значения ограничений, после чего была произведена попытка их запуска. В планах рассмотреть варианты других систем, возможно, менее популярных, оценить сложность их исправления под наши нужды, глубже разобраться в плюсах и минусах btrfs и jfs. Оценить системы EXTx. Добавлен вывод ошибки, если не удается создать файл с длинным название в testfilenames. Изменен предел для xfs, в этой версии создать файл удалось, но после возникла ошибка: [root@host-35 xfs]# ls ls: невозможно открыть каталог .: Ошибка ввода/вывода в планах - увеличить ограничение на ext4, провести более обширный тест btrfs В btrfs файл был создан и была выведена соответствующая информация. Однако, обнаружить файл визуально не удалось. При попытке создать директорию с длинным именем, визуально, создалось множество директорий, имена которых были кусками имени предполагаемой к созданию директории. Но так как btrfs - единственная фс, каталоги с которой все-таки открываются после создания файла с длинным именем, то кажется целесообразным рассмотреть ее в качестве кандидата на решение данной задачи: https://bugs.etersoft.ru/show_bug.cgi?id=9672 создана страница wiki http://wiki.etersoft.ru/Linux/VLFN Так как тесты btrfs не выявляют ошибок, в планах рассмотреть, как необходимо изменить glibc для корректной работы с измененной ФС. Откладываем задачи, к которым не обращались более 100 дней. Обсуждение проблемы на LOR образца 2009 года: http://www.linux.org.ru/forum/general/3451283 Список лимитов на файлы: http://serverfault.com/questions/9546/filename-length-limits-on-linux |