Hiểu mã hóa tên tệp Unix


25

Tôi có một thời gian khó hiểu làm thế nào mã hóa tên tập tin hoạt động. Trên unix.SE tôi thấy giải thích mâu thuẫn.

Tên tệp được lưu trữ dưới dạng ký tự

Để trích dẫn câu trả lời khác: Một số câu hỏi về mã hóa ký tự hệ thống tệp trên linux

[V]] như bạn đã đề cập trong câu hỏi của mình, tên tệp UNIX chỉ là một chuỗi các ký tự; hạt nhân không biết gì về mã hóa, hoàn toàn là một khái niệm không gian người dùng (tức là cấp độ ứng dụng).

Nếu tên tệp được lưu trữ dưới dạng ký tự, thì phải có một số loại mã hóa liên quan, vì cuối cùng tên tệp phải kết thúc dưới dạng chuỗi bit hoặc byte trên đĩa. Nếu người dùng có thể chọn bất kỳ mã hóa nào để ánh xạ các ký tự thành chuỗi byte được đưa vào kernel, có thể tạo bất kỳ chuỗi byte nào cho tên tệp hợp lệ.

Giả sử như sau: Một người dùng sử dụng mã hóa X ngẫu nhiên , dịch tệp này foothành chuỗi byte α và lưu nó vào đĩa. Một công dụng sử dụng mã hóa Y . Trong mã hóa này, α dịch sang /, không được phép dưới dạng tên tệp. Tuy nhiên, đối với người dùng đầu tiên, tệp này là hợp lệ.

Tôi cho rằng kịch bản này không thể xảy ra.

Tên tệp được lưu trữ dưới dạng các đốm nhị phân

Để trích dẫn một câu trả lời khác: Mã hóa bộ ký tự nào được sử dụng cho tên tệp và đường dẫn trên Linux?

Như những người khác lưu ý, thực sự không có câu trả lời cho điều này: tên tệp và đường dẫn không có mã hóa; HĐH chỉ xử lý chuỗi byte. Các ứng dụng riêng lẻ có thể chọn giải thích chúng là được mã hóa theo một cách nào đó, nhưng điều này khác nhau.

Nếu hệ thống không xử lý các ký tự, làm thế nào các ký tự cụ thể (ví dụ /hoặc NULL) bị cấm trong tên tệp? Không có khái niệm về việc / không có mã hóa.

Một lời giải thích là hệ thống tệp có thể lưu trữ tên tệp chứa bất kỳ ký tự nào và chỉ có các chương trình người dùng có mã hóa vào tài khoản sẽ gây nghẹt thở cho tên tệp chứa các ký tự không hợp lệ. Điều đó, có nghĩa là các hệ thống tệp và kernel có thể, không gặp khó khăn gì, xử lý các tên tệp chứa a /.

Tôi cũng cho rằng điều này là sai.

Việc mã hóa diễn ra ở đâu và hạn chế được đặt ra là không cho phép các ký tự cụ thể ở đâu?


Null giống nhau (0) trong tất cả các bảng mã.
Kevin

2
@Kevin Không hoàn toàn: không, ví dụ, UTF-16 hoặc UCS-4 (= UTF-32) hoặc hầu hết các mã hóa đa bào khác không phải là phần mở rộng của ASCII.
Gilles 'SO- ngừng trở nên xấu xa'

1
Trên thực tế, câu trả lời của Riccardo Murri nên đề cập đến byte và không phải ký tự ở đó. Hầu hết các hệ thống tập tin đều lưu trữ byte.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles: một lần nữa Ī̲ thấy bạn thực sự xem những gì được viết .
Incni Mrsi 17/9/2015

Câu trả lời:


25

Câu trả lời ngắn: các hạn chế được áp đặt trong hạt nhân Unix / Linux / BSD namei(). Mã hóa diễn ra trong các chương trình cấp người dùng như xterm, firefoxhoặc ls.

Tôi nghĩ rằng bạn đang bắt đầu từ cơ sở không chính xác. Tên tệp trong Unix là một chuỗi byte có giá trị tùy ý. Một vài giá trị, 0x0 (ASCII Nul) và 0x2f (ASCII '/') chỉ không được phép, không phải là một phần của mã hóa ký tự nhiều byte, không phải là bất cứ điều gì. Một "byte" có thể chứa một số đại diện cho một ký tự (trong ASCII và một số mã hóa khác) nhưng "ký tự" có thể yêu cầu nhiều hơn 1 byte (ví dụ: các điểm mã trên 0x7f trong biểu diễn Unicode của UTF-8).

Những hạn chế này phát sinh từ các quy ước in tên tệp và bộ ký tự ASCII. Các Unix ban đầu đã sử dụng các byte có giá trị ASCII '/' (số 0x2f) để tách các phần của một đường dẫn đủ điều kiện một phần hoặc đủ điều kiện (như '/ usr / bin / cat' có các phần "usr", "bin" và "cat") . Các Unix ban đầu đã sử dụng ASCII Nul để chấm dứt các chuỗi. Khác với hai giá trị đó, byte trong tên tệp có thể giả sử bất kỳ giá trị nào khác. Bạn có thể thấy tiếng vang của điều này trong mã hóa UTF-8 cho Unicode. Các ký tự ASCII có thể in, bao gồm '/', chỉ mất một byte trong UTF-8. UTF-8 cho các điểm mã ở trên không bao gồm bất kỳ byte nào có giá trị Zero, ngoại trừ ký tự điều khiển Nul. UTF-8 được phát minh cho Plan-9, The Pretender to the Throne of Unix.

Các Unix cũ hơn (và có vẻ như Linux) có một namei()chức năng chỉ nhìn vào các đường dẫn một byte tại một thời điểm và chia các đường dẫn thành các mảnh ở các byte có giá trị 0x2F, dừng lại ở một byte có giá trị bằng 0. namei()là một phần của nhân Unix / Linux / BSD, do đó, nơi các giá trị byte đặc biệt được thi hành.

Lưu ý rằng cho đến nay, tôi đã nói về các giá trị byte, không phải ký tự. namei()không thi hành bất kỳ ngữ nghĩa ký tự nào trên các byte. Điều đó tùy thuộc vào các chương trình cấp người dùng, như ls, có thể sắp xếp tên tệp dựa trên giá trị byte hoặc giá trị ký tự. xtermquyết định những pixel nào sáng lên cho tên tệp dựa trên mã hóa ký tự. Nếu bạn không nói rằng xtermbạn đã có tên tệp được mã hóa UTF-8, bạn sẽ thấy rất nhiều điều vô nghĩa khi bạn gọi nó. Nếu vimkhông được biên dịch để phát hiện các mã hóa UTF-8 (hoặc bất cứ thứ gì, UTF-16, UTF-32), bạn sẽ thấy rất nhiều tiếng nói vô nghĩa khi bạn mở một "tệp văn bản" chứa các ký tự được mã hóa UTF-8.


Đúng, namei()đã bị bỏ rơi vào khoảng năm 1986. Các hệ thống UNIX mới hơn sử dụng lookuppn()dựa trên VFS.
schily

17

Vấn đề là, kernel không quan tâm một chút về cách các ứng dụng diễn giải dữ liệu mà nó được cung cấp dưới dạng tên tệp.

Hãy tưởng tượng tôi có một ứng dụng C liên quan đến các chuỗi UTF-16 độc quyền. Và tôi nhập, thông qua một phương thức nhập được cấu hình đúng, ký hiệu ((Unicode 0x222F) vào hộp thoại / nhắc "Lưu dưới dạng".

Nếu ứng dụng không thực hiện bất kỳ hình thức dịch nào và gửi nó, trong một chuỗi C cũ ( char*) đơn giản đến, fopenở chế độ ghi, hạt nhân sẽ không thấy ∯, hoặc thậm chí cố gắng tưởng tượng điều đó. Nó sẽ thấy hai chars, lần lượt từng cái, với các giá trị 0x22 0x2F(giả sử ký tự 8 bit và không có funnies trong thư viện C ).
Đó là, từ quan điểm của hạt nhân, một char ( ") hợp lệ theo sau /(ASCII 0x2F). fopensẽ trả về EISDIR(tức là "trông giống như một thư mục và bạn đã yêu cầu chế độ ghi!").
Nếu tôi đã nhập (Unicode 0x222E), hạt nhân sẽ thấy hai ký tự tốt và tạo một tệp, như được thấy qua ứng dụng nói ASCII, sẽ được đặt tên "..

Nếu tôi đã nhập vào aứng dụng dưới dạng tên tệp và ứng dụng đã chuyển nó trong UTF-16 cho kernel, kernel sẽ đọc 0x00 0x61và thậm chí không xem xét điều đó 0x61, bởi vì nó 0x00đã chấm dứt chuỗi, theo như nó là liên quan. Thông báo lỗi sẽ giống như đối với một tên tệp trống ( ENOENTtôi tin).

Vì vậy, kernel thực sự lấy dữ liệu như một blob. Đó là một dòng của chars. Các "ký tự" không hợp lệ trong mã hóa không gian người dùng mà bạn chọn là những ký tự tạo 0x00hoặc 0x2F("null" và /) trong blob của chúng (biểu diễn nhị phân được truyền vào kernel).


Nếu tôi hiểu bạn đúng, thì không có thứ gọi là ký tự không hợp lệ. Chỉ có các chuỗi byte không hợp lệ. Và các giá trị 0x000x2Fđược mã hóa cứng trong kernel. Điều đó có nghĩa là, các thư mục đó không được phân tách bằng a /, mà với bất kỳ ký tự nào ánh xạ tới 0x2Fmã hóa được sử dụng.
Marco

Vâng, đó là ý tưởng nếu bạn muốn thấy nó theo cách đó. (Nhưng điều đó có thể không chính xác. Một hạt nhân có thể có "mã hóa riêng" trong đó /không phải là 0x2F - thực tế có thể không sử dụng 8 bit chars.) Dấu tách dir "truyền thống" là /. Đó là 0x27 trên các hệ thống ASCII 8 bit byte (không phải EBCDIC).
Mat

Bạn giả sử UTF-16BE, trong khi đó trong UTF-16LE U + 0061 sẽ dẫn đến chuỗi (kết thúc null) a.
Incni Mrsi

4

Việc phân tách byte và ký tự xuất hiện nhiều sau khi Unix được thiết kế. Khi nó được thiết kế, việc sử dụng các từ chỉ truyền đạt một cái gì đó về cách 8 (hoặc 6 hoặc 9) bit được diễn giải nhưng mã hóa từ không được đề cập.

Tên tệp là chuỗi byte. Bất kỳ byte nào ngoại trừ 0x2f "/" đều được cho phép. Một byte chứa 0x00 thậm chí không thể truy cập vào kernel do sử dụng nó như là một bộ kết thúc chuỗi. Một ứng dụng có thể diễn giải chuỗi byte theo mã hóa mà nó chọn. Nếu điều đó nghe có vẻ lộn xộn, tôi cho rằng nó là.

Có nhiều thông tin hơn tại http://www.gtk.org/api/2.6/glib/glib-Character-set-Conversion.html bạn có thể thấy hữu ích.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.