Comprender la codificación de nombre de archivo Unix


25

Me cuesta entender cómo funciona la codificación del nombre del archivo. En unix.SE encuentro explicaciones contradictorias.

Los nombres de los archivos se almacenan como caracteres.

Para citar otra respuesta: varias preguntas sobre la codificación de caracteres del sistema de archivos en Linux

[…] Como mencionas en tu pregunta, un nombre de archivo UNIX es solo una secuencia de caracteres; el kernel no sabe nada acerca de la codificación, que es completamente un concepto de espacio de usuario (es decir, de nivel de aplicación).

Si los nombres de archivo se almacenan como caracteres, debe haber algún tipo de codificación involucrada, ya que finalmente el nombre del archivo debe terminar como una secuencia de bits o bytes en el disco. Si el usuario puede elegir cualquier codificación para asignar los caracteres a una secuencia de bytes que se alimenta al núcleo, es posible crear cualquier secuencia de bytes para un nombre de archivo válido.

Suponga lo siguiente: un usuario utiliza una codificación aleatoria X , que traduce el archivo fooa la secuencia de bytes α y lo guarda en el disco. Otros usos de los usuarios que codifica Y . En esta codificación, α se traduce en /, que no está permitido como nombre de archivo. Sin embargo, para el primer usuario el archivo es válido.

Supongo que este escenario no puede suceder.

Los nombres de archivo se almacenan como blobs binarios

Para citar otra respuesta: ¿Qué codificación charset se usa para nombres de archivos y rutas en Linux?

Como han señalado otros, no hay realmente una respuesta a esto: los nombres de archivo y las rutas no tienen codificación; El sistema operativo solo se ocupa de la secuencia de bytes. Las aplicaciones individuales pueden elegir interpretarlas como codificadas de alguna manera, pero esto varía.

Si el sistema no trata con caracteres, ¿cómo se pueden prohibir caracteres particulares (por ejemplo, /o NULL) en los nombres de archivo? No hay noción de a / sin una codificación.

Una explicación sería que el sistema de archivos puede almacenar nombres de archivos que contienen cualquier carácter y son solo los programas de usuario los que tienen en cuenta una codificación que se atragantaría con los nombres de archivos que contienen caracteres no válidos. Eso, a su vez, significa que los sistemas de archivos y el núcleo pueden, sin ninguna dificultad, manejar los nombres de archivo que contienen a /.

También supongo que esto está mal.

¿Dónde tiene lugar la codificación y dónde se plantea la restricción de no permitir caracteres particulares?


Nulo es el mismo (0) en todas las codificaciones.
Kevin

2
@ Kevin No del todo: no en, por ejemplo, UTF-16 o UCS-4 (= UTF-32), o la mayoría de las otras codificaciones multibyte que no son extensiones de ASCII.
Gilles 'SO- deja de ser malvado'

1
En realidad, la respuesta de Riccardo Murri debería haber mencionado bytes y no caracteres allí. La mayoría de los sistemas de archivos almacenan bytes.
Gilles 'SO- deja de ser malvado'

@Gilles: otra vez Ī̲ veo que realmente ves lo que está escrito .
Incnis Mrsi

Respuestas:


25

Respuesta corta: restricciones impuestas en el núcleo de Unix / Linux / BSD, namei()función. La codificación se lleva a cabo en los programas de nivel de usuario como xterm, firefoxo ls.

Creo que estás comenzando desde premisas incorrectas. Un nombre de archivo en Unix es una cadena de bytes con valores arbitrarios. Algunos valores, 0x0 (ASCII Nul) y 0x2f (ASCII '/') simplemente no están permitidos, no como parte de una codificación de caracteres de varios bytes, no como algo. Un "byte" puede contener un número que representa un carácter (en ASCII y algunas otras codificaciones) pero un "carácter" puede requerir más de 1 byte (por ejemplo, puntos de código por encima de 0x7f en la representación UTF-8 de Unicode).

Estas restricciones surgen de las convenciones de impresión de nombres de archivos y el conjunto de caracteres ASCII. Los Unix originales usaban bytes con valor ASCII '/' (numéricamente 0x2f) para separar partes de una ruta parcial o totalmente calificada (como '/ usr / bin / cat' tiene partes "usr", "bin" y "cat") . Los Unix originales usaban ASCII Nul para terminar las cadenas. Aparte de esos dos valores, los bytes en los nombres de archivo pueden asumir cualquier otro valor. Puede ver un eco de esto en la codificación UTF-8 para Unicode. Los caracteres ASCII imprimibles, incluido '/', toman solo un byte en UTF-8. UTF-8 para los puntos de código anteriores no incluye ningún byte de valor cero, excepto el carácter de control Nul. UTF-8 fue inventado para el Plan-9, El pretendiente al trono de Unix.

Los Unixes más antiguos (y parece que Linux) tenían una namei()función que solo mira las rutas de un byte a la vez, y divide las rutas en pedazos en bytes valorados 0x2F, deteniéndose en un byte de valor cero. namei()es parte del núcleo Unix / Linux / BSD, por lo que es allí donde se aplican los valores de byte excepcionales.

Tenga en cuenta que hasta ahora, he hablado de valores de bytes, no de caracteres. namei()no aplica ninguna semántica de caracteres en los bytes. Eso depende de los programas de nivel de usuario, como ls, que pueden ordenar los nombres de los archivos en función de los valores de bytes o valores de caracteres. xtermdecide qué píxeles se iluminarán para los nombres de archivo en función de la codificación de caracteres. Si no dices xtermque tienes nombres de archivo codificados con UTF-8, verás muchas tonterías cuando lo invoques. Si vimno se compila para detectar codificaciones UTF-8 (o lo que sea, UTF-16, UTF-32), verá muchas tonterías cuando abra un "archivo de texto" que contenga caracteres codificados UTF-8.


Correcto, namei()se abandonó alrededor de 1986. El uso de sistemas UNIX más nuevos lookuppn()está basado en VFS.
schily

17

La cuestión es que al núcleo no le importa un poco cómo las aplicaciones interpretan los datos que se dan como nombre de archivo.

Imaginemos que tengo una aplicación C que trata exclusivamente con cadenas UTF-16. Y entro, a través de un método de entrada configurado correctamente, el símbolo ∯ (Unicode 0x222F) en el mensaje / diálogo "Guardar como".

Si la aplicación no realiza ninguna forma de traducción y la envía, en una cadena C ( char*) simple y antigua para, por ejemplo, fopenen modo de escritura, el núcleo no verá ∯, o incluso tratará de imaginarlo. Verá dos chars, uno tras otro, con valores 0x22 0x2F(suponiendo caracteres de 8 bits y sin funnies en la biblioteca C ).
Es decir, desde el punto de vista del núcleo, un char ( ") válido seguido de /(ASCII 0x2F). fopenregresará EISDIR(es decir, "que se parece a un directorio y solicitó el modo de escritura").
Si hubiera ingresado ∮ (Unicode 0x222E), el núcleo habría visto dos caracteres finos y habría creado un archivo que, como se ve a través de una aplicación de habla ASCII, se nombraría "..

Si hubiera ingresado aen la aplicación como un nombre de archivo, y la aplicación lo pasó en UTF-16 al kernel, el kernel leería 0x00 0x61, y en realidad ni siquiera lo consideraría 0x61, porque 0x00ya termina la cadena, en la medida en que es preocupado. El mensaje de error sería el mismo que para un nombre de archivo vacío ( ENOENTcreo).

Por lo tanto, el núcleo realmente toma los datos como un blob. Es una corriente del chars. Los "caracteres" no válidos en su codificación de espacio de usuario de su elección son aquellos que generan 0x00o 0x2F("nulo" y /) en su blob (representación binaria que se pasa al núcleo).


Si te entiendo bien, entonces no hay caracteres inválidos. Solo hay secuencias de bytes no válidas. Y los valores 0x00y 0x2Festán codificados en el núcleo. Eso a su vez significa que los directorios no están separados por a /, sino por cualquier carácter que se asigne 0x2Fen la codificación en uso.
Marco

Sí, esa es la idea si quieres verlo de esa manera. (Pero eso podría ser incorrecto. Un núcleo podría tener una "codificación nativa" donde /no sea 0x2F; de hecho, podría no usar 8 bits chars). El separador de directorios "tradicional" sí lo es /. Eso es 0x27 en sistemas ASCII de 8 bits byte (no EBCDIC por ejemplo).
Mat

Asume UTF-16BE, mientras que en UTF-16LE U + 0061 dará como resultado la acadena (terminada en nulo) .
Incnis Mrsi

4

La separación de bytes frente a caracteres se produjo mucho después de que se diseñó Unix. Cuando se diseñó, el uso de las palabras solo transmitía algo sobre cómo se interpretaron 8 (o 6, o 9) bits, pero no se mencionaron las codificaciones de palabras .

Los nombres de archivo son secuencias de bytes. Se permite cualquier byte excepto 0x2f "/". Un byte que contiene 0x00 ni siquiera puede llegar al núcleo debido a su uso como un terminador de cadena. Una aplicación puede interpretar la secuencia de bytes de acuerdo con la codificación que elija. Si eso suena desordenado, supongo que lo es.

Hay más información en http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html que puede resultarle útil.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.