É 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 foo
na 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?
Respostas:
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 comoxterm
,firefox
ouls
.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, comols
, que podem classificar nomes de arquivos com base em valores de bytes ou caracteres.xterm
decide quais pixels serão iluminados para nomes de arquivos com base na codificação de caracteres. Se você não disserxterm
que tem nomes de arquivos codificados em UTF-8, verá muita bobagem ao invocá-lo. Sevim
nã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.fonte
namei()
foi abandonado por volta de 1986. Os sistemas UNIX mais novos usamlookuppn()
o VFS.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,fopen
no modo de gravação, o kernel não verá ∯ ou tentará imaginar isso. Ele verá doischar
s, um após o outro, com valores0x22 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).fopen
retornará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
a
o aplicativo como um nome de arquivo, e o aplicativo o transmitisse em UTF-16 para o kernel, o kernel leria0x00 0x61
e nem consideraria isso0x61
, porque0x00
já encerra a string, na medida em que é em causa. A mensagem de erro seria a mesma de um nome de arquivo vazio (ENOENT
acredito).Portanto, o kernel considera os dados como um blob. É um fluxo de
char
s. Os "caracteres" inválidos na codificação de sua escolha no espaço do usuário são aqueles que geram0x00
ou0x2F
("null" e/
) em seu blob (representação binária que é passada para o kernel).fonte
0x00
e0x2F
são codificados no kernel. Isso, por sua vez, significa que os diretórios não são separados por a/
, mas para qualquer caractere mapeado0x2F
na codificação em uso./
não é 0x2F - pode não usar 8 bitschars
, de fato.) O separador de dir "tradicional" é/
. Isso é 0x27 em sistemas ASCII de 8 bits (não EBCDIC, por exemplo).a
string (terminada por nulo) .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.
fonte