wrappers for ->i_mutex access
[linux-drm-fsl-dcu.git] / fs / overlayfs / inode.c
index 964a60fa7afc53ad902c05b23daac017ba78fc1e..49e204560655a8f1737a2a03a38b7a88604a5a4c 100644 (file)
@@ -42,6 +42,19 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
        int err;
        struct dentry *upperdentry;
 
+       /*
+        * Check for permissions before trying to copy-up.  This is redundant
+        * since it will be rechecked later by ->setattr() on upper dentry.  But
+        * without this, copy-up can be triggered by just about anybody.
+        *
+        * We don't initialize inode->size, which just means that
+        * inode_newsize_ok() will always check against MAX_LFS_FILESIZE and not
+        * check for a swapfile (which this won't be anyway).
+        */
+       err = inode_change_ok(dentry->d_inode, attr);
+       if (err)
+               return err;
+
        err = ovl_want_write(dentry);
        if (err)
                goto out;
@@ -50,9 +63,9 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
        if (!err) {
                upperdentry = ovl_dentry_upper(dentry);
 
-               mutex_lock(&upperdentry->d_inode->i_mutex);
+               inode_lock(upperdentry->d_inode);
                err = notify_change(upperdentry, attr, NULL);
-               mutex_unlock(&upperdentry->d_inode->i_mutex);
+               inode_unlock(upperdentry->d_inode);
        }
        ovl_drop_write(dentry);
 out:
@@ -95,6 +108,29 @@ int ovl_permission(struct inode *inode, int mask)
 
        realdentry = ovl_entry_real(oe, &is_upper);
 
+       if (ovl_is_default_permissions(inode)) {
+               struct kstat stat;
+               struct path realpath = { .dentry = realdentry };
+
+               if (mask & MAY_NOT_BLOCK)
+                       return -ECHILD;
+
+               realpath.mnt = ovl_entry_mnt_real(oe, inode, is_upper);
+
+               err = vfs_getattr(&realpath, &stat);
+               if (err)
+                       return err;
+
+               if ((stat.mode ^ inode->i_mode) & S_IFMT)
+                       return -ESTALE;
+
+               inode->i_mode = stat.mode;
+               inode->i_uid = stat.uid;
+               inode->i_gid = stat.gid;
+
+               return generic_permission(inode, mask);
+       }
+
        /* Careful in RCU walk mode */
        realinode = ACCESS_ONCE(realdentry->d_inode);
        if (!realinode) {