ovl: handle umask and posix_acl_default correctly on creation

Setting MS_POSIXACL in sb->s_flags has the side effect of passing mode to
create functions without masking against umask.

Another problem when creating over a whiteout is that the default posix acl
is not inherited from the parent dir (because the real parent dir at the
time of creation is the work directory).

Fix these problems by:

 a) If upper fs does not have MS_POSIXACL, then mask mode with umask.

 b) If creating over a whiteout, call posix_acl_create() to get the
 inherited acls.  After creation (but before moving to the final
 destination) set these acls on the created file.  posix_acl_create() also
 updates the file creation mode as appropriate.

Fixes: 39a25b2b37 ("ovl: define ->get_acl() for overlay inodes")
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Miklos Szeredi 2016-09-01 11:11:59 +02:00
parent 0956254a2d
commit 38b256973e
1 changed files with 54 additions and 0 deletions

View File

@ -12,6 +12,8 @@
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include "overlayfs.h" #include "overlayfs.h"
void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
@ -186,6 +188,9 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
struct dentry *newdentry; struct dentry *newdentry;
int err; int err;
if (!hardlink && !IS_POSIXACL(udir))
stat->mode &= ~current_umask();
inode_lock_nested(udir, I_MUTEX_PARENT); inode_lock_nested(udir, I_MUTEX_PARENT);
newdentry = lookup_one_len(dentry->d_name.name, upperdir, newdentry = lookup_one_len(dentry->d_name.name, upperdir,
dentry->d_name.len); dentry->d_name.len);
@ -335,6 +340,32 @@ out_free:
return ret; return ret;
} }
static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
const struct posix_acl *acl)
{
void *buffer;
size_t size;
int err;
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !acl)
return 0;
size = posix_acl_to_xattr(NULL, acl, NULL, 0);
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
size = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
err = size;
if (err < 0)
goto out_free;
err = vfs_setxattr(upperdentry, name, buffer, size, XATTR_CREATE);
out_free:
kfree(buffer);
return err;
}
static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
struct kstat *stat, const char *link, struct kstat *stat, const char *link,
struct dentry *hardlink) struct dentry *hardlink)
@ -346,10 +377,18 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
struct dentry *upper; struct dentry *upper;
struct dentry *newdentry; struct dentry *newdentry;
int err; int err;
struct posix_acl *acl, *default_acl;
if (WARN_ON(!workdir)) if (WARN_ON(!workdir))
return -EROFS; return -EROFS;
if (!hardlink) {
err = posix_acl_create(dentry->d_parent->d_inode,
&stat->mode, &default_acl, &acl);
if (err)
return err;
}
err = ovl_lock_rename_workdir(workdir, upperdir); err = ovl_lock_rename_workdir(workdir, upperdir);
if (err) if (err)
goto out; goto out;
@ -384,6 +423,17 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (err) if (err)
goto out_cleanup; goto out_cleanup;
} }
if (!hardlink) {
err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_ACCESS,
acl);
if (err)
goto out_cleanup;
err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_DEFAULT,
default_acl);
if (err)
goto out_cleanup;
}
if (!hardlink && S_ISDIR(stat->mode)) { if (!hardlink && S_ISDIR(stat->mode)) {
err = ovl_set_opaque(newdentry); err = ovl_set_opaque(newdentry);
@ -410,6 +460,10 @@ out_dput:
out_unlock: out_unlock:
unlock_rename(workdir, upperdir); unlock_rename(workdir, upperdir);
out: out:
if (!hardlink) {
posix_acl_release(acl);
posix_acl_release(default_acl);
}
return err; return err;
out_cleanup: out_cleanup: