かつての Linux® のファイルシステムは、かなり単純なツリーでした。当時は、プロセスがそれ自体に対して、ファイルシステム・ツリーのルートがシステムのファイルシステム・ルートのサブディレクトリーであるかのように chroot() を実行できました。また、ツリー内でのノードの位置にかかわらず、あらゆるノードで新規デバイスのファイルシステムをオーバーレイすることも可能でした。
2000年、Al Viro により Linux にバインド・マウントとファイルシステム名前空間が導入されました。
- バインド・マウントは、任意のファイルやディレクトリーを他の場所からアクセスできるようにします。
- ファイルシステム名前空間は、異なるプロセスに関連付けられた完全に独立したファイルシステムのツリーです。
プロセスが clone(2) で現行ファイルシステム・ツリーのコピーを要求すると (詳細は「参考文献」を参照)、新規プロセスには元のプロセスとまったく同じファイルシステム・ツリーのコピーが作成されます。このコピーが作成された後は、そのツリーのいずれかのコピーでマウント・アクションが行われても、そのアクションが他のコピーに反映されることはありません。
プロセス単位のファイルシステム名前空間は理論上は非常に有効ですが、実際にはファイルシステム名前空間がプロセス単位で完全に分離されていると、あまりにも大きな制約が生じました。プロセスがシステムのファイルシステム名前空間のコピーの複製を行うと、元のファイルシステム名前空間で行われたマウントはユーザーのコピーに反映されなくなるため、すでに実行中のシステム・デーモンはユーザー用に CD-ROM を自動マウントすることができないのです。
こうした状況に対処するために 2006年に導入されたのが、マウント・オブジェクト間の関係を定義するマウント・プロパゲーションです。このマウント・オブジェクト間の関係によって、マウント・オブジェクトで行われたマウント・イベントがシステム内の他のマウント・オブジェクトにどのように伝播されるかが決まります。
- 2 つのマウント・オブジェクト間に共有 (shared) 関係がある場合、双方のマウント・オブジェクトが相互にマウント・イベントを伝播し合います。
- 2 つのマウント・オブジェクト間に従属 (slave) 関係がある場合、従属するマウント・オブジェクトだけが他方のマウント・オブジェクトでのマウント・イベントを受け取り、従属するマウント・オブジェクトがもう一方にマウント・イベントを伝播することはありません。
イベントを伝播するマウント・オブジェクトは shared マウントと呼ばれ、マウント・イベントを受け取るマウント・オブジェクトは slave マウントと呼ばれます。マウント・イベントを伝播することも、受け取ることもないマウント・オブジェクトは、private マウントと呼ばれます。もう 1 つ、unbindable マウントと呼ばれる特殊なマウント・オブジェクトもあります。これは private マウントに似たマウント・オブジェクトですが、それ自体をバインド・マウントすることはできません。unbindable マウントは特に、マウント・オブジェクトの急激な増加を抑制するのに役立ちます (これについては、後で詳しく説明します)。
デフォルトでは、すべてのマウントが private として設定されます。マウント・オブジェクトを shared マウントにするには、以下のコマンドで明示的に shared のマークを付けます。
mount --make-shared <mount-object> |
例えば、/ に位置するマウントを shared にするには、以下のコマンドを実行します。
mount --make-shared / |
shared マウントから複製されるマウント・オブジェクトも shared マウントになるため、この 2 つのマウント・オブジェクトは互いに伝播し合います。
マウント・オブジェクトに slave のマークを付けるには、以下のコマンドを実行して、shared マウントを slave マウントに明示的に変換します。
mount --make-slave <shared-mount-object> |
slave マウントから複製されるマウント・オブジェクトも同じく slave マウントになります。そのマスターは、元の slave マウントのマスターと同じです。
マウント・オブジェクトに private のマークを付けるには、以下のコマンドを実行します。
mount --make-private <mount-object> |
マウント・オブジェクトに unbindable のマークを付けるには、以下のコマンドを実行します。
mount --make-unbindable <mount-object> |
最後に付け加えると、これらのコマンドはいずれも再帰的に適用することができます。つまり、ターゲット・マウントの配下のすべてのマウントにコマンドが適用されます。
以下はその一例です。
mount --make-rshared / |
上記のコマンドは、/ の配下にあるすべてのマウントを shared マウントに変換します。
リスト 1 は PAM (Pluggable Authentication Module) からの抜粋で、ここでは root 以外のすべてのユーザーを専用の名前空間に配置しています。/tmp/priv/USER ディレクトリーが存在する場合、このディレクトリーがユーザーの専用の名前空間内の /tmp にバインド・マウントされます。
リスト 1. ログイン別名前空間に対応した PAM からの抜粋
#define DIRNAMSZ 200
int handle_login(const char *user)
{
int ret = 0;
struct stat statbuf;
char dirnam[DIRNAMSZ];
if (strcmp(user, "root") == 0)
return PAM_SUCCESS;
ret = unshare(CLONE_NEWNS);
if (ret) {
mysyslog(LOG_ERR, "failed to unshare mounts for %s\n", user);
return PAM_SESSION_ERR;
}
snprintf(dirnam, DIRNAMSZ, "/tmp/priv/%s", user);
ret = stat(dirnam, &statbuf);
if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
ret = mount(dirnam, "/tmp", "none", MS_BIND, NULL);
if (ret) {
mysyslog(LOG_ERR, "failed to mount tmp for %s\n", user);
return PAM_SESSION_ERR;
}
} else
mysyslog(LOG_INFO, "No private /tmp for user %s\n", user);
return PAM_SUCCESS;
}
int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
const char *PAM_user = NULL;
char *fnam;
int ret;
ret = pam_get_user(pamh, &PAM_user, NULL);
if (ret != PAM_SUCCESS) {
mysyslog(LOG_ERR, "PAM-NS: couldn't get user\n");
return PAM_SESSION_ERR;
}
return handle_login(PAM_user);
}
|
この PAM モジュールを利用するには、まず、「ダウンロード」セクションから完全な pam_ns.c ファイルとこれに対応する makefile の一式をダウンロードしてコンパイルします。その結果作成された pam_ns.so ファイルを /lib/security/ にコピーした後、以下のエントリーを追加します。
session required pam_ns.so |
上記のエントリーは、/etc/pam.d/login および /etc/pam.d/sshd に追加します。それが終わったら、ユーザー USER の専用の tmp を作成します。
mkdir /tmp/priv chmod 000 /tmp/priv mkdir /tmp/priv/USER chown -R USER /tmp/priv/USER |
ここで、一方の端末では root としてログインし、もう一方の端末では USER としてログインします。USER として以下を実行してください。
touch /tmp/ab ls /tmp |
USER の /tmp には、新しく作成されたファイルだけが含まれることに注目してください。
次に、root 端末で /tmp に含まれるコンテンツのリストを取得します。このディレクトリーには、他のファイルは含まれていても、/tmp/ab は含まれていません。これは、/tmp ディレクトリーはまさに独立したディレクトリーだからです。root 端末から USER の /tmp を検索するには以下を入力します。
ls /tmp/priv/USER |
ab ファイルがあることを確認したら、今度は root 端末で何らかのものを /mnt にマウントします。
mount --bind /dev /mnt |
これによって、/dev の中身は root 端末の /mnt には表示されますが、USER 端末には表示されません。つまり、この 2 つの端末のマウント・ツリーは完全に切り離されているということです。マウント・プロパゲーションに関するディレクティブを指定するには、mount(8) コマンドを使用します。デフォルトではすべてのマウントが private なので、USER がログインする前に例えば以下のコマンドを実行することができます。
mount --make-rshared / |
このコマンドにより、以降の非共有の名前空間の間でマウント・イベントが伝播されるようになりますが、USER がログインした後は /tmp への /tmp/priv/USER のマウントは親の名前空間に伝播されません。これを解決するには、pam_ns.so のファイルシステムに slave のマークを付けます (リスト 2 を参照)。
リスト 2. ユーザーの名前空間に slave のマークを付ける PAM モジュール
#define DIRNAMSZ 200
#ifndef MS_SLAVE
#define MS_SLAVE 1<<19
#endif
#ifndef MS_REC
#define MS_REC 0x4000
#endif
int handle_login(const char *user)
{
int ret = 0;
struct stat statbuf;
char dirnam[DIRNAMSZ];
if (strcmp(user, "root") == 0)
return PAM_SUCCESS;
ret = unshare(CLONE_NEWNS);
if (ret) {
mysyslog(LOG_ERR, "failed to unshare mounts for %s\n", user);
return PAM_SESSION_ERR;
}
ret = mount("", "/", "dontcare", MS_REC|MS_SLAVE, ""));
if (ret) {
mysyslog(LOG_ERR, "failed to mark / rslave for %s\n", user);
return PAM_SESSION_ERR;
}
snprintf(dirnam, DIRNAMSZ, "/tmp/priv/%s", user);
ret = stat(dirnam, &statbuf);
if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
ret = mount(dirnam, "/tmp", "none", MS_BIND, NULL);
if (ret) {
mysyslog(LOG_ERR, "failed to mount tmp for %s\n", user);
return PAM_SESSION_ERR;
}
} else
mysyslog(LOG_INFO, "No private /tmp for user %s\n", user);
return PAM_SUCCESS;
}
|
「ログイン別名前空間」のセクションでは、ユーザーに専用の名前空間を提供するマウント名前空間の単純な使い方について説明しました。マウント・プロパゲーションを使用すると、ユーザーに /tmp ディレクトリーを提供するには申し分のないソリューションとなります。さらに pam_ns.c で解析される構成ファイルを追加すれば、ユーザーごとに追加ディレクトリーを指定することもできます。LSPP システムではこの方法を使って /home/USER にログイン・プロセスの結果に応じたディレクトリーをマウントし、さまざまにインスタンス化したホーム・ディレクトリーを提供します。
ただし、同じユーザーがログインするごとに専用の slave ファイルシステムを受け取ることになるため、ユーザーがあるログイン・セッションで行ったマウントは、別のログイン・セッションでは反映されません。
管理者以外のユーザーがファイルシステムをマウントする方法はいくつかあります。例えば FUSE を使用すれば、ユーザーが sshfs (SSH ファイルシステム) やループバック・ファイルシステムをマウントすることが可能になります (「参考文献」を参照)。このようにして行ったマウントを他のユーザーと共有するという問題はともかくとして (これについては後で説明)、そのマウントが該当するユーザーがログインしている 1 つの端末には反映されても、他の端末には反映されなければ混乱を免れません。「ログイン別名前空間」のセクションで説明した手法を使えば、まさにこのようなことが起こります。
リスト 3 は、pam_chroot.so pam モジュールからの該当する部分の抜粋です。pam_ns.so ではログイン時にマウント名前空間を複製しますが、pam_chroot.so モジュールではユーザーごとのファイルシステムが /share/USER/root の下に設定されるという前提で、単純に chroot() を使ってユーザーをユーザ専用のファイルシステムにロックします。
リスト 3. chroot() を使用した PAM モジュール
int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
const char *PAM_user = NULL;
char fnam[400];
int ret, err, count, i;
struct mount_entries *entries;
struct stat statbuf;
ret = pam_get_user(pamh, &PAM_user, NULL);
if (ret != PAM_SUCCESS) {
mysyslog(LOG_ERR, "PAM-MOUNT: couldn't get user\n");
return PAM_SESSION_ERR;
}
/* check whether /share/$pam_user/root exists. If so, chroot to it */
sprintf(fnam, "/share/%s/root", PAM_user);
ret = stat(fnam, &statbuf);
if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
ret = chroot(fnam);
if (ret) {
mysyslog(LOG_ERR, "PAM-MOUNT: unable to chroot to %s\n", fnam);
return PAM_SESSION_ERR;
}
}
return PAM_SUCCESS;
}
|
この場合、すべてのマウントはシステムの起動時に前もって行われます。以下は、ブート後に実行するコマンド例です。
mkdir -p /share/USER/root mount --make-rshared / mount --rbind / /share/USER/root mount --make-rslave /share/USER/root mount --bind /share/USER/root/tmp/priv/USER /share/USER/root/tmp |
上記では、専用の名前空間は使用されていません。代わりに USER のそれぞれのログインに対して、同じディレクトリー /share/USER/root の下で chroot() が実行されます。したがって、USER のどのログインで行われたマウントであっても、そのマウントはすべてのログインで表示されます。一方、OTHERUSER の場合は、/share/OTHERUSER/root の下で chroot() が実行されるため、USER のマウント・アクティビティーは表示されません。
この手法の欠点は、何らかの特権は必要になるものの、通常の chroot() から抜け出すことができるということです。一例として、CAP_SYS_CHROOT などの特定の特権で実行すると、chroot() から抜け出すプログラムのソース (「参考文献」を参照) によって、プログラムは実際のファイルシステムのルートへ抜け出ることになります。このような事態は、ユーザー別にファイルシステム・ツリーを使用する実際の動機および使用方法によっては、問題になる可能性があります。
この問題に対処する方法は、chroot(2) の代わりに専用の名前空間で pivot_root(2) を使用し、ログインのルートを /share/USER/root に変更することです。chroot() はプロセスのファイルシステムのルートを指定された新規ディレクトリーに変更するだけですが、pivot_root() は指定された new_root ディレクトリーをマウント・ポイントから切り離し、プロセスのルート・ディレクトリーに接続します。マウント・ツリーには新規ルートの親がないため、chroot() での場合のようにシステムがだまされてルート・ディレクトリーに入り込むことがありません。以降の例では、この pivot_root() による手法を使用します。
これまでは、ログイン時に必要な操作を含め、ユーザーごとに専用のマウント・ツリーを実装する方法を詳しく見てきました。このセクションではユーザー・アカウントの作成時とシステムのブートアップ時に使用する、より完全なスクリプトを検討します。
リスト 4 は、ユーザーの作成時に実行されるスクリプトです。
リスト 4. ユーザー作成用のスクリプト
create_user_tree {
user = $1
mkdir /user/$user
mount --rbind / /user/$user
mount --make-rslave /user/$user
mount --make-rshared /user/$user
#create a private mount. This is to facilitate pivot_root
#to temporarily place the old root here before detaching the
#entire old root tree. NOTE: pivot_root will not allow old root
#to be placed under a shared mount.
pushd /user/$user/
mkdir -p __my_private_mnt__
mount --bind __my_private_mnt__ __my_private_mnt__
mount --make-private __my_private_mnt__
popd
}
|
このスクリプトは、init_per_user_namespaceスクリプト (この後で説明) がすでに実行済みであることを前提とします。/user/ の下にアカウント用のディレクトリーが作成された後、ルート・ディレクトリーが再帰的に /user/$user/ の下にバインド・マウントされます。この再帰的にコピーされるルート・ファイルシステムのツリーは、ユーザーによるマウント・アクティビティーの永続ストア (リブートではなく、ログインするごとに変わることのないストア) となります。
コピーされたツリーはルート・ツリーの slave として設定されるため、ルート・ツリーでのマウント・アクションはこのコピーに伝播されますが、その逆の伝播は行われません。また、このツリーには shared のマークも付けられるため、以降のコピー (つまり、名前空間の複製によって作成されたコピー) はマウントのピアとなります。したがって、いずれかのコピーで行われるマウント・アクションはその他すべてのコピーに伝播されます。
最後に、__my_private_mnt__ という private マウントが作成されます。このマウントを作成する理由は、pivot_root() (リスト 6 に記載) を実行できるようにして、ツリーを削除する前に一時的にルート・マウントを行うためです。このステップに関しては、あまり深く考えないでください。このステップを行う根拠は、この後説明する pivot_root() の動作から明らかになります。ここでは、pivot マウントはマウントのタイプが shared の場合には成功しないということだけ覚えておいてください。
リスト 5 に、システムのブート時に実行されるスクリプトを記載します。
リスト 5. ブート時にシステムを初期化するスクリプト
init_per_user_namespace {
#start with a clean state by marking
#all mounts as private.
mount --make-rprivate /
#create a unbindable mount called 'user'
#and have all the users to bind the entire
#system tree '/' under them.
mkdir /user
mount --bind /user /user
mount --make-rshared /
mount --make-unbindable /user
foreach user in existing_user {
create_user_tree $user
}
}
|
このスクリプトは、ユーザーごとのマウント・ツリーを保持する場所となる /user ディレクトリーを作成し、続いて /user 自体のバインド・マウントを行います。マウント・ポイントに指定できるのは、--rshared などのマウント・プロパゲーション・ディレクティブのみです。このステップにより、共有マウント・ポイントが /user に存在することを確実にしています。
次に、ファイルシステムのルートに --rshared のマークが付けられます。これは、以降のコピー (バインド・マウントあるいはマウント名前空間の複製によって作成) がこのマウントのピアとなり、いずれかのツリーで行われたマウント・アクションがすべてのピアにコピーされるようにするためです。
さらに、/user に位置するマウントには unbindable のマークが付けられます。マウント・ツリー全体はユーザーごとに 1 度、再帰的にコピーされます。そのため通常は、最初のユーザーのコピーが /user/$user_1 に作成されると、/user/$user_2 に作成されるコピーには、/user/$user_2/user/$user_1 の下に /user/$user_1 の再帰的なコピーが含まれることになります。そうなるとご想像のとおり、たちまち大量のメモリーが消費されてしまいます。そこで、/user に unbindable のマークを付けることで、/ が再帰的にバインド・マウントされるときに /user がコピーされないようにしているというわけです。
最後に、リスト 4 のスクリプトが各ユーザーに対して 1 度実行されます。このスクリプトは、/user/$user ディレクトリーが存在しない場合にはこのディレクトリーを作成し、前述の正しいマウント・プロパゲーションをセットアップします。
リスト 6 は、ユーザーがログインするたびに実行される PAM モジュールからの抜粋です。
リスト 6. ユーザー・ログイン時の PAM コードの抜粋
#ifndef MNT_DETACH
#define MNT_DETACH 0x0000002
#endif
#ifndef MS_REC
#define MS_REC 0x4000
#endif
#ifndef MS_PRIVATE
#define MS_PRIVATE 1<<18 /* Private */
#endif
#define DIRNAMSZ 200
int handle_login(const char *user)
{
int ret = 0;
struct stat statbuf;
char dirnam[DIRNAMSZ], oldroot[DIRNAMSZ];
snprintf(dirnam, DIRNAMSZ, "/user/%s", user);
ret = stat(dirnam, &statbuf);
if (ret != 0 || !S_ISDIR(statbuf.st_mode))
return PAM_SUCCESS;
ret = unshare(CLONE_NEWNS);
if (ret) {
mysyslog(LOG_ERR, "failed to unshare mounts for %s, error %d\n",
user, errno);
return PAM_SESSION_ERR;
}
ret = chdir(dirnam);
if (ret) {
mysyslog(LOG_ERR, "failed to unshare mounts for %s, error %d\n",
user, errno);
return PAM_SESSION_ERR;
}
snprintf(oldroot, DIRNAMSZ, "%s/__my_private_mnt__", dirnam);
ret = pivot_root(dirnam, oldroot);
if (ret) {
mysyslog(LOG_ERR, "failed to pivot_root for %s, error %d\n",
user, errno);
mysyslog(LOG_ERR, "pivot_root was (%s,%s)\n", dirnam, oldroot);
return PAM_SESSION_ERR;
}
ret = mount("", "/__my_private_mnt__", "dontcare", MS_REC|MS_PRIVATE, "");
if (ret) {
mysyslog(LOG_ERR, "failed to mark /tmp private for %s, error %d\n",
user, errno);
return PAM_SESSION_ERR;
}
ret = umount2("/__my_private_mnt__", MNT_DETACH);
if (ret) {
mysyslog(LOG_ERR, "failed to umount old_root %s, error %d\n",
user, ret);
return PAM_SESSION_ERR;
}
return PAM_SUCCESS;
}
|
このモジュールはまず、ログインを行っているユーザーを対象とした /user/USER ツリーの有無をチェックします。存在しない場合は単にユーザーをログインさせ、それ以降のアクションは一切行いません。
/user/USER ツリーが存在する場合には、最初のステップとして、このログイン・プロセスでのタスクを対象とした専用の名前空間を複製します。つまり、それぞれのログイン・プロセスに固有のシステム初期マウント・ツリーのコピーが作成されるということですが、これらのツリーは分離されているわけではありません。コピーされたツリー内の各マウント・ノードは、初期ツリーの対応するマウント・ノードと共有されます。
ログイン・プロセスは次に pivot_root() を使用して、ファイルシステムのルートを /user/$user に変更します。元のルートは新規 __my_private_mnt__ にマウントされたままの状態となります。
次のステップでは __my_private_mnt__ に private のマークを付け、元のルート・マウント・ツリー、そしてルート・マウント・ツリーのコピーに以降のアンマウントが伝播されないようにします。
その上で、元のルートが __my_private_mnt__ からアンマウントされます。
ユーザー作成用のスクリプト (リスト 4 を参照) では、__my_private_mnt__ ディレクトリーを private マウントに指定し、これによって pivot_root() を実行できるようにすると説明しました。その根拠となっているのは、十分に文書化されてはいませんが、古いルートと新しいルートのマウント・プロパゲーション・ステータスに関する pivot_root() の制約です。pivot_root() が正常に機能するためには、以下のマウントが共有オブジェクトであってはなりません。
- 古いルートのターゲット・ロケーション
- 新規ルートの現行の親 (
pivot_root()を呼び出した時点での親) - 新規ルートがターゲットとする親
上記の最初の条件には、リスト 4 の最後のほうで __my_private_mount を private にすることで対処できます。2 番目の条件は、新規ルートの現行の親は /user であり、/user にあるマウントは unbindable マウントであることから、すでに満たされています。3 番目の条件についてもすでに満たされており、新規ルートがターゲットとする親は現行ルートの親にもなっています。これに該当するマウントは、表示には現れない rootfs マウントで、これはすでに private となっています。
このセクションではユーザーごとにマウント・ツリーを実装する方法を説明しましたが、この実装の場合、マウント・イベントは該当するユーザーのログイン・セッションのすべてで共有されますが、他のユーザーには表示されません。次のセクションでは、ユーザー同士でマウント・ツリーを共有できるようにする方法を説明します。
ユーザーごとにマウント・ツリーを提供する方法について説明し終わったので、今度はマウント・ツリーを部分的にユーザー共有として指定できるようにする方法を説明します。以下は、システム・ブート用のスクリプトです。
リスト 7. システム・ブート用のスクリプト
init_per_user_namespace {
mkdir -p /user/slave_tree
mkdir -p /user/share_tree
#start with a clean state. Set all mounts to private.
mount --make-rprivate /
mount --bind /user /user
mount --bind /user/share_tree /user/share_tree
mount --bind /user/slave_tree /user/slave_tree
mount --make-rshared /
mount --make-unbindable /user
for user in `cat /etc/user_list`; do
sh /bin/create_user_tree $user
done
}
|
ここでまず作成しているのは、各ユーザーのルート・ディレクトリーを保持する /user マウントです。/user の下には /user/share_tree という別のディレクトリーも作成しています。このディレクトリーには、それぞれのユーザーが他のユーザーと共有しても構わないマウントが保持されます。さらに /user/slave_tree ディレクトリーを作成し、ここに、他のユーザーによる変更を許可せずに共有するマウントを保持するようにします。マウントが無制限に作成されないようにするため、/user に位置するマウントには当然、unbindable のマークを付けます。そして最後に、create_user_tree を呼び出して各ユーザーのマウント・ツリーを作成します。
リスト 8 に、マウント・ツリーを作成し、他のユーザーとマウントを共有できるようにするために必要なステップを説明します。
リスト 8. ユーザー作成用のスクリプト
create_user_tree {
user = $1
mkdir -p /user/$user
#copy over the entire mount tree under /user/$user
mount --rbind / /user/$user
make --make-rslave /user/$user
make --make-rshared /user/$user
cd /user/$user/home/$user
#export my shared exports
mkdir -p my_shared_exports
chown $user my_shared_exports
mount --bind my_shared_exports my_shared_exports
mount --make-private my_shared_exports
mount --make-shared my_shared_exports
mkdir -p /user/share_tree/$user
mount --bind my_shared_exports /user/share_tree/$user
#export my slave exports
mkdir -p my_slave_exports
chown $user my_slave_exports
mount --bind my_slave_exports my_slave_exports
mount --make-private my_slave_exports
mount --make-shared my_slave_exports
mkdir -p /user/slave_tree/$user
mount --bind my_slave_exports /user/slave_tree/$user
cd /user/$user
#import everybody's shared exports
mkdir -p others_shared_exports
mount --rbind /user/share_tree others_shared_exports
#import everybody's slave exports
mkdir -p others_slave_exports
mount --rbind /user/slave_tree others_slave_exports
mount --make-rslave others_slave_exports
#setup a private mount in the user's tree, This is to facilitate
# pivot_mount executed later, during new user-logins.
mkdir -p __my_private_mnt__
mount --bind __my_private_mnt__ __my_private_mnt__
mount --make-private __my_private_mnt__
}
|
まず始めに、マウント・ツリー全体を /user/$user に複製します。ユーザーのツリー内には、my_shared_exports という名前の shared マウントを作成します。このマウントをすべてのユーザーにエクスポートする手段として、/user/share_tree/$user の下にマウントを複製します。同様に、ユーザーのツリー内に my_slave_exports を作成し、/user/slave_tree/$user の下に複製することによって、すべてのユーザーにエクスポートします。ここで重要となる考えは、ユーザーが my_shared_tree に何かをマウントするという選択をした場合、そのマウントは自動的に他のすべてのユーザーと共有されるということです。
次に、他のすべてのユーザーの shared マウントをインポートするため、/user/share_tree の下にあるマウント・ツリーを複製し、ログイン・ユーザーの others_shared_exports にマウントします。同じく /user/slave_tree の下にあるマウント・ツリーを複製して others_slave_exports にマウントし、他のすべてのユーザーの slave マウントをインポートします。当然のことながら、これらのマウントはエクスポーターによって slave としてエクスポートされることが意図されているので、マウントを slave に変換します。
共有のための正しい設定をして初期セットアップを完了すると、ユーザー・ログイン・アルゴリズムはリスト 6 と同一になります。ログインしたユーザーには、他のログイン時とまったく同じマウント・ツリーが表示されるだけでなく、他のすべてのユーザーからエクスポートされたすべての shared および slave マウントもそれぞれ /others_shared_export、/others_slave_exports の下に表示されます。
ユーザーが他のユーザーに何かをエクスポートするために必ず必要なことは、my_shared_exports の下にそのコンテンツをマウントするだけです。すると、まるで魔法のように該当するコンテンツが他のすべてのユーザーに表示されます。
バインド・マウントは任意のファイルまたはディレクトリー同士の結合を容易にし、名前空間はプロセスをその親固有のマウント・ツリーのコピーと併せて複製できるようにします。さらにマウント・プロパゲーションによって、ファイルシステム・ツリーのコピーが他のコピーに対してマウント・イベントを伝播することも、あるいはコピー同士でマウント・イベントを共有することも可能になります。これらの機能は、ユーザーに独自の準専用のマウント・ツリーを提供するだけでなく、ユーザーが CD-ROM マウントなどといったシステム全体のマウント・イベントを把握したり、ユーザー固有のマウント・イベントを他のユーザーと選択的に共有することを可能にします。
言い換えると、この記事で説明したマウント・プロパゲーションの手法により、ユーザーは別個のファイルシステムをセットアップし、専用のファイルシステムのツリーにさまざまなファイルシステムのツリーを部分的にインポート、エクスポートできるようになるということです。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Sample mount propagation code for this article | dw.mountscode.tgz | 3KB | HTTP |
学ぶために
- 「Create uniform namespace using autofs with NFS Version 3 clients and servers」(developerWorks、2007年1月) では、autofs と LDAP のオープン・ソース実装を使用して、複数のファイル・サーバーからエクスポートしたデータに同じグローバル・マウント・ポイントでアクセスする方法を説明しています。
- 「System Administration Toolkit: Migrating and moving UNIX filesystems」(developerWorks、2006年7月) では、ライブ・システムでファイルシステム全体を転送する方法を、作成、コピー、そして再有効化の手順に沿って説明しています。
- 「Using ReiserFS with Linux」(developerWorks、2006年4月) は、「冒険好きな人たちのためのもう 1 つの高度なファイルシステム」を取り上げた記事です。
- 「Differentiating UNIX and Linux」(developerWorks、2006年3月) では、Linux と UNIX でのファイルシステム・サポートの違いを分かりやすく解説しています。
- Linux マニュアル・ページで、clone(2)、unshare(2)、mount(8)、pivot_root(2)、chroot(2) についての詳細を学んでください。
- Common Criteria の LSPP (Labeled Security Protection Profile) では、IT 製品の一連のセキュリティー機能と保証要件 (2 種類のアクセス制御機構) を指定しています。
-
FUSE (Filesystem in Userspace) では、ユーザー・スペース・プログラムで完全に機能するファイルシステムを実装できます。単純な API、カーネルのパッチや再コンパイルの不要、セキュアな実装、実証された安定性という特徴を備え、特権なしのユーザーが使用できるこのファイルシステムは、2.4.x および 2.6.x カーネルで動作します。
-
sshfs は SSH ファイル転送プロトコルをベースとしたファイルシステム・クライアントです。ほとんどの SSH サーバーにはこのプロトコルのサポートが備わっているため、sshfs をセットアップするためにサーバー側で必要なことは何もありません。クライアント側で ssh を使えばサーバーにログインできます。
-
ループバック・ファイルシステムでは、代替パス名を使って既存のファイルにアクセスできる、新しい仮想ファイルシステムを作成できます。このファイルシステムをいったん作成すれば、元のファイルシステムに影響を与えることなく、その内部に他のファイルシステムをマウントできます。
-
chroot() jail から脱出する方法についての説明を読んでください。
- 柔軟性の高いユーザー認証機構、Linux PAM により、開発者は認証方式に依存しないプログラムを作成できます (つまり、「デバイスの新規作成」がすべての認証サポート・プログラムを記録することであるとは限りません)。
-
The Linux Documentation Project には、HOWTO 文書をはじめ、各種の有益な文書が豊富に揃っています。
-
developerWorks Linux ゾーンに豊富に揃った Linux 開発者向けの資料を調べてください。記事とチュートリアルの人気ランキングも要チェックです。
- developerWorks に掲載されているすべての Linux のヒントと Linux チュートリアルを参照してください。
-
developerWorks technical events and webcasts で最新情報を入手してください。
製品や技術を入手するために
-
SEK for Linux を注文してください。この 2 枚組 DVD セットには、Linux 対応の DB2®、Lotus®、Rational®、Tivoli®、そして WebSphere® の最新 IBM トライアル・ソフトウェアが収録されています。
- developerWorks から直接ダウンロードできる IBM トライアル・ソフトウェアを使用して、Linux で次の開発プロジェクトを構築してください。
議論するために
- 新しくなった developerWorks スペースのブログ、フォーラム、ポッドキャスト、そしてコミュニティーの話題を通して developerWorks community に参加してください。