リンクの検索
パスの検索の処理の本体は__link_path_walk()となります。(linkと頭についていますが、この意味はパスが上から繋がっていると言うような意味合いだと思います。)引数のnameは検索するパスで、ndは検索対象となっているパスの情報(dentryとか)が設定されています。この処理nameをパスごとに区切りながら、順にdentryを求めていくものです。なお、./および../の処置とか、キャッシュ上にあるdentryの検索を高速にするため、そのパスのハッシュを設定する処理があったりしています。ここではリンクの処理に限定しています。
__link_path_walk()のforループの上段で、nameから順に区切ったパスがthisに設定され、do_lookup()をコールします。do_lookup()ではndのdentry下でthisのパスをキャシュないし実デバイスを読み込むことで検索します。検索した結果はnextに設定されます。
検索したnextのinodeを調べます。inode->i_op->follow_linkが0でないなら、このdentryはリンクということになります。そしてdo_follow_link()をコールします。ここではnextを展開してその結果がndに設定されてきます。そしてこのndでもって次のパス区切りを検索するということです。
__do_follow_link()はリンク先がリンクの場合、再帰的にコールされます。そのためのチェックがcurrent->link_countカウンタです。従ってリンク先がリンク、そしてその先がリンクというのは、MAX_NESTED_LINKS=8を超えて設定できないということです。
current->total_link_countは全リンク数です。トップ関数で指定されたパスのリンク数は40を超えてはならないということです。
nd_get_link()でnd->saved_names[]のリンク先を取得します。NULLで無いならリンク先を_vfs_follow_link()コールし、そこから再度link_path_walk()を再帰的にコールしています。
__link_path_walk()のforループの上段で、nameから順に区切ったパスがthisに設定され、do_lookup()をコールします。do_lookup()ではndのdentry下でthisのパスをキャシュないし実デバイスを読み込むことで検索します。検索した結果はnextに設定されます。
検索したnextのinodeを調べます。inode->i_op->follow_linkが0でないなら、このdentryはリンクということになります。そしてdo_follow_link()をコールします。ここではnextを展開してその結果がndに設定されてきます。そしてこのndでもって次のパス区切りを検索するということです。
static int __link_path_walk(const char *name, struct nameidata *nd) { : for(;;) { err = exec_permission_lite(inode); if (err == -EAGAIN) err = vfs_permission(nd, MAY_EXEC); if (err) break; : err = do_lookup(nd, &this, &next); if (err) break; err = -ENOENT; inode = next.dentry->d_inode; : if (inode->i_op->follow_link) { err = do_follow_link(&next, nd); if (err) goto return_err; err = -ENOENT; inode = nd->path.dentry->d_inode; if (!inode) break; err = -ENOTDIR; if (!inode->i_op) break; } else : }do_follow_link()はリンク処理にトップ関数となります。ここではまず、current->link_count/current->total_link_countによるチェックが行われています。リンクの実際の処理は下段の__do_follow_link()です。この関数をコールする前に先の2つのカウンターはインクリメントされています。そして__do_follow_link()から戻ると、current->link_countのみデクリメントしています。
__do_follow_link()はリンク先がリンクの場合、再帰的にコールされます。そのためのチェックがcurrent->link_countカウンタです。従ってリンク先がリンク、そしてその先がリンクというのは、MAX_NESTED_LINKS=8を超えて設定できないということです。
current->total_link_countは全リンク数です。トップ関数で指定されたパスのリンク数は40を超えてはならないということです。
enum { MAX_NESTED_LINKS = 8 }; static inline int do_follow_link(struct path *path, struct nameidata *nd) { int err = -ELOOP; if (current->link_count >= MAX_NESTED_LINKS) goto loop; if (current->total_link_count >= 40) goto loop; BUG_ON(nd->depth >= MAX_NESTED_LINKS); cond_resched(); err = security_inode_follow_link(path->dentry, nd); if (err) goto loop; current->link_count++; current->total_link_count++; nd->depth++; err = __do_follow_link(path, nd); current->link_count--; nd->depth--; return err; loop: path_put_conditional(path, nd); path_put(&nd->path); return err; }__do_follow_link()で実リンク先を読み込みます。これはinodeオペレーションのfollow_linkコールバック関数をコールする事で行っています。この関数はinodeを取得する時に、ファイルシステムおよびハードリンク/シンボリックリンクに応じたコールバック関数が設定されていて、結果はdentryのキャッシュページおよび、リンク先がnd->saved_names[]に返されます。
nd_get_link()でnd->saved_names[]のリンク先を取得します。NULLで無いならリンク先を_vfs_follow_link()コールし、そこから再度link_path_walk()を再帰的にコールしています。
static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd) { int error; void *cookie; struct dentry *dentry = path->dentry; touch_atime(path->mnt, dentry); nd_set_link(nd, NULL); if (path->mnt != nd->path.mnt) { path_to_nameidata(path, nd); dget(dentry); } mntget(path->mnt); cookie = dentry->d_inode->i_op->follow_link(dentry, nd); error = PTR_ERR(cookie); if (!IS_ERR(cookie)) { char *s = nd_get_link(nd); error = 0; if (s) error = __vfs_follow_link(nd, s); if (dentry->d_inode->i_op->put_link) dentry->d_inode->i_op->put_link(dentry, nd, cookie); } path_put(path); return error; } static inline char *nd_get_link(struct nameidata *nd) { return nd->saved_names[nd->depth]; } static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) { int res = 0; char *name; if (IS_ERR(link)) goto fail; if (*link == '/') { path_put(&nd->path); walk_init_root(link, nd); } res = link_path_walk(link, nd); : }