リンクの検索
Rev.1を表示中。最新版はこちら。
パスの検索の処理の本体は__link_path_walk()となります。引数の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でもって次のパス区切りを検索するということです。
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); : }