APFSで再燃したNFD問題

  • 45
    いいね
  • 1
    コメント

iOS 10.3 でAPFSが本番投入されました。それでiPhoneやiPadが文鎮化するなどの深刻な問題はみられなかったものの、やはり無問題とはいかなかったようで。

原因を調べてみました。

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