Comprensione della codifica del nome file Unix


25

Ho difficoltà a capire come funziona la codifica del nome file. Su unix.SE trovo spiegazioni contraddittorie.

I nomi dei file sono memorizzati come caratteri

Per citare un'altra risposta: diverse domande sulla codifica dei caratteri del file system su Linux

[…] Come menzioni nella tua domanda, il nome di un file UNIX è solo una sequenza di caratteri; il kernel non sa nulla della codifica, che è interamente un concetto di spazio utente (cioè a livello di applicazione).

Se i nomi dei file sono memorizzati come caratteri, deve esserci un qualche tipo di codifica, poiché alla fine il nome del file deve finire come una sequenza di bit o byte sul disco. Se l'utente può scegliere qualsiasi codifica per mappare i caratteri su una sequenza di byte che viene inviata al kernel, è possibile creare qualsiasi sequenza di byte per un nome file valido.

Supponiamo quanto segue: un utente utilizza una codifica casuale X , che traduce il file foonella sequenza di byte α e lo salva su disco. Altri usi utente codificante Y . In questa codifica α si traduce in /, che non è consentito come nome file. Tuttavia, per il primo utente il file è valido.

Presumo che questo scenario non possa accadere.

I nomi dei file vengono archiviati come BLOB binari

Per citare un'altra risposta: quale codifica set di caratteri viene utilizzata per nomi di file e percorsi su Linux?

Come notato da altri, non c'è davvero una risposta a questa: nomi di file e percorsi non hanno una codifica; il sistema operativo si occupa solo della sequenza di byte. Le singole applicazioni possono scegliere di interpretarle come codificate in qualche modo, ma questo varia.

Se il sistema non tratta i caratteri, come possono essere vietati caratteri particolari (ad es. /O NULL) nei nomi dei file? Non esiste la nozione di a / senza codifica.

Una spiegazione sarebbe che il file system può memorizzare nomi di file contenenti qualsiasi carattere ed è solo i programmi utente che tengono conto di una codifica che soffocano sui nomi di file contenenti caratteri non validi. Ciò, a sua volta, significa che i file system e il kernel possono, senza alcuna difficoltà, gestire nomi di file contenenti a /.

Presumo anche che sia sbagliato.

Dove avviene la codifica e dove si pone la restrizione di non consentire caratteri particolari?


Null è lo stesso (0) in tutte le codifiche.
Kevin,

2
@Kevin Non proprio: non in, diciamo, UTF-16 o UCS-4 (= UTF-32), o la maggior parte delle altre codifiche multibyte che non sono estensioni di ASCII.
Gilles 'SO- smetti di essere malvagio'

1
In realtà, la risposta di Riccardo Murri avrebbe dovuto menzionare byte e non personaggi lì. La maggior parte dei filesystem memorizza i byte.
Gilles 'SO- smetti di essere malvagio'

@Gilles: ancora un'altra volta Ī̲ vederti davvero guardare ciò che è scritto .
Incnis Mrsi,

Risposte:


25

Risposta breve: restrizioni imposte nel kernel Unix / Linux / BSD, namei()funzione. La codifica avviene in programmi a livello di utente come xterm, firefoxo ls.

Penso che stai partendo da premesse errate. Un nome file in Unix è una stringa di byte con valori arbitrari. Alcuni valori, 0x0 (ASCII Nul) e 0x2f (ASCII '/') non sono consentiti, non come parte di una codifica di caratteri multi-byte, non come niente. Un "byte" può contenere un numero che rappresenta un carattere (in ASCII e alcune altre codifiche) ma un "carattere" può richiedere più di 1 byte (ad esempio, punti di codice sopra 0x7f nella rappresentazione UTF-8 di Unicode).

Queste restrizioni derivano dalle convenzioni di stampa del nome file e dal set di caratteri ASCII. Gli Unix originali usavano ASCII '/' (numericamente 0x2f) byte valutati per separare pezzi di un percorso parzialmente o completo (come '/ usr / bin / cat' ha pezzi "usr", "bin" e "cat") . Gli Unix originali usavano ASCII Nul per terminare le stringhe. A parte questi due valori, i byte nei nomi dei file possono assumere qualsiasi altro valore. Puoi vedere un'eco di questo nella codifica UTF-8 per Unicode. I caratteri ASCII stampabili, incluso '/', accettano solo un byte in UTF-8. UTF-8 per i punti di codice sopra non include alcun byte a valore zero, ad eccezione del carattere di controllo Nul. UTF-8 è stato inventato per Plan-9, The Pretender to the Throne of Unix.

Gli Unix più vecchi (e sembra Linux) avevano una namei()funzione che guarda solo i percorsi un byte alla volta e li divide in pezzi a byte con valore 0x2F, fermandosi a un byte con valore zero. namei()fa parte del kernel Unix / Linux / BSD, quindi è qui che vengono applicati i valori di byte eccezionali.

Si noti che finora ho parlato di valori di byte, non di caratteri. namei()non impone alcuna semantica di carattere sui byte. Dipende dai programmi a livello di utente, come ls, che potrebbero ordinare i nomi dei file in base a valori di byte o valori di caratteri. xtermdecide quali pixel illuminare per i nomi dei file in base alla codifica dei caratteri. Se non dici xtermdi avere nomi di file codificati UTF-8, vedrai un sacco di parole incomprensibili quando lo invochi. Se vimnon viene compilato per rilevare codifiche UTF-8 (o qualunque altra, UTF-16, UTF-32), vedrai un sacco di parole incomprensibili quando apri un "file di testo" contenente caratteri codificati UTF-8.


Corretto, è namei()stato abbandonato intorno al 1986. I sistemi UNIX più recenti utilizzano lookuppn()VFS.
schily,

17

Il fatto è che al kernel non interessa un po 'il modo in cui le applicazioni interpretano i dati forniti come nome file.

Immaginiamo di avere un'applicazione C che si occupa esclusivamente di stringhe UTF-16. E inserisco, tramite un metodo di input correttamente configurato, il simbolo ∯ (Unicode 0x222F) nel prompt / finestra di dialogo "Salva con nome".

Se l'applicazione non esegue alcuna forma di traduzione e lo invia, in una semplice vecchia stringa C ( char*) a, diciamo, fopenin modalità di scrittura, il kernel non vedrà ∯, né tenterà nemmeno di immaginarlo. Vedrà due chars, uno dopo l'altro, con valori 0x22 0x2F(assumendo caratteri a 8 bit e nessun divertimento nella libreria C ).
Cioè, dal punto di vista del kernel, un carattere valido ( ") seguito da /(ASCII 0x2F). fopentornerà EISDIR(cioè "che assomiglia a una directory e hai richiesto la modalità di scrittura!").
Se avessi inserito Unic (Unicode 0x222E), il kernel avrebbe visto due caratteri fini e creato un file che, come visto attraverso un'applicazione in lingua ASCII, sarebbe stato chiamato "..

Se avessi inserito al'applicazione come nome di file e l'applicazione lo avesse passato al kernel in UTF-16, il kernel avrebbe letto 0x00 0x61, e in realtà non lo avrebbe nemmeno considerato 0x61, poiché 0x00già termina la stringa, per quanto è ha riguardato. Il messaggio di errore sarebbe lo stesso di un nome file vuoto ( ENOENTcredo).

Quindi il kernel prende davvero i dati come un blob. È un flusso di chars. I "caratteri" non validi nella codifica dello spazio utente di tua scelta sono quelli che generano 0x00o 0x2F("null" e /) nel loro BLOB (rappresentazione binaria che viene passata al kernel).


Se ti capisco bene, allora non esistono personaggi non validi. Esistono solo sequenze di byte non valide. E i valori 0x00e 0x2Fsono codificati nel kernel. Ciò a sua volta significa che le directory non sono separate da a /, ma da qualsiasi carattere mappato 0x2Fnella codifica in uso.
Marco,

Sì, questa è l'idea se vuoi vederla in quel modo. (Ma potrebbe non essere corretto. Un kernel potrebbe avere una "codifica nativa" in cui /non è 0x2F chars, in effetti potrebbe non utilizzare 8 bit .) Il separatore di directory "tradizionale" è /. Questo è 0x27 su sistemi ASCII a 8 bit byte (non EBCDIC per esempio).
Mat,

Si assume UTF-16BE, mentre in UTF-16LE U + 0061 si otterrà la astringa (terminata con null) .
Incnis Mrsi,

4

La separazione tra byte e caratteri è avvenuta molto dopo la progettazione di Unix. Quando è stato progettato, l'uso delle parole trasmetteva solo qualcosa su come venivano interpretati 8 (o 6, o 9) bit, ma le codifiche delle parole non venivano menzionate.

I nomi dei file sono sequenze di byte. È consentito qualsiasi byte tranne 0x2f "/". Un byte contenente 0x00 non può nemmeno passare attraverso il kernel a causa del suo uso come terminatore di stringa. Un'applicazione può interpretare la sequenza di byte secondo una codifica che sceglie. Se sembra disordinato, suppongo che lo sia.

Puoi trovare ulteriori informazioni su http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html che potresti trovare utili.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.