ubuntuでexFAT

ubuntu11.10のリポジトリにはexfatが含まれていなかったので、
本家exfat - Free exFAT file system implementation - Google Project Hosting
より、subbersionリポジトリからソースツリーを引っ張り出してセルビルドする。

http://code.google.com/p/exfat/wiki/HOWTO に説明されているように、てきとーなディレクトリで

svn co http://exfat.googlecode.com/svn/trunk/ exfat-read-only

というふうに、ソースツリーを引っ張り出す。

そしてsconsというビルドツールを用いてビルドするのであるが、sconsはubuntuリポジトリからapt-getなどで取得する。あと、Fuseの開発パッケージもビルドに必要となる。

apt-get install scons libfuse-dev

のようにすると、sconsとfuseの開発パッケージがインストールされる。sconsが要求する依存関係らPerlのライブラリー関係が若干入れ替えに成る場合があるので、sconsをインストールことで消されては困るパッケージがないことをよく確認してsconsをインストールする。

ビルドはexFATのソースディレクトリにcd して

cd exfat-read-only
scons

のようにすると、コンパイルされる。

インストールは、ドキュメントにあるとおりに

sudo scons install

とすればよいのだが、/sbinにいきなりコピーされるだけなので、アンインストールの手順というのがほぼ皆無に近いことを記憶に留めておこう。

マウントは、gnomeなどのデスクトップにログオンしているならば nautilusをひらいて、ドライブのアイコンをクリックすればよい。もしX11のないコンソールのみであるならばドキュメントに記載されているように、

sudo mount.exfat-fuse /dev/sdXn /mnt/exfat

とかすればよいだろう。

ということで、

Dec 21 23:31:09 onion999 kernel: [85500.405230] ISO 9660 Extensions: Microsoft Joliet Level 3
Dec 21 23:31:09 onion999 kernel: [85500.406715] ISO 9660 Extensions: RRIP_1991A
Dec 21 23:32:39 onion999 kernel: [85591.072134] usb 1-5: new high speed USB device number 6 using ehci_hcd
Dec 21 23:32:39 onion999 mtp-probe: checking bus 1, device 6: "/sys/devices/pci0000:00/0000:00:02.1/usb1/1-5"
Dec 21 23:32:39 onion999 kernel: [85591.210439] scsi8 : usb-storage 1-5:1.0
Dec 21 23:32:40 onion999 mtp-probe: bus: 1, device: 6 was not an MTP device
Dec 21 23:32:40 onion999 kernel: [85592.223632] scsi 8:0:0:0: Direct-Access     I-O DATA HDCR-U                PQ: 0 ANSI: 2 CCS
Dec 21 23:32:41 onion999 kernel: [85592.230365] sd 8:0:0:0: Attached scsi generic sg3 type 0
Dec 21 23:32:41 onion999 kernel: [85592.231341] sd 8:0:0:0: [sdc] 2930277168 512-byte logical blocks: (1.50 TB/1.36 TiB)
Dec 21 23:32:41 onion999 kernel: [85592.233839] sd 8:0:0:0: [sdc] Write Protect is off
Dec 21 23:32:41 onion999 kernel: [85592.233846] sd 8:0:0:0: [sdc] Mode Sense: 3c 00 00 00
Dec 21 23:32:41 onion999 kernel: [85592.235836] sd 8:0:0:0: [sdc] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
Dec 21 23:32:41 onion999 kernel: [85592.329766]  sdc: sdc1
Dec 21 23:32:41 onion999 kernel: [85592.335836] sd 8:0:0:0: [sdc] Attached SCSI disk
Dec 21 23:32:41 onion999 ata_id[4614]: HDIO_GET_IDENTITY failed for '/dev/sdc': Invalid argument
Dec 21 23:44:43 onion999 mount.exfat: illegal UTF-8 sequence
Dec 21 23:44:43  mount.exfat: last message repeated 199 times

をつなげてみた。

nautilusから、しばらくの間あっちこっちのアイコンをクリックしていたら、ドライブからの応答がなくなって、エラーダイアログがポップアップするだけになっていた。

syslogをかくにんしてみると

Dec 21 20:30:44 onion999 mount.exfat: name is too long
Dec 21 20:30:44 onion999 mount.exfat: failed to convert name to UTF-8
Dec 21 20:33:16 onion999 mount.exfat: volume was not unmounted cleanly

てなことで、FuseFSがハングアップしてしまったようだ。

とりあえずソースコートを確認ということで・・・

grep -e 'name is too long' -r .

エラーメッセージのある箇所を確認すると、

./libexfat/utf.c:			exfat_error("name is too long");

とかヒットしたので ./libexfat/utf.c をエディタで眺めてみる。

int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
		size_t insize)
{
	const le16_t* inp = input;
	char* outp = output;
	wchar_t wc;

	while (inp - input < insize && le16_to_cpu(*inp))
	{
		inp = utf16_to_wchar(inp, &wc, insize - (inp - input));
		if (inp == NULL)
		{
			exfat_error("illegal UTF-16 sequence");
			return -EILSEQ;
		}
		outp = wchar_to_utf8(outp, wc, outsize - (outp - output));
		if (outp == NULL)
		{
			exfat_error("name is too long");
			return -ENAMETOOLONG;
		}
	}
	*outp = '\0';
	return 0;
}

つぎに utf16_to_utf8 を呼び出している箇所を

grep -e 'utf16_to_utf8' -r . |grep -E '\.c'

とかして、確認する

./libexfat/node.c:			if (utf16_to_utf8(ef->label, label->name,
./libexfat/utf.c:int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
./libexfat/utils.c:	if (utf16_to_utf8(buffer, node->name, n, EXFAT_NAME_MAX) != 0)

そーいうことで、とりあえず ./libexfat/node.c をエディタで開いて、呼び出しているところを眺めてみる。

		case EXFAT_ENTRY_LABEL:
			label = (const struct exfat_entry_label*) entry;
			if (label->length > EXFAT_ENAME_MAX)
			{
				exfat_error("too long label (%hhu chars)", label->length);
				goto error;
			}
			if (utf16_to_utf8(ef->label, label->name,
						sizeof(ef->label), EXFAT_ENAME_MAX) != 0)
				goto error;
			break;

どうやら、EXFAT_NAME_MAXで、ファイルネームの長さを決めているようだ。とりあえずバッファサイズを大きく設定してみれば当面のエラーは回避できそうな感じがする。
ということで、再び検索してみる。

grep -e 'EXFAT_NAME_MAX' -r . |grep -e '#.*EXFAT_NAME_MAX'

とかして、EXFAT_NAME_MAXが定義されている場所を探す。

./exfat-read-only/libexfat/.svn/text-base/exfat.h.svn-base:#define EXFAT_NAME_MAX 256
./exfat-read-only/libexfat/exfat.h:#define EXFAT_NAME_MAX 256

ふむ。./libexfat/exfat.h をいじくってあげればよさげだなぁ。

ちなみにマイクロソフトの資料*1を参考にすると、ファイルネームの長さの最大はフルパス名の長さの最大であると説明されており、そのながさは、「通常の場合、Windows ではファイル名が 260 文字までに制限されています。」と明記されているので、おそらくEXFAT_NAME_MAXは260と定義すべきであろう。

===================================================================
--- libexfat/exfat.h	(リビジョン 315)
+++ libexfat/exfat.h	(作業コピー)
@@ -1,3 +1,4 @@
+
 /*
 	exfat.h (29.08.09)
 	Definitions of structures and constants used in exFAT file system
@@ -31,7 +32,7 @@
 #include "exfatfs.h"
 #include "version.h"
 
-#define EXFAT_NAME_MAX 256
+#define EXFAT_NAME_MAX 260 // issued `name is too long` at 256.
 #define EXFAT_ATTRIB_CONTIGUOUS 0x10000
 #define EXFAT_ATTRIB_CACHED     0x20000
 #define EXFAT_ATTRIB_DIRTY      0x40000

本当は、もう少しよく考えて検討しないと、記憶喪失なドライブや削除できないファイルとか作ってしまうかもしれない。ということで、毎度ながらオープンソースを使うのに「バグは愛嬌。使うは度胸。」を肝に据え置いて人柱するべし。