FAT を正しく case insensitive にする方法
暫く前になるけれど、Linux 上における Mercurial の case insensitive filesystem (icasefs) に対する挙動の確認が必要になった。
僕の開発環境は、Windows ホスト上の VirtualBox で Linux を稼動させているので、ホスト側に挿した FAT フォーマットの USB メモリとかを仮想マシン側に見せる、という手も使えるけれど、一応正統派な方法で FAT 環境を作ることに。
# 要 dosfstools (mkfs.vfat コマンド)
作成する FAT 領域に関する仕様を以下のように仮定する:
- /tmp/vfatdev 直下の 64MB (= 512 バイトブロックで 131072)ファイルをデバイス化
- /tmp/vfatmnt 直下にマウント
- uid 1000 のユーザに対してアクセス権限を解放
作成手順は以下の通り:
# dd if=/dev/zero of=/tmp/vfatdev count=131072 131072+0 records in 131072+0 records out 67108864 bytes (67 MB) copied, 0.301944 s, 222 MB/s # mkfs -t vfat /tmp/vfatdev mkfs.vfat 3.0.9 (31 Jan 2010) # mount -t vfat -o loop,uid=1000 /tmp/vfatdev /tmp/vfatmnt #
"-o loop" 指定は、単なるファイルをブロックデバイスとして見せる(= ループバックデバイス化する)ためのもの。
ここまで出来たなら、ファイル名指定 'a' と 'A' が同一、つまり『case insensitive』になっていることを確認。
$ date > /tmp/vfatmnt/a $ stat /tmp/vfatmnt/a File: `/tmp/vfatmnt/a' Size: 29 Blocks: 4 IO Block: 2048 regular file Device: 700h/1792d Inode: 2 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/fujiwara) Gid: ( 0/ root) Access: 2012-04-04 17:46:15.000000000 +0900 Modify: 2012-04-04 17:49:39.000000000 +0900 Change: 2012-04-04 17:49:39.000000000 +0900 $ stat /tmp/vfatmnt/A File: `/tmp/vfatmnt/A' Size: 29 Blocks: 4 IO Block: 2048 regular file Device: 700h/1792d Inode: 2 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/fujiwara) Gid: ( 0/ root) Access: 2012-04-04 17:46:15.000000000 +0900 Modify: 2012-04-04 17:49:39.000000000 +0900 Change: 2012-04-04 17:49:39.000000000 +0900 $
おぉ、'a' と 'A' のどちらでも stat(2) 出来ている。
では、"hg debugfsinfo" ではどうか?というと:
$ cd /tmp/vfatmnt $ hg debugfsinfo exec: no symlink: no case-sensitive: yes $
はぁ? case sensitive〜?どーゆーことよ?
試行錯誤すること暫し …… "hg debugfsinfo" は、".debugfsinfo" というファイルの生成/アクセス可否で、case seitive か否かを判定しているのだが:
$ echo debugfsinfo > .debugfsinfo $ stat .debugfsinfo File: `.debugfsinfo' Size: 12 Blocks: 4 IO Block: 2048 regular file Device: 700h/1792d Inode: 5 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/fujiwara) Gid: ( 0/ root) Access: 2012-04-04 17:57:27.000000000 +0900 Modify: 2012-04-04 17:57:27.000000000 +0900 Change: 2012-04-04 17:57:27.000000000 +0900 $ stat .DEBUGFSINFO stat: cannot stat `.DEBUGFSINFO': No such file or directory $
どうやら、このファイルの場合では stat(2) が出来ていない。ファイル名形式(「8+3で表現可能」)とかに依存してるのか?
"HoGe" で保存したファイルに、"HOGE" だと stat(2) できるけど、"hoge" だと駄目。うーむ、この程度で駄目だとすると、基本的には上手く機能していないと考えたほうが良いなぁ。
とりあえず、任意のファイル名で case insensitive にするにはどうすれば良いか、更に試行錯誤/ネット彷徨すること暫し …… あ、符号化形式指定の問題か?
そういえば、これまで cp932 絡みの case insensitive 系修正の確認では、いつも iocharset (+ codepage) とかを設定していて、且つちゃんと case insensitive になっていたけど、今回は日本語とか関係無いから、特に設定していなかったなぁ。
とりあえず暫定的に cp932 を設定してみると:
※ root 権限側 # mount -t vfat -o iocharset=cp932,loop,uid=1000 /tmp/vfatdev /tmp/vfatmnt ※ ユーザ側 $ echo debugfsinfo > /tmp/vfatmnt/.debugfsinfo $ stat /tmp/vfatmnt/.debugfsinfo File: `.debugfsinfo' Size: 12 Blocks: 4 IO Block: 2048 regular file Device: 700h/1792d Inode: 5 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/fujiwara) Gid: ( 0/ root) Access: 2012-04-04 17:57:27.000000000 +0900 Modify: 2012-04-04 17:57:27.000000000 +0900 Change: 2012-04-04 17:57:27.000000000 +0900 $ stat /tmp/vfatmnt/.DEBUGFSINFO File: `/tmp/vfatmnt/.DEBUGFSINFO' Size: 12 Blocks: 4 IO Block: 2048 regular file Device: 700h/1792d Inode: 7 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/fujiwara) Gid: ( 0/ root) Access: 2012-04-04 09:00:00.000000000 +0900 Modify: 2012-04-04 18:24:54.000000000 +0900 Change: 2012-04-04 18:24:54.000000000 +0900 $
出来たー!
良く良く考えれば、『文字大小の変換』なんて、符号化方式が確定していなければ、実行しようが無いのだから、符号化方式指定が必要なのは当然なんだよねぇ。
ちなみに、iocharset 指定無しだと、コンソールに:
FAT: utf8 is not a recommended IO charset for FAT filesystems, \ filesystem will be case sensitive
などと出ていたりする。このメッセージをちゃんと目にして、意味を理解していれば …… orz
ちなみに、先述した「"HoGe" に "HOGE" は OK で "hoge" は NG」ってのは、昨年末にバタバタ作業した際に、Matt から『Windows の case normalization 処理は、lower じゃなくて upper』と指摘されたのと、見事に符合している。
つまり、iocharset を指定しないと、指定ファイル名の normalization が行われないので、normalization 実施後相当の大文字指定でないと駄目、ということなのだな。
うーん、でも最初の不十分な設定の際に、 "a" に対して "A" が上手くいったのは、上記の推測で辻褄が合うけど、".debuginfo" に対して ".DEBUGINFO" が上手くいかなかったのは、これでは説明できないなぁ。なんとも謎な話だ……。