merge do_revalidate() into its only caller
[pandora-kernel.git] / fs / namei.c
index 9e425e7..42ccb97 100644 (file)
@@ -176,12 +176,12 @@ EXPORT_SYMBOL(putname);
 /*
  * This does basic POSIX ACL permission checking
  */
-static int acl_permission_check(struct inode *inode, int mask, unsigned int flags,
-               int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
+static int acl_permission_check(struct inode *inode, int mask)
 {
+       int (*check_acl)(struct inode *inode, int mask);
        unsigned int mode = inode->i_mode;
 
-       mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
+       mask &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;
 
        if (current_user_ns() != inode_userns(inode))
                goto other_perms;
@@ -189,8 +189,9 @@ static int acl_permission_check(struct inode *inode, int mask, unsigned int flag
        if (current_fsuid() == inode->i_uid)
                mode >>= 6;
        else {
+               check_acl = inode->i_op->check_acl;
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
-                       int error = check_acl(inode, mask, flags);
+                       int error = check_acl(inode, mask);
                        if (error != -EAGAIN)
                                return error;
                }
@@ -203,7 +204,7 @@ other_perms:
        /*
         * If the DACs are ok we don't need any capability check.
         */
-       if ((mask & ~mode) == 0)
+       if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
                return 0;
        return -EACCES;
 }
@@ -212,7 +213,6 @@ other_perms:
  * 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)
- * @check_acl: optional callback to check for Posix ACLs
  * @flags:     IPERM_FLAG_ flags.
  *
  * Used to check for read/write/execute permissions on a file.
@@ -224,23 +224,32 @@ other_perms:
  * 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, unsigned int flags,
-       int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
+int generic_permission(struct inode *inode, int mask)
 {
        int ret;
 
        /*
         * Do the basic POSIX ACL permission checks.
         */
-       ret = acl_permission_check(inode, mask, flags, check_acl);
+       ret = acl_permission_check(inode, mask);
        if (ret != -EACCES)
                return ret;
 
+       if (S_ISDIR(inode->i_mode)) {
+               /* DACs are overridable for directories */
+               if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE))
+                       return 0;
+               if (!(mask & MAY_WRITE))
+                       if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH))
+                               return 0;
+               return -EACCES;
+       }
        /*
         * Read/write DACs are always overridable.
-        * Executable DACs are overridable if at least one exec bit is set.
+        * Executable DACs are overridable when there is
+        * at least one exec bit set.
         */
-       if (!(mask & MAY_EXEC) || execute_ok(inode))
+       if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
                if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE))
                        return 0;
 
@@ -248,7 +257,7 @@ int generic_permission(struct inode *inode, int mask, unsigned int flags,
         * Searching includes executable on directories, else just read.
         */
        mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
-       if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
+       if (mask == MAY_READ)
                if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH))
                        return 0;
 
@@ -287,10 +296,9 @@ int inode_permission(struct inode *inode, int mask)
        }
 
        if (inode->i_op->permission)
-               retval = inode->i_op->permission(inode, mask, 0);
+               retval = inode->i_op->permission(inode, mask);
        else
-               retval = generic_permission(inode, mask, 0,
-                               inode->i_op->check_acl);
+               retval = generic_permission(inode, mask);
 
        if (retval)
                return retval;
@@ -302,69 +310,6 @@ int inode_permission(struct inode *inode, int mask)
        return security_inode_permission(inode, mask);
 }
 
-/**
- * file_permission  -  check for additional access rights to a given file
- * @file:      file 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 an already opened
- * file.
- *
- * Note:
- *     Do not use this function in new code.  All access checks should
- *     be done using inode_permission().
- */
-int file_permission(struct file *file, int mask)
-{
-       return inode_permission(file->f_path.dentry->d_inode, mask);
-}
-
-/*
- * get_write_access() gets write permission for a file.
- * put_write_access() releases this write permission.
- * This is used for regular files.
- * We cannot support write (and maybe mmap read-write shared) accesses and
- * MAP_DENYWRITE mmappings simultaneously. The i_writecount field of an inode
- * can have the following values:
- * 0: no writers, no VM_DENYWRITE mappings
- * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist
- * > 0: (i_writecount) users are writing to the file.
- *
- * Normally we operate on that counter with atomic_{inc,dec} and it's safe
- * except for the cases where we don't hold i_writecount yet. Then we need to
- * use {get,deny}_write_access() - these functions check the sign and refuse
- * to do the change if sign is wrong. Exclusion between them is provided by
- * the inode->i_lock spinlock.
- */
-
-int get_write_access(struct inode * inode)
-{
-       spin_lock(&inode->i_lock);
-       if (atomic_read(&inode->i_writecount) < 0) {
-               spin_unlock(&inode->i_lock);
-               return -ETXTBSY;
-       }
-       atomic_inc(&inode->i_writecount);
-       spin_unlock(&inode->i_lock);
-
-       return 0;
-}
-
-int deny_write_access(struct file * file)
-{
-       struct inode *inode = file->f_path.dentry->d_inode;
-
-       spin_lock(&inode->i_lock);
-       if (atomic_read(&inode->i_writecount) > 0) {
-               spin_unlock(&inode->i_lock);
-               return -ETXTBSY;
-       }
-       atomic_dec(&inode->i_writecount);
-       spin_unlock(&inode->i_lock);
-
-       return 0;
-}
-
 /**
  * path_get - get a reference to a path
  * @path: path to get the reference to
@@ -432,6 +377,8 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
                        goto err_parent;
                BUG_ON(nd->inode != parent->d_inode);
        } else {
+               if (dentry->d_parent != parent)
+                       goto err_parent;
                spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
                if (!__d_rcu_to_refcount(dentry, nd->seq))
                        goto err_child;
@@ -489,28 +436,6 @@ static inline int d_revalidate(struct dentry *dentry, struct nameidata *nd)
        return dentry->d_op->d_revalidate(dentry, nd);
 }
 
-static struct dentry *
-do_revalidate(struct dentry *dentry, struct nameidata *nd)
-{
-       int status = d_revalidate(dentry, nd);
-       if (unlikely(status <= 0)) {
-               /*
-                * The dentry failed validation.
-                * If d_revalidate returned 0 attempt to invalidate
-                * the dentry otherwise d_revalidate is asking us
-                * to return a fail status.
-                */
-               if (status < 0) {
-                       dput(dentry);
-                       dentry = ERR_PTR(status);
-               } else if (!d_invalidate(dentry)) {
-                       dput(dentry);
-                       dentry = NULL;
-               }
-       }
-       return dentry;
-}
-
 /**
  * complete_walk - successful completion of path walk
  * @nd:  pointer nameidata
@@ -565,40 +490,6 @@ static int complete_walk(struct nameidata *nd)
        return status;
 }
 
-/*
- * Short-cut version of permission(), for calling on directories
- * during pathname resolution.  Combines parts of permission()
- * and generic_permission(), and tests ONLY for MAY_EXEC permission.
- *
- * If appropriate, check DAC only.  If not appropriate, or
- * short-cut DAC fails, then call ->permission() to do more
- * complete permission check.
- */
-static inline int exec_permission(struct inode *inode, unsigned int flags)
-{
-       int ret;
-       struct user_namespace *ns = inode_userns(inode);
-
-       if (inode->i_op->permission) {
-               ret = inode->i_op->permission(inode, MAY_EXEC, flags);
-       } else {
-               ret = acl_permission_check(inode, MAY_EXEC, flags,
-                               inode->i_op->check_acl);
-       }
-       if (likely(!ret))
-               goto ok;
-       if (ret == -ECHILD)
-               return ret;
-
-       if (ns_capable(ns, CAP_DAC_OVERRIDE) ||
-                       ns_capable(ns, CAP_DAC_READ_SEARCH))
-               goto ok;
-
-       return ret;
-ok:
-       return security_inode_exec_permission(inode, flags);
-}
-
 static __always_inline void set_root(struct nameidata *nd)
 {
        if (!nd->root.mnt)
@@ -939,7 +830,6 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                 * Don't forget we might have a non-mountpoint managed dentry
                 * that wants to block transit.
                 */
-               *inode = path->dentry->d_inode;
                if (unlikely(managed_dentry_might_block(path->dentry)))
                        return false;
 
@@ -952,6 +842,12 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                path->mnt = mounted;
                path->dentry = mounted->mnt_root;
                nd->seq = read_seqcount_begin(&path->dentry->d_seq);
+               /*
+                * Update the inode too. We don't need to re-check the
+                * dentry sequence number here after this d_inode read,
+                * because a mount-point is always pinned.
+                */
+               *inode = path->dentry->d_inode;
        }
        return true;
 }
@@ -1011,9 +907,6 @@ failed:
  * Follow down to the covering mount currently visible to userspace.  At each
  * point, the filesystem owning that dentry may be queried as to whether the
  * caller is permitted to proceed or not.
- *
- * Care must be taken as namespace_sem may be held (indicated by mounting_here
- * being true).
  */
 int follow_down(struct path *path)
 {
@@ -1128,6 +1021,30 @@ static struct dentry *d_alloc_and_lookup(struct dentry *parent,
        return dentry;
 }
 
+/*
+ * We already have a dentry, but require a lookup to be performed on the parent
+ * directory to fill in d_inode. Returns the new dentry, or ERR_PTR on error.
+ * parent->d_inode->i_mutex must be held. d_lookup must have verified that no
+ * child exists while under i_mutex.
+ */
+static struct dentry *d_inode_lookup(struct dentry *parent, struct dentry *dentry,
+                                    struct nameidata *nd)
+{
+       struct inode *inode = parent->d_inode;
+       struct dentry *old;
+
+       /* Don't create child dentry for a dead directory. */
+       if (unlikely(IS_DEADDIR(inode)))
+               return ERR_PTR(-ENOENT);
+
+       old = inode->i_op->lookup(inode, dentry, nd);
+       if (unlikely(old)) {
+               dput(dentry);
+               dentry = old;
+       }
+       return dentry;
+}
+
 /*
  *  It's more convoluted than I'd like it to be, but... it's still fairly
  *  small and for now I'd prefer to have fast path as straight as possible.
@@ -1167,6 +1084,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
                                goto unlazy;
                        }
                }
+               if (unlikely(d_need_lookup(dentry)))
+                       goto unlazy;
                path->mnt = mnt;
                path->dentry = dentry;
                if (unlikely(!__follow_mount_rcu(nd, path, inode)))
@@ -1181,6 +1100,10 @@ unlazy:
                dentry = __d_lookup(parent, name);
        }
 
+       if (dentry && unlikely(d_need_lookup(dentry))) {
+               dput(dentry);
+               dentry = NULL;
+       }
 retry:
        if (unlikely(!dentry)) {
                struct inode *dir = parent->d_inode;
@@ -1197,6 +1120,15 @@ retry:
                        /* known good */
                        need_reval = 0;
                        status = 1;
+               } else if (unlikely(d_need_lookup(dentry))) {
+                       dentry = d_inode_lookup(parent, dentry, nd);
+                       if (IS_ERR(dentry)) {
+                               mutex_unlock(&dir->i_mutex);
+                               return PTR_ERR(dentry);
+                       }
+                       /* known good */
+                       need_reval = 0;
+                       status = 1;
                }
                mutex_unlock(&dir->i_mutex);
        }
@@ -1229,13 +1161,13 @@ retry:
 static inline int may_lookup(struct nameidata *nd)
 {
        if (nd->flags & LOOKUP_RCU) {
-               int err = exec_permission(nd->inode, IPERM_FLAG_RCU);
+               int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
                if (err != -ECHILD)
                        return err;
                if (unlazy_walk(nd, NULL))
                        return -ECHILD;
        }
-       return exec_permission(nd->inode, 0);
+       return inode_permission(nd->inode, MAY_EXEC);
 }
 
 static inline int handle_dots(struct nameidata *nd, int type)
@@ -1510,7 +1442,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                        if (!S_ISDIR(dentry->d_inode->i_mode))
                                goto fput_fail;
 
-                       retval = file_permission(file, MAY_EXEC);
+                       retval = inode_permission(dentry->d_inode, MAY_EXEC);
                        if (retval)
                                goto fput_fail;
                }
@@ -1667,7 +1599,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
        struct dentry *dentry;
        int err;
 
-       err = exec_permission(inode, 0);
+       err = inode_permission(inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
 
@@ -1678,8 +1610,34 @@ static struct dentry *__lookup_hash(struct qstr *name,
         */
        dentry = d_lookup(base, name);
 
-       if (dentry && (dentry->d_flags & DCACHE_OP_REVALIDATE))
-               dentry = do_revalidate(dentry, nd);
+       if (dentry && d_need_lookup(dentry)) {
+               /*
+                * __lookup_hash is called with the parent dir's i_mutex already
+                * held, so we are good to go here.
+                */
+               dentry = d_inode_lookup(base, dentry, nd);
+               if (IS_ERR(dentry))
+                       return dentry;
+       }
+
+       if (dentry && (dentry->d_flags & DCACHE_OP_REVALIDATE)) {
+               int status = d_revalidate(dentry, nd);
+               if (unlikely(status <= 0)) {
+                       /*
+                        * The dentry failed validation.
+                        * If d_revalidate returned 0 attempt to invalidate
+                        * the dentry otherwise d_revalidate is asking us
+                        * to return a fail status.
+                        */
+                       if (status < 0) {
+                               dput(dentry);
+                               return ERR_PTR(status);
+                       } else if (!d_invalidate(dentry)) {
+                               dput(dentry);
+                               dentry = NULL;
+                       }
+               }
+       }
 
        if (!dentry)
                dentry = d_alloc_and_lookup(base, name, nd);
@@ -3351,7 +3309,6 @@ EXPORT_SYMBOL(kern_path_parent);
 EXPORT_SYMBOL(kern_path);
 EXPORT_SYMBOL(vfs_path_lookup);
 EXPORT_SYMBOL(inode_permission);
-EXPORT_SYMBOL(file_permission);
 EXPORT_SYMBOL(unlock_rename);
 EXPORT_SYMBOL(vfs_create);
 EXPORT_SYMBOL(vfs_follow_link);