Memahami pengkodean nama file Unix

25

Saya mengalami kesulitan memahami cara kerja pengkodean nama file. Pada unix.SE saya menemukan penjelasan yang bertentangan.

Nama file disimpan sebagai karakter

Mengutip jawaban lain: Beberapa pertanyaan tentang pengkodean karakter sistem file di linux

[...] seperti yang Anda sebutkan dalam pertanyaan Anda, nama file UNIX hanyalah serangkaian karakter; kernel tidak tahu apa-apa tentang pengkodean, yang seluruhnya merupakan konsep ruang pengguna (yaitu, level aplikasi).

Jika nama file disimpan sebagai karakter, harus ada semacam pengkodean, karena akhirnya nama file harus berakhir sebagai urutan bit atau byte pada disk. Jika pengguna dapat memilih pengodean apa pun untuk memetakan karakter ke urutan byte yang diumpankan ke kernel, dimungkinkan untuk membuat urutan byte apa pun untuk nama file yang valid.

Asumsikan sebagai berikut: Pengguna menggunakan pengodean acak X , yang menerjemahkan file fooke dalam urutan byte α dan menyimpannya ke disk. Lain penggunaan pengguna encoding Y . Dalam encoding ini α diterjemahkan menjadi /, yang tidak diizinkan sebagai nama file. Namun, untuk pengguna pertama file tersebut valid.

Saya berasumsi bahwa skenario ini tidak dapat terjadi.

Nama file disimpan sebagai gumpalan biner

Mengutip jawaban lain: Pengkodean charset apa yang digunakan untuk nama file dan jalur di Linux?

Seperti dicatat oleh orang lain, sebenarnya tidak ada jawaban untuk ini: nama file dan jalur tidak memiliki penyandian; OS hanya menangani urutan byte. Aplikasi individual dapat memilih untuk menafsirkannya sebagai dikodekan dalam beberapa cara, tetapi ini bervariasi.

Jika sistem tidak berurusan dengan karakter, bagaimana bisa karakter tertentu (misalnya /atau NULL) dilarang dalam nama file? Tidak ada gagasan tentang / tanpa pengkodean.

Penjelasannya adalah bahwa sistem file dapat menyimpan nama file yang mengandung karakter apa pun dan hanya program pengguna yang memasukkan pengkodean ke dalam akun yang akan mencekik nama file yang mengandung karakter yang tidak valid. Itu, pada gilirannya, berarti bahwa sistem file dan kernel dapat, tanpa kesulitan, menangani nama file yang mengandung a /.

Saya juga berasumsi bahwa ini salah.

Di mana pengkodean berlangsung dan di mana batasan yang diajukan tidak mengizinkan karakter tertentu?

Marco
sumber
Null adalah sama (0) di semua pengkodean.
Kevin
2
@Kevin Tidak cukup: tidak di, katakanlah, UTF-16, atau UCS-4 (= UTF-32), atau sebagian besar penyandian multibyte lain yang bukan ekstensi ASCII.
Gilles 'SANGAT berhenti menjadi jahat'
1
Sebenarnya, jawaban Riccardo Murri seharusnya menyebutkan byte dan bukan karakter di sana. Sebagian besar filesystem menyimpan byte.
Gilles 'SANGAT berhenti menjadi jahat'
@Gilles: lain kali Ī̲ melihat Anda benar-benar menonton apa yang tertulis .
Incnis Mrsi

Jawaban:

25

Jawaban singkat: pembatasan diberlakukan di kernel Unix / Linux / BSD, namei()berfungsi. Pengkodean berlangsung di program tingkat pengguna seperti xterm, firefoxatau ls.

Saya pikir Anda mulai dari tempat yang salah. Nama file di Unix adalah serangkaian byte dengan nilai arbitrer. Beberapa nilai, 0x0 (ASCII Nul) dan 0x2f (ASCII '/') tidak diperbolehkan, bukan sebagai bagian dari pengkodean karakter multi-byte, bukan sebagai apa pun. "Byte" dapat berisi angka yang mewakili karakter (dalam ASCII dan beberapa pengkodean lainnya) tetapi "karakter" dapat membutuhkan lebih dari 1 byte (misalnya, titik kode di atas 0x7f dalam representasi UTF-8 dari Unicode).

Pembatasan ini muncul dari konvensi pencetakan nama file dan set karakter ASCII. Unix yang asli menggunakan ASCII '/' (numerik 0x2f) yang bernilai byte untuk memisahkan bagian-bagian dari jalur yang sebagian atau sepenuhnya memenuhi syarat (seperti '/ usr / bin / cat' memiliki bagian "usr", "bin" dan "cat") . Unix asli menggunakan ASCII Nul untuk mengakhiri string. Selain kedua nilai tersebut, byte dalam nama file dapat mengasumsikan nilai lainnya. Anda dapat melihat gema ini dalam pengkodean UTF-8 untuk Unicode. Karakter ASCII yang dapat dicetak, termasuk '/', hanya membutuhkan satu byte dalam UTF-8. UTF-8 untuk poin kode di atas tidak termasuk byte bernilai Nol, kecuali untuk karakter kontrol Nul. UTF-8 diciptakan untuk Plan-9, The Pretender to Throne of Unix.

Older Unixes (dan kelihatannya seperti Linux) memiliki namei()fungsi yang hanya melihat jalur byte pada satu waktu, dan memecah jalur menjadi potongan-potongan pada 0x2F dihargai byte, berhenti pada byte bernilai nol. namei()adalah bagian dari kernel Unix / Linux / BSD, jadi di situlah nilai byte yang luar biasa ditegakkan.

Perhatikan bahwa sejauh ini, saya sudah bicara tentang nilai byte, bukan karakter. namei()tidak menerapkan semantik karakter apa pun pada byte. Terserah program tingkat pengguna, seperti ls, yang mungkin mengurutkan nama file berdasarkan nilai byte, atau nilai karakter. xtermmemutuskan piksel apa yang menyala untuk nama file berdasarkan pengkodean karakter. Jika Anda tidak memberi tahu xtermAnda memiliki nama file yang disandikan UTF-8, Anda akan melihat banyak omong kosong ketika Anda memintanya. Jika vimtidak dikompilasi untuk mendeteksi pengkodean UTF-8 (atau apa pun, UTF-16, UTF-32), Anda akan melihat banyak omong kosong ketika Anda membuka "file teks" yang berisi karakter yang dikodekan dari UTF-8.

Bruce Ediger
sumber
Benar, namei()ditinggalkan sekitar tahun 1986. Penggunaan sistem UNIX baru lookuppn()berbasis VFS.
schily
17

Masalahnya, kernel tidak peduli sedikitpun bagaimana aplikasi menafsirkan data yang diberikan sebagai nama file.

Mari kita bayangkan saya memiliki aplikasi C yang berhubungan dengan string UTF-16 secara eksklusif. Dan saya masuk, melalui metode input yang dikonfigurasi dengan benar, simbol ∯ (Unicode 0x222F) ke dalam prompt / dialog "Simpan Sebagai".

Jika aplikasi tidak melakukan bentuk terjemahan apa pun dan mengirimkannya, dalam string C lama ( char*) ke, katakanlah, fopendalam mode tulis, kernel tidak akan melihat ∯, atau bahkan mencoba membayangkannya. Ini akan melihat dua chars, satu demi satu, dengan nilai-nilai 0x22 0x2F(dengan asumsi 8bit karakter dan tidak ada lelucon di perpustakaan C ).
Yaitu, dari sudut pandang kernel, karakter yang valid ( ") diikuti oleh /(ASCII 0x2F). fopenakan kembali EISDIR(yaitu "yang terlihat seperti direktori dan Anda meminta mode tulis!").
Jika saya memasukkan ∮ (Unicode 0x222E), kernel akan melihat dua karakter yang bagus, dan membuat file yang, seperti terlihat melalui aplikasi yang berbicara ASCII, akan dinamai "..

Jika saya telah memasukkan adalam aplikasi sebagai nama file, dan aplikasi meneruskannya di UTF-16 ke kernel, kernel akan membaca 0x00 0x61, dan sebenarnya bahkan tidak mempertimbangkan itu 0x61, karena 0x00sudah mengakhiri string, sejauh itu prihatin. Pesan kesalahan akan sama dengan nama file kosong ( ENOENTsaya percaya).

Jadi kernel memang mengambil data sebagai gumpalan. Ini aliran chars. "Karakter" yang tidak valid dalam pengkodean ruang-pengguna pilihan Anda adalah yang menghasilkan 0x00atau 0x2F("null" dan /) di gumpalan mereka (representasi biner yang diteruskan ke kernel).

Tikar
sumber
Jika saya mengerti Anda, maka tidak ada yang namanya karakter tidak valid. Hanya ada urutan byte yang tidak valid. Dan nilai-nilai 0x00dan 0x2Fdikodekan dalam kernel. Itu pada gilirannya berarti, bahwa direktori tidak dipisahkan oleh a /, tetapi untuk karakter apa pun peta ke 0x2Fdalam pengkodean yang digunakan.
Marco
Ya, itulah idenya jika Anda ingin melihatnya seperti itu. (Tapi itu mungkin salah. Kernel mungkin memiliki "penyandian asli" di mana /tidak 0x2F - mungkin tidak menggunakan 8-bit chars, pada kenyataannya.) Pemisah dir "tradisional" adalah /. Itu adalah 0x27 pada sistem ASCII 8bit byte (bukan EBCDIC misalnya).
Mat
Anda menganggap UTF-16BE, sedangkan di UTF-16LE U + 0061 akan menghasilkan astring (null-dihentikan) .
Incnis Mrsi
4

Pemisahan byte vs karakter muncul setelah Unix dirancang. Ketika itu dirancang penggunaan kata-kata hanya menyampaikan sesuatu tentang bagaimana 8 (atau 6, atau 9) bit ditafsirkan tetapi kata penyandian tidak disebutkan.

Nama file adalah urutan byte. Setiap byte kecuali 0x2f "/" diizinkan. Sebuah byte yang berisi 0x00 bahkan tidak dapat menembus kernel karena penggunaannya sebagai terminator string. Suatu aplikasi dapat menginterpretasikan urutan byte menurut suatu encoding yang dipilihnya. Jika itu terdengar berantakan saya kira begitu.

Ada informasi lebih lanjut di http://www.gtk.org/api/2.6/glib/glib-Character-Set-Conversion.html Anda mungkin menemukan berguna.

John S Gruber
sumber