Contents

【Linuxユーザー向け】 Linux版Time Machineが欲しかったのでBtrfs用UIを作ってみた話 【ツール紹介】

この記事にはEnglish版もあります。

btrfsのスナップショットから、ファイルの過去の版をブラウザで辿って復元できるツール「btrfs-timeline」を作ったので紹介します。 MacのTime Machineのような感覚で、「このファイル、先月の時点ではどうなってたっけ?」をブラウザから気軽に確認・復元できるようにするのが狙いです。

https://raw.githubusercontent.com/akivajp/btrfs-timeline/main/docs/screenshots/screenshot-history-ja.png

なんで作ったのか

スナップショットはあるのに、取り戻すのが大変

  • 自分は自作のサーバーを何台か運用していて、どれもbtrfsでRAIDを組んでいます
    • btrfsはRAIDの構成が柔軟で、サイズの違うディスクを組み合わせても無駄なく使えるので、かなり気に入っています
  • snapperなどで自動スナップショットも取っているので、すべてのファイルのすべての過去の版は、実はもう手元にある状態です
  • ところが、いざ誤って消したり上書きしたりしたファイルを1つ取り戻そうとすると、
    • スナップショットがどこに置かれているかを知っていて
    • どのスナップショットが十分古いかを当てて
    • コマンドラインでファイルを見比べる
  • ……という作業になります。できることはできるのですが、正直、玄人向けの作業です
    • そして、こういうときのためにスナップショットを取っているわけで、ここが面倒だと本末転倒です

既存のツールを探してみたが……

作る前に、既存のツールを一通り調べてみました。

ツールできること惜しいところ
httmZFS/btrfsのファイル履歴を対話的に辿れるCLI / TUIのみ
Timeshiftシステム全体のスナップショットと復元システムを丸ごと戻す用途が中心で、ファイルを1つ選んで辿るものではない
Btrfs Assistant, Snapper GUIスナップショットの管理デスクトップアプリで、ファイル単位の履歴ではない
Samba vfs_shadow_copy2Windowsエクスプローラの「以前のバージョン」Windowsクライアントが前提
Rockstor, OpenMediaVaultbtrfs対応のNAS向けWeb UINAS専用ディストリビューション。ロールバックは共有単位
Cockpitbtrfsのファイルシステム・サブボリュームの作成スナップショットの閲覧は無し
  • 機能的に一番近いのはhttmで、とても良くできているのですが、ターミナルで使うツールです
  • GitHubでbtrfsのWeb UIを探してみても、個人の習作レベルのものや、何年も更新が止まっているものばかりでした
  • つまり、「ブラウザを開いて、パスを指定して、時間を遡る」というものが見当たらなかったので、作ってみることにしました

どんなツール?

  • btrfsのスナップショットを見つけて、指定したファイルの過去の版の一覧を出します
  • 各版の中身をプレビューしたり、現在の内容との差分を見たりして、ボタン1つで復元できます
  • 使い方は3通りあります
    • CLI(btrfs-timeline history など)
    • スタンドアロンのWeb UI(btrfs-timeline serve で起動)
    • Cockpitのモジュール(Cockpitを使っている場合は、その中から同じ画面を開ける)
  • snapperのレイアウト(.snapshots/<番号>/snapshot)のほか、btrbkやTimeshift、手動運用のような .snapshots/<名前> 形式にも対応しています

使ってみる

インストール

$ pipx install 'btrfs-timeline[web]'   # Web UI付き
$ pipx install btrfs-timeline          # CLIだけなら依存パッケージ無し
  • Python 3.9以降とLinuxが必要です
  • 履歴を見るだけなら、btrfs-progs も不要です

CLIで履歴を見る

$ btrfs-timeline history ~/.gitconfig
/home/orenoaccount/.gitconfig
  # 最初の観測           最後の観測               サイズ  SNAP数 状態
  1 2024-10-02 02:00:08  2025-01-01 00:00:00           -       3 (存在しない)
  2 2025-12-01 00:00:08  2026-03-01 00:00:00       268 B       4 ok
  3 2026-04-01 00:00:00  2026-09-23 01:00:00       297 B      28 ok
  4 -                    -                         297 B       - live
  • 35個のスナップショットが、意味のある3つの版と、ファイルが作られる前の空白期間の4行にまとまっています
  • ここがこのツールの肝で、詳しくは後で書きます

Web UIで見る

$ btrfs-timeline serve                       # http://127.0.0.1:8088/
$ btrfs-timeline serve --root ~/Documents    # このディレクトリ配下だけを見せる
$ btrfs-timeline serve --read-only           # 閲覧のみ。復元は無効
  • 左でディレクトリを辿って、ファイルをクリックすると、右に版の一覧が出ます
  • ディレクトリをクリックして版を選ぶと、左の一覧がその時点の内容に切り替わります
    • その後に削除されたファイルは、取り消し線付きでここに現れます。今のファイルシステムには存在しないので、消してしまったファイルを探すときはここからたどります
  • プレビューのペインはそのまま差分表示にもなります

復元する

$ btrfs-timeline restore ~/notes.md --index 2    # 版番号を指定して復元
$ btrfs-timeline restore ~/notes.md --dry-run    # 何が起きるかだけ確認
  • Web UIからなら、ボタン1つです。ただし、実行前に必ず「どこに何を書くのか」を見せてから確認を取ります

こだわったところ

履歴を見るのにrootが要らない

  • スナップショットを探すのに、普通なら btrfs subvolume list を使いたくなるのですが、これはrootが必要です
  • 一方で、スナップショットの中のファイル自体は、普通のパーミッションで読めます
  • そこで、/proc/self/mountinfo とディレクトリの走査だけでスナップショットを見つけるようにしました
    • これで、履歴の閲覧にrootが不要になりました。Webサーバーをrootで動かさずに済むので、安心して使えます

同じ内容の版はまとめる

  • btrfsはCoW(コピーオンライト)なので、変更していないファイルも、スナップショットの数だけそのまま並びます
    • 自分の環境では、あるファイルは35個のスナップショットに現れていましたが、中身はすべて同じ1つの版でした
  • 35行並べられても、どれが違うのか分からないので、mtimeとサイズが同じものは1つの版にまとめています
  • さらに、ファイルが存在しなかった期間も1つの項目として残しています
    • そうしないと、一度消して作り直したファイルの履歴が、ずっとつながっていたように見えてしまうためです

復元で今のファイルを壊さない

  • 履歴ツールで一番まずい事故は、「古い版を取り戻そうとして、今の内容を壊す」ことだと思います
  • なので、既定では何も上書きしません。復元先は元のファイルの隣の notes.20260401T000008.md のような別名になります
    • 上書きしたいときは --in-place を明示します。そのときも、今の内容は退避してから上書きします
  • 復元はコピーではなくreflink(btrfsのデータ共有)で行うので、ファイルが大きくても一瞬で終わり、容量も増えません

Web UIを誰でも触れる状態にしない

  • 既定ではループバック(127.0.0.1)でしか待ち受けません
  • それ以外のアドレスで起動しようとすると、--auth USER:PASSWORD を付けない限り起動を拒否します
    • 誰でもファイルを読めて、上書きまでできてしまう状態を、うっかり作らないためです

Cockpitでも同じ画面を使う

  • すでにCockpitを使っている場合は、同じ画面をCockpitの中から開けます

    $ btrfs-timeline cockpit install
    
  • Cockpitのモジュールは、中身が静的ファイルだけで、サーバー側の処理を持てません

    • なので、ツールの中心は「--json で結果を返すCLI」にして、Web UIもCockpitモジュールも同じCLIの結果を使う作りにしました
    • 画面のコードは共通で、違うのはデータの取り方を書いた transport.js だけです(Web UIはHTTP、Cockpitは cockpit.spawn でCLIを呼ぶ)

おまけ: ダッシュボードとメンテナンス

  • ファイル履歴だけでなく、NASっぽい使い方のために、デバイスのダッシュボードも付けました

https://raw.githubusercontent.com/akivajp/btrfs-timeline/main/docs/screenshots/screenshot-devices-ja.png

  • 各ファイルシステムのデバイス構成、RAIDプロファイル、デバイスごとのエラーカウンタ、型番と温度などを一覧できます(これもroot不要です)
  • scrubやbalance、デバイスの追加・取り外し・置換もCLIから実行できます
    • 操作ごとに「安全 / 状態を変更 / 危険」の危険度を表示して、実行するコマンドをそのまま見せてから確認を取ります
    • 特に、失敗するとデータを失いうるデバイスの置換などは、対象のデバイス名を入力しないと実行できないようにしています(GitHubでリポジトリを消すときに名前を入力させるのと同じ考え方です)

まとめ

  • btrfsで自動スナップショットを取っていれば、過去の版は既に手元にある。足りないのは、それを気軽に辿る手段だった
  • btrfs-timelineは、ブラウザ(またはCLI、Cockpit)からファイルの過去の版を辿って、今のファイルを壊さずに復元できる
  • 履歴の閲覧にrootは要らない

まだ開発初期ですが、btrfsを使っている方はぜひ試してみてください。不具合報告や要望は、GitHubのIssueで歓迎しています。

この記事は qiita.com にも掲載しています。