web-dev-qa-db-ja.com

ピボットルート()ドキュメントは、マウント名前空間の機能を予期していましたか?

pivot_root()は、呼び出しプロセスのルートファイルシステムをput_oldディレクトリに移動し、new_rootを呼び出しプロセスの新しいルートファイルシステムにします。

ピボットルート()の一般的な使用法は、システムの起動時に、システムが一時ルートファイルシステム(initrdなど)をマウントし、次に実際のルートファイルシステムをマウントし、最終的に後者をすべての関連プロセスまたはスレッドの現在のルートに変換することです。

ピボットルート()は、古いルートディレクトリを使用するプロセスまたはスレッドの現在のルートと現在の作業ディレクトリを変更する場合と変更しない場合があります。 pivot_root()の呼び出し元は、ルートまたは古いルートに現在の作業ディレクトリがあるプロセスがどちらの場合でも正しく動作することを確認する必要があります。これを確認する簡単な方法は、pivot_root()を呼び出す前に、ルートと現在の作業ディレクトリをnew_rootに変更することです。

上記の段落は、pivot_root()の実装が将来変更される可能性があるため、意図的に曖昧にしています。執筆時点で、pivot_root()は、各プロセスまたはスレッドのルートおよび現在の作業ディレクトリが古いルートディレクトリを指している場合、それらをnew_rootに変更します。これは、カーネルスレッドが、ファイルシステムにアクセスしたことがない場合でも、古いルートディレクトリをルートおよび現在の作業ディレクトリでビジー状態に保つのを防ぐために必要です。将来、カーネルスレッドがファイルシステムへのアクセスを明示的に放棄するメカニズムが存在する可能性があります。これにより、このかなり煩わしいメカニズムをpivot_root()から削除できます。

...

バグ

pivot_root()は、システム内の他のすべてのプロセスのルートおよび現在の作業ディレクトリを変更する必要はありません。

いくつかのよりあいまいなpivot_root()の使用は、すぐに狂気につながる可能性があります。

-man pivot_root、Linuxのmanページ4.15

私は、pivot_root()が呼び出されたときに複数のプロセスが実行されている場合に取り組んでいます。

マンページは、pivot_root()の可能な両方の実装が複数のプロセスのケースをどのように処理できるかについてはあまり明確ではないようです。 S(ystemd)とP(lymouth)の2つのプロセスがあるとします。現在、PとSの両方がルートと作業ディレクトリをnew_rootに変更し、Sがpivot_root()を呼び出します。現在の実装では、これは正常に機能します。

Chroot()を使用して、pivot_root()の前にSとPの両方が「ルートディレクトリを変更する」と言います。しかし、man chrootは、あなたがrootであれば、chroot()の刑務所を離れることができることを示しています(mkdir foo; chroot foo; cd ..; chroot .)。プロセスには2つのルーツが関連付けられていることは明らかです。

  1. 現在のchroot
  2. マウント名前空間のルート

ピボットルート()の後、Sは、マウント名前空間のルートが現在のchrootと等しいことを確認する必要があります。というのは、将来の時点でエスケープできるより深いルートファイルシステムがあった場合、そのルートファイルシステムはビジー状態になり、アンマウントできません。古いルートファイルシステムをアンマウントできるようにすることが、pivot_root()の主な目的だったと思います。

現在、PはSと同じマウント名前空間にあるため、同じことを観察します。

ピボットルート()の代替実装は、呼び出しプロセスを新しい変更されたマウント名前空間に配置するようです。それは有効な読みですか?

(この代替実装は/sbin/pivot_rootほとんど無意味)。

オリジナルのpivot_root()は実際にマウント名前空間よりも古いと思います。このpivot_root()の代替実装の計画が、マウント名前空間のいくつかの機能の必要性を予期していたのか、それともこの要件が見落とされていたのかを知っていますか?

(マウント名前空間も「カーネルスレッドがファイルシステムへのアクセスを明示的に放棄するメカニズム」に非常によく似ていることに注意してください。たとえば、カーネルスレッドはpivot_root()と同等の機能を空のtmpfsに実行できます)。

4
sourcejedi

ピボットルート()の代替実装は、呼び出しプロセスを新しい変更されたマウント名前空間に配置するようです。それは有効な読みですか?

いいえ。IMOこれはあまり明確ではありませんが、はるかに一貫性のある正しい読み方があります。

どちらの実装でも同じでなければならないpivot_root()の重要な部分は次のとおりです。

pivot_root()は、呼び出しプロセスのルートファイルシステムをput_oldディレクトリに移動し、new_rootを呼び出しプロセスの新しいルートファイルシステムにします。

Pivot_root()の本質的な部分は、呼び出しプロセスだけに限定されません。この引用で説明されている操作は、呼び出し元プロセスのマウント名前空間で機能します。同じマウント名前空間内のすべてのプロセスのビューに影響します。

本質的な変更が、作業ディレクトリが古いルートファイルシステムであったそのような2番目のプロセス(またはカーネルスレッド)に与える影響を考慮してください。現在のディレクトリは、古いルートファイルシステムのままです。これは/put_oldマウントポイントをビジー状態に保つため、古いルートファイルシステムをアンマウントすることはできません。

この2番目のプロセスを制御する場合は、manpageに従って、pivot_root()が呼び出される前に作業ディレクトリをnew_rootに設定することで解決します。 pivot_root()が呼び出された後も、その現在のディレクトリは新しいルートファイルシステムのままです。

したがって、プロセスS(ystemd)は、プロセスP(lymouth)に信号を送り、Sがピボットルートを呼び出す前に作業ディレクトリを変更するように構成されています。問題ありません。しかし、 in/。pivot_root()の現在の実装は、カーネルスレッドを処理してくれます。これは、pivot_rootの重要な部分の前に、カーネルスレッドとその他のプロセスの作業ディレクトリをnew_rootに設定することと同じです。 ()。

ただし、pivot_root()の現在の実装では、古い作業ディレクトリが/の場合にのみ、プロセスの作業ディレクトリが変更されます。したがって、これがもたらす違いを実際に確認するのは非常に簡単です。

$ unshare -rm
# cd /tmp    # work in a subdir instead of '/', and pivot_root() will not change it
# /bin/pwd
/tmp
# mount --bind /new-root /new-root
# pivot_root /new-root /new-root/mnt
# /bin/pwd
/mnt/tmp    # see below: if pivot_root had not updated our current chroot, this would still show /tmp

対.

$ unshare -rm
# cd /
# /bin/pwd
/
# ls -lid .
2 dr-xr-xr-x. 19 nfsnobody nfsnobody 4096 Jun 13 01:17 .
# ls -lid /newroot
6424395 dr-xr-xr-x. 20 nfsnobody nfsnobody 4096 May 10 12:53 /new-root
# mount --bind /new-root /new-root
# pivot_root /new-root /new-root/mnt
# /bin/pwd
/
# ls -lid .
6424395 dr-xr-xr-x. 20 nobody nobody 4096 May 10 12:53 .
# ls -lid /
6424395 dr-xr-xr-x. 20 nobody nobody 4096 May 10 12:53 /
# ls -lid /mnt
2 dr-xr-xr-x. 19 nobody nobody 4096 Jun 13 01:17 /mnt

これで、作業ディレクトリで何が起こっているのかがわかりました。chroot()で何が起こっているのかを理解しやすくなりました。ピボット_root()を呼び出すプロセスの現在のchrootは、現在の作業ディレクトリと同様に、元のルートファイルシステムへの参照である可能性があります。

Chdir()+ピボット_root()を実行したがchroot()を忘れた場合、現在のディレクトリはoutside現在のchrootになることに注意してください。現在のディレクトリが現在のchrootの外にある場合、事態は非常に混乱します。この状態でプログラムを実行したくない場合があります。

# cd /
# python
>>> import os
>>> os.chroot("/newroot")
>>> os.system("/bin/pwd")
(unreachable)/
0
>>> os.getcwd()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 2] No such file or directory
>>> os.system("ls -l ./proc/self/cwd")
lrwxrwxrwx. 1 root root 0 Jun 17 13:46 ./proc/self/cwd -> /
0
>>> os.system("ls -lid ./proc/self/cwd/")
2 dr-xr-xr-x. 19 root root 4096 Jun 13 01:17 ./proc/self/cwd/
0
>>> os.system("ls -lid /")
6424395 dr-xr-xr-x. 20 root root 4096 May 10 12:53 /
0

POSIXは、この状況でのpwdまたはgetcwd()の結果を指定しません:)。 POSIXは、getcwd()から「そのようなファイルまたはディレクトリはありません」(ENOENT)エラーが発生する可能性があることを警告しません。 Linuxのマンページでは、作業ディレクトリがリンクされていない場合(たとえば、rmを使用)、このエラーが発生する可能性があると指摘しています。これは非常に良い類似点だと思います。

4
sourcejedi