diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 98 |
1 files changed, 64 insertions, 34 deletions
diff --git a/fs/namei.c b/fs/namei.c index b4589922c0de..85d19e6cc8c0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -265,7 +265,7 @@ static int check_acl(struct inode *inode, int mask) if (!acl) return -EAGAIN; /* no ->get_acl() calls in RCU mode... */ - if (acl == ACL_NOT_CACHED) + if (is_uncached_acl(acl)) return -ECHILD; return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK); } @@ -1603,32 +1603,42 @@ static struct dentry *lookup_slow(const struct qstr *name, struct dentry *dir, unsigned int flags) { - struct dentry *dentry; - inode_lock(dir->d_inode); - dentry = d_lookup(dir, name); - if (unlikely(dentry)) { + struct dentry *dentry = ERR_PTR(-ENOENT), *old; + struct inode *inode = dir->d_inode; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); + + inode_lock_shared(inode); + /* Don't go there if it's already dead */ + if (unlikely(IS_DEADDIR(inode))) + goto out; +again: + dentry = d_alloc_parallel(dir, name, &wq); + if (IS_ERR(dentry)) + goto out; + if (unlikely(!d_in_lookup(dentry))) { if ((dentry->d_flags & DCACHE_OP_REVALIDATE) && !(flags & LOOKUP_NO_REVAL)) { int error = d_revalidate(dentry, flags); if (unlikely(error <= 0)) { - if (!error) + if (!error) { d_invalidate(dentry); + dput(dentry); + goto again; + } dput(dentry); dentry = ERR_PTR(error); } } - if (dentry) { - inode_unlock(dir->d_inode); - return dentry; + } else { + old = inode->i_op->lookup(inode, dentry, flags); + d_lookup_done(dentry); + if (unlikely(old)) { + dput(dentry); + dentry = old; } } - dentry = d_alloc(dir, name); - if (unlikely(!dentry)) { - inode_unlock(dir->d_inode); - return ERR_PTR(-ENOMEM); - } - dentry = lookup_real(dir->d_inode, dentry, flags); - inode_unlock(dir->d_inode); +out: + inode_unlock_shared(inode); return dentry; } @@ -2655,7 +2665,7 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) return NULL; } - mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex); + mutex_lock(&p1->d_sb->s_vfs_rename_mutex); p = d_ancestor(p2, p1); if (p) { @@ -2682,7 +2692,7 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) inode_unlock(p1->d_inode); if (p1 != p2) { inode_unlock(p2->d_inode); - mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex); + mutex_unlock(&p1->d_sb->s_vfs_rename_mutex); } } EXPORT_SYMBOL(unlock_rename); @@ -2828,12 +2838,6 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, BUG_ON(dentry->d_inode); - /* Don't create child dentry for a dead directory. */ - if (unlikely(IS_DEADDIR(dir))) { - error = -ENOENT; - goto out; - } - mode = op->mode; if ((open_flag & O_CREAT) && !IS_POSIXACL(dir)) mode &= ~current_umask(); @@ -2982,6 +2986,9 @@ static int lookup_open(struct nameidata *nd, struct path *path, int error; bool need_lookup = false; + if (unlikely(IS_DEADDIR(dir_inode))) + return -ENOENT; + *opened &= ~FILE_CREATED; dentry = lookup_dcache(&nd->last, dir, nd->flags); if (IS_ERR(dentry)) @@ -2997,7 +3004,7 @@ static int lookup_open(struct nameidata *nd, struct path *path, goto out_no_open; } - if ((nd->flags & LOOKUP_OPEN) && dir_inode->i_op->atomic_open) { + if (dir_inode->i_op->atomic_open) { return atomic_open(nd, dentry, path, file, op, got_write, need_lookup, opened); } @@ -3027,13 +3034,19 @@ static int lookup_open(struct nameidata *nd, struct path *path, goto out_dput; } *opened |= FILE_CREATED; - error = security_path_mknod(&nd->path, dentry, mode, 0); + audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); + error = may_o_create(&nd->path, dentry, mode); if (error) goto out_dput; - error = vfs_create(dir->d_inode, dentry, mode, - nd->flags & LOOKUP_EXCL); + if (!dir_inode->i_op->create) { + error = -EACCES; + goto out_dput; + } + error = dir_inode->i_op->create(dir_inode, dentry, mode, + op->open_flag & O_EXCL); if (error) goto out_dput; + fsnotify_create(dir_inode, dentry); } out_no_open: path->dentry = dentry; @@ -3197,7 +3210,7 @@ finish_open: return error; } audit_inode(nd->name, nd->path.dentry, 0); - if (unlikely(d_is_symlink(nd->path.dentry)) && !(open_flag & O_PATH)) { + if (unlikely(d_is_symlink(nd->path.dentry))) { error = -ELOOP; goto out; } @@ -3217,11 +3230,9 @@ finish_open: got_write = true; } finish_open_created: - if (likely(!(open_flag & O_PATH))) { - error = may_open(&nd->path, acc_mode, open_flag); - if (error) - goto out; - } + error = may_open(&nd->path, acc_mode, open_flag); + if (error) + goto out; BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */ error = vfs_open(&nd->path, file, current_cred()); if (!error) { @@ -3335,6 +3346,18 @@ out: return error; } +static int do_o_path(struct nameidata *nd, unsigned flags, struct file *file) +{ + struct path path; + int error = path_lookupat(nd, flags, &path); + if (!error) { + audit_inode(nd->name, path.dentry, 0); + error = vfs_open(&path, file, current_cred()); + path_put(&path); + } + return error; +} + static struct file *path_openat(struct nameidata *nd, const struct open_flags *op, unsigned flags) { @@ -3354,6 +3377,13 @@ static struct file *path_openat(struct nameidata *nd, goto out2; } + if (unlikely(file->f_flags & O_PATH)) { + error = do_o_path(nd, flags, file); + if (!error) + opened |= FILE_OPENED; + goto out2; + } + s = path_init(nd, flags); if (IS_ERR(s)) { put_filp(file); |