Created
May 5, 2019 05:39
-
-
Save knknkn1162/3e4b9beb11959e36543bd47f34ebc67c to your computer and use it in GitHub Desktop.
link_path_walk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* | |
* 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. | |
*/ | |
int fastcall 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->dentry->d_inode; | |
if (nd->depth) | |
lookup_flags = LOOKUP_FOLLOW; | |
/* At this point we know we have a real path component. */ | |
for(;;) { | |
unsigned long hash; | |
struct qstr this; | |
unsigned int c; | |
err = exec_permission_lite(inode, nd); | |
if (err == -EAGAIN) { | |
err = permission(inode, MAY_EXEC, nd); | |
} | |
if (err) | |
break; | |
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); | |
/* remove trailing slashes? */ | |
if (!c) | |
goto last_component; | |
while (*++name == '/'); | |
if (!*name) | |
goto last_with_slashes; | |
/* | |
* "." 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->mnt, &nd->dentry); | |
inode = nd->dentry->d_inode; | |
/* fallthrough */ | |
case 1: | |
continue; | |
} | |
/* | |
* See if the low-level filesystem might want | |
* to use its own hash.. | |
*/ | |
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) { | |
err = nd->dentry->d_op->d_hash(nd->dentry, &this); | |
if (err < 0) | |
break; | |
} | |
nd->flags |= LOOKUP_CONTINUE; | |
/* This does the actual lookups.. */ | |
err = do_lookup(nd, &this, &next); | |
if (err) | |
break; | |
/* Check mountpoints.. */ | |
follow_mount(&next.mnt, &next.dentry); | |
err = -ENOENT; | |
inode = next.dentry->d_inode; | |
if (!inode) | |
goto out_dput; | |
err = -ENOTDIR; | |
if (!inode->i_op) | |
goto out_dput; | |
if (inode->i_op->follow_link) { | |
mntget(next.mnt); | |
err = do_follow_link(next.dentry, nd); | |
dput(next.dentry); | |
mntput(next.mnt); | |
if (err) | |
goto return_err; | |
err = -ENOENT; | |
inode = nd->dentry->d_inode; | |
if (!inode) | |
break; | |
err = -ENOTDIR; | |
if (!inode->i_op) | |
break; | |
} else { | |
dput(nd->dentry); | |
nd->mnt = next.mnt; | |
nd->dentry = next.dentry; | |
} | |
err = -ENOTDIR; | |
if (!inode->i_op->lookup) | |
break; | |
continue; | |
/* here ends the main loop */ | |
last_with_slashes: | |
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; | |
last_component: | |
nd->flags &= ~LOOKUP_CONTINUE; | |
if (lookup_flags & LOOKUP_PARENT) | |
goto lookup_parent; | |
if (this.name[0] == '.') switch (this.len) { | |
default: | |
break; | |
case 2: | |
if (this.name[1] != '.') | |
break; | |
follow_dotdot(&nd->mnt, &nd->dentry); | |
inode = nd->dentry->d_inode; | |
/* fallthrough */ | |
case 1: | |
goto return_reval; | |
} | |
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) { | |
err = nd->dentry->d_op->d_hash(nd->dentry, &this); | |
if (err < 0) | |
break; | |
} | |
err = do_lookup(nd, &this, &next); | |
if (err) | |
break; | |
follow_mount(&next.mnt, &next.dentry); | |
inode = next.dentry->d_inode; | |
if ((lookup_flags & LOOKUP_FOLLOW) | |
&& inode && inode->i_op && inode->i_op->follow_link) { | |
mntget(next.mnt); | |
err = do_follow_link(next.dentry, nd); | |
dput(next.dentry); | |
mntput(next.mnt); | |
if (err) | |
goto return_err; | |
inode = nd->dentry->d_inode; | |
} else { | |
dput(nd->dentry); | |
nd->mnt = next.mnt; | |
nd->dentry = next.dentry; | |
} | |
err = -ENOENT; | |
if (!inode) | |
break; | |
if (lookup_flags & LOOKUP_DIRECTORY) { | |
err = -ENOTDIR; | |
if (!inode->i_op || !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; | |
return_reval: | |
/* | |
* We bypassed the ordinary revalidation routines. | |
* We may need to check the cached dentry for staleness. | |
*/ | |
if (nd->dentry && nd->dentry->d_sb && | |
(nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) { | |
err = -ESTALE; | |
/* Note: we do not d_invalidate() */ | |
if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd)) | |
break; | |
} | |
return_base: | |
return 0; | |
out_dput: | |
dput(next.dentry); | |
break; | |
} | |
path_release(nd); | |
return_err: | |
return err; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment