Commit f28929ba authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs fixes from Miklos Szeredi:
 "Most of this is regression fixes for posix acl behavior introduced in
  4.8-rc1 (these were caught by the pjd-fstest suite).  The are also
  miscellaneous fixes marked as stable material and cleanups.

  Other than overlayfs code, it touches <linux/fs.h> to add a constant
  with which to disable posix acl caching.  No changes needed to the
  actual caching code, it automatically does the right thing, although
  later we may want to optimize this case.

  I'm now testing overlayfs with the following test suites to catch
  regressions:

   - unionmount-testsuite
   - xfstests
   - pjd-fstest"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: update doc
  ovl: listxattr: use strnlen()
  ovl: Switch to generic_getxattr
  ovl: copyattr after setting POSIX ACL
  ovl: Switch to generic_removexattr
  ovl: Get rid of ovl_xattr_noacl_handlers array
  ovl: Fix OVL_XATTR_PREFIX
  ovl: fix spelling mistake: "directries" -> "directories"
  ovl: don't cache acl on overlay layer
  ovl: use cached acl on underlying layer
  ovl: proper cleanup of workdir
  ovl: remove posix_acl_default from workdir
  ovl: handle umask and posix_acl_default correctly on creation
  ovl: don't copy up opaqueness
parents ac810384 026e5e0c
...@@ -183,12 +183,10 @@ The copy_up operation essentially creates a new, identical file and ...@@ -183,12 +183,10 @@ The copy_up operation essentially creates a new, identical file and
moves it over to the old name. The new file may be on a different moves it over to the old name. The new file may be on a different
filesystem, so both st_dev and st_ino of the file may change. filesystem, so both st_dev and st_ino of the file may change.
Any open files referring to this inode will access the old data and Any open files referring to this inode will access the old data.
metadata. Similarly any file locks obtained before copy_up will not
apply to the copied up file.
On a file opened with O_RDONLY fchmod(2), fchown(2), futimesat(2) and Any file locks (and leases) obtained before copy_up will not apply
fsetxattr(2) will fail with EROFS. to the copied up file.
If a file with multiple hard links is copied up, then this will If a file with multiple hard links is copied up, then this will
"break" the link. Changes will not be propagated to other names "break" the link. Changes will not be propagated to other names
......
...@@ -80,6 +80,8 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new) ...@@ -80,6 +80,8 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
} }
for (name = buf; name < (buf + list_size); name += strlen(name) + 1) { for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
if (ovl_is_private_xattr(name))
continue;
retry: retry:
size = vfs_getxattr(old, name, value, value_size); size = vfs_getxattr(old, name, value, value_size);
if (size == -ERANGE) if (size == -ERANGE)
......
...@@ -12,6 +12,8 @@ ...@@ -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, ...@@ -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 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) ...@@ -335,6 +340,32 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
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, ...@@ -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, ...@@ -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 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -410,6 +460,10 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
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:
...@@ -950,9 +1004,9 @@ const struct inode_operations ovl_dir_inode_operations = { ...@@ -950,9 +1004,9 @@ const struct inode_operations ovl_dir_inode_operations = {
.permission = ovl_permission, .permission = ovl_permission,
.getattr = ovl_dir_getattr, .getattr = ovl_dir_getattr,
.setxattr = generic_setxattr, .setxattr = generic_setxattr,
.getxattr = ovl_getxattr, .getxattr = generic_getxattr,
.listxattr = ovl_listxattr, .listxattr = ovl_listxattr,
.removexattr = ovl_removexattr, .removexattr = generic_removexattr,
.get_acl = ovl_get_acl, .get_acl = ovl_get_acl,
.update_time = ovl_update_time, .update_time = ovl_update_time,
}; };
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/posix_acl.h>
#include "overlayfs.h" #include "overlayfs.h"
static int ovl_copy_up_truncate(struct dentry *dentry) static int ovl_copy_up_truncate(struct dentry *dentry)
...@@ -191,32 +192,44 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ...@@ -191,32 +192,44 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
return err; return err;
} }
static bool ovl_is_private_xattr(const char *name) bool ovl_is_private_xattr(const char *name)
{ {
#define OVL_XATTR_PRE_NAME OVL_XATTR_PREFIX "." return strncmp(name, OVL_XATTR_PREFIX,
return strncmp(name, OVL_XATTR_PRE_NAME, sizeof(OVL_XATTR_PREFIX) - 1) == 0;
sizeof(OVL_XATTR_PRE_NAME) - 1) == 0;
} }
int ovl_setxattr(struct dentry *dentry, struct inode *inode, int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
const char *name, const void *value, size_t size, int flags)
size_t size, int flags)
{ {
int err; int err;
struct dentry *upperdentry; struct path realpath;
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
const struct cred *old_cred; const struct cred *old_cred;
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
if (err) if (err)
goto out; goto out;
if (!value && !OVL_TYPE_UPPER(type)) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0)
goto out_drop_write;
}
err = ovl_copy_up(dentry); err = ovl_copy_up(dentry);
if (err) if (err)
goto out_drop_write; goto out_drop_write;
upperdentry = ovl_dentry_upper(dentry); if (!OVL_TYPE_UPPER(type))
ovl_path_upper(dentry, &realpath);
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_setxattr(upperdentry, name, value, size, flags); if (value)
err = vfs_setxattr(realpath.dentry, name, value, size, flags);
else {
WARN_ON(flags != XATTR_REPLACE);
err = vfs_removexattr(realpath.dentry, name);
}
revert_creds(old_cred); revert_creds(old_cred);
out_drop_write: out_drop_write:
...@@ -225,16 +238,13 @@ int ovl_setxattr(struct dentry *dentry, struct inode *inode, ...@@ -225,16 +238,13 @@ int ovl_setxattr(struct dentry *dentry, struct inode *inode,
return err; return err;
} }
ssize_t ovl_getxattr(struct dentry *dentry, struct inode *inode, int ovl_xattr_get(struct dentry *dentry, const char *name,
const char *name, void *value, size_t size) void *value, size_t size)
{ {
struct dentry *realdentry = ovl_dentry_real(dentry); struct dentry *realdentry = ovl_dentry_real(dentry);
ssize_t res; ssize_t res;
const struct cred *old_cred; const struct cred *old_cred;
if (ovl_is_private_xattr(name))
return -ENODATA;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_getxattr(realdentry, name, value, size); res = vfs_getxattr(realdentry, name, value, size);
revert_creds(old_cred); revert_creds(old_cred);
...@@ -245,7 +255,8 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ...@@ -245,7 +255,8 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{ {
struct dentry *realdentry = ovl_dentry_real(dentry); struct dentry *realdentry = ovl_dentry_real(dentry);
ssize_t res; ssize_t res;
int off; size_t len;
char *s;
const struct cred *old_cred; const struct cred *old_cred;
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
...@@ -255,73 +266,39 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ...@@ -255,73 +266,39 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
return res; return res;
/* filter out private xattrs */ /* filter out private xattrs */
for (off = 0; off < res;) { for (s = list, len = res; len;) {
char *s = list + off; size_t slen = strnlen(s, len) + 1;
size_t slen = strlen(s) + 1;
BUG_ON(off + slen > res); /* underlying fs providing us with an broken xattr list? */
if (WARN_ON(slen > len))
return -EIO;
len -= slen;
if (ovl_is_private_xattr(s)) { if (ovl_is_private_xattr(s)) {
res -= slen; res -= slen;
memmove(s, s + slen, res - off); memmove(s, s + slen, len);
} else { } else {
off += slen; s += slen;
} }
} }
return res; return res;
} }
int ovl_removexattr(struct dentry *dentry, const char *name)
{
int err;
struct path realpath;
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
const struct cred *old_cred;
err = ovl_want_write(dentry);
if (err)
goto out;
err = -ENODATA;
if (ovl_is_private_xattr(name))
goto out_drop_write;
if (!OVL_TYPE_UPPER(type)) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0)
goto out_drop_write;
err = ovl_copy_up(dentry);
if (err)
goto out_drop_write;
ovl_path_upper(dentry, &realpath);
}
old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_removexattr(realpath.dentry, name);
revert_creds(old_cred);
out_drop_write:
ovl_drop_write(dentry);
out:
return err;
}
struct posix_acl *ovl_get_acl(struct inode *inode, int type) struct posix_acl *ovl_get_acl(struct inode *inode, int type)
{ {
struct inode *realinode = ovl_inode_real(inode, NULL); struct inode *realinode = ovl_inode_real(inode, NULL);
const struct cred *old_cred; const struct cred *old_cred;
struct posix_acl *acl; struct posix_acl *acl;
if (!IS_POSIXACL(realinode)) if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
return NULL; return NULL;
if (!realinode->i_op->get_acl) if (!realinode->i_op->get_acl)
return NULL; return NULL;
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
acl = realinode->i_op->get_acl(realinode, type); acl = get_acl(realinode, type);
revert_creds(old_cred); revert_creds(old_cred);
return acl; return acl;
...@@ -391,9 +368,9 @@ static const struct inode_operations ovl_file_inode_operations = { ...@@ -391,9 +368,9 @@ static const struct inode_operations ovl_file_inode_operations = {
.permission = ovl_permission, .permission = ovl_permission,
.getattr = ovl_getattr, .getattr = ovl_getattr,
.setxattr = generic_setxattr, .setxattr = generic_setxattr,
.getxattr = ovl_getxattr, .getxattr = generic_getxattr,
.listxattr = ovl_listxattr, .listxattr = ovl_listxattr,
.removexattr = ovl_removexattr, .removexattr = generic_removexattr,
.get_acl = ovl_get_acl, .get_acl = ovl_get_acl,
.update_time = ovl_update_time, .update_time = ovl_update_time,
}; };
...@@ -404,9 +381,9 @@ static const struct inode_operations ovl_symlink_inode_operations = { ...@@ -404,9 +381,9 @@ static const struct inode_operations ovl_symlink_inode_operations = {
.readlink = ovl_readlink, .readlink = ovl_readlink,
.getattr = ovl_getattr, .getattr = ovl_getattr,
.setxattr = generic_setxattr, .setxattr = generic_setxattr,
.getxattr = ovl_getxattr, .getxattr = generic_getxattr,
.listxattr = ovl_listxattr, .listxattr = ovl_listxattr,
.removexattr = ovl_removexattr, .removexattr = generic_removexattr,
.update_time = ovl_update_time, .update_time = ovl_update_time,
}; };
...@@ -415,6 +392,9 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode) ...@@ -415,6 +392,9 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode)
inode->i_ino = get_next_ino(); inode->i_ino = get_next_ino();
inode->i_mode = mode; inode->i_mode = mode;
inode->i_flags |= S_NOCMTIME; inode->i_flags |= S_NOCMTIME;
#ifdef CONFIG_FS_POSIX_ACL
inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
#endif
mode &= S_IFMT; mode &= S_IFMT;
switch (mode) { switch (mode) {
......
...@@ -24,8 +24,8 @@ enum ovl_path_type { ...@@ -24,8 +24,8 @@ enum ovl_path_type {
(OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type)) (OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type))
#define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay" #define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay."
#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX ".opaque" #define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
#define OVL_ISUPPER_MASK 1UL #define OVL_ISUPPER_MASK 1UL
...@@ -179,20 +179,21 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list); ...@@ -179,20 +179,21 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list); void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list);
void ovl_cache_free(struct list_head *list); void ovl_cache_free(struct list_head *list);
int ovl_check_d_type_supported(struct path *realpath); int ovl_check_d_type_supported(struct path *realpath);
void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct dentry *dentry, int level);
/* inode.c */ /* inode.c */
int ovl_setattr(struct dentry *dentry, struct iattr *attr); int ovl_setattr(struct dentry *dentry, struct iattr *attr);
int ovl_permission(struct inode *inode, int mask); int ovl_permission(struct inode *inode, int mask);
int ovl_setxattr(struct dentry *dentry, struct inode *inode, int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
const char *name, const void *value, size_t size, int flags);
size_t size, int flags); int ovl_xattr_get(struct dentry *dentry, const char *name,
ssize_t ovl_getxattr(struct dentry *dentry, struct inode *inode, void *value, size_t size);
const char *name, void *value, size_t size);
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size); ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
int ovl_removexattr(struct dentry *dentry, const char *name);
struct posix_acl *ovl_get_acl(struct inode *inode, int type); struct posix_acl *ovl_get_acl(struct inode *inode, int type);
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags); int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
bool ovl_is_private_xattr(const char *name);
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode); struct inode *ovl_new_inode(struct super_block *sb, umode_t mode);
struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode); struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode);
......
...@@ -248,7 +248,7 @@ static inline int ovl_dir_read(struct path *realpath, ...@@ -248,7 +248,7 @@ static inline int ovl_dir_read(struct path *realpath,
err = rdd->err; err = rdd->err;
} while (!err && rdd->count); } while (!err && rdd->count);
if (!err && rdd->first_maybe_whiteout) if (!err && rdd->first_maybe_whiteout && rdd->dentry)
err = ovl_check_whiteouts(realpath->dentry, rdd); err = ovl_check_whiteouts(realpath->dentry, rdd);
fput(realfile); fput(realfile);
...@@ -606,3 +606,64 @@ int ovl_check_d_type_supported(struct path *realpath) ...@@ -606,3 +606,64 @@ int ovl_check_d_type_supported(struct path *realpath)
return rdd.d_type_supported; return rdd.d_type_supported;
} }
static void ovl_workdir_cleanup_recurse(struct path *path, int level)
{
int err;
struct inode *dir = path->dentry->d_inode;
LIST_HEAD(list);
struct ovl_cache_entry *p;
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge,
.dentry = NULL,
.list = &list,
.root = RB_ROOT,
.is_lowest = false,
};
err = ovl_dir_read(path, &rdd);
if (err)
goto out;
inode_lock_nested(dir, I_MUTEX_PARENT);
list_for_each_entry(p, &list, l_node) {
struct dentry *dentry;
if (p->name[0] == '.') {
if (p->len == 1)
continue;
if (p->len == 2 && p->name[1] == '.')
continue;
}
dentry = lookup_one_len(p->name, path->dentry, p->len);
if (IS_ERR(dentry))
continue;
if (dentry->d_inode)
ovl_workdir_cleanup(dir, path->mnt, dentry, level);
dput(dentry);
}
inode_unlock(dir);
out:
ovl_cache_free(&list);
}
void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct dentry *dentry, int level)
{
int err;
if (!d_is_dir(dentry) || level > 1) {
ovl_cleanup(dir, dentry);
return;
}
err = ovl_do_rmdir(dir, dentry);
if (err) {
struct path path = { .mnt = mnt, .dentry = dentry };
inode_unlock(dir);
ovl_workdir_cleanup_recurse(&path, level + 1);
inode_lock_nested(dir, I_MUTEX_PARENT);
ovl_cleanup(dir, dentry);
}
}
...@@ -814,6 +814,10 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt, ...@@ -814,6 +814,10 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
struct kstat stat = { struct kstat stat = {
.mode = S_IFDIR | 0, .mode = S_IFDIR | 0,
}; };
struct iattr attr = {
.ia_valid = ATTR_MODE,
.ia_mode = stat.mode,
};
if (work->d_inode) { if (work->d_inode) {
err = -EEXIST; err = -EEXIST;
...@@ -821,7 +825,7 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt, ...@@ -821,7 +825,7 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
goto out_dput; goto out_dput;
retried = true; retried = true;
ovl_cleanup(dir, work); ovl_workdir_cleanup(dir, mnt, work, 0);
dput(work); dput(work);
goto retry; goto retry;
} }
...@@ -829,6 +833,21 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt, ...@@ -829,6 +833,21 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
err = ovl_create_real(dir, work, &stat, NULL, NULL, true); err = ovl_create_real(dir, work, &stat, NULL, NULL, true);
if (err) if (err)
goto out_dput; goto out_dput;
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
if (err && err != -ENODATA)
goto out_dput;
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_ACCESS);
if (err && err != -ENODATA)
goto out_dput;
/* Clear any inherited mode bits */
inode_lock(work->d_inode);
err = notify_change(work, &attr, NULL);
inode_unlock(work->d_inode);
if (err)
goto out_dput;
} }
out_unlock: out_unlock:
inode_unlock(dir); inode_unlock(dir);
...@@ -967,10 +986,19 @@ static unsigned int ovl_split_lowerdirs(char *str) ...@@ -967,10 +986,19 @@ static unsigned int ovl_split_lowerdirs(char *str)
return ctr; return ctr;
} }
static int ovl_posix_acl_xattr_set(const struct xattr_handler *handler, static int __maybe_unused
struct dentry *dentry, struct inode *inode, ovl_posix_acl_xattr_get(const struct xattr_handler *handler,
const char *name, const void *value, struct dentry *dentry, struct inode *inode,
size_t size, int flags) const char *name, void *buffer, size_t size)
{
return ovl_xattr_get(dentry, handler->name, buffer, size);
}
static int __maybe_unused
ovl_posix_acl_xattr_set(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value,
size_t size, int flags)
{ {
struct dentry *workdir = ovl_workdir(dentry); struct dentry *workdir = ovl_workdir(dentry);
struct inode *realinode = ovl_inode_real(inode, NULL); struct inode *realinode = ovl_inode_real(inode, NULL);
...@@ -998,19 +1026,22 @@ static int ovl_posix_acl_xattr_set(const struct xattr_handler *handler, ...@@ -998,19 +1026,22 @@ static int ovl_posix_acl_xattr_set(const struct xattr_handler *handler,
posix_acl_release(acl); posix_acl_release(acl);
return ovl_setxattr(dentry, inode, handler->name, value, size, flags); err = ovl_xattr_set(dentry, handler->name, value, size, flags);
if (!err)
ovl_copyattr(ovl_inode_real(inode, NULL), inode);
return err;
out_acl_release: out_acl_release:
posix_acl_release(acl); posix_acl_release(acl);
return err; return err;
} }
static int ovl_other_xattr_set(const struct xattr_handler *handler, static int ovl_own_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode, struct dentry *dentry, struct inode *inode,
const char *name, const void *value, const char *name, void *buffer, size_t size)
size_t size, int flags)
{ {
return ovl_setxattr(dentry, inode, name, value, size, flags); return -EPERM;
} }
static int ovl_own_xattr_set(const struct xattr_handler *handler, static int ovl_own_xattr_set(const struct xattr_handler *handler,
...@@ -1021,42 +1052,59 @@ static int ovl_own_xattr_set(const struct xattr_handler *handler, ...@@ -1021,42 +1052,59 @@ static int ovl_own_xattr_set(const struct xattr_handler *handler,
return -EPERM; return -EPERM;
} }
static const struct xattr_handler ovl_posix_acl_access_xattr_handler = { static int ovl_other_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
return ovl_xattr_get(dentry, name, buffer, size);
}
static int ovl_other_xattr_set(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value,
size_t size, int flags)
{
return ovl_xattr_set(dentry, name, value, size, flags);
}
static const struct xattr_handler __maybe_unused
ovl_posix_acl_access_xattr_handler = {
.name = XATTR_NAME_POSIX_ACL_ACCESS, .name = XATTR_NAME_POSIX_ACL_ACCESS,
.flags = ACL_TYPE_ACCESS, .flags = ACL_TYPE_ACCESS,
.get = ovl_posix_acl_xattr_get,
.set = ovl_posix_acl_xattr_set, .set = ovl_posix_acl_xattr_set,
}; };
static const struct xattr_handler ovl_posix_acl_default_xattr_handler = { static const struct xattr_handler __maybe_unused
ovl_posix_acl_default_xattr_handler = {
.name = XATTR_NAME_POSIX_ACL_DEFAULT, .name = XATTR_NAME_POSIX_ACL_DEFAULT,
.flags = ACL_TYPE_DEFAULT, .flags = ACL_TYPE_DEFAULT,
.get = ovl_posix_acl_xattr_get,
.set = ovl_posix_acl_xattr_set, .set = ovl_posix_acl_xattr_set,
}; };
static const struct xattr_handler ovl_own_xattr_handler = { static const struct xattr_handler ovl_own_xattr_handler = {
.prefix = OVL_XATTR_PREFIX, .prefix = OVL_XATTR_PREFIX,
.get = ovl_own_xattr_get,
.set = ovl_own_xattr_set, .set = ovl_own_xattr_set,
}; };
static const struct xattr_handler ovl_other_xattr_handler = { static const struct xattr_handler ovl_other_xattr_handler = {
.prefix = "", /* catch all */ .prefix = "", /* catch all */
.get = ovl_other_xattr_get,
.set = ovl_other_xattr_set, .set = ovl_other_xattr_set,
}; };
static const struct xattr_handler *ovl_xattr_handlers[] = { static const struct xattr_handler *ovl_xattr_handlers[] = {
#ifdef CONFIG_FS_POSIX_ACL
&ovl_posix_acl_access_xattr_handler, &ovl_posix_acl_access_xattr_handler,
&ovl_posix_acl_default_xattr_handler, &ovl_posix_acl_default_xattr_handler,
#endif
&ovl_own_xattr_handler, &ovl_own_xattr_handler,
&ovl_other_xattr_handler, &ovl_other_xattr_handler,
NULL NULL
}; };
static const struct xattr_handler *ovl_xattr_noacl_handlers[] = {
&ovl_own_xattr_handler,
&ovl_other_xattr_handler,
NULL,
};
static int ovl_fill_super(struct super_block *sb, void *data, int silent) static int ovl_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct path upperpath = { NULL, NULL }; struct path upperpath = { NULL, NULL };
...@@ -1132,7 +1180,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1132,7 +1180,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
err = -EINVAL; err = -EINVAL;
stacklen = ovl_split_lowerdirs(lowertmp); stacklen = ovl_split_lowerdirs(lowertmp);
if (stacklen > OVL_MAX_STACK) { if (stacklen > OVL_MAX_STACK) {
pr_err("overlayfs: too many lower directries, limit is %d\n", pr_err("overlayfs: too many lower directories, limit is %d\n",
OVL_MAX_STACK); OVL_MAX_STACK);
goto out_free_lowertmp; goto out_free_lowertmp;
} else if (!ufs->config.upperdir && stacklen == 1) { } else if (!ufs->config.upperdir && stacklen == 1) {
...@@ -1269,10 +1317,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1269,10 +1317,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_magic = OVERLAYFS_SUPER_MAGIC;
sb->s_op = &ovl_super_operations; sb->s_op = &ovl_super_operations;
if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) sb->s_xattr = ovl_xattr_handlers;
sb->s_xattr = ovl_xattr_handlers;
else
sb->s_xattr = ovl_xattr_noacl_handlers;
sb->s_root = root_dentry; sb->s_root = root_dentry;
sb->s_fs_info = ufs; sb->s_fs_info = ufs;
sb->s_flags |= MS_POSIXACL; sb->s_flags |= MS_POSIXACL;
......
...@@ -574,6 +574,7 @@ static inline void mapping_allow_writable(struct address_space *mapping) ...@@ -574,6 +574,7 @@ static inline void mapping_allow_writable(struct address_space *mapping)
struct posix_acl; struct posix_acl;
#define ACL_NOT_CACHED ((void *)(-1)) #define ACL_NOT_CACHED ((void *)(-1))
#define ACL_DONT_CACHE ((void *)(-3))
static inline struct posix_acl * static inline struct posix_acl *
uncached_acl_sentinel(struct task_struct *task) uncached_acl_sentinel(struct task_struct *task)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment