fork: report pid reservation failure properly
[linux-2.6-block.git] / fs / namei.c
index c83145af4bfc0ea9bb159002e3545e8a8cd65157..76fb76a0818bc274fc67b2d87b582db6690d62a6 100644 (file)
  * PATH_MAX includes the nul terminator --RR.
  */
 
-#define EMBEDDED_NAME_MAX      (PATH_MAX - sizeof(struct filename))
+#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, *err;
-       int len;
-       long max;
+       struct filename *result;
        char *kname;
+       int len;
 
        result = audit_reusename(filename);
        if (result)
@@ -136,22 +135,18 @@ getname_flags(const char __user *filename, int flags, int *empty)
        result = __getname();
        if (unlikely(!result))
                return ERR_PTR(-ENOMEM);
-       result->refcnt = 1;
 
        /*
         * First, try to embed the struct filename inside the names_cache
         * allocation
         */
-       kname = (char *)result + sizeof(*result);
+       kname = (char *)result->iname;
        result->name = kname;
-       result->separate = false;
-       max = EMBEDDED_NAME_MAX;
 
-recopy:
-       len = strncpy_from_user(kname, filename, max);
+       len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX);
        if (unlikely(len < 0)) {
-               err = ERR_PTR(len);
-               goto error;
+               __putname(result);
+               return ERR_PTR(len);
        }
 
        /*
@@ -160,43 +155,49 @@ recopy:
         * names_cache allocation for the pathname, and re-do the copy from
         * userland.
         */
-       if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) {
+       if (unlikely(len == EMBEDDED_NAME_MAX)) {
+               const size_t size = offsetof(struct filename, iname[1]);
                kname = (char *)result;
 
-               result = kzalloc(sizeof(*result), GFP_KERNEL);
-               if (!result) {
-                       err = ERR_PTR(-ENOMEM);
-                       result = (struct filename *)kname;
-                       goto error;
+               /*
+                * 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;
-               result->separate = true;
-               result->refcnt = 1;
-               max = PATH_MAX;
-               goto recopy;
+               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;
-               err = ERR_PTR(-ENOENT);
-               if (!(flags & LOOKUP_EMPTY))
-                       goto error;
+               if (!(flags & LOOKUP_EMPTY)) {
+                       putname(result);
+                       return ERR_PTR(-ENOENT);
+               }
        }
 
-       err = ERR_PTR(-ENAMETOOLONG);
-       if (unlikely(len >= PATH_MAX))
-               goto error;
-
        result->uptr = filename;
        result->aname = NULL;
        audit_getname(result);
        return result;
-
-error:
-       putname(result);
-       return err;
 }
 
 struct filename *
@@ -216,8 +217,7 @@ getname_kernel(const char * filename)
                return ERR_PTR(-ENOMEM);
 
        if (len <= EMBEDDED_NAME_MAX) {
-               result->name = (char *)(result) + sizeof(*result);
-               result->separate = false;
+               result->name = (char *)result->iname;
        } else if (len <= PATH_MAX) {
                struct filename *tmp;
 
@@ -227,7 +227,6 @@ getname_kernel(const char * filename)
                        return ERR_PTR(-ENOMEM);
                }
                tmp->name = (char *)result;
-               tmp->separate = true;
                result = tmp;
        } else {
                __putname(result);
@@ -249,7 +248,7 @@ void putname(struct filename *name)
        if (--name->refcnt > 0)
                return;
 
-       if (name->separate) {
+       if (name->name != name->iname) {
                __putname(name->name);
                kfree(name);
        } else
@@ -1851,10 +1850,11 @@ static int link_path_walk(const char *name, struct nameidata *nd)
        return err;
 }
 
-static int path_init(int dfd, const char *name, unsigned int flags,
+static int path_init(int dfd, const struct filename *name, unsigned int flags,
                     struct nameidata *nd)
 {
        int retval = 0;
+       const char *s = name->name;
 
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
@@ -1863,7 +1863,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
        if (flags & LOOKUP_ROOT) {
                struct dentry *root = nd->root.dentry;
                struct inode *inode = root->d_inode;
-               if (*name) {
+               if (*s) {
                        if (!d_can_lookup(root))
                                return -ENOTDIR;
                        retval = inode_permission(inode, MAY_EXEC);
@@ -1885,7 +1885,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
        nd->root.mnt = NULL;
 
        nd->m_seq = read_seqbegin(&mount_lock);
-       if (*name=='/') {
+       if (*s == '/') {
                if (flags & LOOKUP_RCU) {
                        rcu_read_lock();
                        nd->seq = set_root_rcu(nd);
@@ -1919,7 +1919,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
 
                dentry = f.file->f_path.dentry;
 
-               if (*name) {
+               if (*s) {
                        if (!d_can_lookup(dentry)) {
                                fdput(f);
                                return -ENOTDIR;
@@ -1949,7 +1949,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
        return -ECHILD;
 done:
        current->total_link_count = 0;
-       return link_path_walk(name, nd);
+       return link_path_walk(s, nd);
 }
 
 static void path_cleanup(struct nameidata *nd)
@@ -1972,7 +1972,7 @@ static inline int lookup_last(struct nameidata *nd, struct path *path)
 }
 
 /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
-static int path_lookupat(int dfd, const char *name,
+static int path_lookupat(int dfd, const struct filename *name,
                                unsigned int flags, struct nameidata *nd)
 {
        struct path path;
@@ -2027,31 +2027,17 @@ static int path_lookupat(int dfd, const char *name,
 static int filename_lookup(int dfd, struct filename *name,
                                unsigned int flags, struct nameidata *nd)
 {
-       int retval = path_lookupat(dfd, name->name, flags | LOOKUP_RCU, nd);
+       int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd);
        if (unlikely(retval == -ECHILD))
-               retval = path_lookupat(dfd, name->name, flags, nd);
+               retval = path_lookupat(dfd, name, flags, nd);
        if (unlikely(retval == -ESTALE))
-               retval = path_lookupat(dfd, name->name,
-                                               flags | LOOKUP_REVAL, nd);
+               retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd);
 
        if (likely(!retval))
                audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT);
        return retval;
 }
 
-static int do_path_lookup(int dfd, const char *name,
-                               unsigned int flags, struct nameidata *nd)
-{
-       struct filename *filename = getname_kernel(name);
-       int retval = PTR_ERR(filename);
-
-       if (!IS_ERR(filename)) {
-               retval = filename_lookup(dfd, filename, flags, nd);
-               putname(filename);
-       }
-       return retval;
-}
-
 /* does lookup, returns the object with parent locked */
 struct dentry *kern_path_locked(const char *name, struct path *path)
 {
@@ -2089,9 +2075,15 @@ out:
 int kern_path(const char *name, unsigned int flags, struct path *path)
 {
        struct nameidata nd;
-       int res = do_path_lookup(AT_FDCWD, name, flags, &nd);
-       if (!res)
-               *path = nd.path;
+       struct filename *filename = getname_kernel(name);
+       int res = PTR_ERR(filename);
+
+       if (!IS_ERR(filename)) {
+               res = filename_lookup(AT_FDCWD, filename, flags, &nd);
+               putname(filename);
+               if (!res)
+                       *path = nd.path;
+       }
        return res;
 }
 EXPORT_SYMBOL(kern_path);
@@ -2108,15 +2100,22 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
                    const char *name, unsigned int flags,
                    struct path *path)
 {
-       struct nameidata nd;
-       int err;
-       nd.root.dentry = dentry;
-       nd.root.mnt = mnt;
+       struct filename *filename = getname_kernel(name);
+       int err = PTR_ERR(filename);
+
        BUG_ON(flags & LOOKUP_PARENT);
-       /* the first argument of do_path_lookup() is ignored with LOOKUP_ROOT */
-       err = do_path_lookup(AT_FDCWD, name, flags | LOOKUP_ROOT, &nd);
-       if (!err)
-               *path = nd.path;
+
+       /* the first argument of filename_lookup() is ignored with LOOKUP_ROOT */
+       if (!IS_ERR(filename)) {
+               struct nameidata nd;
+               nd.root.dentry = dentry;
+               nd.root.mnt = mnt;
+               err = filename_lookup(AT_FDCWD, filename,
+                                     flags | LOOKUP_ROOT, &nd);
+               if (!err)
+                       *path = nd.path;
+               putname(filename);
+       }
        return err;
 }
 EXPORT_SYMBOL(vfs_path_lookup);
@@ -2138,9 +2137,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
  * @len:       maximum length @len should be interpreted to
  *
  * Note that this routine is purely a helper for filesystem usage and should
- * not be called by generic code.  Also note that by using this function the
- * nameidata argument is passed to the filesystem methods and a filesystem
- * using this helper needs to be prepared for that.
+ * not be called by generic code.
  */
 struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
 {
@@ -2341,7 +2338,8 @@ out:
  * Returns 0 and "path" will be valid on success; Returns error otherwise.
  */
 static int
-path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags)
+path_mountpoint(int dfd, const struct filename *name, struct path *path,
+               unsigned int flags)
 {
        struct nameidata nd;
        int err;
@@ -2370,20 +2368,20 @@ out:
 }
 
 static int
-filename_mountpoint(int dfd, struct filename *s, struct path *path,
+filename_mountpoint(int dfd, struct filename *name, struct path *path,
                        unsigned int flags)
 {
        int error;
-       if (IS_ERR(s))
-               return PTR_ERR(s);
-       error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU);
+       if (IS_ERR(name))
+               return PTR_ERR(name);
+       error = path_mountpoint(dfd, name, path, flags | LOOKUP_RCU);
        if (unlikely(error == -ECHILD))
-               error = path_mountpoint(dfd, s->name, path, flags);
+               error = path_mountpoint(dfd, name, path, flags);
        if (unlikely(error == -ESTALE))
-               error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_REVAL);
+               error = path_mountpoint(dfd, name, path, flags | LOOKUP_REVAL);
        if (likely(!error))
-               audit_inode(s, path->dentry, 0);
-       putname(s);
+               audit_inode(name, path->dentry, 0);
+       putname(name);
        return error;
 }
 
@@ -3156,7 +3154,7 @@ static int do_tmpfile(int dfd, struct filename *pathname,
        static const struct qstr name = QSTR_INIT("/", 1);
        struct dentry *dentry, *child;
        struct inode *dir;
-       int error = path_lookupat(dfd, pathname->name,
+       int error = path_lookupat(dfd, pathname,
                                  flags | LOOKUP_DIRECTORY, nd);
        if (unlikely(error))
                return error;
@@ -3229,7 +3227,7 @@ static struct file *path_openat(int dfd, struct filename *pathname,
                goto out;
        }
 
-       error = path_init(dfd, pathname->name, flags, nd);
+       error = path_init(dfd, pathname, flags, nd);
        if (unlikely(error))
                goto out;