Zrozumienie kodowania nazw plików w systemie Unix


25

Trudno mi zrozumieć, jak działa kodowanie nazw plików. Na unix.SE znajduję sprzeczne wyjaśnienia.

Nazwy plików są przechowywane jako znaki

Cytując inną odpowiedź: Kilka pytań na temat kodowania znaków w systemie plików w systemie Linux

[…] Jak wspominasz w swoim pytaniu, nazwa pliku UNIX to po prostu ciąg znaków; jądro nie wie nic o kodowaniu, które jest całkowicie pojęciem przestrzeni użytkownika (tj. poziomu aplikacji).

Jeśli nazwy plików są przechowywane jako znaki, musi istnieć jakiś rodzaj kodowania, ponieważ ostatecznie nazwa pliku musi kończyć się na sekwencji jako bit lub bajt. Jeśli użytkownik może wybrać dowolne kodowanie, aby odwzorować znaki na sekwencję bajtów dostarczaną do jądra, możliwe jest utworzenie dowolnej sekwencji bajtów dla prawidłowej nazwy pliku.

Załóżmy, że: Użytkownik używa losowego kodowania X , które tłumaczy plik foona sekwencję bajtów α i zapisuje go na dysku. Kolejne zastosowania użytkownika kodującego Y . W tym kodowaniu α tłumaczy się /, co nie jest dozwolone jako nazwa pliku. Jednak dla pierwszego użytkownika plik jest prawidłowy.

Zakładam, że ten scenariusz nie może się zdarzyć.

Nazwy plików są przechowywane jako binarne obiekty BLOB

Cytując inną odpowiedź: Jakiego kodowania zestawu znaków używa się w nazwach plików i ścieżkach w systemie Linux?

Jak zauważają inni, tak naprawdę nie ma na to odpowiedzi: nazwy plików i ścieżki nie mają kodowania; system operacyjny zajmuje się tylko sekwencją bajtów. Poszczególne aplikacje mogą interpretować je jako zakodowane w pewien sposób, ale to się różni.

Jeśli system nie radzi sobie ze znakami, jak można zabronić poszczególnym znakom (np. /Lub NULL) w nazwach plików? Nie ma pojęcia / bez kodowania.

Wyjaśnieniem byłoby, że system plików może przechowywać nazwy plików zawierające dowolny znak, a tylko programy użytkownika, które biorą pod uwagę kodowanie, dławią nazwy plików zawierające nieprawidłowe znaki. To z kolei oznacza, że ​​systemy plików i jądro mogą bez problemu obsługiwać nazwy plików zawierające /.

Zakładam również, że to jest złe.

Gdzie odbywa się kodowanie i jakie jest ograniczenie polegające na niedopuszczaniu określonych znaków?


Null jest taki sam (0) we wszystkich kodowaniach.
Kevin

2
@Kevin Niezupełnie: nie w, powiedzmy, UTF-16 lub UCS-4 (= UTF-32), ani w większości innych kodowań wielobajtowych, które nie są rozszerzeniami ASCII.
Gilles „SO- przestań być zły”

1
W rzeczywistości odpowiedź Riccardo Murri powinna zawierać bajty, a nie znaki . Większość systemów plików przechowuje bajty.
Gilles 'SO - przestań być zły'

@Gilles: jeszcze raz widzę, że naprawdę oglądasz, co jest napisane .
Incnis Mrsi,

Odpowiedzi:


25

Krótka odpowiedź: ograniczenia nałożone na jądro Unix / Linux / BSD, namei()funkcja. Kodowanie odbywa się w ramach programów na poziomie użytkownika jak xterm, firefoxlub ls.

Myślę, że zaczynasz od niepoprawnych przesłanek. Nazwa pliku w systemie Unix to ciąg bajtów o dowolnych wartościach. Kilka wartości, 0x0 (ASCII Nul) i 0x2f (ASCII '/') jest po prostu niedozwolonych, nie jako część wielobajtowego kodowania znaków, a nie jakkolwiek. „Bajt” może zawierać liczbę reprezentującą znak (w ASCII i niektórych innych kodowaniach), ale „znak” może wymagać więcej niż 1 bajtu (na przykład punkty kodowe powyżej 0x7f w reprezentacji Unicode w standardzie UTF-8).

Ograniczenia te wynikają z konwencji drukowania nazw plików i zestawu znaków ASCII. Oryginalne Uniksy wykorzystywały bajty o wartości ASCII '/' (numerycznie 0x2f) do oddzielenia części częściowo lub w pełni kwalifikowanej ścieżki (np. „/ Usr / bin / cat” zawiera elementy „usr”, „bin” i „cat”) . Oryginalne Uniksy używały ASCII Nul do kończenia ciągów. Oprócz tych dwóch wartości bajty w nazwach plików mogą przyjmować dowolne inne wartości. Możesz zobaczyć echo tego w kodowaniu UTF-8 dla Unicode. Drukowane znaki ASCII, w tym „/”, zajmują tylko jeden bajt w UTF-8. UTF-8 dla powyższych punktów kodowych nie zawiera żadnych bajtów o wartości zerowej, z wyjątkiem znaku sterującego Nul. UTF-8 został wynaleziony dla Plan-9, The Pretender to the Throne of Unix.

Starsze Uniksy (i wygląda na Linuksa) miały namei()funkcję, która po prostu patrzy na ścieżki bajt naraz, i dzieli ścieżki na kawałki w bajtach o wartości 0x2F, zatrzymując się na bajcie o wartości zerowej. namei()jest częścią jądra Unix / Linux / BSD, więc tam wymuszane są wyjątkowe wartości bajtów.

Zauważ, że do tej pory mówiłem o wartościach bajtów, a nie o znakach. namei()nie wymusza żadnej semantyki znaków w bajtach. To zależy od programów na poziomie użytkownika, takich jak ls, które mogą sortować nazwy plików na podstawie wartości bajtów lub wartości znaków. xtermdecyduje, które piksele mają się świecić w przypadku nazw plików na podstawie kodowania znaków. Jeśli nie powiesz xterm, że masz nazwy plików zakodowane w UTF-8, zobaczysz wiele bełkotów podczas ich wywoływania. Jeśli vimnie jest skompilowany w celu wykrycia kodowania UTF-8 (lub cokolwiek innego, UTF-16, UTF-32), zobaczysz dużo bełkotu po otwarciu „pliku tekstowego” zawierającego znaki zakodowane w UTF-8.


Prawidłowo, namei()został porzucony około 1986 roku. Nowsze systemy UNIX korzystają z lookuppn()VFS.
schily

17

Chodzi o to, że jądro nie obchodzi ani trochę, jak aplikacje interpretują dane, które podano jako nazwę pliku.

Wyobraźmy sobie, że mam aplikację C, która obsługuje wyłącznie łańcuchy UTF-16. I wprowadzam, za pomocą odpowiednio skonfigurowanej metody wprowadzania, symbol ((Unicode 0x222F) do monitu / okna dialogowego „Zapisz jako”.

Jeśli aplikacja nie wykona żadnej formy tłumaczenia i wyśle ​​to, zwykłym ciągiem C ( char*), powiedzmy fopenw trybie zapisu, jądro nie zobaczy ∯, a nawet spróbuje to sobie wyobrazić. Zobaczy dwa chars, jeden po drugim, z wartościami 0x22 0x2F(przy założeniu 8-bitowych znaków i żadnych zabawek w bibliotece C ).
To znaczy, z punktu widzenia jądra, prawidłowy char ( "), po którym następuje /(ASCII 0x2F). fopenzwróci EISDIR(tzn. „wygląda jak katalog i zażądałeś trybu zapisu!”).
Gdybym wprowadził ∮ (Unicode 0x222E), jądro zobaczyłoby dwa dobre znaki i stworzyło plik, który, jak widać w aplikacji obsługującej ASCII, zostałby nazwany "..

Gdybym wszedł ado aplikacji jako nazwa pliku, a aplikacja przekazała ją do UTF-16 do jądra, jądro przeczytałoby 0x00 0x61, a nawet nawet nie wzięło tego pod uwagę 0x61, ponieważ 0x00już kończy łańcuch, o ile jest zaniepokojony. Komunikat o błędzie byłby taki sam jak w przypadku pustej nazwy pliku ( ENOENTwierzę).

Jądro rzeczywiście bierze dane za obiekt blob. To strumień chars. Nieprawidłowe „znaki” w wybranym przez ciebie kodowaniu w przestrzeni użytkownika to te, które generują 0x00lub 0x2F(„null” i /) w ich obiektach blob (reprezentacja binarna przekazywana do jądra).


Jeśli dobrze zrozumiem, nie ma czegoś takiego jak nieprawidłowe postacie. Istnieją tylko nieprawidłowe sekwencje bajtów. A wartości 0x00i 0x2Fsą na stałe zakodowane w jądrze. To z kolei oznacza, że ​​katalogi nie są oddzielone znakiem a /, lecz do dowolnego znaku, który mapuje 0x2Fw używanym kodowaniu.
Marco

Tak, to jest pomysł, jeśli chcesz to zobaczyć w ten sposób. (Ale to może być niepoprawne. Jądro może mieć „natywne kodowanie”, gdzie /nie ma 0x2F - w rzeczywistości może nie używać 8-bitów chars.) „Tradycyjnym” separatorem dir jest /. To jest 0x27 w 8-bajtowych systemach ASCII (nie na przykład EBCDIC).
Mat

Zakładasz, że UTF-16BE, podczas gdy w UTF-16LE U + 0061 spowoduje ałańcuch (zakończony zerem) .
Incnis Mrsi,

4

Rozdzielenie bajtów od znaków nastąpiło znacznie po zaprojektowaniu Uniksa. Kiedy zostało zaprojektowane, użycie słów przekazało tylko coś o interpretacji 8 (lub 6 lub 9) bitów, ale kodowania słów nie wspomniano.

Nazwy plików to sekwencje bajtów. Dowolny bajt oprócz 0x2f „/” jest dozwolony. Bajt zawierający 0x00 nie może nawet dostać się do jądra z powodu jego użycia jako terminatora łańcucha. Aplikacja może interpretować sekwencję bajtów zgodnie z wybranym kodowaniem. Jeśli brzmi to niechlujnie, to chyba tak.

Więcej informacji można znaleźć na stronie http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html, które mogą okazać się przydatne.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.