proc: use seq_puts()/seq_putc() where possible
[cascardo/linux.git] / fs / namei.c
index 90bd287..24ece10 100644 (file)
@@ -169,8 +169,8 @@ EXPORT_SYMBOL(putname);
 /*
  * This does basic POSIX ACL permission checking
  */
-static inline int __acl_permission_check(struct inode *inode, int mask,
-               int (*check_acl)(struct inode *inode, int mask), int rcu)
+static int acl_permission_check(struct inode *inode, int mask, unsigned int flags,
+               int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
 {
        umode_t                 mode = inode->i_mode;
 
@@ -180,13 +180,9 @@ static inline int __acl_permission_check(struct inode *inode, int mask,
                mode >>= 6;
        else {
                if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
-                       if (rcu) {
-                               return -ECHILD;
-                       } else {
-                               int error = check_acl(inode, mask);
-                               if (error != -EAGAIN)
-                                       return error;
-                       }
+                       int error = check_acl(inode, mask, flags);
+                       if (error != -EAGAIN)
+                               return error;
                }
 
                if (in_group_p(inode->i_gid))
@@ -201,32 +197,31 @@ static inline int __acl_permission_check(struct inode *inode, int mask,
        return -EACCES;
 }
 
-static inline int acl_permission_check(struct inode *inode, int mask,
-               int (*check_acl)(struct inode *inode, int mask))
-{
-       return __acl_permission_check(inode, mask, check_acl, 0);
-}
-
 /**
- * generic_permission  -  check for access rights on a Posix-like filesystem
+ * 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.
  * We use "fsuid" for this, letting us set arbitrary permissions
  * for filesystem access without changing the "normal" uids which
- * are used for other things..
+ * 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 (*check_acl)(struct inode *inode, int mask))
+int generic_permission(struct inode *inode, int mask, unsigned int flags,
+       int (*check_acl)(struct inode *inode, int mask, unsigned int flags))
 {
        int ret;
 
        /*
         * Do the basic POSIX ACL permission checks.
         */
-       ret = acl_permission_check(inode, mask, check_acl);
+       ret = acl_permission_check(inode, mask, flags, check_acl);
        if (ret != -EACCES)
                return ret;
 
@@ -281,9 +276,10 @@ int inode_permission(struct inode *inode, int mask)
        }
 
        if (inode->i_op->permission)
-               retval = inode->i_op->permission(inode, mask);
+               retval = inode->i_op->permission(inode, mask, 0);
        else
-               retval = generic_permission(inode, mask, inode->i_op->check_acl);
+               retval = generic_permission(inode, mask, 0,
+                               inode->i_op->check_acl);
 
        if (retval)
                return retval;
@@ -371,6 +367,18 @@ void path_get(struct path *path)
 }
 EXPORT_SYMBOL(path_get);
 
+/**
+ * path_get_long - get a long 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_long(struct path *path)
+{
+       mntget_long(path->mnt);
+       dget(path->dentry);
+}
+
 /**
  * path_put - put a reference to a path
  * @path: path to put the reference to
@@ -384,10 +392,22 @@ void path_put(struct path *path)
 }
 EXPORT_SYMBOL(path_put);
 
+/**
+ * path_put_long - put a long 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_long(struct path *path)
+{
+       dput(path->dentry);
+       mntput_long(path->mnt);
+}
+
 /**
  * nameidata_drop_rcu - drop this nameidata out of rcu-walk
  * @nd: nameidata pathwalk data to drop
- * @Returns: 0 on success, -ECHLID on failure
+ * Returns: 0 on success, -ECHILD on failure
  *
  * Path walking has 2 modes, rcu-walk and ref-walk (see
  * Documentation/filesystems/path-lookup.txt). __drop_rcu* functions attempt
@@ -448,7 +468,7 @@ static inline int nameidata_drop_rcu_maybe(struct nameidata *nd)
  * nameidata_dentry_drop_rcu - drop nameidata and dentry out of rcu-walk
  * @nd: nameidata pathwalk data to drop
  * @dentry: dentry to drop
- * @Returns: 0 on success, -ECHLID on failure
+ * Returns: 0 on success, -ECHILD on failure
  *
  * nameidata_dentry_drop_rcu attempts to drop the current nd->path and nd->root,
  * and dentry into ref-walk. @dentry must be a path found by a do_lookup call on
@@ -510,7 +530,7 @@ static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct d
 /**
  * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk
  * @nd: nameidata pathwalk data to drop
- * @Returns: 0 on success, -ECHLID on failure
+ * Returns: 0 on success, -ECHILD on failure
  *
  * nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk.
  * nd->path should be the final element of the lookup, so nd->root is discarded.
@@ -563,10 +583,26 @@ void release_open_intent(struct nameidata *nd)
                fput(nd->intent.open.file);
 }
 
+static int d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+       int status;
+
+       status = dentry->d_op->d_revalidate(dentry, nd);
+       if (status == -ECHILD) {
+               if (nameidata_dentry_drop_rcu(nd, dentry))
+                       return status;
+               status = dentry->d_op->d_revalidate(dentry, nd);
+       }
+
+       return status;
+}
+
 static inline struct dentry *
 do_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       int status = dentry->d_op->d_revalidate(dentry, nd);
+       int status;
+
+       status = d_revalidate(dentry, nd);
        if (unlikely(status <= 0)) {
                /*
                 * The dentry failed validation.
@@ -574,14 +610,20 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
                 * the dentry otherwise d_revalidate is asking us
                 * to return a fail status.
                 */
-               if (!status) {
+               if (status < 0) {
+                       /* If we're in rcu-walk, we don't have a ref */
+                       if (!(nd->flags & LOOKUP_RCU))
+                               dput(dentry);
+                       dentry = ERR_PTR(status);
+
+               } else {
+                       /* Don't d_invalidate in rcu-walk mode */
+                       if (nameidata_dentry_drop_rcu_maybe(nd, dentry))
+                               return ERR_PTR(-ECHILD);
                        if (!d_invalidate(dentry)) {
                                dput(dentry);
                                dentry = NULL;
                        }
-               } else {
-                       dput(dentry);
-                       dentry = ERR_PTR(status);
                }
        }
        return dentry;
@@ -626,7 +668,7 @@ force_reval_path(struct path *path, struct nameidata *nd)
        if (!need_reval_dot(dentry))
                return 0;
 
-       status = dentry->d_op->d_revalidate(dentry, nd);
+       status = d_revalidate(dentry, nd);
        if (status > 0)
                return 0;
 
@@ -646,22 +688,19 @@ force_reval_path(struct path *path, struct nameidata *nd)
  * short-cut DAC fails, then call ->permission() to do more
  * complete permission check.
  */
-static inline int __exec_permission(struct inode *inode, int rcu)
+static inline int exec_permission(struct inode *inode, unsigned int flags)
 {
        int ret;
 
        if (inode->i_op->permission) {
-               if (rcu)
-                       return -ECHILD;
-               ret = inode->i_op->permission(inode, MAY_EXEC);
-               if (!ret)
-                       goto ok;
-               return ret;
+               ret = inode->i_op->permission(inode, MAY_EXEC, flags);
+       } else {
+               ret = acl_permission_check(inode, MAY_EXEC, flags,
+                               inode->i_op->check_acl);
        }
-       ret = __acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl, rcu);
-       if (!ret)
+       if (likely(!ret))
                goto ok;
-       if (rcu && ret == -ECHILD)
+       if (ret == -ECHILD)
                return ret;
 
        if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH))
@@ -669,17 +708,7 @@ static inline int __exec_permission(struct inode *inode, int rcu)
 
        return ret;
 ok:
-       return security_inode_exec_permission(inode, rcu);
-}
-
-static int exec_permission(struct inode *inode)
-{
-       return __exec_permission(inode, 0);
-}
-
-static int exec_permission_rcu(struct inode *inode)
-{
-       return __exec_permission(inode, 1);
+       return security_inode_exec_permission(inode, flags);
 }
 
 static __always_inline void set_root(struct nameidata *nd)
@@ -1039,12 +1068,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
                        return -ECHILD;
 
                nd->seq = seq;
-               if (dentry->d_flags & DCACHE_OP_REVALIDATE) {
-                       /* We commonly drop rcu-walk here */
-                       if (nameidata_dentry_drop_rcu(nd, dentry))
-                               return -ECHILD;
+               if (dentry->d_flags & DCACHE_OP_REVALIDATE)
                        goto need_revalidate;
-               }
                path->mnt = mnt;
                path->dentry = dentry;
                __follow_mount_rcu(nd, path, inode);
@@ -1147,7 +1172,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 
                nd->flags |= LOOKUP_CONTINUE;
                if (nd->flags & LOOKUP_RCU) {
-                       err = exec_permission_rcu(nd->inode);
+                       err = exec_permission(nd->inode, IPERM_FLAG_RCU);
                        if (err == -ECHILD) {
                                if (nameidata_drop_rcu(nd))
                                        return -ECHILD;
@@ -1155,7 +1180,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                        }
                } else {
 exec_again:
-                       err = exec_permission(nd->inode);
+                       err = exec_permission(nd->inode, 0);
                }
                if (err)
                        break;
@@ -1292,12 +1317,11 @@ return_reval:
                 * We may need to check the cached dentry for staleness.
                 */
                if (need_reval_dot(nd->path.dentry)) {
-                       if (nameidata_drop_rcu_maybe(nd))
-                               return -ECHILD;
-                       err = -ESTALE;
                        /* Note: we do not d_invalidate() */
-                       if (!nd->path.dentry->d_op->d_revalidate(
-                                       nd->path.dentry, nd))
+                       err = d_revalidate(nd->path.dentry, nd);
+                       if (!err)
+                               err = -ESTALE;
+                       if (err < 0)
                                break;
                }
 return_base:
@@ -1603,7 +1627,7 @@ static struct dentry *__lookup_hash(struct qstr *name,
        struct dentry *dentry;
        int err;
 
-       err = exec_permission(inode);
+       err = exec_permission(inode, 0);
        if (err)
                return ERR_PTR(err);
 
@@ -2080,10 +2104,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                dir = nd->path.dentry;
        case LAST_DOT:
                if (need_reval_dot(dir)) {
-                       if (!dir->d_op->d_revalidate(dir, nd)) {
+                       error = d_revalidate(nd->path.dentry, nd);
+                       if (!error)
                                error = -ESTALE;
+                       if (error < 0)
                                goto exit;
-                       }
                }
                /* fallthrough */
        case LAST_ROOT: