Мне трудно понять, как работает кодировка имени файла. На unix.SE я нахожу противоречивые объяснения.
Имена файлов хранятся в виде символов
Процитирую другой ответ: Несколько вопросов о кодировке символов файловой системы в Linux.
[…] Как вы упоминаете в своем вопросе, имя файла UNIX - это просто последовательность символов; ядро ничего не знает о кодировке, которая полностью является концепцией пользовательского пространства (т.е. уровня приложения).
Если имена файлов хранятся в виде символов, должна использоваться какая-то кодировка, поскольку в конечном итоге имя файла должно заканчиваться последовательностью битов или байтов на диске. Если пользователь может выбрать любую кодировку для сопоставления символов с последовательностью байтов, которая подается в ядро, можно создать любую последовательность байтов для действительного имени файла.
Предположим следующее: пользователь использует случайную кодировку X , которая переводит файл foo
в последовательность байтов α и сохраняет его на диск. Другой пользователь использует кодирующий Y . В этой кодировке α переводится как /
, что не допускается в качестве имени файла. Однако для первого пользователя файл действителен.
Я предполагаю, что этот сценарий не может произойти.
Имена файлов хранятся в виде двоичных объектов
Процитирую другой ответ: Какая кодировка charset используется для имен файлов и путей в Linux?
Как отмечают другие, на самом деле нет ответа на это: имена файлов и пути не имеют кодировки; ОС работает только с последовательностью байтов. Отдельные приложения могут по своему усмотрению интерпретировать их как кодируемые, но это варьируется.
Если система не работает с символами, как можно запретить использование определенных символов (например, /
или NULL
) в именах файлов? Там нет понятия /
без кодировки.
Объяснение может состоять в том, что файловая система может хранить имена файлов, содержащие любой
символ, и только пользовательские программы, принимающие во внимание кодировку, будут подавлять имена файлов, содержащие недопустимые символы. Это, в свою очередь, означает, что файловые системы и ядро могут без каких-либо затруднений обрабатывать имена файлов, содержащие /
.
Я также предполагаю, что это неправильно.
Где происходит кодирование и где накладывается ограничение, запрещающее использование определенных символов?
Ответы:
Краткий ответ: ограничения, налагаемые в ядре Unix / Linux / BSD,
namei()
функция. Кодирование происходит в программах уровня пользователя, таких какxterm
,firefox
илиls
.Я думаю, что вы начинаете с неправильных посылок. Имя файла в Unix - это строка байтов с произвольными значениями. Несколько значений 0x0 (ASCII Nul) и 0x2f (ASCII '/') просто недопустимы, ни как часть многобайтовой кодировки символов, ни как что-либо еще. «Байт» может содержать число, представляющее символ (в ASCII и некоторых других кодировках), но «символ» может требовать более 1 байта (например, кодовые точки выше 0x7f в представлении Unicode в UTF-8).
Эти ограничения вытекают из соглашений о печати имен файлов и набора символов ASCII. Исходные Unix-ы использовали ASCII '/' (численно 0x2f) байты для разделения частей частично или полностью квалифицированного пути (например, '/ usr / bin / cat' содержит фрагменты "usr", "bin" и "cat") , Оригинальные Unixes использовали ASCII Nul для завершения строк. Помимо этих двух значений, байты в именах файлов могут принимать любые другие значения. Эхо этого можно увидеть в кодировке UTF-8 для Unicode. Печатные символы ASCII, включая «/», занимают в UTF-8 только один байт. UTF-8 для кодовых точек выше не содержит байтов с нулевым значением, кроме управляющего символа Nul. UTF-8 был изобретен для Plan-9, претендента на трон Unix.
В более ранних версиях Unix (и, похоже, в Linux) была
namei()
функция, которая просто просматривает пути по байту за раз и разбивает пути на части по 0x2F-значным байтам, останавливаясь на нулевом значении.namei()
является частью ядра Unix / Linux / BSD, поэтому здесь применяются исключительные байтовые значения.Обратите внимание, что до сих пор я говорил о значениях байтов, а не символов.
namei()
не применяет семантику символов в байтах. Это зависит от программ уровня пользователя, напримерls
, которые могут сортировать имена файлов по байтовым значениям или символьным значениям.xterm
решает, какие пиксели загораться для имен файлов, основываясь на кодировке символов. Если вы не скажете,xterm
что у вас есть имена файлов в кодировке UTF-8, вы увидите много тарабарщины, когда вызовете его. Еслиvim
он не скомпилирован для обнаружения кодировок UTF-8 (или чего-либо другого, UTF-16, UTF-32), вы увидите много бреда при открытии «текстового файла», содержащего символы в кодировке UTF-8.источник
namei()
было заброшено около 1986 года. Более новые UNIX-системы используютlookuppn()
VFS.Дело в том, что ядру все равно, как приложения интерпретируют данные, которые ему даны как имя файла.
Давайте представим, что у меня есть приложение на C, которое работает исключительно со строками UTF-16. И я ввожу через правильно настроенный метод ввода символ ∯ (Unicode 0x222F) в подсказку / диалог «Сохранить как».
Если приложение не выполняет какую-либо форму перевода и отправляет ее в простой старой строке C (
char*
), скажем,fopen
в режиме записи, ядро не увидит ∯ или даже не попытается это представить. Он будет видеть дваchar
s, одно за другим, со значениями0x22 0x2F
(при условии, что 8-битные символы отсутствуют, а в библиотеке C нет ничего смешного ).То есть, с точки зрения ядра, допустимый char (
"
), за которым следует/
(ASCII 0x2F).fopen
вернетсяEISDIR
(т. е. «это похоже на каталог, и вы запросили режим записи!»).Если бы я ввел ∮ (Unicode
0x222E
), ядро увидело бы два точных символа и создало бы файл, который, как видно из приложения, говорящего на ASCII, был бы назван".
.Если бы я ввел
a
в приложение имя файла, и приложение передало его в UTF-16 ядру, ядро прочитало бы0x00 0x61
и даже не приняло это во внимание0x61
, потому0x00
что строка уже заканчивает строку, насколько она есть. обеспокоен. Сообщение об ошибке будет таким же, как и для пустого имени файла (ENOENT
я считаю).Таким образом, ядро действительно воспринимает данные как блоб. Это поток
char
с. Недопустимые «символы» в выбранной вами кодировке пользовательского пространства - это те, которые генерируют0x00
или0x2F
(«ноль» и/
) в своем двоичном объекте (двоичное представление, которое передается ядру).источник
0x00
и0x2F
жестко закодированы в ядре. Это, в свою очередь, означает, что каталоги разделены не/
символом a , а тем символом, который отображается0x2F
в используемой кодировке./
не 0x2F -chars
фактически не может использовать 8-битный .) «Традиционный» разделитель dir есть/
. Это 0x27 в 8-битных системах ASCII (не EBCDIC, например).a
строке с нулевым символом в конце.Разделение байтов и символов произошло после того, как был разработан Unix. Когда это было разработано, использование слов только сообщало о том, как 8 (или 6, или 9) бит интерпретировались, но кодировки слов не упоминались.
Имена файлов - это последовательности байтов. Допускается любой байт, кроме 0x2f "/". Байт, содержащий 0x00, не может даже проникнуть в ядро из-за его использования в качестве ограничителя строки. Приложение может интерпретировать последовательность байтов в соответствии с выбранной кодировкой. Если это звучит грязно, я полагаю, что это так.
Более подробную информацию можно найти по адресу http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html, который может оказаться полезным.
источник