-
/*
-
* Name resolution.
-
* This is the basic name resolution function, turning a pathname into
-
* the final dentry. We expect 'base' to be positive and a directory.
-
*
-
* Returns 0 and nd will have valid dentry and mnt on success.
-
* Returns error and drops reference to input namei data on failure.
-
*/
-
static int link_path_walk(const char *name, struct nameidata *nd)
-
{
-
struct path next;
-
struct inode *inode;
-
int err;
-
unsigned int lookup_flags = nd->flags;
-
-
while (*name=='/')
-
name++;
-
if (!*name)
-
goto return_reval;
-
-
inode = nd->path.dentry->d_inode;
-
if (nd->depth)
-
lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
-
-
/* At this point we know we have a real path component. */
-
for(;;) { /* 在这个循环里,每次循环截取一个路径分量 */
-
unsigned long hash;
-
struct qstr this;
-
unsigned int c;
-
-
nd->flags |= LOOKUP_CONTINUE;
-
err = exec_permission(inode);
-
if (err)
-
break;
-
/* struct qstr this; 这个变量,就是为了记录路径分量的字符串,字符串长度,路径分量hash值 */
-
-
/* 路径分量的截取,begin */
-
this.name = name;
-
c = *(const unsigned char *)name;
-
-
hash = init_name_hash();
-
do {
-
name++;
-
hash = partial_name_hash(c, hash);
-
c = *(const unsigned char *)name;
-
} while (c && (c != '/'));
-
this.len = name - (const char *) this.name;
-
this.hash = end_name_hash(hash);
-
/* 路径分量的截取, end */
-
-
/* remove trailing slashes? */
-
if (!c)
-
goto last_component;
-
while (*++name == '/');
-
if (!*name)
-
goto last_with_slashes;
/* 路径中间分量的处理,begin*/
/* 对于路径分量中出现的"."和".."的特殊处理,
如果是"."则不更新nd->path
如果是"..",则调用follow_dotdot,更新nd->path为父目录的dentry和vfsmount,返回父目录要着重考虑跨越mountponit 的情况
*/
-
/*
-
* "." and ".." are special - ".." especially so because it has
-
* to be able to know about the current root directory and
-
* parent relationships.
-
*/
-
if (this.name[0] == '.') switch (this.len) {
-
default:
-
break;
-
case 2:
-
if (this.name[1] != '.')
-
break;
-
follow_dotdot(nd);
-
inode = nd->path.dentry->d_inode;
-
/* fallthrough */
-
case 1:
-
continue;
-
}
-
/* This does the actual lookups.. */
-
/* 根据路径名称查找dentry的过程:先在内存hash表里查找,如果查找不到,再进行文件系统的查找 */
-
err = do_lookup(nd, &this, &next);
-
if (err)
-
break;
-
-
err = -ENOENT;
-
inode = next.dentry->d_inode;
-
if (!inode)
-
goto out_dput;
-
/* 路径的中间分量,需要对符号链接进行跟踪 */
-
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;
-
} else
-
path_to_nameidata(&next, nd);
-
err = -ENOTDIR;
-
if (!inode->i_op->lookup)
-
break;
-
continue; /* 对于一个路径中间分量,处理到此就结束,回到for循环开始,继续下一个路径分量 */
-
/* here ends the main loop */
-
/* 路径中间分量的处理,end */
/* 路径最后分量的处理,begin
路径最后分量与路径中间分量处理的不同点在于
1. 如果lookup_flags & LOOKUP_PARENT,则不需要对最后一个分量进行查找。
这样的情形比如要删除一个"/mnt/tmp1/abc"这个目录,那么我们需要的是先找到"/mnt/tmp1"这个目录,因为删除"/mnt/tmp1"的child 目录,很多操作都要在"/mnt/tmp1"的目录数据结构上操作,没有必要直接找到"/mnt/tmp1/abc"这个目录。 有了"/mnt/tmp1"这个目录的数据结构后,再查找目录下的"abc"目录,完成dentry的删除操作
如下面的调用关系:
do_rmdir
user_path_parent
do_path_lookup(dfd, s, LOOKUP_PARENT, nd)
nd->last = this, 所以nd->last变量记录了最后一个分量的信息。并且nd.last_type能够指示:
如果最后一个分量是".", nd.last_type=LAST_DOT
如果最后一个分量是"..", nd.last_type=LAST_DOTDOT
如果最后一个分量是正常字符, nd.last_type=LAST_NORM
2. 对于路径的中间分量,如果是符号链接,一定要跟踪过去。但对于路径的最后分量,却要判断通过follow_on_final来判断,如果是目录的话才会跟踪过去。
3. 对于路径的中间分量,如果inode->i_op->lookup==NULL, 因为中间节点需要是目录,如果lookup函数为空,意味着无法进一步查找了。所以要返回失败。
但是对于路径的最后分量,inode->i_op->lookup==NUL,只有LOOKUP_DIRECTORY标志设置时(比如执行"cd /mnt/tmp1/"这样的命令),才会返回失败。
*/
/*
如果最后一个分量后面带一个"/", 比如要查找"/mnt/tmp1/",而不是查找“/mnt/tmp",那么就要在lookup_flags上加上LOOKUP_FOLLOW | LOOKUP_DIRECTORY两个标志。
LOOKUP_DIRECTORY,表示要查找的对象时一个目录。
*/
last_with_slashes:
-
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
-
last_component:
-
/* Clear LOOKUP_CONTINUE iff it was previously unset */
-
nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
-
if (lookup_flags & LOOKUP_PARENT)
-
goto lookup_parent;
/* 对于最后分量的处理,同样要对"."和".."的特殊处理,
如果是"."则不更新nd->path
如果是"..",则调用follow_dotdot,更新nd->path为父目录的dentry和vfsmount,返回父目录要着重考虑跨越mountponit的情况 */
-
if (this.name[0] == '.') switch (this.len) {
-
default:
-
break;
-
case 2:
-
if (this.name[1] != '.')
-
break;
-
follow_dotdot(nd);
-
inode = nd->path.dentry->d_inode;
-
/* fallthrough */
-
case 1:
-
goto return_reval;
-
}
-
err = do_lookup(nd, &this, &next);
-
if (err)
-
break;
-
inode = next.dentry->d_inode;
-
if (follow_on_final(inode, lookup_flags)) {
-
err = do_follow_link(&next, nd);
-
if (err)
-
goto return_err;
-
inode = nd->path.dentry->d_inode;
-
} else
-
path_to_nameidata(&next, nd);
-
err = -ENOENT;
-
if (!inode)
-
break;
-
if (lookup_flags & LOOKUP_DIRECTORY) {
-
err = -ENOTDIR;
-
/* 如果查找对象是一个目录,则inode必须有lookup函数,否则就返回错误信息 */
-
if (!inode->i_op->lookup)
-
break;
-
}
-
goto return_base;
-
lookup_parent:
-
nd->last = this;
-
nd->last_type = LAST_NORM;
-
if (this.name[0] != '.')
-
goto return_base;
-
if (this.len == 1)
-
nd->last_type = LAST_DOT;
-
else if (this.len == 2 && this.name[1] == '.')
-
nd->last_type = LAST_DOTDOT;
-
else
-
goto return_base;
-
/* 路径最后分量的处理,end
-
return_reval:
-
/*
-
* We bypassed the ordinary revalidation routines.
-
* We may need to check the cached dentry for staleness.
-
*/
-
if (nd->path.dentry && nd->path.dentry->d_sb &&
-
(nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
-
err = -ESTALE;
-
/* Note: we do not d_invalidate() */
-
if (!nd->path.dentry->d_op->d_revalidate(
-
nd->path.dentry, nd))
-
break;
-
}
-
return_base:
-
return 0;
-
out_dput:
-
path_put_conditional(&next, nd);
-
break;
-
} /* for循环的结束 */
-
path_put(&nd->path);
-
return_err:
-
return err;
-
}