概要
VFATファイルシステムでは、ユーザプログラムの文字コードを iocharset
で、short name をWindows側の codepage
によって文字コードを取り扱う。
はじめに
複数の環境で動作させなければならないシステムにおいて、文字コードは考えなければならない要素の一つである。
文字コードは、規格が多数存在していることや暗黙的な変換が多数実施されていることといったこともあり理解することは困難である。
様々な環境で利用されることが考えられる外部記憶装置のSDカードでは、FATボリュームレイアウトでフォーマットされることが多い。 Linuxでは、FATボリュームレイアウトにあるファイルを閲覧/編集できるようにVFATファイルシステムが実装されている。
VFATファイルシステムでは、複数の文字コードに対応できるように複数のマウントオプションが用意されている。
マウントオプション iocharset
とcodepage
は、ファイル名を指定した文字コードで変換する。
そのため、これらのマウントオプションを適切に指定していない場合には、ファイルを正しく検索することができなくなる。
ここでは、フォントやローカル環境変数を考慮しない。
FATボリュームレイアウト
FATボリュームレイアウトでは、ファイル(またはディレクトリ)に対して short name と long name を設定することができる。
short nameは、MS-DOSとの互換性の観点から 8.3形式 XXXXXXXX.YYY
(X
とY
は文字/数字)で保存する。
そのため、8.3形式ではないファイル名は8.3形式になるように切り詰められる。
long nameは、8.3形式ではないファイル名を8.3形式に切り詰められたときに完全なファイル名を保存する。
このとき、ファイル名は Unicodeコードポイントでストレージ上には保存される。
動作例
ここでは、次のような環境で実験している。
環境 | 概要 |
---|---|
CPU | AMD Ryzen 3 3300X |
RAM | DDR4-2666 16GB ×2 |
Host OS | Ubuntu Desktop 22.04.2 |
kernel | 5.19.0-41-generic |
System locale | LANG=ja_JP.UTF-8 |
Codepageが対応している文字列でファイルを作成する
UTF-8である場合には、iocharset
マウントオプションの代わりにutf8
マウントオプションを利用することが推奨されている。
また、Codepage 932は MicrosoftがShift-JISを拡張したコードページとなっている。
leava@ubuntu:/work/$ sudo mount -t vfat -o utf8,codepage=932 /dev/sda1 /mnt/
Long nameが作成され、Shift-JISに変換できる文字列 SJIS
で作成を作成する。
leava@ubuntu:/work/$ sudo touch /mnt/SJIS
ここで、これらの文字列は次のように表現される。
文字 | UTF-8 | Unicode | Shift-JIS | OEM-US |
---|---|---|---|---|
S |
EFBCB3 | U+FF33 | 8272 | n/a |
J |
EFBCAA | U+FF2A | 8269 | n/a |
I |
EFBCA9 | U+FF29 | 8268 | n/a |
S |
EFBCB3 | U+FF33 | 8272 | n/a |
short nameとlong nameの両方で期待する結果となっている。
Codepageが対応していない文字列でファイルを作成する
Codepage 437は OEM-USとも呼ばれており、ギリシャ文字や特殊文字が含まれている。
ただし、このCodepageには大文字英数字が含まれていない。
leava@ubuntu:/work/$ sudo mount -t vfat -o utf8,codepage=437 /dev/sda1 /mnt/; leava@ubuntu:/work/$ sudo touch /mnt/SJIS
OEM-USでは、SJIS
を変換することができないため、short nameは_
に置き換えられる。
システムとは異なるiocharsetでファイルを作成する
ISO8859-1、通称Latin-1は西ヨーロッパ諸言語向けの符号化集合である。
ただし、システムで使用している UTF-8とは互換性がなく大文字英数字が含まれていない。
leava@ubuntu:/work/$ sudo mount -t vfat -o iocharset=iso8859-1,codepage=932 /dev/sda1 /mnt/ leava@ubuntu:/work/$ sudo touch /mnt/SJIS
ISO8859-1は、1バイト文字コードであり UTF-8で入力された文字列は1バイトずつUTF-16に変換されてしまう。
その結果、short nameもlong nameも意図しないデータとして書き込まれてしまう。
Codepageが対応している文字列でファイルを検索する
ユーザプログラムからファイル名を検索するとき、作成するときと同様に文字コードの変換がされる。
そこで、Shift-JIS形式で保存された"SJIS"というファイルがトップディレクトリに存在する場合を考える。
Shift-JISと互換のあるcp932でマウントする。
leava@ubuntu:/work/$ sudo mount -t vfat -o utf8,codepage=932 /dev/sda1 /mnt/ leava@ubuntu:/work/$ cat /mnt/SJIS; echo $? 0
この場合、short nameの変換した結果とユーザプログラムから入力された文字列"SJIS"と一致するため、ファイル検索に成功する。
Codepageが対応していない文字列でファイルを検索する
Shift-JISとは互換がない cp437でマウントする。
leava@ubuntu:/work/$ sudo mount -t vfat -o utf8,codepage=437 /dev/sda1 /mnt/ leava@ubuntu:/work/$ cat /mnt/SJIS; echo $? 0
この場合、short nameは適切に変換することができない。
ただし、long nameはUTF-8に変換することができ、ユーザプログラムから入力された文字列"SJIS"と一致するため、ファイル検索に成功する。
システムとは異なるiocharsetでファイルを検索する
UTF-8と互換性がないISO8859-1でマウントする。
leava@ubuntu:/work/$ sudo mount -t vfat -o iocharset=iso8859-1,codepage=437 /dev/sda1 /mnt/ leava@ubuntu:/work/$ cat /mnt/SJIS; echo $? 2
この場合、short nameは適切に変換することができない。
また、long nameもISO8859-1で変換することができないため、ファイル検索に失敗する。
おわりに
FATボリュームレイアウトでは、short nameとlong nameで二つのファイル名を持ち、変換規則がそれぞれ異なる。
そのため、システムとは異なるiocharsetを設定してしまうと文字化けしてしまう恐れがある。
codepageは対向のWindowsと揃えておくことで互換性を保持することができる。
ただし、今回は ローカル環境変数とユーザプログラムで扱う文字列は同じUTF-8であることを想定している。
実際においては、これに加えて"フォントがその文字コードをサポートしている"かどうかなど複雑になるため注意。
変更履歴
- 2023/5/9: 記事公開