2023年12月23日土曜日

FAT filesystem のVolume label

先日UbuntuのInstallに利用したUSBメモリをWindowsで再利用しようとしたら、うまくできなかった。
UEFIのパーティションや、パーティションテーブル、なんならMBRも消して作り直したいのだが、どうにもできない。
Windowsも少しずつ変わっている。私が昔のやり方しか知らないためかもしれない。

悩んでいてもしょうがないので、Ubuntuでやることにした。
UbuntuのInstallのための後始末を、Ubuntuでやるのは当然であるとも言える。
fdiskでMBRのパーティションテーブルを作り直して、mkfs.vfatでフォーマットを行った。
昔とほとんど変わらない。
これでWindowsで使えるようになる。

ふと「わかりやすいボリュームラベルでも付けておこうかな。」と、思った。
昔のことを色々思い出していた。そのせいか、PC-9801のMS-DOSのころ"dir"と打つたときの「ボリュームラベルは”xxxx”です。」が頭に浮かんだ。

mkfs.vfatでは、-n <ボリュームラベル> と --codepage <コードページ番号> を指定すれば、BPB上のボリュームラベル領域にセットできる様子。
コードページを指定するということは、OEM文字セット、すなわち日本語も正しく指定すれば使えるようだ。
実際に使おうとしたら、以下のように、エラーになる。
$ mkfs.vfat -n "試験用" --codepage 932 /dev/sdc1
mkfs.fat 4.2 (2021-01-31)
mkfs.vfat: Labels with characters below 0x20 are not allowed
0x20より小さいって、CP932(Shift_JIS)の半角カナや漢字の第一バイトを符号付き8bitで扱っている?

ソースパッケージ(dosfstools)をダウンロードして、確認した。
src/common.cの最後の、関数validate_volume_label()で、エラーチェックを行っている。
引数doslabel[]はOEMコードページで書かれたボリュームラベル文字列だ。
char型であり、そのまま0x20と比較していた。
   312	/*
   313	 * Validate volume label
   314	 *
   315	 * @param[in]   doslabel   Label stored according to current DOS codepage
   316	 *
   317	 * @return   bitmask of errors
   318	 *           0x01 - lowercase character
   319	 *           0x02 - character below 0x20
   320	 *           0x04 - character in disallowed set
   321	 *           0x08 - empty or space-only label
   322	 *           0x10 - space at beginning
   323	 */
   324	int validate_volume_label(char *doslabel)
   325	{
   326	    int i;
   327	    int ret = 0;
   328	    wchar_t wlabel[12];
   329	
   330	    if (dos_string_to_wchar_string(wlabel, doslabel, sizeof(wlabel))) {
   331	        for (i = 0; wlabel[i]; i++) {
   332	            /* FAT specification: Lower case characters are not allowed in DIR_Name
   333	                                  (what these characters are is country specific)
   334	               Original label is stored in DOS OEM code page, so islower() function
   335	               cannot be used. Therefore convert original label to locale independent
   336	               wchar_t* and then use iswlower() function for it.
   337	            */
   338	            if (iswlower(wlabel[i])) {
   339	                ret |= 0x01;
   340	                break;
   341	            }
   342	        }
   343	    }
   344	
   345	    /* According to FAT specification those bytes (after conversion to DOS OEM
   346	       code page) are not allowed.
   347	     */
   348	    for (i = 0; i < 11; i++) {
   349	        if (doslabel[i] < 0x20)
   350	            ret |= 0x02;
   351	        if (doslabel[i] == 0x22 ||
   352	            (doslabel[i] >= 0x2A && doslabel[i] <= 0x2C) ||
   353	            doslabel[i] == 0x2E ||
   354	            doslabel[i] == 0x2F ||
   355	            (doslabel[i] >= 0x3A && doslabel[i] <= 0x3F) ||
   356	            (doslabel[i] >= 0x5B && doslabel[i] <= 0x5D) ||
   357	            doslabel[i] == 0x7C)
   358	            ret |= 0x04;
   359	    }
   360	
   361	    if (memcmp(doslabel, "           ", 11) == 0)
   362	        ret |= 0x08;
   363	
   364	    if (doslabel[0] == ' ')
   365	        ret |= 0x10;
   366	
   367	    return ret;
   368	}
他のOEM文字セットでは、8bit文字を使わないのかな?
符号なし8bitで比較するように直して、バイナリパッケージをリビルドして、インストール。
それで、mkfs.vfatをしたら、日本語ボリュームラベルを付けることができた。

ここまではいい。

ボリュームラベルは、ルートディレクトリ上に特別な属性をもつディレクトリエントリを作ることでも表現できる。
本来、この文字列とBPB内のボリュームラベルは同じものを示すはずだ。
そして、CP932ではディレクトリエントリで特別な考慮が必要になる。

CP932の文字セットには、0xE5で始まる文字がいくつか存在している。
ファイル名が0xE5で始まるディレクトリエントリは、削除されたエントリとして扱われる。そのため、ファイル名の先頭で0xE5を使いたい場合、0x05に置き換えるというルールがMS-DOSのころから存在している。
このルールは、どうなるんだろう?

BPBのボリュームラベルは、ディレクトリエントリの一部ではない。
しかし、両方同じボリュームラベルであることは違いない。
試しに0xE5で始まる文字列"蛆虫"(「うじむし」こんな名前普通は使わないけど)を、mkfs.vfatでボリュームラベルにしてみた。

WindowsにUSBメモリを挿入すると、"蛆虫"の"蛆"が文字化けした。
やっぱ、0xE5がダメだったのかな?と思って、ddを使って無理やり0xE5を0x05にしてみた。
それでも、Windowsでは、文字化けしてしまう。

Windowsは、何を求めているんだろう?
Windowsでボリュームラベルに"蛆虫"を指定してフォーマットしようとした。
エラーになって、フォーマットできない。
今度は試しに、ボリュームラベルに”良い蛆虫”を指定した。
すると、エラーなしにフォーマットできた。
なんと! Windows では、0xE5 で始まる文字列をボリュームラベルに使えないのだ。
なんか微妙に違っているがやっぱり、BPBでもダメってことだ。

そんなのあり?
とはいえ、0xE5で始まる文字は、JIS第2水準であり、使えなくても多くの場合問題にならない。
ま、いいか。

2022/12/23 ソースコードの色が悪く、読みにくかったので直した。ついでに細かく修正。

0 件のコメント:

コメントを投稿