パスワードを忘れた? アカウント作成
71641 story

Ext3のコミット間隔を当てにしたアプリケーションは、Ext4でデータロスの恐れあり 92

ストーリー by soara
調整は慎重に 部門より

あるAnonymous Coward 曰く、

4月にリリースされる予定のUbuntu 9.04(開発コード名:Jaunty Jackalope)のオプションで提供されているファイルシステムExt4を使用した場合、Ext3のコミット間隔を当てにしたアプリケーションによってはデータロスが発生する恐れがあるそうだ(本家記事より)。

バグレポートではKDE 4のデスクトップファイルがロードされた後にクラッシュし、KDEコンフィギュレーションなどのデータが全て失われるという状況が報告されている。

Ext4の開発者Ts'o氏によると、Ext4はXFSのように遅延アロケーションが用いられており、新しいデータの書き込みは最大60秒かかることもある。遅延アロケーションはディスク領域の割り当てを効率化し、書き込みパフォーマンスを向上させることが出来るのが利点とされている。KDEやGNOMEのデスクトップアプリケーションはコンフィギュレーションファイルなどの小さなファイルを複数読んだり書き込んだりするが、システムがクラッシュするとExt4ではデータのアロケーションが行われず、ハードディスクに書き込まれることなくファイルが破損してしまう恐れがあるとのこと。

Ts'o氏はバグレポートで回避策を提示しているが、XFSやBtrfでも同様の問題が起きる恐れがあると指摘する。パッチはカーネル2.6.29には間に合わないが、2.6.30のキューに追加されるとのこと。

Ext3ではコミット間隔が5秒であり、またジャーナリング機能はデフォルトで「data=ordered」モードに設定されているが、この動作を当てにしているアプリケーションはExt4ではデータロスが発生する恐れがあるということのようだ。詳細に関してはバグレポート#317781を参照のこと。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by Anonymous Coward on 2009年03月13日 23時15分 (#1530640)

    Ubuntuのext4でのみ問題が起こるようにも読める文章ですが、
    カーネルの問題であって基本的にディストリビューションは関係ないですよね?
    (もちろん独自にパッチをバックポート、とかはあるでしょうが)

    本家記事は元のバグレポートがUbuntuに対してなされてるのがわかるので意味が通るんですが
    こっちでは何が何やら、ミスリードチックです。

    • by Anonymous Coward

      Ext4の問題でも、Ext3のコミット間隔を当てにしたアプリケーションの問題でもなく、Linuxカーネルの問題……ってことですよね?
      何のことなのか訳分からなくてLinux環境ではファイルシステムを意識してソフト開発しないといけないのかなんて思ってました。

      • by greentea (17971) on 2009年03月14日 12時34分 (#1530876) 日記

        まとめてみた。

        直感:
        <落ちたらファイル壊れる部分ここから>
        ファイル開く
        書き込み
        ファイル閉じる
        <落ちたらファイル壊れる部分ここまで>

        実際:
        <落ちたらファイル壊れる部分ここから>
        ファイル開く
        書き込み
        ファイル閉じる
        (システムによって実際にファイルが書き込まれる)
        <落ちたらファイル壊れる部分ここまで>

        そして、ファイル閉じてから実際にファイルが書き込まれるまでの時間が今までは5秒と短かったため、直感に近い状態であった。
        ところがext4などの遅延書き込みを採用しているFSでは、書き込まれるまでの時間が長いため、直感とは違う状況になってしまった。

        そこで、思う。これはアプリケーションのバグではない。実際にいつファイルを書き込むかを考えるのは、一般にはアプリケーションプログラマの仕事ではない。
        それに、そもそもここ [srad.jp]で例示されているプログラムは、Ext3のコミット間隔を当てにしているのではなく、システムがちゃんと落ちずにファイルを書き込んでくれることを当てにしている。
        実際に本当に落ちないかは別として、相当な慎重性を要求されるプログラムを除いて、大抵のプログラムはOSが落ちないで働いてくれることを前提としているし、それは別におかしなことではない。

        一方、FSはOSに非常に近い部分であり、慎重性も要求される。実際、Linuxでは(FUSEとかめんどいので考えないことにすると)FSはOSに組み込まれている。
        なので、今回の問題は、落ちるOSが悪い。が、落ちないOSなんて不可能なので、落ちたときのリスクを考えていないFSが悪い。

        ただし、慎重さが要求されており「書き込み前にOSが落ちちゃいました。けど俺が悪いんじゃないよ。テヘッ」が通用しないようなアプリケーションにおいては、その対策をアプリケーション側でも施さないといけない。先ほどの例 [srad.jp]での3をきっちりとする必要があり、1とか2とかやってるんだったら、それはアプリケーションのバグ。

        --
        1を聞いて0を知れ!
        親コメント
      • by Anonymous Coward

        Ext3のコミット間隔を当てにしたアプリケーションの問題ですよ。

        • by Anonymous Coward
          バグ報告や本家を読んでもイマイチ分からないのですが、逆にコミット間隔を当てにするにはどういったコードを書けば良いのでしょうね。コミット間隔が5秒だからってアプリケーションで何をどうするんでしょうか。
          突然のシステムダウンから復帰したときに、色んなファイルがゼロバイトになる現象のようなんですど、複数のファイルがごっそりゼロバイトになることに未対応であるアプリがあるってだけなんじゃないかと。
          • by BonTf (8325) on 2009年03月14日 6時18分 (#1530766) 日記

            要はPOSIXで制定されている以上の事を求めるプログラマ多すぎ、というのが結論。
            Ext3の挙動が標準であると思い込んでアプリケーション書いていたら、その挙動はExt3固有で POSIXにはそんなこと書いてなかった。
            それで、Ext4じゃその挙動が変わっちゃったという話みたい。

            Ted Tsoがバグレポートの中 [launchpad.net]で書いていることを引用すると、

            1.a) open and read file ~/.kde/foo/bar/baz
            1.b) fd = open("~/.kde/foo/bar/baz", O_WRONLY|O_TRUNC|O_CREAT) --- this truncates the file
            1.c) write(fd, buf-of-new-contents-of-file, size-of-new-contents-of-file)
            1.d) close(fd)

            Slightly more sophisticated application writers will do this:

            2.a) open and read file ~/.kde/foo/bar/baz
            2.b) fd = open("~/.kde/foo/bar/baz.new", O_WRONLY|O_TRUNC|O_CREAT)
            2.c) write(fd, buf-of-new-contents-of-file, size-of-new-contents-of-file)
            2.d) close(fd)
            2.e) rename("~/.kde/foo/bar/baz.new", "~/.kde/foo/bar/baz")

            What emacs (and very sophisticated, careful application writers) will do is this:

            3.a) open and read file ~/.kde/foo/bar/baz
            3.b) fd = open("~/.kde/foo/bar/baz.new", O_WRONLY|O_TRUNC|O_CREAT)
            3.c) write(fd, buf-of-new-contents-of-file, size-of-new-contents-of-file)
            3.d) fsync(fd) --- and check the error return from the fsync
            3.e) close(fd)
            3.f) rename("~/.kde/foo/bar/baz", "~/.kde/foo/bar/baz~") --- this is optional
            3.g) rename("~/.kde/foo/bar/baz.new", "~/.kde/foo/bar/baz")

            データが実際にHDD/SSD等に書き込まれたことを保証するには、1)は論外、2)でも不十分、3)までやらないと駄目だと。

            1)の場合は、ファイルを"O_TRUNC"で開いた時点でもとの内容は全て消えちゃってる。 だから、1.b)以降データが実際に 書き込まれるまでクラッシュが発生したら、リブート後そのファイルは空ですよと。
            2)の場合は、新しいファイルを開くだけまし。 だけど、2.e)以降データ書き込み以前にクラッシュが発生しちゃうと、 やっぱりリブート後には元のファイルは空になっちゃう。 なぜなら、2.e)で、新しいファイルが元のファイルに上書きされちゃうけど、 この時点で、新しいファイルがディスク に書き込まれている保証はないから。
            3)の場合は、3.d)で、データがディスク上に保存されている(もしくは、保存に失敗した)ことが、 fsync()が帰ってきた 時点で保証されると。

            バグ報告や本家を読んでもイマイチ分からないのですが、逆にコミット間隔を当てにするにはどういったコードを書けば良いのでしょうね。

            5秒間隔のコミットに依存したプログラムというのは上記の1)と2)。
            Ext3においては、コミット間隔が短かったのでクラッシュした際のデータロスとかが比較的少かった。
            だけど、Ext4では、そのコミット間隔の長さに由来して、今迄存在したけど顕在化しなかったこの問題が 浮上したと。

            今回、KDEとGNOMEのドットファイル等でこの問題が発覚した理由は、 アプリケーションがこれらのファイルを 比較的高頻度で1)や2)の手法を使って書き変えているから。
            んで、ディスクへの書き込みを保証しなくてはいけないデータにはfsync()か fdatasync()を 使えとのこと。
            大量のfsync()に伴うパフォーマンスの減退がいやならば、sqliteとか使って纏めて 書き込めよっていうのがTed Tsoのお勧め。

            複数のファイルがごっそりゼロバイトになることに未対応であるアプリがあるってだけなんじゃないかと。

            これはまとはずれだと思う。

            親コメント
            • by taka2 (14791) on 2009年03月14日 10時07分 (#1530831) ホームページ 日記

              たとえ、ファイル書き込みが完全に同期していたとしても、

              1.a) open and read file ~/.kde/foo/bar/baz
              1.b) fd = open("~/.kde/foo/bar/baz", O_WRONLY|O_TRUNC|O_CREAT) --- this truncates the file
              1.c) write(fd, buf-of-new-contents-of-file, size-of-new-contents-of-file)
              1.d) close(fd)

              この方法だと、write中にクラッシュすればファイルが失われますし、
              OSがクラッシュしなくても、該当アプリだけが死んだ場合でもアウト。

              でも、世の中には、これを採用しているアプリが結構多いような気がします。最低限でも、

              3.a) open and read file ~/.kde/foo/bar/baz
              3.b) fd = open("~/.kde/foo/bar/baz.new", O_WRONLY|O_TRUNC|O_CREAT)
              3.c) write(fd, buf-of-new-contents-of-file, size-of-new-contents-of-file)
              3.d) fsync(fd) --- and check the error return from the fsync
              3.e) close(fd)
              3.f) rename("~/.kde/foo/bar/baz", "~/.kde/foo/bar/baz~") --- this is optional
              3.g) rename("~/.kde/foo/bar/baz.new", "~/.kde/foo/bar/baz")

              ぐらいはしなきゃダメじゃないかな。3.fは必須。fsync無しなら高レベルI/Oでも可能な範囲だし、アプリそのものの死亡に対してはほぼ安全です。
              fとgの間で死んだら困りものですから、読み込み時に「バックアップファイルしか無い時は、バックアップファイルから読み込む」ぐらいのことはした方がいいと思いますが…

              私がこの問題にぶち当たったのは mrtg 。
              元のログファイルは*.oldという名前で残っているのですが、
              先にリネームしてから、新しいログファイルを書き出しているっぽく、
              そこで落ちたら0バイトのログファイルが出来ます。
              次回実行までにログが消えたのに気がつかなかったら、次の書き出しで、空のログファイルから読み込んで処理が行われて、バックアップファイルも空になってしまいます。

              そのせいで何年かため込んだログがばっさり消えてしまいました。

              親コメント
              • 3.f) rename("~/.kde/foo/bar/baz", "~/.kde/foo/bar/baz~") --- this is optional 3.g) rename("~/.kde/foo/bar/baz.new", "~/.kde/foo/bar/baz")

                ぐらいはしなきゃダメじゃないかな。3.fは必須。fsync無しなら高レベルI/Oでも可能な範囲だし、アプリそのものの死亡に対してはほぼ安全です。 fとgの間で死んだら困りものですから、読み込み時に「バックアップファイルしか無い時は、バックアップファイルから読み込む」ぐらいのことはした方がいいと思いますが…

                • 3.f.1) unlink("~/.kde/foo/bar/baz~");
                • 3.f.2) link("~/.kde/foo/bar/baz", "~/.kde/foo/bar/baz~");

                とかすればokかな。linkできないときは自前でコピー。

                --
                yoshfuji
                親コメント
            • by thorin (14200) on 2009年03月14日 12時35分 (#1530879)
              細かい点だけど 3.f) は普通は
              3.f) link("~/.kde/foo/bar/baz", "~/.kde/foo/bar/baz~") --- this is optional
              とするのが常識じゃないかな? 3.f) と 3.g) の間に割込まれる可能性も考慮しないと!

              どこか別の場所で事前にロックとって排他しているのでない場合には、3.b) の
              O_TRUNC とかも論外な気がする。NFS まで考えると気休めけど O_EXCL くらいは
              つけときたい所。
              親コメント
          • by Anonymous Coward

            ひとつにはファイルシステムとカーネルは切っても切れない関係にあること。
            んで、Linuxの標準Cライブラリであるglibcなどが狭義のシステムコールを元に書かれている以上、
            遅延書き込みなどといったファイルシステム上の現象はglibcなんかの上に書かれた
            アプリケーションすべてに影響するってこと。
            なのでなにかとLinuxが叩かれる原因になっている遅延書き込みを避けるために
            ファイル書き込みを標準Cライブラリのfopen()なんかを使わず
            直接システムコールを叩くようなコードが書かれたりしたりして、その場合、
            ファイルシステム差を吸収してくれるライブラリを使っていないのだから
            ファイルシステムの実装いかんによって動かないコードが出てきたりする。

            …てことじゃないのかと。

  • STOPキーを押してから電源を切ればいいんだっけ。

    • Re:なんだっけ (スコア:2, おもしろおかしい)

      by kicchy (4711) on 2009年03月14日 3時07分 (#1530736)

      違うよ、リセットボタン押しながら電源を切るんだ。

      # 冒険の書を失わないための基本だろ

      親コメント
    • by Anonymous Coward
      N」「8」「0」を押しながらリセットすると
      データを保存できるんだよ。
  • そういう配慮がないアプリは以前からたくさんあったが問題にはなってなかった. システムがクラッシュしなければいいだけのこと. 問題はそんな注意をアプリケーションプログラマに負わせるほどクラッシュが多いカーネルでは?

    「停電でもない限り1年でも2年でもクラッシュせずに動き続けるのが当然」くらいの気概をもってほしいものだ.

  • by oltio (3848) on 2009年03月14日 9時44分 (#1530825) 日記

    普段XFSを使用していますが、何度か経験しています。ThinkPad や Let's Note で何度かサスペンドから復帰できなくなったり USB デバイスドライバがらみでフリーズした際に、ドットファイルの類を喪失していることがたまにありました。多かったのは、Sylpheed の設定ファイル各種と、uim-skk の学習結果。

    #それでも XFS の使用をやめなかったのは移行が面倒だったため。

  • by Anonymous Coward on 2009年03月13日 22時59分 (#1530627)
    おかしなドライバ突っ込まない限り遅延書き込みなんてあぶなっかしい真似なんかしません。クラッシュでスクリーンがブルーに染まることもありません。
  • by Anonymous Coward on 2009年03月13日 23時13分 (#1530636)
    おかしなOS使わない限り遅延書き込みなんてあぶなっかしい真似なんかしません。
    クラッシュでスクリーンがブルーに染まることもありません。
    • by pharmer (32222) on 2009年03月14日 1時42分 (#1530699)

      >おかしなOS使わない限り遅延書き込みなんてあぶなっかしい真似なんかしません。

      最近は、遅延書き込みしないOSの方が少ないです。
      もちろん *BSD も遅延書き込みします。

      遅延書き込み後にファイルシステムのメタデータを危険に
      さらさないようにジャーナリングしたり、ソフトアップデートしたり
      するので、最近はクラッシュ後にファイルシステムが崩壊することは
      少なくなりました。

      それ以上の保護を望む人は open() 時に同期書き込みを
      指示するフラグを設定するか、
      書き込み後に、fsync() を呼ぶと良いはずです。

      ただし、Linux はファイルシステム側のオプションも
      チェックしないと自分の意図と違う動きをするかも。

      間違ってたら指摘して下さい > 教祖様

      親コメント
    • by Anonymous Coward

      あれれー? [wikipedia.org]

  • by Anonymous Coward on 2009年03月13日 23時36分 (#1530653)
    システムファイルを書き込んだ後はsync()を呼べばいいてこと?
    • by haratake (365) on 2009年03月13日 23時51分 (#1530660)

      system("sync; sync; sync");でしょ

      親コメント
      • Re:sync (スコア:1, おもしろおかしい)

        by Anonymous Coward on 2009年03月14日 12時33分 (#1530875)

        system("sync; sync; sync");でしょ

        最近のコンパイラーは賢いので、その程度では機械語を生成しません。

        親コメント
        • by Tsann (15931) on 2009年03月14日 17時35分 (#1530981)
          機械語を生成しないコンパイラっていったい…javacみたいなのを想像してます?
          加えてsystem(3)もsync(1)もわかってないコメントですね。
          親コメント
          • わかっていないのは君だ。

            最近のコンパイラは恐ろしく、

            printf( "hello world\n" );

            と書いても、

            puts( "hello world\n" );

            というコードがアセンブラレベルでは出てくる。

            例:

            C:
            #include <stdio.h>
             
            int
            main( int argc, char *argv[] )
            {
                printf( "hello world\n" );
            }

            出力結果: gcc (GCC) 3.4.4
                .file    "afo.c"
                .def    ___main;    .scl    2;    .type    32;    .endef
                .section .rdata,"dr"
            LC0:
                .ascii "hello world\0"
                .text
                .p2align 4,,15
            .globl _main
                .def    _main;    .scl    2;    .type    32;    .endef
            _main:
                pushl    %ebp
                movl    $16, %eax
                movl    %esp, %ebp
                subl    $8, %esp
                andl    $-16, %esp
                call    __alloca
                call    ___main
                movl    $LC0, (%esp)
                call    _puts
                leave
                ret
                .def    _puts;    .scl    3;    .type    32;    .endef

            以上を理解したうえで。

            system("sync; sync; sync");

            と言うコードに対して「あぁ、それは何もしなくてよいのだな。どうせ非同期か同期化なんて、ちゃんと判っているユーザーはいないし」とコンパイラが判断して、何も出力しない、というギャグだ。

            --
            fjの教祖様
            親コメント
            • by Tsann (15931) on 2009年03月14日 21時41分 (#1531039)
              そこまで最適化できるとは知りませんでした。
              「機械語を生成しない」に釣られてたわけですね。
              例に挙げられているprintf()も機械語(正確にはアセンブリですが)を出力しているわけですし。
              最適化で消されるとかの表現だったらギャグと理解できたかもですが。

              どう釣られてたかというと「元ACさんが
              system("sync; sync; sync");

              asm("sync; sync; sync");
              と誤解しているのでは?」と私が誤解してました。もちろんsyncなどという機械語は存在しませんが。
              まさかsystem()呼び出しを消してしまう壮大なギャグだとは…。
              親コメント
    • by Anonymous Coward
      3回繰り返さないと神に祈りは通じません。
      • by Anonymous Coward
        sync は2回で充分だって教祖さまが言ってた
      • by Anonymous Coward

        2回の人はそもそも神に祈ってないので問題ありません。

    • by Anonymous Coward

      この例だとユーザの設定ファイルだからなあ・・・

      面倒なのは、設定が複数のファイルの連携になっている場合で、
      片方が壊れて片方が正常に更新された場合に
      どうしょうもなくなりますな。

      Vistaにトランザクション付きファイルシステムが導入された理由って
      この辺だったりするのかな、とか思った。

  • by Anonymous Coward on 2009年03月13日 23時56分 (#1530664)
    流行のRAMデーターベースよりもマシじゃないの?
typodupeerror

目玉の数さえ十分あれば、どんなバグも深刻ではない -- Eric Raymond

読み込み中...