Grundlegendes zur Kodierung von Unix-Dateinamen

25

Es fällt mir schwer zu verstehen, wie die Dateinamencodierung funktioniert. Auf unix.SE finde ich widersprüchliche Erklärungen.

Dateinamen werden als Zeichen gespeichert

Um eine andere Antwort zu zitieren: Mehrere Fragen zur Zeichencodierung im Dateisystem unter Linux

[…] Wie Sie in Ihrer Frage erwähnt haben, besteht ein UNIX-Dateiname nur aus einer Folge von Zeichen. Der Kernel weiß nichts über die Kodierung, die ein Konzept auf Benutzerbereichs- (dh Anwendungsebene) ist.

Wenn Dateinamen als Zeichen gespeichert werden, muss eine Art Codierung erforderlich sein, da der Dateiname schließlich als Bit- oder Byte-Sequenz auf der Festplatte gespeichert werden muss. Wenn der Benutzer eine Codierung auswählen kann , um die Zeichen einer Bytefolge zuzuordnen, die dem Kernel zugeführt wird, kann eine beliebige Bytefolge für einen gültigen Dateinamen erstellt werden.

Angenommen, ein Benutzer verwendet eine zufällige Codierung X , die die Datei fooin die Bytefolge α übersetzt und auf der Festplatte speichert. Ein anderer Benutzer Verwendungen kodierend Y . Bei dieser Kodierung wird α übersetzt /, was als Dateiname nicht erlaubt ist. Für den ersten Benutzer ist die Datei jedoch gültig.

Ich gehe davon aus, dass dieses Szenario nicht passieren kann.

Dateinamen werden als binäre Blobs gespeichert

Um eine andere Antwort zu zitieren: Welche Zeichensatzkodierung wird für Dateinamen und Pfade unter Linux verwendet?

Wie von anderen bemerkt, gibt es keine wirkliche Antwort darauf: Dateinamen und Pfade haben keine Kodierung; Das Betriebssystem behandelt nur die Reihenfolge der Bytes. Einzelne Anwendungen interpretieren sie möglicherweise so, dass sie auf irgendeine Weise codiert sind. Dies ist jedoch unterschiedlich.

Wenn das System keine Zeichen verarbeitet, wie können bestimmte Zeichen (z. B. /oder NULL) in Dateinamen verboten werden? Es gibt keine Vorstellung von einem / ohne Kodierung.

Eine Erklärung wäre, dass das Dateisystem Dateinamen speichern kann, die beliebige Zeichen enthalten, und dass nur die Benutzerprogramme, die eine Kodierung berücksichtigen, Dateinamen mit ungültigen Zeichen verschlüsseln würden. Dies bedeutet wiederum, dass Dateisysteme und der Kernel problemlos mit Dateinamen umgehen können, die a enthalten /.

Ich gehe auch davon aus, dass dies falsch ist.

Wo findet die Codierung statt und wo besteht die Einschränkung, bestimmte Zeichen nicht zuzulassen?

Marco
quelle
Null ist in allen Kodierungen gleich (0).
Kevin
2
@ Kevin Nicht ganz: Zum Beispiel nicht in UTF-16 oder UCS-4 (= UTF-32) oder den meisten anderen Multibyte-Codierungen, die keine ASCII-Erweiterungen sind.
Gilles 'SO- hör auf böse zu sein'
1
Eigentlich hätte Riccardo Murris Antwort dort Bytes und keine Zeichen erwähnen sollen . Die meisten Dateisysteme speichern Bytes.
Gilles 'SO- hör auf böse zu sein'
@ Gilles: noch ein anderes Mal, bis du wirklich zuschaust, was geschrieben steht .
Incnis Mrsi

Antworten:

25

Kurze Antwort: Einschränkungen im Unix / Linux / BSD-Kernel, namei()Funktion. Die Kodierung erfolgt in Programmen auf Benutzerebene wie xterm, firefoxoder ls.

Ich glaube, Sie gehen von falschen Voraussetzungen aus. Ein Dateiname in Unix ist eine Folge von Bytes mit beliebigen Werten. Einige Werte, 0x0 (ASCII Nul) und 0x2f (ASCII '/'), sind einfach nicht zulässig, nicht als Teil einer Mehrbyte-Zeichencodierung, und auch nicht. Ein "Byte" kann eine Zahl enthalten, die ein Zeichen darstellt (in ASCII und einigen anderen Codierungen), ein "Zeichen" kann jedoch mehr als 1 Byte erfordern (z. B. Codepunkte über 0x7f in der UTF-8-Darstellung von Unicode).

Diese Einschränkungen ergeben sich aus den Druckkonventionen für Dateinamen und dem ASCII-Zeichensatz. Die ursprünglichen Unixe verwendeten ASCII '/' (numerisch 0x2f) Bytes, um Teile eines teilweise oder vollständig qualifizierten Pfades zu trennen (wie '/ usr / bin / cat' hat Teile "usr", "bin" und "cat"). . Die ursprünglichen Unixe verwendeten ASCII Nul, um Zeichenfolgen zu beenden. Abgesehen von diesen beiden Werten können Bytes in Dateinamen einen anderen Wert annehmen. Sie können ein Echo davon in der UTF-8-Codierung für Unicode sehen. Druckbare ASCII-Zeichen, einschließlich '/', benötigen in UTF-8 nur ein Byte. UTF-8 für die obigen Codepunkte enthält keine nullwertigen Bytes mit Ausnahme des Steuerzeichens Nul. UTF-8 wurde für Plan-9, The Pretender to the Throne of Unix, erfunden.

Ältere Unixe (und es sieht aus wie Linux) hatten eine namei()Funktion, die nur die Pfade eines Bytes aufteilte und die Pfade bei 0x2F-Bytes in Teile zerlegte, wobei sie bei einem Null-Byte stoppten. namei()ist Teil des Unix / Linux / BSD-Kernels, daher werden hier die außergewöhnlichen Bytewerte erzwungen.

Beachten Sie, dass ich bisher von Bytewerten gesprochen habe, nicht von Zeichen. namei()erzwingt keine Zeichensemantik für die Bytes. Dies hängt von den Programmen auf Benutzerebene ab ls, die Dateinamen nach Byte- oder Zeichenwerten sortieren. xtermentscheidet anhand der Zeichenkodierung, welche Pixel für Dateinamen aufleuchten. Wenn Sie nicht sagen, dass xtermSie UTF-8-codierte Dateinamen haben, werden Sie beim Aufrufen viel Kauderwelsch sehen. Wenn vimes nicht kompiliert wurde, um UTF-8-Codierungen (oder was auch immer, UTF-16, UTF-32) zu erkennen, wird beim Öffnen einer "Textdatei" mit UTF-8-codierten Zeichen viel Kauderwelsch angezeigt.

Bruce Ediger
quelle
Richtig, namei()wurde um 1986 aufgegeben. Neuere UNIX-Systeme verwenden lookuppn()VFS-basierte.
Schily
17

Die Sache ist, dem Kernel ist es egal, wie die Anwendungen die Daten interpretieren, die er als Dateinamen angibt.

Stellen wir uns vor, ich habe eine C-Anwendung, die sich ausschließlich mit UTF-16-Zeichenfolgen befasst. Und ich gebe über eine richtig konfigurierte Eingabemethode das Symbol ∯ (Unicode 0x222F) in die Eingabeaufforderung / den Dialog "Speichern unter" ein.

Wenn die Anwendung keine Übersetzung ausführt und diese in einem einfachen alten C-String ( char*) beispielsweise fopenim Schreibmodus sendet, wird der Kernel el nicht sehen oder versucht, sich das auch nur vorzustellen. Es werden charnacheinander zwei s mit Werten 0x22 0x2Fangezeigt (vorausgesetzt 8-Bit-Zeichen und keine lustigen Zeichen in der C-Bibliothek ).
Aus Sicht des Kernels ist dies ein gültiges char ( ") gefolgt von /(ASCII 0x2F). fopenwird zurückgegeben EISDIR(dh "das sieht aus wie ein Verzeichnis und Sie haben den Schreibmodus angefordert!").
Wenn ich ∮ (Unicode 0x222E) eingegeben hätte , hätte der Kernel zwei feine Zeichen gesehen und eine Datei erstellt, die, wie durch eine ASCII-sprechende Anwendung gesehen, benannt würde "..

Wenn ich ain der Anwendung einen Dateinamen eingegeben hätte und die Anwendung ihn in UTF-16 an den Kernel weiterleitet, würde der Kernel dies lesen 0x00 0x61und dies auch gar nicht berücksichtigen 0x61, da 0x00der String bereits beendet wird, so wie er ist besorgt. Die Fehlermeldung ist die gleiche wie bei einem leeren Dateinamen ( ENOENTglaube ich).

Der Kernel nimmt die Daten also tatsächlich als Blob. Es ist ein Strom von chars. Die ungültigen "Zeichen" in Ihrer User-Space-Codierung Ihrer Wahl sind diejenigen, die 0x00oder 0x2F("null" und /) in ihrem Blob (binäre Darstellung, die an den Kernel übergeben wird) generieren .

Matte
quelle
Wenn ich Sie richtig verstehe, gibt es keine ungültigen Zeichen. Es gibt nur ungültige Bytefolgen. Und die Werte 0x00und 0x2Fsind im Kernel fest codiert. Dies bedeutet wiederum, dass Verzeichnisse nicht durch ein getrennt werden /, sondern auf welche Zeichen 0x2Fin der verwendeten Codierung abgebildet werden.
Marco
Ja, das ist die Idee, wenn Sie es so sehen wollen. (Aber das könnte falsch sein. Ein Kernel könnte eine "native Codierung" haben, bei der /nicht 0x2F ist - möglicherweise wird 8-Bit nicht verwendet chars.) Das "traditionelle" Dir-Trennzeichen lautet /. Das ist 0x27 auf 8-Bit-ASCII-Systemen (zum Beispiel nicht EBCDIC).
Mat
Sie gehen von UTF-16BE aus, während in UTF-16LE U + 0061 die (nullterminierte) aZeichenfolge ergibt .
Incnis Mrsi
4

Die Trennung von Bytes und Zeichen erfolgte viel nach der Entwicklung von Unix. Als es entworfen wurde, vermittelte die Verwendung der Wörter nur etwas darüber, wie 8 (oder 6 oder 9) Bits interpretiert wurden, aber die Wortcodierungen wurden nicht erwähnt.

Dateinamen sind Folgen von Bytes. Jedes Byte außer 0x2f "/" ist zulässig. Ein Byte mit 0x00 kann aufgrund seiner Verwendung als String-Terminator nicht einmal zum Kernel durchdringen. Eine Anwendung kann die Folge von Bytes gemäß der von ihr gewählten Codierung interpretieren. Wenn das chaotisch klingt, dann ist es es wohl.

Weitere Informationen finden Sie unter http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html .

John S Gruber
quelle