namespaces: proc_ns_operations構造体のget()の呼び出し方

名前空間は/proc//ns以下にファイルとしてエクスポートされていて、nsenterがこの辺を使うわけですが、proc_ns_operations構造体に設定した関数はどのような流れで呼びだされているのかを見てみます。

対象をuts namespaceとし、utsns_get()にWARN_ONを仕込んでスタックトレースを取ったのが↓です。本当はftraceでやろうかと思ったんだけど、お手軽?なほうで。。。

[  322.987517] Call Trace:
[  322.987523]  [<ffffffff8158f61f>] dump_stack+0x4c/0x6e
[  322.987528]  [<ffffffff81078d4a>] warn_slowpath_common+0x8a/0xc0
[  322.987531]  [<ffffffff81078e7a>] warn_slowpath_null+0x1a/0x20
[  322.987535]  [<ffffffff811161f7>] utsns_get+0x27/0xb0
[  322.987540]  [<ffffffff812179da>] ns_get_path+0x12a/0x1c0
[  322.987544]  [<ffffffff8125608f>] proc_ns_follow_link+0x9f/0xc0
[  322.987548]  [<ffffffff811ff03a>] ? touch_atime+0x12a/0x180
[  322.987553]  [<ffffffff811f2d4a>] path_openat+0x4ba/0x690
[  322.987557]  [<ffffffff811f43b9>] do_filp_open+0x49/0xd0
[  322.987562]  [<ffffffff812e0bda>] ? find_next_zero_bit+0x1a/0x30
[  322.987566]  [<ffffffff81201a27>] ? __alloc_fd+0xa7/0x130
[  322.987570]  [<ffffffff811e246d>] do_sys_open+0x14d/0x250
[  322.987573]  [<ffffffff811e258e>] SyS_open+0x1e/0x20
[  322.987577]  [<ffffffff81594eee>] system_call_fastpath+0x12/0x71

このトレースからproc_ns_follow_link() -> ns_get_path() -> utsns_get()という流れということがわかります。
proc_ns_follow_link()は以前の記事

kernhack.hatenablog.com

でinodeからtask_struct構造体の取得の仕方を調べたとこですね。ns_get_path()を呼んでいるのはproc_ns_follow_link()のこの部分です。

 45         if (ptrace_may_access(task, PTRACE_MODE_READ)) {
 46                 error = ns_get_path(&ns_path, task, ns_ops);
 47                 if (!error)
 48                         nd_jump_link(nd, &ns_path);
 49         }

proc_ns_operations構造体は関数の冒頭でこのように取得しています。ここのPROC_I()もLinux: inodeからtask_struct構造体を取得 - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモで見ています。

 36         const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;

ns_get_path()はnsfs.cで定義されています。このファイルはnamespace用に最近追加されたファイルです。 それでは、ns_get_path()を見てみます。って、いきなりget()を読んでますね。

 46 void *ns_get_path(struct path *path, struct task_struct *task,
 47                         const struct proc_ns_operations *ns_ops)
 48 {
 49         struct vfsmount *mnt = mntget(nsfs_mnt);
 50         struct qstr qname = { .name = "", };
 51         struct dentry *dentry;
 52         struct inode *inode;
 53         struct ns_common *ns;
 54         unsigned long d;
 55 
 56 again:
 57         ns = ns_ops->get(task);

ここで引数にtask_struct構造体を渡していて、それがutsns_get()に渡されて使われるわけです。

 98 static struct ns_common *utsns_get(struct task_struct *task)
 99 {
100         struct uts_namespace *ns = NULL;
101         struct nsproxy *nsproxy;
102 
103         task_lock(task);
104         nsproxy = task->nsproxy;
105         if (nsproxy) {
106                 ns = nsproxy->uts_ns;
107                 get_uts_ns(ns);
108         }
109         task_unlock(task);
110 
111         return ns ? &ns->ns : NULL;
112 }

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)