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 foo
na 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?
Odpowiedzi:
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 jakxterm
,firefox
lubls
.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 jakls
, które mogą sortować nazwy plików na podstawie wartości bajtów lub wartości znaków.xterm
decyduje, które piksele mają się świecić w przypadku nazw plików na podstawie kodowania znaków. Jeśli nie powieszxterm
, że masz nazwy plików zakodowane w UTF-8, zobaczysz wiele bełkotów podczas ich wywoływania. Jeślivim
nie 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.źródło
namei()
został porzucony około 1986 roku. Nowsze systemy UNIX korzystają zlookuppn()
VFS.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*
), powiedzmyfopen
w trybie zapisu, jądro nie zobaczy ∯, a nawet spróbuje to sobie wyobrazić. Zobaczy dwachar
s, jeden po drugim, z wartościami0x22 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).fopen
zwróciEISDIR
(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ł
a
do aplikacji jako nazwa pliku, a aplikacja przekazała ją do UTF-16 do jądra, jądro przeczytałoby0x00 0x61
, a nawet nawet nie wzięło tego pod uwagę0x61
, ponieważ0x00
już kończy łańcuch, o ile jest zaniepokojony. Komunikat o błędzie byłby taki sam jak w przypadku pustej nazwy pliku (ENOENT
wierzę).Jądro rzeczywiście bierze dane za obiekt blob. To strumień
char
s. Nieprawidłowe „znaki” w wybranym przez ciebie kodowaniu w przestrzeni użytkownika to te, które generują0x00
lub0x2F
(„null” i/
) w ich obiektach blob (reprezentacja binarna przekazywana do jądra).źródło
0x00
i0x2F
są na stałe zakodowane w jądrze. To z kolei oznacza, że katalogi nie są oddzielone znakiem a/
, lecz do dowolnego znaku, który mapuje0x2F
w używanym kodowaniu./
nie ma 0x2F - w rzeczywistości może nie używać 8-bitówchars
.) „Tradycyjnym” separatorem dir jest/
. To jest 0x27 w 8-bajtowych systemach ASCII (nie na przykład EBCDIC).a
łańcuch (zakończony zerem) .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.
źródło