ivot_root()は、呼び出しプロセスのルートファイルシステムをput_oldディレクトリに移動し、new_rootを呼び出しプロセスの新しいルートファイルシステムにします。
ivot_root()の一般的な使用は、システムの起動中にシステムが一時ルートファイルシステム(initrdなど)をマウントし、実際のルートファイルシステムをマウントし、最終的に後者をすべての関連プロセスまたはスレッドの現在のルートにすることです。
ivot_root() は、以前のルートディレクトリを使用しているプロセスや、スレッドの現在のルートディレクトリと現在の作業ディレクトリを変更したり変更したりすることはできません。 ivot_root()呼び出し元は、ルートディレクトリまたは現在の作業ディレクトリが、以前のルートディレクトリにあるプロセスが両方の場合に正しく実行されていることを確認する必要があります。これを保証する簡単な方法は、ivot_root()を呼び出す前にルートディレクトリと現在の作業ディレクトリをnew_rootに変更することです。
上記の段落は将来のivot_root()実装が変更される可能性があるため、意図的にあいまいに書かれています。この記事を書いた時点で、ivot_root()は各プロセスまたはスレッドのルートディレクトリと現在の作業ディレクトリをnew_rootに変更します(以前のルートディレクトリを指している場合)。これは、カーネルスレッドが何らかの方法でファイルシステムにアクセスしなくても、以前のルートディレクトリがルートディレクトリと現在の作業ディレクトリに忙しくなるのを防ぐために必要です。将来、カーネルスレッドがファイルシステムへのすべてのアクセスを明示的に放棄するメカニズムがある可能性があるため、これらのやや妨害的なメカニズムはivot_root()から削除される可能性があります。
...
間違い
ivot_root()は、システム内の他のすべてのプロセスのルートディレクトリと現在の作業ディレクトリを変更する必要はありません。
ivot_root() のより曖昧な使用はすぐに狂気につながる可能性があります。
- man pivot_root
、Linuxのマニュアルページ4.15
私はivot_root()が呼び出されたときに複数のプロセスが実行される状況で作業しています。
マンページでは、2つの可能なivot_root()実装がマルチプロセス状況をどのように処理するかについては明らかではないようです。 S(ystemd)とP(lymouth)という2つのプロセスがあるとします。現在、PとSはルートと作業ディレクトリをnew_rootに変更し、Sはivot_root()を呼び出します。現在の実装によると、これは正常に機能します。
SとPがPivot_root()の前にchroot()を使用して「それらのルートを変更する」とします。しかしman chroot
、私たちが聞いたように、あなたがroot()なら、chroot()刑務所を去ることができますmkdir foo; chroot foo; cd ..; chroot .
。明らかに、プロセスには2つの関連ルートがあります。
- 現在のchroot
- マウントネームスペースのルート
Pivot_root()を実行した後、Sはマウントされた名前空間のルートが現在のchrootと同じであることを確認する必要があります。後でエスケープできるより深いルートファイルシステムがある場合、そのルートファイルシステムは使用中であるため、マウント解除できません。私は以前のルートファイルシステムをアンマウントすることを許可することがivot_root()の主な目的だと思います。
現在、PはSと同じマウント名前空間にあるため、同じことを観察します。
Pivot_root()の代替実装は、呼び出しプロセスを新しく変更されたインストール名前空間に入れるように聞こえます。これが有効な読み取りですか?
(私はこの代替実装が/sbin/pivot_root
ほとんど意味がないことに気づきました)。
私は元のivot_root()が実際にマウントされている名前空間よりも先にあると思います。 Pivot_root()の代替実装のためのこの計画が、マウントされた名前空間のいくつかの機能の必要性を予見するのか、それともこの要件を無視するのかを知っていますか?
(マウントネームスペースは、「カーネルスレッドがファイルシステムへのすべてのアクセスを明示的に放棄するメカニズム」とよく似ています。したがって、カーネルスレッドは空のtmpfsでivot_root()と同等の操作を実行できます。)
答え1
Pivot_root()の代替実装は、呼び出しプロセスを新しく変更されたインストール名前空間に入れるように聞こえます。これが有効な読み取りですか?
いいえ、それほど明確ではありませんが、より一貫して正確な読み取りがあると思います。
ivot_root() の基本部分(両方の実装が同じである必要があります)は次のとおりです。
ivot_root()は、呼び出しプロセスのルートファイルシステムをput_oldディレクトリに移動し、new_rootを呼び出しプロセスの新しいルートファイルシステムにします。
ivot_root() の重要な部分は、呼び出しプロセスに限定されません。この参照で説明されているタスクは、呼び出しプロセスのマウントネームスペースに適用されます。これは、同じマウント名前空間内のすべてのプロセスのビューに影響します。
作業ディレクトリが以前のルートファイルシステムである2番目のプロセス(またはカーネルスレッド)に対するデフォルトの変更の影響を検討してください。現在のディレクトリはまだ古いルートファイルシステムです。これにより、/put_old
マウントポイントが引き続き使用されるため、古いルートファイルシステムをアンマウントできなくなります。
2番目のプロセスを制御する場合は、マンページに従ってPivot_root()を呼び出す前に、そのプロセスの作業ディレクトリをnew_rootに設定することでこの問題を解決できます。 ivot_root()が呼び出された後でも、現在のディレクトリはまだ新しいルートファイルシステムになります。
したがって、プロセスS(ystemd)は、Sがivot_root()を呼び出す前に作業ディレクトリを変更するためにプロセスP(lymouth)にシグナルを送信するように構成されています。大丈夫です。しかし、カーネルスレッドもあります。現在の ivot_root() 実装は、カーネルスレッドの作業ディレクトリと ivot_root() の基本部分の前に/
他のプロセスを設定するのと同じです。new_root
さらに、現在のivot_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()で何が起こっているのかを理解するのは簡単です。 ivot_root()を呼び出すプロセスの現在のchrootは、現在の作業ディレクトリと同様に、元のルートファイルシステムへの参照にすることができます。
chdir()+pivot_root() を実行しても chroot() を忘れた場合、現在のディレクトリは次のようになります。外部現在の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()またはgetcwd()の結果を指定しません:)。 POSIXは、getcwd()で「ファイルまたはディレクトリなし」(ENOENT)エラーが発生する可能性があることを警告しません。 Linuxのマンページには、rm
作業ディレクトリが接続されていない場合(例:を使用して)、このエラーが発生する可能性があることが示されています。私はそれが良いたとえ話だと思います。