パス走査(親ディレクトリ/カレントディレクトリ)
システムファイル構成は、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);
}
}







