LeavaTailの日記

LeavaTailの日記

Linuxエンジニアを目指した技術者の備忘録

VFATファイルシステムにおけるファイル名のコード変換

概要

VFATファイルシステムでは、ユーザプログラムの文字コードiocharsetで、short name をWindows側の codepageによって文字コードを取り扱う。

FATファイルシステムにおけるファイル名の変換まとめ

はじめに

複数の環境で動作させなければならないシステムにおいて、文字コードは考えなければならない要素の一つである。
文字コードは、規格が多数存在していること暗黙的な変換が多数実施されていることといったこともあり理解することは困難である。

様々な環境で利用されることが考えられる外部記憶装置のSDカードでは、FATボリュームレイアウトでフォーマットされることが多い。 Linuxでは、FATボリュームレイアウトにあるファイルを閲覧/編集できるようにVFATファイルシステムが実装されている。

VFATファイルシステムでは、複数の文字コードに対応できるように複数のマウントオプションが用意されている。
マウントオプション iocharsetcodepageは、ファイル名を指定した文字コードで変換する。
そのため、これらのマウントオプションを適切に指定していない場合には、ファイルを正しく検索することができなくなる。

memo

ここでは、フォントやローカル環境変数を考慮しない。

FATボリュームレイアウト

FATボリュームレイアウトでは、ファイル(またはディレクトリ)に対して short namelong name を設定することができる。

FATボリュームレイアウトにおけるファイル名

short nameは、MS-DOSとの互換性の観点から 8.3形式 XXXXXXXX.YYY(XYは文字/数字)で保存する。
そのため、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
EFBCB3 U+FF33 8272 n/a
EFBCAA U+FF2A 8269 n/a
EFBCA9 U+FF29 8268 n/a
EFBCB3 U+FF33 8272 n/a

Shift-JISでファイルを作成

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でファイルを作成

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でファイル作成

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"と一致するため、ファイル検索に成功する。

cp932でファイル検索

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"と一致するため、ファイル検索に成功する。

cp437でファイル検索

システムとは異なる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で変換することができないため、ファイル検索に失敗する。

ISO8859-1でファイル検索

おわりに

FATボリュームレイアウトでは、short nameとlong nameで二つのファイル名を持ち、変換規則がそれぞれ異なる。

そのため、システムとは異なるiocharsetを設定してしまうと文字化けしてしまう恐れがある。
codepageは対向のWindowsと揃えておくことで互換性を保持することができる。

ただし、今回は ローカル環境変数とユーザプログラムで扱う文字列は同じUTF-8であることを想定している。
実際においては、これに加えて"フォントがその文字コードをサポートしている"かどうかなど複雑になるため注意。

変更履歴

  • 2023/5/9: 記事公開

参考