Edited at

APFSで再燃したNFD問題

More than 1 year has passed since last update.

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