cregit-Linux how code gets into the kernel

Release 4.7 fs/namei.c

Directory: fs
/*
 *  linux/fs/namei.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/*
 * Some corrections by tytso.
 */

/* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname
 * lookup logic.
 */
/* [Feb-Apr 2000, AV] Rewrite to the new namespace architecture.
 */

#include <linux/init.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/fsnotify.h>
#include <linux/personality.h>
#include <linux/security.h>
#include <linux/ima.h>
#include <linux/syscalls.h>
#include <linux/mount.h>
#include <linux/audit.h>
#include <linux/capability.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/device_cgroup.h>
#include <linux/fs_struct.h>
#include <linux/posix_acl.h>
#include <linux/hash.h>
#include <linux/bitops.h>
#include <asm/uaccess.h>

#include "internal.h"
#include "mount.h"

/* [Feb-1997 T. Schoebel-Theuer]
 * Fundamental changes in the pathname lookup mechanisms (namei)
 * were necessary because of omirr.  The reason is that omirr needs
 * to know the _real_ pathname, not the user-supplied one, in case
 * of symlinks (and also when transname replacements occur).
 *
 * The new code replaces the old recursive symlink resolution with
 * an iterative one (in case of non-nested symlink chains).  It does
 * this with calls to <fs>_follow_link().
 * As a side effect, dir_namei(), _namei() and follow_link() are now 
 * replaced with a single function lookup_dentry() that can handle all 
 * the special cases of the former code.
 *
 * With the new dcache, the pathname is stored at each inode, at least as
 * long as the refcount of the inode is positive.  As a side effect, the
 * size of the dcache depends on the inode cache and thus is dynamic.
 *
 * [29-Apr-1998 C. Scott Ananian] Updated above description of symlink
 * resolution to correspond with current state of the code.
 *
 * Note that the symlink resolution is not *completely* iterative.
 * There is still a significant amount of tail- and mid- recursion in
 * the algorithm.  Also, note that <fs>_readlink() is not used in
 * lookup_dentry(): lookup_dentry() on the result of <fs>_readlink()
 * may return different results than <fs>_follow_link().  Many virtual
 * filesystems (including /proc) exhibit this behavior.
 */

/* [24-Feb-97 T. Schoebel-Theuer] Side effects caused by new implementation:
 * New symlink semantics: when open() is called with flags O_CREAT | O_EXCL
 * and the name already exists in form of a symlink, try to create the new
 * name indicated by the symlink. The old code always complained that the
 * name already exists, due to not following the symlink even if its target
 * is nonexistent.  The new semantics affects also mknod() and link() when
 * the name is a symlink pointing to a non-existent name.
 *
 * I don't know which semantics is the right one, since I have no access
 * to standards. But I found by trial that HP-UX 9.0 has the full "new"
 * semantics implemented, while SunOS 4.1.1 and Solaris (SunOS 5.4) have the
 * "old" one. Personally, I think the new semantics is much more logical.
 * Note that "ln old new" where "new" is a symlink pointing to a non-existing
 * file does succeed in both HP-UX and SunOs, but not in Solaris
 * and in the old Linux semantics.
 */

/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink
 * semantics.  See the comments in "open_namei" and "do_link" below.
 *
 * [10-Sep-98 Alan Modra] Another symlink change.
 */

/* [Feb-Apr 2000 AV] Complete rewrite. Rules for symlinks:
 *      inside the path - always follow.
 *      in the last component in creation/removal/renaming - never follow.
 *      if LOOKUP_FOLLOW passed - follow.
 *      if the pathname has trailing slashes - follow.
 *      otherwise - don't follow.
 * (applied in that order).
 *
 * [Jun 2000 AV] Inconsistent behaviour of open() in case if flags==O_CREAT
 * restored for 2.4. This is the last surviving part of old 4.2BSD bug.
 * During the 2.4 we need to fix the userland stuff depending on it -
 * hopefully we will be able to get rid of that wart in 2.5. So far only
 * XEmacs seems to be relying on it...
 */
/*
 * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland)
 * implemented.  Let's see if raised priority of ->s_vfs_rename_mutex gives
 * any extra contention...
 */

/* In order to reduce some races, while at the same time doing additional
 * checking and hopefully speeding things up, we copy filenames to the
 * kernel data space before using them..
 *
 * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
 * PATH_MAX includes the nul terminator --RR.
 */


#define EMBEDDED_NAME_MAX	(PATH_MAX - offsetof(struct filename, iname))


struct filename * getname_flags(const char __user *filename, int flags, int *empty) { struct filename *result; char *kname; int len; result = audit_reusename(filename); if (result) return result; result = __getname(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); /* * First, try to embed the struct filename inside the names_cache * allocation */ kname = (char *)result->iname; result->name = kname; len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX); if (unlikely(len < 0)) { __putname(result); return ERR_PTR(len); } /* * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a * separate struct filename so we can dedicate the entire * names_cache allocation for the pathname, and re-do the copy from * userland. */ if (unlikely(len == EMBEDDED_NAME_MAX)) { const size_t size = offsetof(struct filename, iname[1]); kname = (char *)result; /* * size is chosen that way we to guarantee that * result->iname[0] is within the same object and that * kname can't be equal to result->iname, no matter what. */ result = kzalloc(size, GFP_KERNEL); if (unlikely(!result)) { __putname(kname); return ERR_PTR(-ENOMEM); } result->name = kname; len = strncpy_from_user(kname, filename, PATH_MAX); if (unlikely(len < 0)) { __putname(kname); kfree(result); return ERR_PTR(len); } if (unlikely(len == PATH_MAX)) { __putname(kname); kfree(result); return ERR_PTR(-ENAMETOOLONG); } } result->refcnt = 1; /* The empty path is special. */ if (unlikely(!len)) { if (empty) *empty = 1; if (!(flags & LOOKUP_EMPTY)) { putname(result); return ERR_PTR(-ENOENT); } } result->uptr = filename; result->aname = NULL; audit_getname(result); return result; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro13341.05%317.65%
jeff laytonjeff layton10331.79%317.65%
linus torvaldslinus torvalds3310.19%317.65%
pre-gitpre-git268.02%423.53%
eric pariseric paris113.40%15.88%
andy whitcroftandy whitcroft113.40%15.88%
paul moorepaul moore61.85%15.88%
chris wrightchris wright10.31%15.88%
Total324100.00%17100.00%


struct filename * getname(const char __user * filename) { return getname_flags(filename, 0, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro1982.61%125.00%
jeff laytonjeff layton28.70%125.00%
linus torvaldslinus torvalds14.35%125.00%
andy whitcroftandy whitcroft14.35%125.00%
Total23100.00%4100.00%


struct filename * getname_kernel(const char * filename) { struct filename *result; int len = strlen(filename) + 1; result = __getname(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); if (len <= EMBEDDED_NAME_MAX) { result->name = (char *)result->iname; } else if (len <= PATH_MAX) { struct filename *tmp; tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); if (unlikely(!tmp)) { __putname(result); return ERR_PTR(-ENOMEM); } tmp->name = (char *)result; result = tmp; } else { __putname(result); return ERR_PTR(-ENAMETOOLONG); } memcpy((char *)result->name, filename, len); result->uptr = NULL; result->aname = NULL; result->refcnt = 1; audit_getname(result); return result; }

Contributors

PersonTokensPropCommitsCommitProp
paul moorepaul moore11863.44%360.00%
linus torvaldslinus torvalds6736.02%120.00%
al viroal viro10.54%120.00%
Total186100.00%5100.00%


void putname(struct filename *name) { BUG_ON(name->refcnt <= 0); if (--name->refcnt > 0) return; if (name->name != name->iname) { __putname(name->name); kfree(name); } else __putname(name); }

Contributors

PersonTokensPropCommitsCommitProp
paul moorepaul moore3355.93%116.67%
andrew mortonandrew morton915.25%116.67%
chris wrightchris wright915.25%116.67%
al viroal viro58.47%116.67%
jeff laytonjeff layton23.39%116.67%
pre-gitpre-git11.69%116.67%
Total59100.00%6100.00%


static int check_acl(struct inode *inode, int mask) { #ifdef CONFIG_FS_POSIX_ACL struct posix_acl *acl; if (mask & MAY_NOT_BLOCK) { acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS); if (!acl) return -EAGAIN; /* no ->get_acl() calls in RCU mode... */ if (is_uncached_acl(acl)) return -ECHILD; return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK); } acl = get_acl(inode, ACL_TYPE_ACCESS); if (IS_ERR(acl)) return PTR_ERR(acl); if (acl) { int error = posix_acl_permission(inode, acl, mask); posix_acl_release(acl); return error; } #endif return -EAGAIN; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds7155.91%327.27%
al viroal viro2519.69%218.18%
christoph hellwigchristoph hellwig1612.60%218.18%
pre-gitpre-git97.09%218.18%
ari savolainenari savolainen32.36%19.09%
andreas gruenbacherandreas gruenbacher32.36%19.09%
Total127100.00%11100.00%

/* * This does the basic permission checking */
static int acl_permission_check(struct inode *inode, int mask) { unsigned int mode = inode->i_mode; if (likely(uid_eq(current_fsuid(), inode->i_uid))) mode >>= 6; else { if (IS_POSIXACL(inode) && (mode & S_IRWXG)) { int error = check_acl(inode, mask); if (error != -EAGAIN) return error; } if (in_group_p(inode->i_gid)) mode >>= 3; } /* * If the DACs are ok we don't need any capability check. */ if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) return 0; return -EACCES; }

Contributors

PersonTokensPropCommitsCommitProp
christoph hellwigchristoph hellwig4840.34%17.14%
pre-gitpre-git3428.57%321.43%
linus torvaldslinus torvalds1915.97%535.71%
al viroal viro119.24%214.29%
eric w. biedermaneric w. biederman43.36%17.14%
david howellsdavid howells21.68%17.14%
nick pigginnick piggin10.84%17.14%
Total119100.00%14100.00%

/** * generic_permission - check for access rights on a Posix-like filesystem * @inode: inode to check access rights for * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...) * * Used to check for read/write/execute permissions on a file. * We use "fsuid" for this, letting us set arbitrary permissions * for filesystem access without changing the "normal" uids which * are used for other things. * * generic_permission is rcu-walk aware. It returns -ECHILD in case an rcu-walk * request cannot be satisfied (eg. requires blocking or too much complexity). * It would then be called again in ref-walk mode. */
int generic_permission(struct inode *inode, int mask) { int ret; /* * Do the basic permission checks. */ ret = acl_permission_check(inode, mask); if (ret != -EACCES) return ret; if (S_ISDIR(inode->i_mode)) { /* DACs are overridable for directories */ if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) return 0; if (!(mask & MAY_WRITE)) if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH)) return 0; return -EACCES; } /* * Read/write DACs are always overridable. * Executable DACs are overridable when there is * at least one exec bit set. */ if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO)) if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) return 0; /* * Searching includes executable on directories, else just read. */ mask &= MAY_READ | MAY_WRITE | MAY_EXEC; if (mask == MAY_READ) if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH)) return 0; return -EACCES; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro5537.67%18.33%
linus torvaldslinus torvalds4530.82%216.67%
pre-gitpre-git2517.12%433.33%
serge hallynserge hallyn138.90%216.67%
andy lutomirskiandy lutomirski42.74%18.33%
andrew mortonandrew morton32.05%18.33%
andreas gruenbacherandreas gruenbacher10.68%18.33%
Total146100.00%12100.00%

EXPORT_SYMBOL(generic_permission); /* * We _really_ want to just do "generic_permission()" without * even looking at the inode->i_op values. So we keep a cache * flag in inode->i_opflags, that says "this has not special * permission function, use the fast case". */
static inline int do_inode_permission(struct inode *inode, int mask) { if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) { if (likely(inode->i_op->permission)) return inode->i_op->permission(inode, mask); /* This gets set once for the inode lifetime */ spin_lock(&inode->i_lock); inode->i_opflags |= IOP_FASTPERM; spin_unlock(&inode->i_lock); } return generic_permission(inode, mask); }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds85100.00%1100.00%
Total85100.00%1100.00%

/** * __inode_permission - Check for access rights to a given inode * @inode: Inode to check permission on * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) * * Check for read/write/execute permissions on an inode. * * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask. * * This does not check for a read-only file system. You probably want * inode_permission(). */
int __inode_permission(struct inode *inode, int mask) { int retval; if (unlikely(mask & MAY_WRITE)) { /* * Nobody gets write access to an immutable file. */ if (IS_IMMUTABLE(inode)) return -EACCES; } retval = do_inode_permission(inode, mask); if (retval) return retval; retval = devcgroup_inode_permission(inode, mask); if (retval) return retval; return security_inode_permission(inode, mask); }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells5569.62%120.00%
pre-gitpre-git1113.92%120.00%
christoph hellwigchristoph hellwig78.86%120.00%
stephen d. smalleystephen d. smalley33.80%120.00%
linus torvaldslinus torvalds33.80%120.00%
Total79100.00%5100.00%

EXPORT_SYMBOL(__inode_permission); /** * sb_permission - Check superblock-level permissions * @sb: Superblock of inode to check permission on * @inode: Inode to check permission on * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) * * Separate out file-system wide checks from inode-specific permission checks. */
static int sb_permission(struct super_block *sb, struct inode *inode, int mask) { if (unlikely(mask & MAY_WRITE)) { umode_t mode = inode->i_mode; /* Nobody gets write access to a read-only fs. */ if ((sb->s_flags & MS_RDONLY) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) return -EROFS; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells3852.78%133.33%
christoph hellwigchristoph hellwig2737.50%133.33%
miklos szeredimiklos szeredi79.72%133.33%
Total72100.00%3100.00%

/** * inode_permission - Check for access rights to a given inode * @inode: Inode to check permission on * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) * * Check for read/write/execute permissions on an inode. We use fs[ug]id for * this, letting us set arbitrary permissions for filesystem access without * changing the "normal" UIDs which are used for other things. * * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask. */
int inode_permission(struct inode *inode, int mask) { int retval; retval = sb_permission(inode->i_sb, inode, mask); if (retval) return retval; return __inode_permission(inode, mask); }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells1840.91%120.00%
serge hallynserge hallyn1534.09%120.00%
pre-gitpre-git818.18%120.00%
stephen d. smalleystephen d. smalley24.55%120.00%
al viroal viro12.27%120.00%
Total44100.00%5100.00%

EXPORT_SYMBOL(inode_permission); /** * path_get - get a reference to a path * @path: path to get the reference to * * Given a path increment the reference count to the dentry and the vfsmount. */
void path_get(const struct path *path) { mntget(path->mnt); dget(path->dentry); }

Contributors

PersonTokensPropCommitsCommitProp
jan blunckjan blunck2496.00%150.00%
al viroal viro14.00%150.00%
Total25100.00%2100.00%

EXPORT_SYMBOL(path_get); /** * path_put - put a reference to a path * @path: path to put the reference to * * Given a path decrement the reference count to the dentry and the vfsmount. */
void path_put(const struct path *path) { dput(path->dentry); mntput(path->mnt); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git1768.00%125.00%
jan blunckjan blunck728.00%250.00%
al viroal viro14.00%125.00%
Total25100.00%4100.00%

EXPORT_SYMBOL(path_put); #define EMBEDDED_LEVELS 2 struct nameidata { struct path path; struct qstr last; struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; unsigned seq, m_seq; int last_type; unsigned depth; int total_link_count; struct saved { struct path link; struct delayed_call done; const char *name; unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; struct filename *name; struct nameidata *saved; struct inode *link_inode; unsigned root_seq; int dfd; };
static void set_nameidata(struct nameidata *p, int dfd, struct filename *name) { struct nameidata *old = current->nameidata; p->stack = p->internal; p->dfd = dfd; p->name = name; p->total_link_count = old ? old->total_link_count : 0; p->saved = old; current->nameidata = p; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro4258.33%375.00%
neil brownneil brown3041.67%125.00%
Total72100.00%4100.00%


static void restore_nameidata(void) { struct nameidata *now = current->nameidata, *old = now->saved; current->nameidata = old; if (old) old->total_link_count = now->total_link_count; if (now->stack != now->internal) kfree(now->stack); }

Contributors

PersonTokensPropCommitsCommitProp
neil brownneil brown3050.85%133.33%
al viroal viro2949.15%266.67%
Total59100.00%3100.00%


static int __nd_alloc_stack(struct nameidata *nd) { struct saved *p; if (nd->flags & LOOKUP_RCU) { p= kmalloc(MAXSYMLINKS * sizeof(struct saved), GFP_ATOMIC); if (unlikely(!p)) return -ECHILD; } else { p= kmalloc(MAXSYMLINKS * sizeof(struct saved), GFP_KERNEL); if (unlikely(!p)) return -ENOMEM; } memcpy(p, nd->internal, sizeof(nd->internal)); nd->stack = p; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro108100.00%2100.00%
Total108100.00%2100.00%

/** * path_connected - Verify that a path->dentry is below path->mnt.mnt_root * @path: nameidate to verify * * Rename can sometimes move a file or directory outside of a bind * mount, path_connected allows those cases to be detected. */
static bool path_connected(const struct path *path) { struct vfsmount *mnt = path->mnt; /* Only bind mounts can have disconnected paths */ if (mnt->mnt_root == mnt->mnt_sb->s_root) return true; return is_subdir(path->dentry, mnt->mnt_root); }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman49100.00%1100.00%
Total49100.00%1100.00%


static inline int nd_alloc_stack(struct nameidata *nd) { if (likely(nd->depth != EMBEDDED_LEVELS)) return 0; if (likely(nd->stack != nd->internal)) return 0; return __nd_alloc_stack(nd); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro48100.00%1100.00%
Total48100.00%1100.00%


static void drop_links(struct nameidata *nd) { int i = nd->depth; while (i--) { struct saved *last = nd->stack + i; do_delayed_call(&last->done); clear_delayed_call(&last->done); } }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro52100.00%2100.00%
Total52100.00%2100.00%


static void terminate_walk(struct nameidata *nd) { drop_links(nd); if (!(nd->flags & LOOKUP_RCU)) { int i; path_put(&nd->path); for (i = 0; i < nd->depth; i++) path_put(&nd->stack[i].link); if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { path_put(&nd->root); nd->root.mnt = NULL; } } else { nd->flags &= ~LOOKUP_RCU; if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; rcu_read_unlock(); } nd->depth = 0; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro141100.00%2100.00%
Total141100.00%2100.00%

/* path_put is needed afterwards regardless of success or failure */
static bool legitimize_path(struct nameidata *nd, struct path *path, unsigned seq) { int res = __legitimize_mnt(path->mnt, nd->m_seq); if (unlikely(res)) { if (res > 0) path->mnt = NULL; path->dentry = NULL; return false; } if (unlikely(!lockref_get_not_dead(&path->dentry->d_lockref))) { path->dentry = NULL; return false; } return !read_seqcount_retry(&path->dentry->d_seq, seq); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro104100.00%1100.00%
Total104100.00%1100.00%


static bool legitimize_links(struct nameidata *nd) { int i; for (i = 0; i < nd->depth; i++) { struct saved *last = nd->stack + i; if (unlikely(!legitimize_path(nd, &last->link, last->seq))) { drop_links(nd); nd->depth = i + 1; return false; } } return true; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro83100.00%1100.00%
Total83100.00%1100.00%

/* * Path walking has 2 modes, rcu-walk and ref-walk (see * Documentation/filesystems/path-lookup.txt). In situations when we can't * continue in RCU mode, we attempt to drop out of rcu-walk mode and grab * normal reference counts on dentries and vfsmounts to transition to ref-walk * mode. Refcounts are grabbed at the last known good point before rcu-walk * got stuck, so ref-walk may continue from there. If this is not successful * (eg. a seqcount has changed), then failure is returned and it's up to caller * to restart the path walk from the beginning in ref-walk mode. */ /** * unlazy_walk - try to switch to ref-walk mode. * @nd: nameidata pathwalk data * @dentry: child of nd->path.dentry or NULL * @seq: seq number to check dentry against * Returns: 0 on success, -ECHILD on failure * * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry * for ref-walk mode. @dentry must be a path found by a do_lookup call on * @nd or NULL. Must be called from rcu-walk context. * Nothing should touch nameidata between unlazy_walk() failure and * terminate_walk(). */
static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq) { struct dentry *parent = nd->path.dentry; BUG_ON(!(nd->flags & LOOKUP_RCU)); nd->flags &= ~LOOKUP_RCU; if (unlikely(!legitimize_links(nd))) goto out2; if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq))) goto out2; if (unlikely(!lockref_get_not_dead(&parent->d_lockref))) goto out1; /* * For a negative lookup, the lookup sequence point is the parents * sequence point, and it only needs to revalidate the parent dentry. * * For a positive lookup, we need to move both the parent and the * dentry from the RCU domain to be properly refcounted. And the * sequence number in the dentry validates *both* dentry counters, * since we checked the sequence number of the parent after we got * the child sequence number. So we know the parent must still * be valid if the child sequence number is still valid. */ if (!dentry) { if (read_seqcount_retry(&parent->d_seq, nd->seq)) goto out; BUG_ON(nd->inode != parent->d_inode); } else { if (!lockref_get_not_dead(&dentry->d_lockref)) goto out; if (read_seqcount_retry(&dentry->d_seq, seq)) goto drop_dentry; } /* * Sequence counts matched. Now make sure that the root is * still valid and get it if required. */ if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { if (unlikely(!legitimize_path(nd, &nd->root, nd->root_seq))) { rcu_read_unlock(); dput(dentry); return -ECHILD; } } rcu_read_unlock(); return 0; drop_dentry: rcu_read_unlock(); dput(dentry); goto drop_root_mnt; out2: nd->path.mnt = NULL; out1: nd->path.dentry = NULL; out: rcu_read_unlock(); drop_root_mnt: if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; return -ECHILD; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro11940.61%942.86%
linus torvaldslinus torvalds8830.03%419.05%
nick pigginnick piggin4916.72%14.76%
trond myklebusttrond myklebust144.78%14.76%
ian kentian kent134.44%29.52%
christoph hellwigchristoph hellwig72.39%29.52%
josef 'jeff' sipekjosef 'jeff' sipek20.68%14.76%
hanna linderhanna linder10.34%14.76%
Total293100.00%21100.00%


static int unlazy_link(struct nameidata *nd, struct path *link, unsigned seq) { if (unlikely(!legitimize_path(nd, link, seq))) { drop_links(nd); nd->depth = 0; nd->flags &= ~LOOKUP_RCU; nd->path.mnt = NULL; nd->path.dentry = NULL; if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; rcu_read_unlock(); } else if (likely(unlazy_walk(nd, NULL, 0)) == 0) { return 0; } path_put(link); return -ECHILD; }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro123100.00%1100.00%
Total123100.00%1100.00%


static inline int d_revalidate(struct dentry *dentry, unsigned int flags) { return dentry->d_op->d_revalidate(dentry, flags); }

Contributors

PersonTokensPropCommitsCommitProp
nick pigginnick piggin2382.14%125.00%
al viroal viro517.86%375.00%
Total28100.00%4100.00%

/** * complete_walk - successful completion of path walk * @nd: pointer nameidata * * If we had been in RCU mode, drop out of it and legitimize nd->path. * Revalidate the final result, unless we'd already done that during * the path walk or the filesystem doesn't ask for it. Return 0 on * success, -error on failure. In case of failure caller does not * need to drop nd->path. */
static int complete_walk(struct nameidata *nd) { struct dentry *dentry = nd->path.dentry; int status; if (nd->flags & LOOKUP_RCU) { if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; if (unlikely(unlazy_walk(nd, NULL, 0))) return -ECHILD; } if (likely(!(nd->flags & LOOKUP_JUMPED))) return 0; if (likely(!(dentry->d_flags & DCACHE_OP_WEAK_REVALIDATE))) return 0; status = dentry->d_op->d_weak_revalidate(dentry, nd->flags); if (status > 0) return 0; if (!status) status = -