iOS 10.3 でAPFSが本番投入されました。それでiPhoneやiPadが文鎮化するなどの深刻な問題はみられなかったものの、やはり無問題とはいかなかったようで。
iOS10.3で不具合の出るアプリは、APFS関連の影響かも(Unicode Normalizationの振る舞いが変わったらしい)。GoodReaderとDropboxの同期でエラーが出るようになったのだが、ファイル名に濁点を含むファイルを除いたら同期できるように。
— 山路達也 (@Tats_y) March 29, 2017
原因を調べてみました。
nfdtest.pl
#!/usr/bin/env perl
use strict;
use warnings;
use feature ':all';
use Encode;
use Unicode::Normalize;
use utf8;
binmode STDOUT, ':utf8';
sub listdir {
my $dn = shift;
opendir my $dh, $dn or die "$dn: $!";
my @fn = map { decode_utf8($_) } readdir $dh;
closedir $dh;
map { encode( 'ascii', $_, Encode::FB_PERLQQ ) } @fn;
}
my $dir = shift or die "usage: $0 dirname";
my $fn = 'かばん.txt';
my $fn_nfd = NFD($fn);
say 'fn =', encode( 'ascii', $fn, Encode::FB_PERLQQ );
say 'fn_nfd =', encode( 'ascii', $fn_nfd, Encode::FB_PERLQQ );
eval {
my $path = "$dir/$fn";
open my $fh, '>:utf8', $path or die "$path:$!";
say $fh "食べないでくださーい";
close $fh;
};
say $@ if $@;
eval {
my $path = "$dir/$fn_nfd";
open my $fh, '<:utf8', "$dir/$fn_nfd" or die "$fn:$!";
say <$fh>;
close $fh;
};
say $@ if $@;
{
say for listdir($dir);
unlink "$dir/$fn";
}
上記のscriptを macOS Sierra v10.2.4 で実行した場合、HFS+では
HFS+
fn =\x{304b}\x{3070}\x{3093}.txt
fn_nfd =\x{304b}\x{306f}\x{3099}\x{3093}.txt
食べないでくださーい
.
..
\x{304b}\x{306f}\x{3099}\x{3093}.txt
となりますが、APFSでは
APFS
fn =\x{304b}\x{3070}\x{3093}.txt
fn_nfd =\x{304b}\x{306f}\x{3099}\x{3093}.txt
かばん.txt:No such file or directory at ./test.pl line 32.
.
..
\x{304b}\x{3070}\x{3093}.txt
となります。かばん.txt
というファイルは:
- HFS+
- '\x{304b}\x{3070}\x{3093}.txt'でも'\x{304b}\x{306f}\x{3099}\x{3093} .txt'でも読み書きできる
- 正式名称は
\x{304b}\x{306f}\x{3099}\x{3093}.txt
- APFS
- '\x{304b}\x{3070}\x{3093}.txt'のみ読み書きできる
- 正式名称は
\x{304b}\x{3070}\x{3093}.txt
余談ですが、
- ZFS + normalization=FormD
- 双方読み書きできる
- 正式名称は
\x{304b}\x{3070}\x{3093}.txt
でした。
より深刻なのは後者の、正式名称がNFD分解されるかいなかでしょう。HFS+では分解された方(decomposed)が正式名称ですが、APFS(というよりHFS+以外)は逆です。
ご覧の通り、HFS+の方はどちらのファイル名でもアクセスできるので、常に分解されていない方(composed)を用いればよいだけの話ではあるのですが…
Dan the Man with too Many Filesystems to Support