Compreendendo a codificação do nome do arquivo Unix

25

Eu tenho dificuldade em entender como funciona a codificação do nome do arquivo. No unix.SE Eu acho explicações contraditórias.

Os nomes dos 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

[…] as you mention in your question, a UNIX file name is just a sequence of characters; the kernel knows nothing about the encoding, which entirely a user-space (i.e., application-level) concept.

Se os nomes dos arquivos forem armazenados como caracteres, deve haver algum tipo de codificação envolvido, desde que finalmente o nome do arquivo tem que acabar como uma seqüência de bits ou bytes no disco. Se o usuário puder escolher uma codificação qualquer para mapear os caracteres para um sequência de byte que é alimentada ao kernel, é possível criar qualquer byte sequência para um nome de arquivo válido.

Suponha o seguinte: Um usuário usa uma codificação aleatória X , que traduz o arquivo foo na sequência de bytes α e salva no disco. Outro usuário usa codificação 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.

Suponho que esse cenário não pode acontecer.

Os nomes dos 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?

As noted by others, there isn't really an answer to this: filenames and paths do not have an encoding; the OS only deals with sequence of bytes. Individual applications may choose to interpret them as being encoded in some way, but this varies.

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

Uma explicação seria que o sistema de arquivos pode armazenar nomes de arquivos contendo qualquer personagem e é apenas os programas do usuário que levam em conta uma codificação isso sufocaria em nomes de arquivo contendo caracteres inválidos. Isso, por sua vez, significa que os sistemas de arquivos e o kernel podem, sem qualquer dificuldade, manipular nomes de arquivos contendo / .

Eu também presumo que isso esteja errado.

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

    
por Marco 22.05.2012 / 21:34

3 respostas

25

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

Acho que você está começando de premissas incorretas. Um nome de arquivo no Unix é uma string 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 múltiplos bytes, não como qualquer coisa. Um "byte" pode conter um número representando um caractere (em ASCII e algumas outras codificações), mas um "caractere" pode exigir mais de 1 byte (por exemplo, pontos de código acima 0x7f na representação UTF-8 do Unicode).

Essas restrições surgem das convenções de impressão de nomes de arquivos e do conjunto de caracteres ASCII. Os Unixes originais usavam bytes valorizados ASCII '/' (numericamente 0x2f) para separar partes de um caminho parcialmente ou totalmente qualificado (como '/ usr / bin / cat' tem partes "usr", "bin" e "cat") . Os Unixes originais usavam ASCII Nul para finalizar strings. 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. Caracteres ASCII imprimíveis, incluindo '/', levam apenas um byte em UTF-8. UTF-8 para pontos de código acima não inclui nenhum byte de valor zero, exceto para o caractere de controle Nul. O UTF-8 foi inventado para o Plan-9, o pretendente ao trono do Unix.

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

Observe que até agora, falei sobre valores de bytes, não caracteres. namei() não impõe nenhuma semântica de caracteres nos bytes. Isso depende dos programas em nível de usuário, como ls , que podem classificar nomes de arquivos com base em valores de bytes ou valores de caracteres. xterm decide quais pixels acender para nomes de arquivos com base na codificação de caracteres. Se você não disser xterm você tem nomes de arquivos codificados em UTF-8, você verá muitos detalhes quando invocá-los. Se vim não for compilado para detectar codificações UTF-8 (ou qualquer outra coisa, UTF-16, UTF-32), você verá muitos detalhes quando abrir um "arquivo de texto" contendo caracteres codificados em UTF-8.

    
por 22.05.2012 / 22:26
17

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

Vamos imaginar que eu tenha um aplicativo C que lide exclusivamente com strings UTF-16. E eu entro, através de um método de entrada configurado corretamente, o símbolo ((Unicode 0x222F) no prompt / diálogo "Salvar como".

Se o aplicativo não fizer nenhuma forma de tradução e enviar isso, em uma cadeia C simples ( char* ) para, digamos, fopen no modo de gravação, o kernel não verá ∯ ou tentará imaginar isso. Ele verá dois char s, um após o outro, com valores 0x22 0x2F (assumindo 8 bits e sem funnies na biblioteca C ).
Ou seja, do ponto de vista do kernel, um caractere válido ( " ) seguido por / (ASCII 0x2F). fopen retornará EISDIR (ou seja, "parece 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 criado um arquivo que, como visto através de um aplicativo que fala ASCII, seria nomeado ". .

Se eu tivesse inserido a no aplicativo como um nome de arquivo, e o aplicativo passasse em UTF-16 para o kernel, o kernel iria ler 0x00 0x61 e, na verdade, nem mesmo consideraria 0x61 , porque o 0x00 já termina a string, no que diz respeito a ela. Mensagem de erro seria o mesmo que para um nome de arquivo vazio ( ENOENT eu acredito).

Assim, o kernel realmente usa os dados como um blob. É um fluxo de char s. Os "caracteres" inválidos em sua codificação do espaço do usuário de sua escolha são aqueles que geram 0x00 ou 0x2F ("null" e / ) em seu blob (representação binária que é passada para o kernel).

    
por 22.05.2012 / 22:14
4

A separação de bytes x caracteres veio muito depois que o Unix foi criado. Quando foi projetado, o uso das palavras apenas transmitiu algo sobre como 8 (ou 6, ou 9) bits foram interpretados, mas a palavra codificações não foi mencionada.

Nomes de arquivos são seqüências de bytes. Qualquer byte, exceto 0x2f "/", é permitido. Um byte contendo 0x00 não pode sequer passar para o kernel devido ao seu uso como terminador de string. Um aplicativo pode interpretar a seqüência de bytes de acordo com uma codificação escolhida. Se isso parece confuso, suponho que seja.

Há mais informações no link que você pode achar útil.

    
por 22.05.2012 / 22:13