Comprendre le codage des noms de fichiers Unix

25

J'ai du mal à comprendre comment fonctionne l'encodage du nom de fichier. Sur unix.SE je trouve des explications contradictoires.

Les noms de fichiers sont stockés sous forme de caractères

Pour citer une autre réponse: plusieurs questions sur le codage des caractères du système de fichiers sous Linux

[…] Comme vous le mentionnez dans votre question, un nom de fichier UNIX n'est qu'une séquence de caractères; le noyau ne sait rien de l'encodage, qui est entièrement un concept d'espace utilisateur (c'est-à-dire au niveau de l'application).

Si les noms de fichiers sont stockés sous forme de caractères, il doit y avoir une sorte d'encodage impliqué, car finalement le nom de fichier doit finir comme une séquence de bits ou d'octets sur le disque. Si l'utilisateur peut choisir n'importe quel encodage pour mapper les caractères à une séquence d'octets qui est envoyée au noyau, il est possible de créer n'importe quelle séquence d'octets pour un nom de fichier valide.

Supposons ce qui suit: Un utilisateur utilise un codage aléatoire X , qui traduit le fichier foodans la séquence d'octets α et l'enregistre sur le disque. Une autre utilisation de l' utilisateur codant pour Y . Dans ce codage, α se traduit par /, ce qui n'est pas autorisé comme nom de fichier. Cependant, pour le premier utilisateur, le fichier est valide.

Je suppose que ce scénario ne peut pas se produire.

Les noms de fichiers sont stockés sous forme de blobs binaires

Pour citer une autre réponse: quel codage de jeu de caractères est utilisé pour les noms de fichiers et les chemins sous Linux?

Comme indiqué par d'autres, il n'y a pas vraiment de réponse à cela: les noms de fichiers et les chemins n'ont pas d'encodage; le système d'exploitation ne traite que la séquence d'octets. Les applications individuelles peuvent choisir de les interpréter comme étant codées d'une manière ou d'une autre, mais cela varie.

Si le système ne gère pas les caractères, comment des caractères particuliers (par exemple /ou NULL) peuvent-ils être interdits dans les noms de fichiers? Il n'y a aucune notion d'un / sans encodage.

Une explication serait que le système de fichiers peut stocker des noms de fichiers contenant n'importe quel caractère et que seuls les programmes utilisateur qui prennent en compte un codage s'étoufferaient avec des noms de fichiers contenant des caractères non valides. Cela, à son tour, signifie que les systèmes de fichiers et le noyau peuvent, sans aucune difficulté, gérer les noms de fichiers contenant a /.

Je suppose également que c'est faux.

Où s'effectue l'encodage et où se situe la restriction de ne pas autoriser certains caractères?

Marco
la source
Null est le même (0) dans tous les encodages.
Kevin
2
@Kevin Pas tout à fait: pas dans, disons, UTF-16 ou UCS-4 (= UTF-32), ou la plupart des autres codages multi-octets qui ne sont pas des extensions de l'ASCII.
Gilles 'SO- arrête d'être méchant'
1
En fait, la réponse de Riccardo Murri aurait dû mentionner des octets et non des caractères . La plupart des systèmes de fichiers stockent des octets.
Gilles 'SO- arrête d'être méchant'
@Gilles: encore une fois Ī̲ voyez-vous vraiment regarder ce qui est écrit .
Incnis Mrsi

Réponses:

25

Réponse courte: restrictions imposées dans le noyau Unix / Linux / BSD, namei()fonction. L'encodage a lieu dans des programmes de niveau utilisateur tels que xterm, firefoxou ls.

Je pense que vous partez de prémisses incorrectes. Un nom de fichier sous Unix est une chaîne d'octets avec des valeurs arbitraires. Quelques valeurs, 0x0 (ASCII Nul) et 0x2f (ASCII '/') ne sont tout simplement pas autorisées, pas dans le cadre d'un codage de caractères multi-octets, pas comme quoi que ce soit. Un "octet" peut contenir un nombre représentant un caractère (en ASCII et certains autres encodages) mais un "caractère" peut nécessiter plus de 1 octet (par exemple, des points de code supérieurs à 0x7f dans la représentation UTF-8 d'Unicode).

Ces restrictions proviennent des conventions d'impression des noms de fichiers et du jeu de caractères ASCII. Les Unix d'origine utilisaient des octets de valeur ASCII '/' (numériquement 0x2f) pour séparer des morceaux d'un chemin partiellement ou entièrement qualifié (comme '/ usr / bin / cat' a des morceaux "usr", "bin" et "cat") . Les Unix d'origine utilisaient ASCII Nul pour terminer les chaînes. À part ces deux valeurs, les octets dans les noms de fichiers peuvent prendre toute autre valeur. Vous pouvez voir un écho de cela dans le codage UTF-8 pour Unicode. Les caractères ASCII imprimables, y compris '/', ne prennent qu'un octet en UTF-8. UTF-8 pour les points de code ci-dessus n'inclut aucun octet de valeur zéro, à l'exception du caractère de contrôle Nul. UTF-8 a été inventé pour Plan-9, The Pretender to the Throne of Unix.

Les Unix plus anciens (et cela ressemble à Linux) avaient une namei()fonction qui ne regarde que les chemins un octet à la fois, et casse les chemins en morceaux à des octets de valeur 0x2F, s'arrêtant à un octet de valeur zéro. namei()fait partie du noyau Unix / Linux / BSD, c'est donc là que les valeurs d'octets exceptionnelles sont appliquées.

Notez que jusqu'à présent, j'ai parlé de valeurs d'octets, pas de caractères. namei()n'applique aucune sémantique de caractères sur les octets. Cela dépend des programmes de niveau utilisateur, comme ls, qui peuvent trier les noms de fichiers en fonction des valeurs d'octets ou des valeurs de caractères. xtermdécide quels pixels allumer pour les noms de fichiers en fonction de l'encodage des caractères. Si vous ne dites pas que xtermvous avez des noms de fichiers encodés en UTF-8, vous verrez beaucoup de charabia lorsque vous l'invoquerez. S'il vimn'est pas compilé pour détecter les encodages UTF-8 (ou autre, UTF-16, UTF-32), vous verrez beaucoup de charabia lorsque vous ouvrez un "fichier texte" contenant des caractères encodés UTF-8.

Bruce Ediger
la source
Correct, a namei()été abandonné vers 1986. Les nouveaux systèmes UNIX utilisent lookuppn()VFS.
schily
17

Le fait est que le noyau ne se soucie pas du tout de la façon dont les applications interprètent les données qui lui sont données en tant que nom de fichier.

Imaginons que j'ai une application C qui traite exclusivement des chaînes UTF-16. Et j'entre, via une méthode de saisie correctement configurée, le symbole ∯ (Unicode 0x222F) dans l'invite / la boîte de dialogue "Enregistrer sous".

Si l'application ne fait aucune forme de traduction et l'envoie, dans une ancienne chaîne C simple ( char*), disons, fopenen mode écriture, le noyau ne verra pas ∯, ni même tentera d'imaginer cela. Il verra deux chars, l'un après l'autre, avec des valeurs 0x22 0x2F(en supposant des caractères 8 bits et aucun funnies dans la bibliothèque C ).
Autrement dit, du point de vue du noyau, un char ( ") valide suivi de /(ASCII 0x2F). fopenretournera EISDIR(c'est-à-dire "qui ressemble à un répertoire et vous avez demandé le mode d'écriture!").
Si j'avais entré ∮ (Unicode 0x222E), le noyau aurait vu deux caractères fins et créé un fichier qui, comme vu à travers une application parlant ASCII, serait nommé "..

Si j'avais entré adans l'application en tant que nom de fichier et que l'application le transmettait en UTF-16 au noyau, le noyau lirait 0x00 0x61et ne considérerait même pas cela 0x61, car le 0x00termine déjà la chaîne, pour autant qu'elle soit concerné. Le message d'erreur serait le même que pour un nom de fichier vide ( ENOENTje crois).

Le noyau prend donc bien les données comme un blob. C'est un flux de chars. Les "caractères" non valides dans l'encodage de votre espace utilisateur de votre choix sont ceux qui génèrent 0x00ou 0x2F("null" et /) dans leur blob (représentation binaire qui est transmise au noyau).

Tapis
la source
Si je vous comprends bien, les caractères invalides n'existent pas. Il n'y a que des séquences d'octets invalides. Et les valeurs 0x00et 0x2Fsont codées en dur dans le noyau. Cela signifie à son tour que les répertoires ne sont pas séparés par un /, mais par tout mappage de caractères 0x2Fdans l'encodage utilisé.
Marco
Oui, c'est l'idée si vous voulez le voir de cette façon. (Mais cela peut être incorrect. Un noyau peut avoir un "encodage natif" où /n'est pas 0x2F - peut ne pas utiliser 8 bits chars, en fait.) Le séparateur de dir "traditionnel" l'est /. Il s'agit de 0x27 sur les systèmes ASCII à 8 bits (pas EBCDIC par exemple).
Mat
Vous supposez UTF-16BE, alors qu'en UTF-16LE U + 0061 entraînera la achaîne (terminée par null) .
Incnis Mrsi
4

La séparation des octets par rapport aux caractères est intervenue bien après la conception d'Unix. Quand il a été conçu, l'utilisation des mots ne traduisait que quelque chose sur la façon dont 8 (ou 6 ou 9) bits étaient interprétés, mais les encodages des mots n'étaient pas mentionnés.

Les noms de fichiers sont des séquences d'octets. Tout octet sauf 0x2f "/" est autorisé. Un octet contenant 0x00 ne peut même pas atteindre le noyau en raison de son utilisation comme terminateur de chaîne. Une application peut interpréter la séquence d'octets selon un encodage qu'elle choisit. Si cela semble désordonné, je suppose que c'est le cas.

Il y a plus d'informations sur http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html que vous pouvez trouver utiles.

John S Gruber
la source