Розуміння кодування імені файлу Unix


25

Мені важко зрозуміти, як працює кодування імені файлів. На unix.SE я знаходжу суперечливі пояснення.

Імена файлів зберігаються як символи

Напрошу ще одну відповідь: Кілька питань щодо кодування символів файлової системи на Linux

[…] Як ви згадуєте у своєму запитанні, ім'я файлу UNIX - це лише послідовність символів; ядро нічого не знає про кодування, яке цілком є ​​концепцією простору користувача (тобто на рівні додатків).

Якщо імена файлів зберігаються як символи, повинно бути якесь кодування, оскільки нарешті ім'я файлу має закінчитися бітом або байтом на диску. Якщо користувач може обрати будь-яке кодування для відображення символів у послідовності байтів, яка подається в ядро, можна створити будь-яку послідовність байтів для дійсного імені файлу.

Припустимо наступне: Користувач використовує випадкове кодування X , яке переводить файл fooу послідовність байтів α і зберігає його на диску. Інший користувач використовує кодує Y . У цьому кодуванні α перекладається на /, що не дозволено як ім'я файлу. Однак для першого користувача файл дійсний.

Я припускаю, що такого сценарію не може статися.

Імена файлів зберігаються у вигляді двійкових крапок

Процитую ще одну відповідь: Яке кодування шаблонів використовується для назви файлів та шляхів у Linux?

Як зазначають інші, насправді немає відповіді на це: у іменах і каналах немає кодування; ОС працює лише з послідовністю байтів. Окремі додатки можуть вирішити інтерпретувати їх як певне кодування, але це різниться.

Якщо система не працює з символами, то як окремі символи (наприклад, /або NULL) можуть бути заборонені у назвах файлів? Існує поняття / без кодування.

Поясненням цього є те, що файлова система може зберігати імена файлів, що містять будь-який символ, і тільки користувацькі програми, які враховують кодування, враховують імена файлів, що містять недійсні символи. Це, в свою чергу, означає, що файлові системи та ядро ​​можуть без особливих труднощів обробляти імена файлів, що містять /.

Я також припускаю, що це неправильно.

Де відбувається кодування і де існує обмеження, що не дозволяє певні символи?


Нуль однаковий (0) у всіх кодировках.
Кевін

2
@Kevin Не зовсім: не, скажімо, UTF-16 або UCS-4 (= UTF-32), або більшість інших багатобайтових кодувань, які не є розширеннями ASCII.
Жиль "ТАК - перестань бути злим"

1
Власне, у відповіді Ріккардо Муррі мали бути вказані байти, а не символи . Більшість файлових систем зберігають байти.
Жиль «ТАК - перестань бути злим»

@Gilles: ще раз Ī̲ побачиш, що ти справді дивишся, що написано .
Incnis Mrsi

Відповіді:


25

Коротка відповідь: обмеження, накладені в ядрі Unix / Linux / BSD, namei()функція. Кодування відбувається в програмах на рівні користувача , як xterm, firefoxабо ls.

Я думаю, ви починаєте з неправильних приміщень. Ім'я файлу в Unix - це рядок байтів з довільними значеннями. Кілька значень, 0x0 (ASCII Nul) та 0x2f (ASCII '/'), просто не дозволено, не як частина багатобайтового кодування символів, ні як нічого. "Байт" може містити число, що представляє символ (в ASCII та деяких інших кодуваннях), але "символу" може знадобитися більше 1 байта (наприклад, кодові точки вище 0x7f у представленні UTF-8 Unicode).

Ці обмеження випливають із правил друку імен файлів та набору символів ASCII. Оригінальні Unixes, що використовуються ASCII '/' (числово 0x2f), обчислені байтами, щоб розділити фрагменти частково або повністю кваліфікованого шляху (наприклад, '/ usr / bin / cat' має шматки "usr", "bin" та "cat") . Оригінальні Unixes використовували ASCII Nul для завершення рядків. Крім цих двох значень, байти в іменах файлів можуть приймати будь-яке інше значення. Відгомін цього ви можете побачити в кодуванні UTF-8 для Unicode. Друковані символи ASCII, включаючи '/', беруть лише один байт в UTF-8. UTF-8 для наведених вище кодів не містить жодних нульових байтів, за винятком символу управління Nul. UTF-8 був винайдений для плану-9, «Претендера на трон Unix».

Старі Unixes (і це виглядає як 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.
schily

17

Справа в тому, що ядро ​​не хвилює жодного біту, як програми інтерпретують дані, які йому надаються, як ім'я файлу.

Уявімо, у мене є програма C, яка займається виключно рядками UTF-16. І я вводя через правильно налаштований метод введення символ ((Unicode 0x222F) у рядок / діалогове вікно «Зберегти як».

Якщо додаток не здійснює жодної форми перекладу і надсилає це у звичайному старому рядку C ( char*), скажімо, fopenв режимі запису, ядро ​​не побачить ∯ або навіть спробує це уявити. Він побачить два chars, одна за одною, зі значеннями 0x22 0x2F(якщо припустити, 8-бітові символи та без забав у бібліотеці C ).
Тобто, з точки зору ядра, дійсний char ( "), за яким слідує /(ASCII 0x2F). fopenповернеться EISDIR(тобто "виглядає як каталог і ви запитували режим запису!").
Якби я ввійшов ∮ (Unicode 0x222E), ядро ​​побачило б дві тонкі символи і створило файл, який, як видно через ASCII-розмовляючий додаток, був би названий "..

Якби я вписав aу додаток як ім'я файлу, і програма передала його в UTF-16 ядру, ядро ​​прочитало б 0x00 0x61, а насправді навіть не вважало б це 0x61, оскільки 0x00рядок вже закінчує, наскільки це зацікавлений. Повідомлення про помилку буде таким самим, як і для порожнього імені файлу ( ENOENTя вважаю).

Таким чином, ядро ​​дійсно сприймає дані як краплину. Це потік chars. Недійсні "символи" у кодуванні вашого простору користувача - це ті, що генерують 0x00або 0x2F("null" і /) у своїй краплі (двійкове представлення, яке передається ядру).


Якщо я зрозумію вас правильно, то не існує таких понять, як недійсні символи. Існують просто недійсні послідовності байтів. А значення 0x00і 0x2Fважко закодовані в ядрі. Це, в свою чергу, означає, що каталоги не розділені а /, а будь-якими символьними картами 0x2Fв кодуванні, яке використовується.
Марко

Так, це ідея, якщо ви хочете бачити це саме так. (Але це може бути неправильно. Ядро може мати " /нативне кодування", де немає 0x2F - насправді він може не використовувати 8-бітний chars.) "Традиційний" роздільник dir є /. Це 0x27 на 8-байтових системах ASCII (не для EBCDIC, наприклад).
Мат

Ви припускаєте, що UTF-16BE, тоді як в UTF-16LE U + 0061 призведе до (нульового завершення) aрядка.
Incnis Mrsi

4

Розмежування байтів проти символів відбулося значно після створення Unix. Коли воно було розроблено, використання слів лише передало щось про те, як інтерпретували 8 (або 6, або 9) бітів, але кодування слова не згадувались.

Імена файлів - це послідовності байтів. Будь-який байт, крім 0x2f "/", дозволений. Байт, що містить 0x00, навіть не може проникнути до ядра через його використання в якості термінального рядка. Додаток може інтерпретувати послідовність байтів відповідно до обраного кодуванням. Якщо це звучить безладним, я вважаю, що так.

Більше інформації можна знайти на веб- сайті http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.