パス走査(link_path_walk)
link_path_walk()でパス検索を行います。nameは検索するパスで、ndは検索すべき親ディレクトリ等の情報が設定されています。検索は、walk_component()からdo_lookup()で行われます。パスの先頭から検索するコンポーネント('/'で区切られたファイル/ディレクトリ名)を順次取得しながら、1つづつwalk_component()で取得していきます。
may_lookup()は、検索する親ディレクトリが実行属性を有しているかチェックします。ディレクトリは実行属性を有してないと、配下のファイル/ディレクトリを参照できません。
hash_name()は検索コンポーネントのファイル名ないしディレクトリ名のハッシュ値を取得し、this.hashにセットします。dentryキャッシュ検索時にキーとして使用されます。
検索コンポーネントが [.][..][通常名]により、type=LAST_DOT/LAST_DOTDOT/LAST_NORMとし、walk_component()をコールします。walk_component()の帰り値が1の時、コンポーネントはシンボリックリンクで、nested_symlink()がコールされます。
handle_dots()は'.''..'のケースで、通常はdo_lookup()でnd->pathのinode->dirコールバック関数を、必要に応じてコールします。
follow_link()はinodeのfollow_linkコールバック関数で、リンク先path名をnd->saved_names[nd->depth]に設定され、再度link_path_walk()をコールし、リンク先のファイルを取得します。取得したファイルpathはnd->lastに設定されます。walk_component()でnd->lastがpathに設定されることになります。取得したリンク先ファイルで再帰的にコールします。
・ディレクトリ属性は、READでファイル/ディレクトリ名が取得でき、WRITEはファイル/ディレクトリ名を更新でき、EXEで配下のファイル/ディレクトリその物を参照できます。ディレクトリのEXE属性を削除すれば、ファイルが参照できないだけでなく、配下のディレクトリも参照できなくすることができます。
may_lookup()は、検索する親ディレクトリが実行属性を有しているかチェックします。ディレクトリは実行属性を有してないと、配下のファイル/ディレクトリを参照できません。
hash_name()は検索コンポーネントのファイル名ないしディレクトリ名のハッシュ値を取得し、this.hashにセットします。dentryキャッシュ検索時にキーとして使用されます。
検索コンポーネントが [.][..][通常名]により、type=LAST_DOT/LAST_DOTDOT/LAST_NORMとし、walk_component()をコールします。walk_component()の帰り値が1の時、コンポーネントはシンボリックリンクで、nested_symlink()がコールされます。
static int link_path_walk(const char *name, struct nameidata *nd) { struct path next; int err; while (*name=='/') name++; if (!*name) return 0; for(;;) { struct qstr this; long len; int type; err = may_lookup(nd); if (err) break; len = hash_name(name, &this.hash); this.name = name; this.len = len; type = LAST_NORM; if (name[0] == '.') switch (len) { case 2: if (name[1] == '.') { type = LAST_DOTDOT; nd->flags |= LOOKUP_JUMPED; } break; case 1: type = LAST_DOT; } if (likely(type == LAST_NORM)) { struct dentry *parent = nd->path.dentry; nd->flags &= ~LOOKUP_JUMPED; if (unlikely(parent->d_flags & DCACHE_OP_HASH)) { err = parent->d_op->d_hash(parent, nd->inode, &this); if (err < 0) break; } } if (!name[len]) goto last_component; do { len++; } while (unlikely(name[len] == '/')); if (!name[len]) goto last_component; name += len; err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW); if (err < 0) return err; if (err) { err = nested_symlink(&next, nd); if (err) return err; } if (can_lookup(nd->inode)) continue; err = -ENOTDIR; break; last_component: nd->last = this; nd->last_type = type; return 0; } terminate_walk(nd); return err; }nd->pathディレクトリ下のnameファイル/ディレクトリを取得し、pathに設定して返値を0とします。followはshould_follow_link()のシンボリックリンクだった場合の返値で、followをNULLでない場合で、nameがシンボリックリンクの時、返値は1となります。followがNULLだと、should_follow_link()でシンボリックリンクでもNULLとなってしまいます。walk_component()でシンボリックリンクの考慮しない場合、followを0でコールします。
handle_dots()は'.''..'のケースで、通常はdo_lookup()でnd->pathのinode->dirコールバック関数を、必要に応じてコールします。
static inline int walk_component(struct nameidata *nd, struct path *path, struct qstr *name, int type, int follow) { struct inode *inode; int err; if (unlikely(type != LAST_NORM)) return handle_dots(nd, type); err = do_lookup(nd, name, path, &inode); if (unlikely(err)) { terminate_walk(nd); return err; } if (!inode) { path_to_nameidata(path, nd); terminate_walk(nd); return -ENOENT; } if (should_follow_link(inode, follow)) { if (nd->flags & LOOKUP_RCU) { if (unlikely(unlazy_walk(nd, path->dentry))) { terminate_walk(nd); return -ECHILD; } } BUG_ON(inode != path->dentry->d_inode); return 1; } path_to_nameidata(path, nd); nd->inode = inode; return 0; }nested_symlink()はpathがシンボリックリンクの時にコールされます。current->link_count >= MAX_NESTED_LINKSは1プロセスが多重リンクできるマックスのチェックです。ハードリンクは考慮されません。
follow_link()はinodeのfollow_linkコールバック関数で、リンク先path名をnd->saved_names[nd->depth]に設定され、再度link_path_walk()をコールし、リンク先のファイルを取得します。取得したファイルpathはnd->lastに設定されます。walk_component()でnd->lastがpathに設定されることになります。取得したリンク先ファイルで再帰的にコールします。
enum { MAX_NESTED_LINKS = 8 }; static inline int nested_symlink(struct path *path, struct nameidata *nd) { int res; if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { path_put_conditional(path, nd); path_put(&nd->path); return -ELOOP; } BUG_ON(nd->depth >= MAX_NESTED_LINKS); nd->depth++; current->link_count++; do { struct path link = *path; void *cookie; res = follow_link(&link, nd, &cookie); if (!res) res = walk_component(nd, path, &nd->last, nd->last_type, LOOKUP_FOLLOW); put_link(nd, &link, cookie); } while (res > 0); current->link_count--; nd->depth--; return res; }シンボリックリンクかどうかのチェックは、inode->i_op->follow_linkコールバックが設定されているかどうかで行います。シンボリックリンクを作成時、inode->i_opにシンボリックリンク用のコールバックが設定され、follow_link関数は、このオペレーションコールバックにのみ設定されるコールバックです。
static inline int should_follow_link(struct inode *inode, int follow) { if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) { if (likely(inode->i_op->follow_link)) return follow; spin_lock(&inode->i_lock); inode->i_opflags |= IOP_NOFOLLOW; spin_unlock(&inode->i_lock); } return 0; }
備考
・/hoge1/hoge2のパスを//hoge1/////hoge2のようにしても問題ありません。・ディレクトリ属性は、READでファイル/ディレクトリ名が取得でき、WRITEはファイル/ディレクトリ名を更新でき、EXEで配下のファイル/ディレクトリその物を参照できます。ディレクトリのEXE属性を削除すれば、ファイルが参照できないだけでなく、配下のディレクトリも参照できなくすることができます。