]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/namei.c
Merge remote-tracking branch 'userns/for-next'
[karo-tx-linux.git] / fs / namei.c
index 33e9495a31293e2c080b5b0bd2e50523a460ceee..2b729d253715ba183e912fae98cfec1e68665e24 100644 (file)
@@ -955,26 +955,23 @@ static bool safe_hardlink_source(struct inode *inode)
  *  - sysctl_protected_hardlinks enabled
  *  - fsuid does not match inode
  *  - hardlink source is unsafe (see safe_hardlink_source() above)
- *  - not CAP_FOWNER
+ *  - not CAP_FOWNER in a namespace with the inode owner uid mapped
  *
  * Returns 0 if successful, -ve on error.
  */
 static int may_linkat(struct path *link)
 {
-       const struct cred *cred;
        struct inode *inode;
 
        if (!sysctl_protected_hardlinks)
                return 0;
 
-       cred = current_cred();
        inode = link->dentry->d_inode;
 
        /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
         * otherwise, it must be a safe source.
         */
-       if (uid_eq(cred->fsuid, inode->i_uid) || safe_hardlink_source(inode) ||
-           capable(CAP_FOWNER))
+       if (inode_owner_or_capable(inode) || safe_hardlink_source(inode))
                return 0;
 
        audit_log_link_denied("linkat", link);
@@ -1969,7 +1966,7 @@ OK:
                if (err) {
                        const char *s = get_link(nd);
 
-                       if (unlikely(IS_ERR(s)))
+                       if (IS_ERR(s))
                                return PTR_ERR(s);
                        err = 0;
                        if (unlikely(!s)) {
@@ -2282,6 +2279,8 @@ EXPORT_SYMBOL(vfs_path_lookup);
  *
  * Note that this routine is purely a helper for filesystem usage and should
  * not be called by generic code.
+ *
+ * The caller must hold base->i_mutex.
  */
 struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
 {
@@ -2325,6 +2324,78 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
 }
 EXPORT_SYMBOL(lookup_one_len);
 
+/**
+ * lookup_one_len_unlocked - filesystem helper to lookup single pathname component
+ * @name:      pathname component to lookup
+ * @base:      base directory to lookup from
+ * @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.
+ *
+ * Unlike lookup_one_len, it should be called without the parent
+ * i_mutex held, and will take the i_mutex itself if necessary.
+ */
+struct dentry *lookup_one_len_unlocked(const char *name,
+                                      struct dentry *base, int len)
+{
+       struct qstr this;
+       unsigned int c;
+       int err;
+       struct dentry *ret;
+
+       this.name = name;
+       this.len = len;
+       this.hash = full_name_hash(name, len);
+       if (!len)
+               return ERR_PTR(-EACCES);
+
+       if (unlikely(name[0] == '.')) {
+               if (len < 2 || (len == 2 && name[1] == '.'))
+                       return ERR_PTR(-EACCES);
+       }
+
+       while (len--) {
+               c = *(const unsigned char *)name++;
+               if (c == '/' || c == '\0')
+                       return ERR_PTR(-EACCES);
+       }
+       /*
+        * See if the low-level filesystem might want
+        * to use its own hash..
+        */
+       if (base->d_flags & DCACHE_OP_HASH) {
+               int err = base->d_op->d_hash(base, &this);
+               if (err < 0)
+                       return ERR_PTR(err);
+       }
+
+       err = inode_permission(base->d_inode, MAY_EXEC);
+       if (err)
+               return ERR_PTR(err);
+
+       ret = __d_lookup(base, &this);
+       if (ret)
+               return ret;
+       /*
+        * __d_lookup() is used to try to get a quick answer and avoid the
+        * mutex.  A false-negative does no harm.
+        */
+       ret = __d_lookup(base, &this);
+       if (ret && ret->d_flags & DCACHE_OP_REVALIDATE) {
+               dput(ret);
+               ret = NULL;
+       }
+       if (ret)
+               return ret;
+
+       mutex_lock(&base->d_inode->i_mutex);
+       ret =  __lookup_hash(&this, base, 0);
+       mutex_unlock(&base->d_inode->i_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(lookup_one_len_unlocked);
+
 int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
                 struct path *path, int *empty)
 {
@@ -3383,7 +3454,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
                return ERR_PTR(-ELOOP);
 
        filename = getname_kernel(name);
-       if (unlikely(IS_ERR(filename)))
+       if (IS_ERR(filename))
                return ERR_CAST(filename);
 
        set_nameidata(&nd, -1, filename);