FUSE ファイルシステムでブロックデバイスをフォーマットするときの EPERM

FUSE ファイルシステムでブロックデバイスをフォーマットするときの EPERM

要約

FUSEファイルシステムでブロックデバイスをフォーマットしようとすると、システムコールで失敗しますEPERMopen権限は次のように設定されています。777必要なioctlはスタブ化されますが、FUSE ハンドラー内からログは出力されません。

背景

私は仮想ディスクイメージを作成するプログラムを書いています。私の基準の1つは、スーパーユーザーアクセスなしで実行できることです。つまり、ループバックデバイスをマウントしたり、ファイルの所有者を変更したり、編集したりすることはできません。設定ファイルこのため、私のアプローチはかなり長ったらしいものになっています。具体的には、ディスク上のさまざまなパーティションをフォーマットするために、システム ツールを使用できるようにしたいのです。そうすることで、はるかに幅広いファイル システムが可能になるからです。これには、VDisk 上のさまざまなパーティションをブロック デバイスとしてシステムに公開することが含まれます。ただし、私が見つけたすべての可能な方法では、nbds またはループバック デバイスのいずれかが必要でした。どちらもスーパー ユーザー アクセスが必要です。

FUSEを自分で実装する

ただし、FUSE でブロック デバイスを実装することは可能であるだけでなく、サポートされています。残念ながら、この件に関するドキュメントはあまり見つかりませんでした。また、私はこれをすべて Rust で行っているため、この件に関するドキュメントはさらに不足しています。

以下の FUSE メソッドを実装しました。

  • init
  • lookup
  • getattr
  • open
  • read
  • write
  • readdir
  • ioctl
    • BLKGETSIZE
    • BLKFLSBUF
    • BLKSSZGET

ファイルシステムの内容を一覧表示し、ディレクトリ/ファイル情報を取得できます。リソースを作成または変更するメソッドはビルドプロセスを通じて実行されるため、意図的に無視しています。

エラー

前述したように、私は許可が拒否されました( EPERM) エラー。呼び出しstraceを実行すると、カーネル側でブロック デバイスへの呼び出しが失敗しているmkfsことがわかります。open完全なstrace結果

execve("/usr/sbin/mkfs.fat", ["mkfs.fat", "out/partitions/EFI"], 0x7ffd42f64ab8 /* 76 vars */) = 0

    --- snip ---

openat(AT_FDCWD, "out/partitions/EFI", O_RDWR|O_EXCL) = -1 EACCES (Permission denied)
write(2, "mkfs.fat: unable to open out/par"..., 63mkfs.fat: unable to open out/partitions/EFI: Permission denied
) = 63
exit_group(1)                           = ?

わかりやすくするために、ディレクトリ構造は次のようになります。

out
├── minimal.qcow2 [raw disk image] (shadows minimal.qcow2 [qcow2 file] with qemu-storage-daemon)
├── partitions
│   ├── EFI [Block device]
│   └── System [Block device]
└── qemu-monitor.sock [UNIX domain socket]

もちろん、すべてのメソッドをトレースするログ機能があります。パーティションをリストするときにはログが表示されますが、フォーマットするときにはログは表示されません。

前述したように、このエラーの実際の原因に関するドキュメントはほとんど見つかりませんでした。

さらなる洞察

@ore​​nkishon の洞察のおかげで、私を困惑させる詳細をさらにいくつか見つけました。

  1. fuser興味深いオプションがいくつか見つかりました:

    • MountOption::Dev 特殊文字とブロックデバイスを有効にする
    • MountOption::DefaultPermission カーネルでの権限チェックを有効にする
    • MountOption::RW 読み書き可能なファイルシステム(どうやらデフォルトのオプションではないようです)

    残念ながら、その組み合わせでは問題は解決しませんでした。

  2. ログ関数はすぐには呼び出されません。何らかのフラッシュ操作に関連付けられているようです。コマンドを実行してmkfs.fat1 つまたは 2 つのログを確認し、IDE に戻ると 1 ページ分のログが表示されます。

    これは、ファイルを生成しているディレクトリがプロジェクトのディレクトリ内にあるため、IDE から見えるためである可能性がありますが、非常に異常なことのように思えます。

  3. 関数内のログaccessは表示されませんが、関数内のログは表示されます。ただし、ディレクトリ外から呼び出されたstatfs場合のみです。mkfsoutそしてあらゆる通話の最初のものですmkfs

    project > cd ./out
    project/out > mkfs.fat partitions/EFI
    mkfs.fat 4.2 (2021-01-31)
    mkfs.fat: unable to open partitions/EFI: Permission denied
    
    # No logs
    
    project > mkfs.fat out/partitions/EFI
    mkfs.fat 4.2 (2021-01-31)
    mkfs.fat: unable to open out/partitions/EFI: Permission denied
    
    # No logs
    project > cargo run ...
    project > mkfs.fat out/partitions
    mkfs.fat 4.2 (2021-01-31)
    mkfs.fat: unable to open out/partitions/EFI: Permission denied
    
    # Logs appear after switching to IDE
    
  1. 今日は初めてこのログメッセージを見ました:
[2024-04-21T16:58:24Z DEBUG fuser::mnt::fuse_pure] fusermount: 
[2024-04-21T16:58:24Z DEBUG fuser::mnt::fuse_pure] fusermount: fusermount3: unsafe option dev ignored

ブロック デバイスと文字デバイスのサポートを追加すると思われる が指定されていますMountOption::Dev。ただし、拒否される理由を説明できないようです。 のパッチ バージョンを使用できることを期待していましたlibfuse3が、どうやらそうではないようです。

役に立つかもしれない追加情報

システム仕様

KDE のシステム情報から直接コピー

  • オペレーティング システム: Kubuntu 23.10
  • KDE プラズマ バージョン: 5.27.8
  • KDE フレームワーク バージョン: 5.110.0
  • バージョン: 5.15.10
  • カーネル バージョン: 6.5.0-28-generic (64 ビット)
  • グラフィックプラットフォーム: Wayland
  • プロセッサー: 32 × 第 13 世代 Intel® Core™ i9-13900
  • メモリ: 31.1 GiB の RAM
  • グラフィックプロセッサ: AMD Radeon RX 7900 XT
  • メーカー: ASUS

一般的な書き込み操作も失敗する

mkfsブロック デバイスをサポートしていない場合に失敗するかどうかを確認することが提案されましたfat32。ただし、他のファイル システムでフォーマットしても同じ結果になるため、これは当てはまらないようです。

mkfsまた、ブロック デバイスに直接書き込む他のすぐに利用できるシステム ユーティリティを現在知らないため、テスト プラットフォームとしても使用していますmkfs。いずれにしても、これを使用するつもりです。

悪いニュース :(

マンページを読んでいるときに、この段落それは私の心を沈ませました。

mount で説明されている一般的なマウント オプションのほとんどがサポートされています (ro、rw、suid、nosuid、dev、nodev、exec、noexec、atime、noatime、sync、async、dirsync)。ファイル システムは、デフォルトで nodev、nosuid を使用してマウントされますが、これは特権ユーザーによってのみ上書きできます。

したがって、これは不可能のようです。それでも、ここでの洞察、ほんのわずかな希望でも、大いに感謝いたします。

答え1

私の理解が正しければ、FUSE ファイルシステムを介してブロックデバイスを公開しますが、これはセキュリティ上の理由から機能しません。

Unix ライクなシステムでは、ファイルは単なるファイル以上のものです。権限のないユーザーに任意のブロック デバイスの作成を許可すると、rootfs デバイスに対応するブロック デバイスを作成することでシステムが危険にさらされる可能性があるため、問題があります。

プログラムが何をするか確認してみるといいでしょうfakeroot。プログラムを実行するプログラムが使用するすべての関数をフックしますlibc。アクセスを必要とする操作root(デバイスの作成など) が行われるたびに、その操作が記録されます。後でfakeroot禁止ファイルの存在を偽装します。

パーティションを公開したいだけなので、同様のアプローチを取り、ioctl偽のデバイス上の一部の を偽装することができますが、実際にはそれは単なる通常のファイルです。これが役立つかどうかはわかりませんが、これを念頭に置いておくのは良いことです。

関連情報