Noções básicas sobre codificação de nome de arquivo Unix


25

É difícil entender como funciona a codificação de nome de arquivo. No unix.SE, encontro explicações contraditórias.

Os nomes de arquivos são armazenados como caracteres

Para citar outra resposta: Várias perguntas sobre a codificação de caracteres do sistema de arquivos no linux

[…] Como você mencionou na sua pergunta, um nome de arquivo UNIX é apenas uma sequência de caracteres; o kernel não sabe nada sobre a codificação, que é inteiramente um conceito de espaço do usuário (ou seja, no nível do aplicativo).

Se os nomes dos arquivos são armazenados como caracteres, é necessário que haja algum tipo de codificação, pois, finalmente, o nome do arquivo deve terminar como uma sequência de bits ou bytes no disco. Se o usuário puder escolher qualquer codificação para mapear os caracteres para uma sequência de bytes que é alimentada no kernel, é possível criar qualquer sequência de bytes para um nome de arquivo válido.

Suponha o seguinte: Um usuário usa uma codificação aleatória X , que converte o arquivo foona sequência de bytes α e o salva no disco. Outros usos do utilizador que codifica Y . Nesta codificação, α traduz para /, o que não é permitido como um nome de arquivo. No entanto, para o primeiro usuário, o arquivo é válido.

Presumo que esse cenário não possa acontecer.

Os nomes de arquivos são armazenados como blobs binários

Para citar outra resposta: Qual codificação de charset é usada para nomes de arquivos e caminhos no Linux?

Como observado por outros, não há realmente uma resposta para isso: nomes de arquivos e caminhos não possuem uma codificação; o sistema operacional lida apenas com a sequência de bytes. Aplicativos individuais podem optar por interpretá-los como sendo codificados de alguma forma, mas isso varia.

Se o sistema não lida com caracteres, como caracteres específicos (por exemplo, /ou NULL) podem ser proibidos em nomes de arquivos? Não existe noção de a / sem codificação.

Uma explicação seria que o sistema de arquivos pode armazenar nomes de arquivos que contenham qualquer caractere e são apenas os programas do usuário que levam em consideração uma codificação que sufocariam os nomes de arquivos que contenham caracteres inválidos. Isso, por sua vez, significa que os sistemas de arquivos e o kernel podem, sem nenhuma dificuldade, manipular nomes de arquivos contendo a /.

Eu também assumo que isso está errado.

Onde ocorre a codificação e onde está a restrição de não permitir caracteres específicos?


Nulo é o mesmo (0) em todas as codificações.
22412 Kevin

2
@ Kevin Não exatamente: não em, digamos, UTF-16 ou UCS-4 (= UTF-32), ou a maioria das outras codificações multibyte que não são extensões do ASCII.
Gilles 'SO- stop be evil'

11
Na verdade, a resposta de Riccardo Murri deveria ter mencionado bytes e não caracteres . A maioria dos sistemas de arquivos armazena bytes.
Gilles 'SO- stop be evil'

@ Gilles: mais uma vez, vejo você realmente assistir o que está escrito .
Incnis MRSI

Respostas:


25

Resposta curta: restrições impostas no kernel Unix / Linux / BSD, namei()função. A codificação ocorre em programas no nível do usuário como xterm, firefoxou ls.

Eu acho que você está começando a partir de premissas incorretas. Um nome de arquivo no Unix é uma cadeia de bytes com valores arbitrários. Alguns valores, 0x0 (ASCII Nul) e 0x2f (ASCII '/') simplesmente não são permitidos, não como parte de uma codificação de caracteres de vários bytes, não como qualquer outra coisa. Um "byte" pode conter um número que representa um caractere (em ASCII e em algumas outras codificações), mas um "caractere" pode exigir mais de 1 byte (por exemplo, pontos de código acima de 0x7f na representação UTF-8 do Unicode).

Essas restrições surgem das convenções de impressão de nome de arquivo e do conjunto de caracteres ASCII. Os Unixes originais usavam bytes com valor ASCII '/' (numericamente 0x2f) para separar partes de um caminho parcialmente ou totalmente qualificado (como '/ usr / bin / cat' possui as partes "usr", "bin" e "cat") . Os Unixes originais usavam ASCII Nul para finalizar seqüências de caracteres. Além desses dois valores, os bytes nos nomes dos arquivos podem assumir qualquer outro valor. Você pode ver um eco disso na codificação UTF-8 para Unicode. Os caracteres ASCII imprimíveis, incluindo '/', recebem apenas um byte em UTF-8. UTF-8 para os pontos de código acima não inclui bytes com valor zero, exceto o caractere de controle Nul. O UTF-8 foi inventado para o Plano 9, O Pretendente ao Trono do Unix.

Os Unixes mais antigos (e parece que o Linux) tinham uma namei()função que apenas examina os caminhos, um byte de cada vez, e os divide em pedaços com bytes com valor 0x2F, parando com um byte com valor zero. namei()faz parte do kernel Unix / Linux / BSD, então é aí que os valores excepcionais de bytes são impostos.

Observe que, até agora, eu falei sobre valores de bytes, não caracteres. namei()não impõe nenhuma semântica de caracteres nos bytes. Isso depende dos programas no nível do usuário, como ls, que podem classificar nomes de arquivos com base em valores de bytes ou caracteres. xtermdecide quais pixels serão iluminados para nomes de arquivos com base na codificação de caracteres. Se você não disser xtermque tem nomes de arquivos codificados em UTF-8, verá muita bobagem ao invocá-lo. Se vimnão for compilado para detectar as codificações UTF-8 (ou o que seja, UTF-16, UTF-32), você verá muitas bobagens quando abrir um "arquivo de texto" contendo caracteres codificados em UTF-8.


Correto, namei()foi abandonado por volta de 1986. Os sistemas UNIX mais novos usam lookuppn()o VFS.
schily 17/09/2015

17

O problema é que o kernel não se importa nem um pouco com a maneira como os aplicativos interpretam os dados que recebem como um nome de arquivo.

Vamos imaginar que eu tenho um aplicativo C que lida exclusivamente com seqüências UTF-16. E insiro, através de um método de entrada configurado corretamente, o símbolo ((Unicode 0x222F) no prompt / caixa de diálogo "Salvar como".

Se o aplicativo não fizer nenhuma forma de conversão e enviar isso, em uma antiga cadeia C simples ( char*) para, digamos, fopenno modo de gravação, o kernel não verá ∯ ou tentará imaginar isso. Ele verá dois chars, um após o outro, com valores 0x22 0x2F(assumindo caracteres de 8 bits e sem piadas na biblioteca C ).
Ou seja, do ponto de vista do kernel, um char ( ") válido seguido por /(ASCII 0x2F). fopenretornará EISDIR(ou seja, "que se parece com um diretório e você solicitou o modo de gravação!").
Se eu tivesse digitado ∮ (Unicode 0x222E), o kernel teria visto dois caracteres finos e criaria um arquivo que, como visto através de um aplicativo que fala ASCII, seria nomeado "..

Se eu tivesse inserido ao aplicativo como um nome de arquivo, e o aplicativo o transmitisse em UTF-16 para o kernel, o kernel leria 0x00 0x61e nem consideraria isso 0x61, porque 0x00já encerra a string, na medida em que é em causa. A mensagem de erro seria a mesma de um nome de arquivo vazio ( ENOENTacredito).

Portanto, o kernel considera os dados como um blob. É um fluxo de chars. Os "caracteres" inválidos na codificação de sua escolha no espaço do usuário são aqueles que geram 0x00ou 0x2F("null" e /) em seu blob (representação binária que é passada para o kernel).


Se eu entendi direito, não existem caracteres inválidos. Existem apenas sequências de bytes inválidas. E os valores 0x00e 0x2Fsão codificados no kernel. Isso, por sua vez, significa que os diretórios não são separados por a /, mas para qualquer caractere mapeado 0x2Fna codificação em uso.
22412 Marco

Sim, essa é a ideia se você quiser ver dessa maneira. (Mas isso pode estar incorreto. Um kernel pode ter uma "codificação nativa" onde /não é 0x2F - pode não usar 8 bits chars, de fato.) O separador de dir "tradicional" é /. Isso é 0x27 em sistemas ASCII de 8 bits (não EBCDIC, por exemplo).
Mat

Você assume UTF-16BE, enquanto que em UTF-16LE U + 0061 resultará na astring (terminada por nulo) .
Incnis MRSI

4

A separação de bytes vs. caracteres ocorreu muito depois que o Unix foi projetado. Quando foi projetado, o uso das palavras transmitia apenas algo sobre como 8 (ou 6 ou 9) bits eram interpretados, mas as codificações de palavras não foram mencionadas.

Os nomes de arquivos são sequências de bytes. Qualquer byte, exceto 0x2f "/", é permitido. Um byte contendo 0x00 não consegue acessar o kernel devido ao seu uso como terminador de string. Um aplicativo pode interpretar a sequência de bytes de acordo com a codificação escolhida. Se isso soa confuso, suponho que seja.

Há mais informações em http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html que você pode achar útil.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.