]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/namei.c
fs: allow AT_EMPTY_PATH in linkat(), limit that to CAP_DAC_READ_SEARCH
[mv-sheeva.git] / fs / namei.c
index 441f1106de08065f3319dba5557dd5b5841b99e1..c9b7f5b7e92a0c289b41cab0df7538becd27f993 100644 (file)
@@ -136,7 +136,7 @@ static int do_getname(const char __user *filename, char *page)
        return retval;
 }
 
-char * getname(const char __user * filename)
+static char *getname_flags(const char __user * filename, int flags)
 {
        char *tmp, *result;
 
@@ -147,14 +147,21 @@ char * getname(const char __user * filename)
 
                result = tmp;
                if (retval < 0) {
-                       __putname(tmp);
-                       result = ERR_PTR(retval);
+                       if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
+                               __putname(tmp);
+                               result = ERR_PTR(retval);
+                       }
                }
        }
        audit_getname(result);
        return result;
 }
 
+char *getname(const char __user * filename)
+{
+       return getname_flags(filename, 0);
+}
+
 #ifdef CONFIG_AUDITSYSCALL
 void putname(const char *name)
 {
@@ -401,9 +408,11 @@ static int nameidata_drop_rcu(struct nameidata *nd)
 {
        struct fs_struct *fs = current->fs;
        struct dentry *dentry = nd->path.dentry;
+       int want_root = 0;
 
        BUG_ON(!(nd->flags & LOOKUP_RCU));
-       if (nd->root.mnt) {
+       if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
+               want_root = 1;
                spin_lock(&fs->lock);
                if (nd->root.mnt != fs->root.mnt ||
                                nd->root.dentry != fs->root.dentry)
@@ -414,7 +423,7 @@ static int nameidata_drop_rcu(struct nameidata *nd)
                goto err;
        BUG_ON(nd->inode != dentry->d_inode);
        spin_unlock(&dentry->d_lock);
-       if (nd->root.mnt) {
+       if (want_root) {
                path_get(&nd->root);
                spin_unlock(&fs->lock);
        }
@@ -427,7 +436,7 @@ static int nameidata_drop_rcu(struct nameidata *nd)
 err:
        spin_unlock(&dentry->d_lock);
 err_root:
-       if (nd->root.mnt)
+       if (want_root)
                spin_unlock(&fs->lock);
        return -ECHILD;
 }
@@ -454,9 +463,11 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry
 {
        struct fs_struct *fs = current->fs;
        struct dentry *parent = nd->path.dentry;
+       int want_root = 0;
 
        BUG_ON(!(nd->flags & LOOKUP_RCU));
-       if (nd->root.mnt) {
+       if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
+               want_root = 1;
                spin_lock(&fs->lock);
                if (nd->root.mnt != fs->root.mnt ||
                                nd->root.dentry != fs->root.dentry)
@@ -476,7 +487,7 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry
        parent->d_count++;
        spin_unlock(&dentry->d_lock);
        spin_unlock(&parent->d_lock);
-       if (nd->root.mnt) {
+       if (want_root) {
                path_get(&nd->root);
                spin_unlock(&fs->lock);
        }
@@ -490,7 +501,7 @@ err:
        spin_unlock(&dentry->d_lock);
        spin_unlock(&parent->d_lock);
 err_root:
-       if (nd->root.mnt)
+       if (want_root)
                spin_unlock(&fs->lock);
        return -ECHILD;
 }
@@ -501,7 +512,8 @@ static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct d
        if (nd->flags & LOOKUP_RCU) {
                if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) {
                        nd->flags &= ~LOOKUP_RCU;
-                       nd->root.mnt = NULL;
+                       if (!(nd->flags & LOOKUP_ROOT))
+                               nd->root.mnt = NULL;
                        rcu_read_unlock();
                        br_read_unlock(vfsmount_lock);
                        return -ECHILD;
@@ -525,7 +537,8 @@ static int nameidata_drop_rcu_last(struct nameidata *nd)
 
        BUG_ON(!(nd->flags & LOOKUP_RCU));
        nd->flags &= ~LOOKUP_RCU;
-       nd->root.mnt = NULL;
+       if (!(nd->flags & LOOKUP_ROOT))
+               nd->root.mnt = NULL;
        spin_lock(&dentry->d_lock);
        if (!__d_rcu_to_refcount(dentry, nd->seq))
                goto err_unlock;
@@ -589,29 +602,6 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
        return dentry;
 }
 
-static inline struct dentry *
-do_revalidate_rcu(struct dentry *dentry, struct nameidata *nd)
-{
-       int status = d_revalidate(dentry, nd);
-       if (likely(status > 0))
-               return dentry;
-       if (status == -ECHILD) {
-               if (nameidata_dentry_drop_rcu(nd, dentry))
-                       return ERR_PTR(-ECHILD);
-               return do_revalidate(dentry, nd);
-       }
-       if (status < 0)
-               return ERR_PTR(status);
-       /* Don't d_invalidate in rcu-walk mode */
-       if (nameidata_dentry_drop_rcu(nd, dentry))
-               return ERR_PTR(-ECHILD);
-       if (!d_invalidate(dentry)) {
-               dput(dentry);
-               dentry = NULL;
-       }
-       return dentry;
-}
-
 /*
  * handle_reval_path - force revalidation of a dentry
  *
@@ -776,8 +766,14 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
                error = 0;
                if (s)
                        error = __vfs_follow_link(nd, s);
-               else if (nd->last_type == LAST_BIND)
+               else if (nd->last_type == LAST_BIND) {
                        nd->flags |= LOOKUP_JUMPED;
+                       if (nd->path.dentry->d_inode->i_op->follow_link) {
+                               /* stepped on a _really_ weird one */
+                               path_put(&nd->path);
+                               error = -ELOOP;
+                       }
+               }
        }
        return error;
 }
@@ -1076,7 +1072,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 
 failed:
        nd->flags &= ~LOOKUP_RCU;
-       nd->root.mnt = NULL;
+       if (!(nd->flags & LOOKUP_ROOT))
+               nd->root.mnt = NULL;
        rcu_read_unlock();
        br_read_unlock(vfsmount_lock);
        return -ECHILD;
@@ -1213,7 +1210,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
 {
        struct vfsmount *mnt = nd->path.mnt;
        struct dentry *dentry, *parent = nd->path.dentry;
-       struct inode *dir;
+       int need_reval = 1;
+       int status = 1;
        int err;
 
        /*
@@ -1223,48 +1221,74 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
         */
        if (nd->flags & LOOKUP_RCU) {
                unsigned seq;
-
                *inode = nd->inode;
                dentry = __d_lookup_rcu(parent, name, &seq, inode);
-               if (!dentry) {
-                       if (nameidata_drop_rcu(nd))
-                               return -ECHILD;
-                       goto need_lookup;
-               }
+               if (!dentry)
+                       goto unlazy;
+
                /* Memory barrier in read_seqcount_begin of child is enough */
                if (__read_seqcount_retry(&parent->d_seq, nd->seq))
                        return -ECHILD;
-
                nd->seq = seq;
+
                if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
-                       dentry = do_revalidate_rcu(dentry, nd);
-                       if (!dentry)
-                               goto need_lookup;
-                       if (IS_ERR(dentry))
-                               goto fail;
-                       if (!(nd->flags & LOOKUP_RCU))
-                               goto done;
+                       status = d_revalidate(dentry, nd);
+                       if (unlikely(status <= 0)) {
+                               if (status != -ECHILD)
+                                       need_reval = 0;
+                               goto unlazy;
+                       }
                }
                path->mnt = mnt;
                path->dentry = dentry;
                if (likely(__follow_mount_rcu(nd, path, inode, false)))
                        return 0;
-               if (nameidata_drop_rcu(nd))
-                       return -ECHILD;
-               /* fallthru */
+unlazy:
+               if (dentry) {
+                       if (nameidata_dentry_drop_rcu(nd, dentry))
+                               return -ECHILD;
+               } else {
+                       if (nameidata_drop_rcu(nd))
+                               return -ECHILD;
+               }
+       } else {
+               dentry = __d_lookup(parent, name);
        }
-       dentry = __d_lookup(parent, name);
-       if (!dentry)
-               goto need_lookup;
-found:
-       if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
-               dentry = do_revalidate(dentry, nd);
-               if (!dentry)
-                       goto need_lookup;
-               if (IS_ERR(dentry))
-                       goto fail;
+
+retry:
+       if (unlikely(!dentry)) {
+               struct inode *dir = parent->d_inode;
+               BUG_ON(nd->inode != dir);
+
+               mutex_lock(&dir->i_mutex);
+               dentry = d_lookup(parent, name);
+               if (likely(!dentry)) {
+                       dentry = d_alloc_and_lookup(parent, name, 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);
+       }
+       if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE) && need_reval)
+               status = d_revalidate(dentry, nd);
+       if (unlikely(status <= 0)) {
+               if (status < 0) {
+                       dput(dentry);
+                       return status;
+               }
+               if (!d_invalidate(dentry)) {
+                       dput(dentry);
+                       dentry = NULL;
+                       need_reval = 1;
+                       goto retry;
+               }
        }
-done:
+
        path->mnt = mnt;
        path->dentry = dentry;
        err = follow_managed(path, nd->flags);
@@ -1274,39 +1298,6 @@ done:
        }
        *inode = path->dentry->d_inode;
        return 0;
-
-need_lookup:
-       dir = parent->d_inode;
-       BUG_ON(nd->inode != dir);
-
-       mutex_lock(&dir->i_mutex);
-       /*
-        * First re-do the cached lookup just in case it was created
-        * while we waited for the directory semaphore, or the first
-        * lookup failed due to an unrelated rename.
-        *
-        * This could use version numbering or similar to avoid unnecessary
-        * cache lookups, but then we'd have to do the first lookup in the
-        * non-racy way. However in the common case here, everything should
-        * be hot in cache, so would it be a big win?
-        */
-       dentry = d_lookup(parent, name);
-       if (likely(!dentry)) {
-               dentry = d_alloc_and_lookup(parent, name, nd);
-               mutex_unlock(&dir->i_mutex);
-               if (IS_ERR(dentry))
-                       goto fail;
-               goto done;
-       }
-       /*
-        * Uhhuh! Nasty case: the cache was re-populated while
-        * we waited on the semaphore. Need to revalidate.
-        */
-       mutex_unlock(&dir->i_mutex);
-       goto found;
-
-fail:
-       return PTR_ERR(dentry);
 }
 
 static inline int may_lookup(struct nameidata *nd)
@@ -1339,7 +1330,8 @@ static void terminate_walk(struct nameidata *nd)
                path_put(&nd->path);
        } else {
                nd->flags &= ~LOOKUP_RCU;
-               nd->root.mnt = NULL;
+               if (!(nd->flags & LOOKUP_ROOT))
+                       nd->root.mnt = NULL;
                rcu_read_unlock();
                br_read_unlock(vfsmount_lock);
        }
@@ -1506,6 +1498,27 @@ static int path_init(int dfd, const char *name, unsigned int flags,
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED;
        nd->depth = 0;
+       if (flags & LOOKUP_ROOT) {
+               struct inode *inode = nd->root.dentry->d_inode;
+               if (*name) {
+                       if (!inode->i_op->lookup)
+                               return -ENOTDIR;
+                       retval = inode_permission(inode, MAY_EXEC);
+                       if (retval)
+                               return retval;
+               }
+               nd->path = nd->root;
+               nd->inode = inode;
+               if (flags & LOOKUP_RCU) {
+                       br_read_lock(vfsmount_lock);
+                       rcu_read_lock();
+                       nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
+               } else {
+                       path_get(&nd->path);
+               }
+               return 0;
+       }
+
        nd->root.mnt = NULL;
 
        if (*name=='/') {
@@ -1537,20 +1550,22 @@ static int path_init(int dfd, const char *name, unsigned int flags,
        } else {
                struct dentry *dentry;
 
-               file = fget_light(dfd, &fput_needed);
+               file = fget_raw_light(dfd, &fput_needed);
                retval = -EBADF;
                if (!file)
                        goto out_fail;
 
                dentry = file->f_path.dentry;
 
-               retval = -ENOTDIR;
-               if (!S_ISDIR(dentry->d_inode->i_mode))
-                       goto fput_fail;
+               if (*name) {
+                       retval = -ENOTDIR;
+                       if (!S_ISDIR(dentry->d_inode->i_mode))
+                               goto fput_fail;
 
-               retval = file_permission(file, MAY_EXEC);
-               if (retval)
-                       goto fput_fail;
+                       retval = file_permission(file, MAY_EXEC);
+                       if (retval)
+                               goto fput_fail;
+               }
 
                nd->path = file->f_path;
                if (flags & LOOKUP_RCU) {
@@ -1616,7 +1631,7 @@ static int path_lookupat(int dfd, const char *name,
        if (base)
                fput(base);
 
-       if (nd->root.mnt) {
+       if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
                path_put(&nd->root);
                nd->root.mnt = NULL;
        }
@@ -1667,46 +1682,10 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
                    const char *name, unsigned int flags,
                    struct nameidata *nd)
 {
-       int result;
-
-       /* same as do_path_lookup */
-       nd->last_type = LAST_ROOT;
-       nd->flags = flags | LOOKUP_JUMPED;
-       nd->depth = 0;
-
-       nd->path.dentry = dentry;
-       nd->path.mnt = mnt;
-       path_get(&nd->path);
-       nd->root = nd->path;
-       path_get(&nd->root);
-       nd->inode = nd->path.dentry->d_inode;
-
-       current->total_link_count = 0;
-
-       result = link_path_walk(name, nd);
-       if (!result)
-               result = handle_reval_path(nd);
-       if (result == -ESTALE) {
-               /* nd->path had been dropped */
-               current->total_link_count = 0;
-               nd->path.dentry = dentry;
-               nd->path.mnt = mnt;
-               nd->inode = dentry->d_inode;
-               path_get(&nd->path);
-               nd->flags = flags | LOOKUP_JUMPED | LOOKUP_REVAL;
-
-               result = link_path_walk(name, nd);
-               if (!result)
-                       result = handle_reval_path(nd);
-       }
-       if (unlikely(!result && !audit_dummy_context() && nd->path.dentry &&
-                               nd->inode))
-               audit_inode(name, nd->path.dentry);
-
-       path_put(&nd->root);
-       nd->root.mnt = NULL;
-
-       return result;
+       nd->root.dentry = dentry;
+       nd->root.mnt = mnt;
+       /* the first argument of do_path_lookup() is ignored with LOOKUP_ROOT */
+       return do_path_lookup(AT_FDCWD, name, flags | LOOKUP_ROOT, nd);
 }
 
 static struct dentry *__lookup_hash(struct qstr *name,
@@ -1795,7 +1774,7 @@ int user_path_at(int dfd, const char __user *name, unsigned flags,
                 struct path *path)
 {
        struct nameidata nd;
-       char *tmp = getname(name);
+       char *tmp = getname_flags(name, flags);
        int err = PTR_ERR(tmp);
        if (!IS_ERR(tmp)) {
 
@@ -1975,12 +1954,16 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
        return error;
 }
 
-int may_open(struct path *path, int acc_mode, int flag)
+static int may_open(struct path *path, int acc_mode, int flag)
 {
        struct dentry *dentry = path->dentry;
        struct inode *inode = dentry->d_inode;
        int error;
 
+       /* O_PATH? */
+       if (!acc_mode)
+               return 0;
+
        if (!inode)
                return -ENOENT;
 
@@ -2083,7 +2066,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        int open_flag = op->open_flag;
        int will_truncate = open_flag & O_TRUNC;
        int want_write = 0;
-       int skip_perm = 0;
+       int acc_mode = op->acc_mode;
        struct file *filp;
        struct inode *inode;
        int error;
@@ -2122,8 +2105,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        }
 
        if (!(open_flag & O_CREAT)) {
+               int symlink_ok = 0;
                if (nd->last.name[nd->last.len])
                        nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+               if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
+                       symlink_ok = 1;
                /* we _can_ be in RCU mode here */
                error = do_lookup(nd, &nd->last, path, &inode);
                if (error) {
@@ -2135,7 +2121,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                        terminate_walk(nd);
                        return ERR_PTR(-ENOENT);
                }
-               if (unlikely(inode->i_op->follow_link)) {
+               if (unlikely(inode->i_op->follow_link && !symlink_ok)) {
                        /* We drop rcu-walk here */
                        if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
                                return ERR_PTR(-ECHILD);
@@ -2183,11 +2169,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        path->dentry = dentry;
        path->mnt = nd->path.mnt;
 
-       if (IS_ERR(nd->intent.open.file)) {
-               error = PTR_ERR(nd->intent.open.file);
-               goto exit_mutex_unlock;
-       }
-
        /* Negative dentry, just create the file */
        if (!dentry->d_inode) {
                int mode = op->mode;
@@ -2207,7 +2188,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                /* Don't check for write permission, don't truncate */
                open_flag &= ~O_TRUNC;
                will_truncate = 0;
-               skip_perm = 1;
+               acc_mode = MAY_OPEN;
                error = security_path_mknod(&nd->path, dentry, mode, 0);
                if (error)
                        goto exit_mutex_unlock;
@@ -2257,7 +2238,7 @@ ok:
                want_write = 1;
        }
 common:
-       error = may_open(&nd->path, skip_perm ? 0 : op->acc_mode, open_flag);
+       error = may_open(&nd->path, acc_mode, open_flag);
        if (error)
                goto exit;
        filp = nameidata_to_filp(nd);
@@ -2293,11 +2274,10 @@ exit:
 }
 
 static struct file *path_openat(int dfd, const char *pathname,
-               const struct open_flags *op, int flags)
+               struct nameidata *nd, const struct open_flags *op, int flags)
 {
        struct file *base = NULL;
        struct file *filp;
-       struct nameidata nd;
        struct path path;
        int count = 0;
        int error;
@@ -2307,29 +2287,30 @@ static struct file *path_openat(int dfd, const char *pathname,
                return ERR_PTR(-ENFILE);
 
        filp->f_flags = op->open_flag;
-       nd.intent.open.file = filp;
-       nd.intent.open.flags = open_to_namei_flags(op->open_flag);
-       nd.intent.open.create_mode = op->mode;
+       nd->intent.open.file = filp;
+       nd->intent.open.flags = open_to_namei_flags(op->open_flag);
+       nd->intent.open.create_mode = op->mode;
 
-       error = path_init(dfd, pathname, flags | LOOKUP_PARENT, &nd, &base);
+       error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
        if (unlikely(error))
                goto out_filp;
 
        current->total_link_count = 0;
-       error = link_path_walk(pathname, &nd);
+       error = link_path_walk(pathname, nd);
        if (unlikely(error))
                goto out_filp;
 
-       filp = do_last(&nd, &path, op, pathname);
+       filp = do_last(nd, &path, op, pathname);
        while (unlikely(!filp)) { /* trailing symlink */
                struct path link = path;
                struct inode *linki = link.dentry->d_inode;
                void *cookie;
-               error = -ELOOP;
-               if (!(nd.flags & LOOKUP_FOLLOW))
-                       goto exit_dput;
-               if (count++ == 32)
-                       goto exit_dput;
+               if (!(nd->flags & LOOKUP_FOLLOW) || count++ == 32) {
+                       path_put_conditional(&path, nd);
+                       path_put(&nd->path);
+                       filp = ERR_PTR(-ELOOP);
+                       break;
+               }
                /*
                 * This is subtle. Instead of calling do_follow_link() we do
                 * the thing by hands. The reason is that this way we have zero
@@ -2341,28 +2322,25 @@ static struct file *path_openat(int dfd, const char *pathname,
                 * have to putname() it when we are done. Procfs-like symlinks
                 * just set LAST_BIND.
                 */
-               nd.flags |= LOOKUP_PARENT;
-               nd.flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
-               error = __do_follow_link(&link, &nd, &cookie);
+               nd->flags |= LOOKUP_PARENT;
+               nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
+               error = __do_follow_link(&link, nd, &cookie);
                if (unlikely(error))
                        filp = ERR_PTR(error);
                else
-                       filp = do_last(&nd, &path, op, pathname);
+                       filp = do_last(nd, &path, op, pathname);
                if (!IS_ERR(cookie) && linki->i_op->put_link)
-                       linki->i_op->put_link(link.dentry, &nd, cookie);
+                       linki->i_op->put_link(link.dentry, nd, cookie);
                path_put(&link);
        }
 out:
-       if (nd.root.mnt)
-               path_put(&nd.root);
+       if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))
+               path_put(&nd->root);
        if (base)
                fput(base);
-       release_open_intent(&nd);
+       release_open_intent(nd);
        return filp;
 
-exit_dput:
-       path_put_conditional(&path, &nd);
-       path_put(&nd.path);
 out_filp:
        filp = ERR_PTR(error);
        goto out;
@@ -2371,16 +2349,39 @@ out_filp:
 struct file *do_filp_open(int dfd, const char *pathname,
                const struct open_flags *op, int flags)
 {
+       struct nameidata nd;
        struct file *filp;
 
-       filp = path_openat(dfd, pathname, op, flags | LOOKUP_RCU);
+       filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
        if (unlikely(filp == ERR_PTR(-ECHILD)))
-               filp = path_openat(dfd, pathname, op, flags);
+               filp = path_openat(dfd, pathname, &nd, op, flags);
        if (unlikely(filp == ERR_PTR(-ESTALE)))
-               filp = path_openat(dfd, pathname, op, flags | LOOKUP_REVAL);
+               filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
        return filp;
 }
 
+struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
+               const char *name, const struct open_flags *op, int flags)
+{
+       struct nameidata nd;
+       struct file *file;
+
+       nd.root.mnt = mnt;
+       nd.root.dentry = dentry;
+
+       flags |= LOOKUP_ROOT;
+
+       if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN)
+               return ERR_PTR(-ELOOP);
+
+       file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU);
+       if (unlikely(file == ERR_PTR(-ECHILD)))
+               file = path_openat(-1, name, &nd, op, flags);
+       if (unlikely(file == ERR_PTR(-ESTALE)))
+               file = path_openat(-1, name, &nd, op, flags | LOOKUP_REVAL);
+       return file;
+}
+
 /**
  * lookup_create - lookup a dentry, creating it if it doesn't exist
  * @nd: nameidata info
@@ -2918,7 +2919,11 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
                return error;
 
        mutex_lock(&inode->i_mutex);
-       error = dir->i_op->link(old_dentry, dir, new_dentry);
+       /* Make sure we don't allow creating hardlink to an unlinked file */
+       if (inode->i_nlink == 0)
+               error =  -ENOENT;
+       else
+               error = dir->i_op->link(old_dentry, dir, new_dentry);
        mutex_unlock(&inode->i_mutex);
        if (!error)
                fsnotify_link(dir, inode, new_dentry);
@@ -2940,15 +2945,27 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
        struct dentry *new_dentry;
        struct nameidata nd;
        struct path old_path;
+       int how = 0;
        int error;
        char *to;
 
-       if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
+       if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
                return -EINVAL;
+       /*
+        * To use null names we require CAP_DAC_READ_SEARCH
+        * This ensures that not everyone will be able to create
+        * handlink using the passed filedescriptor.
+        */
+       if (flags & AT_EMPTY_PATH) {
+               if (!capable(CAP_DAC_READ_SEARCH))
+                       return -ENOENT;
+               how = LOOKUP_EMPTY;
+       }
+
+       if (flags & AT_SYMLINK_FOLLOW)
+               how |= LOOKUP_FOLLOW;
 
-       error = user_path_at(olddfd, oldname,
-                            flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
-                            &old_path);
+       error = user_path_at(olddfd, oldname, how, &old_path);
        if (error)
                return error;