where was "ゆ〜すけの日記" in the old days…
RSS icon Email icon Home icon
  • ドラえもん問題

    Posted on April 3rd, 2009 Yusuke 1 comment

    名前は僕が勝手につけました。ドラえもんを見ようとしたときに発覚した問題だから。

    picture-9

    あれ、「ドラえもん」フォルダが2つ存在する?ファイルシステム上でこんなことがありうるのだろうか。

    今までファイル共有ではCIFS(Samba)を使っていたのだけれど、Windowsがいないのにこいつを積極的に使う理由もない(最後が.(ドット)で終わるフォルダが扱えなかったってこともあった)。かといってAFPを使うにはnetatalkのインストールをしなきゃいけないし、微妙だなぁと思っていた。でもよく考えたらOS XはUNIXなのだからNFSが使えるじゃん!ってことで、この前から使ってた。NFSv4もいずれ試したいしね。

    でも、たまにファイルが開けなくなって、規則性を見ようとしたら、あんがい簡単に判明した。ファイル名に濁点、半濁点がある時で、これが原因となるNo.1といえば、そう、Unicode Normalization!(シラネーよ)

    • 正規化形式 D (Normalization Form D, NFD)
    • 正規化形式 C (Normalization Form C, NFC)

    OS XはNFDを使う。Windowsやその他はNFCを使う。NFCはある条件で問題が発生する可能性があると聞いたことがある。復習終わり。

    picture-10

    両方ともドラえもんだが。。

    左がNFCで表された文字、「ド」を構成しているのは”30C9″。右がNFDで表された文字、「ド」は”30C8″ “3099”の2つで構成されている。だから見た目が一緒でも、ファイルシステム上全く一緒というわけではないファイル名。今までサーバ側では、おそらくNFCで記録されていたと推定される(前から単純コピーしてるため)。そして、OS X のSamba clientか、サーバのSamba daemonのどっちかで吸収が入っていたんだろう。 NFS daemonやclientはそのへんの処理をまったく行っていないと思われる。

    さて、いくらファイルシステム上できるからと言って、許してしまうと困る。アプリケーションがNFCとNFDをそれぞれ好き勝手に使うと、きちんとしたデータ処理が行えない。前述のファイルが開けない理由もここにあると推定される。「NFC:ドラえもん」をFinderは理解するが、Quicktime Playerなどの他のアプリケーションに渡す際には、ただ単に「ドラえもん」と渡している(そのときは無意識にNFDを使っている)。そして、「ドラえもん」を受け取ったQuicktime Playerがリクエストを送る際には「NFD:ドラえもん」としてリクエストするから、そんなのないよとなる。

    やはりファイルシステム上で制限するのが妥当だろう。ZFSマニュアルで Normalization を検索してみる。

    picture-11

    Sorry, English only.

    ありました。さすがZFS。もう一度言う。さすがZFS。
    (ZFSに関しては、また別記事を書こうと思う)

    normalization = formD をつければ解決しそうである。自動的に utf8only も on にセットされるよって書いてあります。正直ZFSを扱いだした当初は何に使うのか分からなかった。問題にぶち当たって初めて人は理解するのですね(?)。

    下の図みたいに、NFCで書き込もうとしても、NFDで記録される前提なので、その時点で変換が入る。ファイル名重複もこの段階で分かる。

    nfs-normalization

    上の白い四角の範囲がOpenSolaris Serverの範囲ですよ。

    ということで早速変更!

    # zfs set normalization=formD spool/media
    cannot set property for 'spool/media': 'normalization' is readonly

    あ。。そうか、ボリュームに格納される時に変換だもんな。現行混じってるのにどうしようもないよ。てかさっきのマニュアルに、ファイルシステム/ボリューム作成後に変えられないですよ(in English)、って書いてあったよ。

    ちょっとテストしてみる。

    (作ってから変更)
    # zfs create spool/normalization.none
    # zfs get utf8only,normalization spool/normalization.none
    NAME                      PROPERTY       VALUE                     SOURCE
    spool/normalization.none  utf8only       off                       -
    spool/normalization.none  normalization  none                      -

    # zfs set normalization=formD spool/normalization.none
    cannot set property for 'spool/normalization.none': 'normalization' is readonly

    (作る時に指定)
    # zfs create -o normalization=formD spool/normalization.formD
    # zfs get utf8only,normalization spool/normalization.formD
    NAME                       PROPERTY       VALUE                      SOURCE
    spool/normalization.formD  utf8only       on                         -
    spool/normalization.formD  normalization  formD                      -

    normalization.formD のディレクトリをNFS共有してNFC:ドラえもん、NFD:ドラえもんを書き込んでみると、後から書いたほうは上書き警告が出た。なのでうまくいっている。この他にもformC, formKC, formKDを作成して試してみたが、いずれもファイル名重複を避ける目的では回避できた。デフォルトのnoneだけが、特段制限されないので、混在の可能性があるということだ。

    チャットで後輩に愚痴ったら、convmv (perl script)を紹介された。とりあえずでいいのであれば、ファイル名の変換が可能だ。もちろん、新たに作られるファイルは無理だが。

    # convmv -f UTF-8 -t UTF-8 --nfd --notest --nosmart --exec "echo #1" -r [ディレクトリ名]

    やってることはmvを再帰的にやるだけ。notest をつけないと、ただ単に候補を表示して終わる。exec を付けるとmv 一回毎に後に続くコマンドを行う。nosmart を付けないとうまく変換できないものがあった。

    また、古いファイルシステムの制約に合わせているのか、ファイル名の長さが255byteに制限されていた。convmv の中の $maxfilenamelength1024にしとけばいいだろう。これでとりあえず現行のファイル名はすべてNFD形式になりました。

    まぁ、最終的にはボリュームを作りなおしですな。2TB近くあるので、退避場所を確保しないと。。どこかの本番環境でこういうの作るときには、きちんと要件(今回ならUnicodeファイル名完全対応?)に該当する部分のマニュアルに目を通して理解しましょうね。じゃないとイタい目を見るかも。

     

    1 responses to “ドラえもん問題” RSS icon

    • 5年以上前の記事にReplyするのも気が引けますが,man zfs によると,

      > File names are always stored unmodified, names are normalized as part of any comparison process.
      “ファイル名は常にそのまま記憶され,ファイル名の正規化はあらゆる比較処理の一環として行われる.”(拙訳)

      とありますから,
      > ボリュームに格納される時に変換
      されるという認識は誤りであるように思われます.

      なお,私はMacを所有しておりませんので未検証ですが,
      MacOSX Lion からはmount_nfsのoptionsに”nfc”が追加されたため,
      これを指定すればClient側でNFCに変換してから NFS Server に送信するようになるそうです.


    Leave a reply