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 |