パス走査(親ディレクトリ/カレントディレクトリ)
システムファイル構成は、dentry間のリンクで実現していますが、異なるファイルシステムシステム間のリンクはされません。mountでデバイス(ファイルシステム)をバインドすると、マウントポイントのdentry->d_flagsが設定されるだけで、ファイルシステムのrootのdentryとリンクされません。
path検索では、dentryがマウントポイントなら、vfs_mntのリストを走査して、マウントポイントのvfs_mnt->mnt_mountpointと一致するvfs_mnを取得し、そのマウントルートをマウントポイントdentryと差し替えることで実現します。
従って、../の親ディレクトリを走査する場合、カレント及びその親が、マウントポイント(ファイルシステムのルート)なら、path検索とは逆にmntからdentryを取得することになります。
walk_component()で検索パスコンポーネントが、親/カレントディレクトリなら(typeがLAST_DOT/LAST_DOTDOTの時)、handle_dots()がコールされて復帰します。
nd->pathがカレントルートなら、それ以上cdできません。nd->pathがファイルシステムのrootでないなら、dget_parent()でその親ディレクトリとなります。そうでないならディレクトリはファイルシステムのルートで、マウントポイントを取得するためfollow_up()をコールすることになります。follow_up()の返値が0なら、ルートファイルシステムです。
取得できた親ディレクトリがマウントポイントの場合、follow_mount()でマウント先のファイルシステムのrootが親ディレクトリとなります。
path検索では、dentryがマウントポイントなら、vfs_mntのリストを走査して、マウントポイントのvfs_mnt->mnt_mountpointと一致するvfs_mnを取得し、そのマウントルートをマウントポイントdentryと差し替えることで実現します。
従って、../の親ディレクトリを走査する場合、カレント及びその親が、マウントポイント(ファイルシステムのルート)なら、path検索とは逆にmntからdentryを取得することになります。
walk_component()で検索パスコンポーネントが、親/カレントディレクトリなら(typeがLAST_DOT/LAST_DOTDOTの時)、handle_dots()がコールされて復帰します。
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; }handle_dots()でカレント/親ディレクトリの処理となりますが、実装は親ディレクトリのみです。カレントディレクトリはパス移動の必要はありません。
static inline int handle_dots(struct nameidata *nd, int type) { if (type == LAST_DOTDOT) { if (nd->flags & LOOKUP_RCU) { if (follow_dotdot_rcu(nd)) return -ECHILD; } else follow_dotdot(nd); } return 0; }nd->pathは検索起点のディレクトリです。まず、カレントルートを超えないようset_root()でnd->root=current->fsとします。
nd->pathがカレントルートなら、それ以上cdできません。nd->pathがファイルシステムのrootでないなら、dget_parent()でその親ディレクトリとなります。そうでないならディレクトリはファイルシステムのルートで、マウントポイントを取得するためfollow_up()をコールすることになります。follow_up()の返値が0なら、ルートファイルシステムです。
取得できた親ディレクトリがマウントポイントの場合、follow_mount()でマウント先のファイルシステムのrootが親ディレクトリとなります。
static void follow_dotdot(struct nameidata *nd) { set_root(nd); while(1) { struct dentry *old = nd->path.dentry; if (nd->path.dentry == nd->root.dentry && nd->path.mnt == nd->root.mnt) { break; } if (nd->path.dentry != nd->path.mnt->mnt_root) { /* rare case of legitimate dget_parent()... */ nd->path.dentry = dget_parent(nd->path.dentry); dput(old); break; } if (!follow_up(&nd->path)) break; } follow_mount(&nd->path); nd->inode = nd->path.dentry->d_inode; } struct dentry *dget_parent(struct dentry *dentry) { struct dentry *ret; repeat: rcu_read_lock(); ret = dentry->d_parent; spin_lock(&ret->d_lock); if (unlikely(ret != dentry->d_parent)) { spin_unlock(&ret->d_lock); rcu_read_unlock(); goto repeat; } rcu_read_unlock(); BUG_ON(!ret->d_count); ret->d_count++; spin_unlock(&ret->d_lock); return ret; }follow_mount()はpathのファイルシステムからマウントポイントを取得します。
int follow_up(struct path *path) { struct mount *mnt = real_mount(path->mnt); struct mount *parent; struct dentry *mountpoint; br_read_lock(vfsmount_lock); parent = mnt->mnt_parent; if (&parent->mnt == path->mnt) { br_read_unlock(vfsmount_lock); return 0; } mntget(&parent->mnt); mountpoint = dget(mnt->mnt_mountpoint); br_read_unlock(vfsmount_lock); dput(path->dentry); path->dentry = mountpoint; mntput(path->mnt); path->mnt = &parent->mnt; return 1; }follow_mount()はマウントポイントからマウント先ファイルシステムのルートを取得します。
static void follow_mount(struct path *path) { while (d_mountpoint(path->dentry)) { struct vfsmount *mounted = lookup_mnt(path); if (!mounted) break; dput(path->dentry); mntput(path->mnt); path->mnt = mounted; path->dentry = dget(mounted->mnt_root); } }