First two patches weren't marked for stable but are dependencies for laters ones. The rest of patches was marked for stable but most likely will be backported to 4.5+ only so we need to get them on our own. An important fix is eea2fb4851e9d ("ovl: proper cleanup of workdir") as it allows mounting overlayfs with dirty workdir, e.g. after power cut. Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
parent
81dfbfb069
commit
42f559ed70
@ -0,0 +1,72 @@ |
||||
From 56656e960b555cb98bc414382566dcb59aae99a2 Mon Sep 17 00:00:00 2001
|
||||
From: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Date: Mon, 21 Mar 2016 17:31:46 +0100
|
||||
Subject: [PATCH] ovl: rename is_merge to is_lowest
|
||||
|
||||
The 'is_merge' is an historical naming from when only a single lower layer
|
||||
could exist. With the introduction of multiple lower layers the meaning of
|
||||
this flag was changed to mean only the "lowest layer" (while all lower
|
||||
layers were being merged).
|
||||
|
||||
So now 'is_merge' is inaccurate and hence renaming to 'is_lowest'
|
||||
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
---
|
||||
fs/overlayfs/readdir.c | 16 ++++++++--------
|
||||
1 file changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/fs/overlayfs/readdir.c
|
||||
+++ b/fs/overlayfs/readdir.c
|
||||
@@ -36,7 +36,7 @@ struct ovl_dir_cache {
|
||||
|
||||
struct ovl_readdir_data {
|
||||
struct dir_context ctx;
|
||||
- bool is_merge;
|
||||
+ bool is_lowest;
|
||||
struct rb_root root;
|
||||
struct list_head *list;
|
||||
struct list_head middle;
|
||||
@@ -139,9 +139,9 @@ static int ovl_cache_entry_add_rb(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int ovl_fill_lower(struct ovl_readdir_data *rdd,
|
||||
- const char *name, int namelen,
|
||||
- loff_t offset, u64 ino, unsigned int d_type)
|
||||
+static int ovl_fill_lowest(struct ovl_readdir_data *rdd,
|
||||
+ const char *name, int namelen,
|
||||
+ loff_t offset, u64 ino, unsigned int d_type)
|
||||
{
|
||||
struct ovl_cache_entry *p;
|
||||
|
||||
@@ -193,10 +193,10 @@ static int ovl_fill_merge(struct dir_con
|
||||
container_of(ctx, struct ovl_readdir_data, ctx);
|
||||
|
||||
rdd->count++;
|
||||
- if (!rdd->is_merge)
|
||||
+ if (!rdd->is_lowest)
|
||||
return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type);
|
||||
else
|
||||
- return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
|
||||
+ return ovl_fill_lowest(rdd, name, namelen, offset, ino, d_type);
|
||||
}
|
||||
|
||||
static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
|
||||
@@ -289,7 +289,7 @@ static int ovl_dir_read_merged(struct de
|
||||
.ctx.actor = ovl_fill_merge,
|
||||
.list = list,
|
||||
.root = RB_ROOT,
|
||||
- .is_merge = false,
|
||||
+ .is_lowest = false,
|
||||
};
|
||||
int idx, next;
|
||||
|
||||
@@ -306,7 +306,7 @@ static int ovl_dir_read_merged(struct de
|
||||
* allows offsets to be reasonably constant
|
||||
*/
|
||||
list_add(&rdd.middle, rdd.list);
|
||||
- rdd.is_merge = true;
|
||||
+ rdd.is_lowest = true;
|
||||
err = ovl_dir_read(&realpath, &rdd);
|
||||
list_del(&rdd.middle);
|
||||
}
|
@ -0,0 +1,336 @@ |
||||
From 3fe6e52f062643676eb4518d68cee3bc1272091b Mon Sep 17 00:00:00 2001
|
||||
From: Antonio Murdaca <amurdaca@redhat.com>
|
||||
Date: Thu, 7 Apr 2016 15:48:25 +0200
|
||||
Subject: [PATCH] ovl: override creds with the ones from the superblock mounter
|
||||
|
||||
In user namespace the whiteout creation fails with -EPERM because the
|
||||
current process isn't capable(CAP_SYS_ADMIN) when setting xattr.
|
||||
|
||||
A simple reproducer:
|
||||
|
||||
$ mkdir upper lower work merged lower/dir
|
||||
$ sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged
|
||||
$ unshare -m -p -f -U -r bash
|
||||
|
||||
Now as root in the user namespace:
|
||||
|
||||
\# touch merged/dir/{1,2,3} # this will force a copy up of lower/dir
|
||||
\# rm -fR merged/*
|
||||
|
||||
This ends up failing with -EPERM after the files in dir has been
|
||||
correctly deleted:
|
||||
|
||||
unlinkat(4, "2", 0) = 0
|
||||
unlinkat(4, "1", 0) = 0
|
||||
unlinkat(4, "3", 0) = 0
|
||||
close(4) = 0
|
||||
unlinkat(AT_FDCWD, "merged/dir", AT_REMOVEDIR) = -1 EPERM (Operation not
|
||||
permitted)
|
||||
|
||||
Interestingly, if you don't place files in merged/dir you can remove it,
|
||||
meaning if upper/dir does not exist, creating the char device file works
|
||||
properly in that same location.
|
||||
|
||||
This patch uses ovl_sb_creator_cred() to get the cred struct from the
|
||||
superblock mounter and override the old cred with these new ones so that
|
||||
the whiteout creation is possible because overlay is wrong in assuming that
|
||||
the creds it will get with prepare_creds will be in the initial user
|
||||
namespace. The old cap_raise game is removed in favor of just overriding
|
||||
the old cred struct.
|
||||
|
||||
This patch also drops from ovl_copy_up_one() the following two lines:
|
||||
|
||||
override_cred->fsuid = stat->uid;
|
||||
override_cred->fsgid = stat->gid;
|
||||
|
||||
This is because the correct uid and gid are taken directly with the stat
|
||||
struct and correctly set with ovl_set_attr().
|
||||
|
||||
Signed-off-by: Antonio Murdaca <runcom@redhat.com>
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
---
|
||||
fs/overlayfs/copy_up.c | 26 +------------------
|
||||
fs/overlayfs/dir.c | 67 ++++--------------------------------------------
|
||||
fs/overlayfs/overlayfs.h | 1 +
|
||||
fs/overlayfs/readdir.c | 14 +++-------
|
||||
fs/overlayfs/super.c | 18 ++++++++++++-
|
||||
5 files changed, 27 insertions(+), 99 deletions(-)
|
||||
|
||||
--- a/fs/overlayfs/copy_up.c
|
||||
+++ b/fs/overlayfs/copy_up.c
|
||||
@@ -303,7 +303,6 @@ int ovl_copy_up_one(struct dentry *paren
|
||||
struct dentry *upperdir;
|
||||
struct dentry *upperdentry;
|
||||
const struct cred *old_cred;
|
||||
- struct cred *override_cred;
|
||||
char *link = NULL;
|
||||
|
||||
if (WARN_ON(!workdir))
|
||||
@@ -322,28 +321,7 @@ int ovl_copy_up_one(struct dentry *paren
|
||||
return PTR_ERR(link);
|
||||
}
|
||||
|
||||
- err = -ENOMEM;
|
||||
- override_cred = prepare_creds();
|
||||
- if (!override_cred)
|
||||
- goto out_free_link;
|
||||
-
|
||||
- override_cred->fsuid = stat->uid;
|
||||
- override_cred->fsgid = stat->gid;
|
||||
- /*
|
||||
- * CAP_SYS_ADMIN for copying up extended attributes
|
||||
- * CAP_DAC_OVERRIDE for create
|
||||
- * CAP_FOWNER for chmod, timestamp update
|
||||
- * CAP_FSETID for chmod
|
||||
- * CAP_CHOWN for chown
|
||||
- * CAP_MKNOD for mknod
|
||||
- */
|
||||
- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
|
||||
- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
||||
- cap_raise(override_cred->cap_effective, CAP_FOWNER);
|
||||
- cap_raise(override_cred->cap_effective, CAP_FSETID);
|
||||
- cap_raise(override_cred->cap_effective, CAP_CHOWN);
|
||||
- cap_raise(override_cred->cap_effective, CAP_MKNOD);
|
||||
- old_cred = override_creds(override_cred);
|
||||
+ old_cred = ovl_override_creds(dentry->d_sb);
|
||||
|
||||
err = -EIO;
|
||||
if (lock_rename(workdir, upperdir) != NULL) {
|
||||
@@ -366,9 +344,7 @@ int ovl_copy_up_one(struct dentry *paren
|
||||
out_unlock:
|
||||
unlock_rename(workdir, upperdir);
|
||||
revert_creds(old_cred);
|
||||
- put_cred(override_cred);
|
||||
|
||||
-out_free_link:
|
||||
if (link)
|
||||
free_page((unsigned long) link);
|
||||
|
||||
--- a/fs/overlayfs/dir.c
|
||||
+++ b/fs/overlayfs/dir.c
|
||||
@@ -405,28 +405,13 @@ static int ovl_create_or_link(struct den
|
||||
err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
|
||||
} else {
|
||||
const struct cred *old_cred;
|
||||
- struct cred *override_cred;
|
||||
|
||||
- err = -ENOMEM;
|
||||
- override_cred = prepare_creds();
|
||||
- if (!override_cred)
|
||||
- goto out_iput;
|
||||
-
|
||||
- /*
|
||||
- * CAP_SYS_ADMIN for setting opaque xattr
|
||||
- * CAP_DAC_OVERRIDE for create in workdir, rename
|
||||
- * CAP_FOWNER for removing whiteout from sticky dir
|
||||
- */
|
||||
- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
|
||||
- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
||||
- cap_raise(override_cred->cap_effective, CAP_FOWNER);
|
||||
- old_cred = override_creds(override_cred);
|
||||
+ old_cred = ovl_override_creds(dentry->d_sb);
|
||||
|
||||
err = ovl_create_over_whiteout(dentry, inode, &stat, link,
|
||||
hardlink);
|
||||
|
||||
revert_creds(old_cred);
|
||||
- put_cred(override_cred);
|
||||
}
|
||||
|
||||
if (!err)
|
||||
@@ -656,32 +641,11 @@ static int ovl_do_remove(struct dentry *
|
||||
if (OVL_TYPE_PURE_UPPER(type)) {
|
||||
err = ovl_remove_upper(dentry, is_dir);
|
||||
} else {
|
||||
- const struct cred *old_cred;
|
||||
- struct cred *override_cred;
|
||||
-
|
||||
- err = -ENOMEM;
|
||||
- override_cred = prepare_creds();
|
||||
- if (!override_cred)
|
||||
- goto out_drop_write;
|
||||
-
|
||||
- /*
|
||||
- * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
|
||||
- * CAP_DAC_OVERRIDE for create in workdir, rename
|
||||
- * CAP_FOWNER for removing whiteout from sticky dir
|
||||
- * CAP_FSETID for chmod of opaque dir
|
||||
- * CAP_CHOWN for chown of opaque dir
|
||||
- */
|
||||
- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
|
||||
- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
||||
- cap_raise(override_cred->cap_effective, CAP_FOWNER);
|
||||
- cap_raise(override_cred->cap_effective, CAP_FSETID);
|
||||
- cap_raise(override_cred->cap_effective, CAP_CHOWN);
|
||||
- old_cred = override_creds(override_cred);
|
||||
+ const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
|
||||
|
||||
err = ovl_remove_and_whiteout(dentry, is_dir);
|
||||
|
||||
revert_creds(old_cred);
|
||||
- put_cred(override_cred);
|
||||
}
|
||||
out_drop_write:
|
||||
ovl_drop_write(dentry);
|
||||
@@ -720,7 +684,6 @@ static int ovl_rename2(struct inode *old
|
||||
bool new_is_dir = false;
|
||||
struct dentry *opaquedir = NULL;
|
||||
const struct cred *old_cred = NULL;
|
||||
- struct cred *override_cred = NULL;
|
||||
|
||||
err = -EINVAL;
|
||||
if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
|
||||
@@ -789,26 +752,8 @@ static int ovl_rename2(struct inode *old
|
||||
old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
|
||||
new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
|
||||
|
||||
- if (old_opaque || new_opaque) {
|
||||
- err = -ENOMEM;
|
||||
- override_cred = prepare_creds();
|
||||
- if (!override_cred)
|
||||
- goto out_drop_write;
|
||||
-
|
||||
- /*
|
||||
- * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
|
||||
- * CAP_DAC_OVERRIDE for create in workdir
|
||||
- * CAP_FOWNER for removing whiteout from sticky dir
|
||||
- * CAP_FSETID for chmod of opaque dir
|
||||
- * CAP_CHOWN for chown of opaque dir
|
||||
- */
|
||||
- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
|
||||
- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
||||
- cap_raise(override_cred->cap_effective, CAP_FOWNER);
|
||||
- cap_raise(override_cred->cap_effective, CAP_FSETID);
|
||||
- cap_raise(override_cred->cap_effective, CAP_CHOWN);
|
||||
- old_cred = override_creds(override_cred);
|
||||
- }
|
||||
+ if (old_opaque || new_opaque)
|
||||
+ old_cred = ovl_override_creds(old->d_sb);
|
||||
|
||||
if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
|
||||
opaquedir = ovl_check_empty_and_clear(new);
|
||||
@@ -939,10 +884,8 @@ out_dput_old:
|
||||
out_unlock:
|
||||
unlock_rename(new_upperdir, old_upperdir);
|
||||
out_revert_creds:
|
||||
- if (old_opaque || new_opaque) {
|
||||
+ if (old_opaque || new_opaque)
|
||||
revert_creds(old_cred);
|
||||
- put_cred(override_cred);
|
||||
- }
|
||||
out_drop_write:
|
||||
ovl_drop_write(old);
|
||||
out:
|
||||
--- a/fs/overlayfs/overlayfs.h
|
||||
+++ b/fs/overlayfs/overlayfs.h
|
||||
@@ -150,6 +150,7 @@ void ovl_drop_write(struct dentry *dentr
|
||||
bool ovl_dentry_is_opaque(struct dentry *dentry);
|
||||
void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
|
||||
bool ovl_is_whiteout(struct dentry *dentry);
|
||||
+const struct cred *ovl_override_creds(struct super_block *sb);
|
||||
void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
|
||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags);
|
||||
--- a/fs/overlayfs/readdir.c
|
||||
+++ b/fs/overlayfs/readdir.c
|
||||
@@ -36,6 +36,7 @@ struct ovl_dir_cache {
|
||||
|
||||
struct ovl_readdir_data {
|
||||
struct dir_context ctx;
|
||||
+ struct dentry *dentry;
|
||||
bool is_lowest;
|
||||
struct rb_root root;
|
||||
struct list_head *list;
|
||||
@@ -205,17 +206,8 @@ static int ovl_check_whiteouts(struct de
|
||||
struct ovl_cache_entry *p;
|
||||
struct dentry *dentry;
|
||||
const struct cred *old_cred;
|
||||
- struct cred *override_cred;
|
||||
-
|
||||
- override_cred = prepare_creds();
|
||||
- if (!override_cred)
|
||||
- return -ENOMEM;
|
||||
|
||||
- /*
|
||||
- * CAP_DAC_OVERRIDE for lookup
|
||||
- */
|
||||
- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
||||
- old_cred = override_creds(override_cred);
|
||||
+ old_cred = ovl_override_creds(rdd->dentry->d_sb);
|
||||
|
||||
err = mutex_lock_killable(&dir->d_inode->i_mutex);
|
||||
if (!err) {
|
||||
@@ -231,7 +223,6 @@ static int ovl_check_whiteouts(struct de
|
||||
mutex_unlock(&dir->d_inode->i_mutex);
|
||||
}
|
||||
revert_creds(old_cred);
|
||||
- put_cred(override_cred);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -287,6 +278,7 @@ static int ovl_dir_read_merged(struct de
|
||||
struct path realpath;
|
||||
struct ovl_readdir_data rdd = {
|
||||
.ctx.actor = ovl_fill_merge,
|
||||
+ .dentry = dentry,
|
||||
.list = list,
|
||||
.root = RB_ROOT,
|
||||
.is_lowest = false,
|
||||
--- a/fs/overlayfs/super.c
|
||||
+++ b/fs/overlayfs/super.c
|
||||
@@ -42,6 +42,8 @@ struct ovl_fs {
|
||||
long lower_namelen;
|
||||
/* pathnames of lower and upper dirs, for show_options */
|
||||
struct ovl_config config;
|
||||
+ /* creds of process who forced instantiation of super block */
|
||||
+ const struct cred *creator_cred;
|
||||
};
|
||||
|
||||
struct ovl_dir_cache;
|
||||
@@ -246,6 +248,13 @@ bool ovl_is_whiteout(struct dentry *dent
|
||||
return inode && IS_WHITEOUT(inode);
|
||||
}
|
||||
|
||||
+const struct cred *ovl_override_creds(struct super_block *sb)
|
||||
+{
|
||||
+ struct ovl_fs *ofs = sb->s_fs_info;
|
||||
+
|
||||
+ return override_creds(ofs->creator_cred);
|
||||
+}
|
||||
+
|
||||
static bool ovl_is_opaquedir(struct dentry *dentry)
|
||||
{
|
||||
int res;
|
||||
@@ -587,6 +596,7 @@ static void ovl_put_super(struct super_b
|
||||
kfree(ufs->config.lowerdir);
|
||||
kfree(ufs->config.upperdir);
|
||||
kfree(ufs->config.workdir);
|
||||
+ put_cred(ufs->creator_cred);
|
||||
kfree(ufs);
|
||||
}
|
||||
|
||||
@@ -1068,10 +1078,14 @@ static int ovl_fill_super(struct super_b
|
||||
else
|
||||
sb->s_d_op = &ovl_dentry_operations;
|
||||
|
||||
+ ufs->creator_cred = prepare_creds();
|
||||
+ if (!ufs->creator_cred)
|
||||
+ goto out_put_lower_mnt;
|
||||
+
|
||||
err = -ENOMEM;
|
||||
oe = ovl_alloc_entry(numlower);
|
||||
if (!oe)
|
||||
- goto out_put_lower_mnt;
|
||||
+ goto out_put_cred;
|
||||
|
||||
root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
|
||||
if (!root_dentry)
|
||||
@@ -1104,6 +1118,8 @@ static int ovl_fill_super(struct super_b
|
||||
|
||||
out_free_oe:
|
||||
kfree(oe);
|
||||
+out_put_cred:
|
||||
+ put_cred(ufs->creator_cred);
|
||||
out_put_lower_mnt:
|
||||
for (i = 0; i < ufs->numlower; i++)
|
||||
mntput(ufs->lower_mnt[i]);
|
@ -0,0 +1,73 @@ |
||||
From 0956254a2d5b9e2141385514553aeef694dfe3b5 Mon Sep 17 00:00:00 2001
|
||||
From: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Date: Mon, 8 Aug 2016 15:08:49 +0200
|
||||
Subject: [PATCH] ovl: don't copy up opaqueness
|
||||
|
||||
When a copy up of a directory occurs which has the opaque xattr set, the
|
||||
xattr remains in the upper directory. The immediate behavior with overlayfs
|
||||
is that the upper directory is not treated as opaque, however after a
|
||||
remount the opaque flag is used and upper directory is treated as opaque.
|
||||
This causes files created in the lower layer to be hidden when using
|
||||
multiple lower directories.
|
||||
|
||||
Fix by not copying up the opaque flag.
|
||||
|
||||
To reproduce:
|
||||
|
||||
----8<---------8<---------8<---------8<---------8<---------8<----
|
||||
mkdir -p l/d/s u v w mnt
|
||||
mount -t overlay overlay -olowerdir=l,upperdir=u,workdir=w mnt
|
||||
rm -rf mnt/d/
|
||||
mkdir -p mnt/d/n
|
||||
umount mnt
|
||||
mount -t overlay overlay -olowerdir=u:l,upperdir=v,workdir=w mnt
|
||||
touch mnt/d/foo
|
||||
umount mnt
|
||||
mount -t overlay overlay -olowerdir=u:l,upperdir=v,workdir=w mnt
|
||||
ls mnt/d
|
||||
----8<---------8<---------8<---------8<---------8<---------8<----
|
||||
|
||||
output should be: "foo n"
|
||||
|
||||
Reported-by: Derek McGowan <dmcg@drizz.net>
|
||||
Link: https://bugzilla.kernel.org/show_bug.cgi?id=151291
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
---
|
||||
fs/overlayfs/copy_up.c | 2 ++
|
||||
fs/overlayfs/inode.c | 2 +-
|
||||
fs/overlayfs/overlayfs.h | 1 +
|
||||
3 files changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/fs/overlayfs/copy_up.c
|
||||
+++ b/fs/overlayfs/copy_up.c
|
||||
@@ -48,6 +48,8 @@ int ovl_copy_xattr(struct dentry *old, s
|
||||
}
|
||||
|
||||
for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
|
||||
+ if (ovl_is_private_xattr(name))
|
||||
+ continue;
|
||||
retry:
|
||||
size = vfs_getxattr(old, name, value, value_size);
|
||||
if (size == -ERANGE)
|
||||
--- a/fs/overlayfs/inode.c
|
||||
+++ b/fs/overlayfs/inode.c
|
||||
@@ -219,7 +219,7 @@ static int ovl_readlink(struct dentry *d
|
||||
}
|
||||
|
||||
|
||||
-static bool ovl_is_private_xattr(const char *name)
|
||||
+bool ovl_is_private_xattr(const char *name)
|
||||
{
|
||||
return strncmp(name, OVL_XATTR_PRE_NAME, OVL_XATTR_PRE_LEN) == 0;
|
||||
}
|
||||
--- a/fs/overlayfs/overlayfs.h
|
||||
+++ b/fs/overlayfs/overlayfs.h
|
||||
@@ -175,6 +175,7 @@ ssize_t ovl_getxattr(struct dentry *dent
|
||||
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
|
||||
int ovl_removexattr(struct dentry *dentry, const char *name);
|
||||
struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags);
|
||||
+bool ovl_is_private_xattr(const char *name);
|
||||
|
||||
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
|
||||
struct ovl_entry *oe);
|
@ -0,0 +1,49 @@ |
||||
From c11b9fdd6a612f376a5e886505f1c54c16d8c380 Mon Sep 17 00:00:00 2001
|
||||
From: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Date: Thu, 1 Sep 2016 11:11:59 +0200
|
||||
Subject: [PATCH] ovl: remove posix_acl_default from workdir
|
||||
|
||||
Clear out posix acl xattrs on workdir and also reset the mode after
|
||||
creation so that an inherited sgid bit is cleared.
|
||||
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
---
|
||||
fs/overlayfs/super.c | 19 +++++++++++++++++++
|
||||
1 file changed, 19 insertions(+)
|
||||
|
||||
--- a/fs/overlayfs/super.c
|
||||
+++ b/fs/overlayfs/super.c
|
||||
@@ -773,6 +773,10 @@ retry:
|
||||
struct kstat stat = {
|
||||
.mode = S_IFDIR | 0,
|
||||
};
|
||||
+ struct iattr attr = {
|
||||
+ .ia_valid = ATTR_MODE,
|
||||
+ .ia_mode = stat.mode,
|
||||
+ };
|
||||
|
||||
if (work->d_inode) {
|
||||
err = -EEXIST;
|
||||
@@ -788,6 +792,21 @@ retry:
|
||||
err = ovl_create_real(dir, work, &stat, NULL, NULL, true);
|
||||
if (err)
|
||||
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 */
|
||||
+ mutex_lock(&work->d_inode->i_mutex);
|
||||
+ err = notify_change(work, &attr, NULL);
|
||||
+ mutex_unlock(&work->d_inode->i_mutex);
|
||||
+ if (err)
|
||||
+ goto out_dput;
|
||||
}
|
||||
out_unlock:
|
||||
mutex_unlock(&dir->i_mutex);
|
@ -0,0 +1,131 @@ |
||||
From eea2fb4851e9dcbab6b991aaf47e2e024f1f55a0 Mon Sep 17 00:00:00 2001
|
||||
From: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Date: Thu, 1 Sep 2016 11:11:59 +0200
|
||||
Subject: [PATCH] ovl: proper cleanup of workdir
|
||||
|
||||
When mounting overlayfs it needs a clean "work" directory under the
|
||||
supplied workdir.
|
||||
|
||||
Previously the mount code removed this directory if it already existed and
|
||||
created a new one. If the removal failed (e.g. directory was not empty)
|
||||
then it fell back to a read-only mount not using the workdir.
|
||||
|
||||
While this has never been reported, it is possible to get a non-empty
|
||||
"work" dir from a previous mount of overlayfs in case of crash in the
|
||||
middle of an operation using the work directory.
|
||||
|
||||
In this case the left over state should be discarded and the overlay
|
||||
filesystem will be consistent, guaranteed by the atomicity of operations on
|
||||
moving to/from the workdir to the upper layer.
|
||||
|
||||
This patch implements cleaning out any files left in workdir. It is
|
||||
implemented using real recursion for simplicity, but the depth is limited
|
||||
to 2, because the worst case is that of a directory containing whiteouts
|
||||
under "work".
|
||||
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
---
|
||||
fs/overlayfs/overlayfs.h | 2 ++
|
||||
fs/overlayfs/readdir.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++-
|
||||
fs/overlayfs/super.c | 2 +-
|
||||
3 files changed, 65 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/fs/overlayfs/overlayfs.h
|
||||
+++ b/fs/overlayfs/overlayfs.h
|
||||
@@ -164,6 +164,8 @@ extern const struct file_operations ovl_
|
||||
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_cache_free(struct list_head *list);
|
||||
+void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
|
||||
+ struct dentry *dentry, int level);
|
||||
|
||||
/* inode.c */
|
||||
int ovl_setattr(struct dentry *dentry, struct iattr *attr);
|
||||
--- a/fs/overlayfs/readdir.c
|
||||
+++ b/fs/overlayfs/readdir.c
|
||||
@@ -247,7 +247,7 @@ static inline int ovl_dir_read(struct pa
|
||||
err = rdd->err;
|
||||
} 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);
|
||||
|
||||
fput(realfile);
|
||||
@@ -569,3 +569,64 @@ void ovl_cleanup_whiteouts(struct dentry
|
||||
}
|
||||
mutex_unlock(&upper->d_inode->i_mutex);
|
||||
}
|
||||
+
|
||||
+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;
|
||||
+
|
||||
+ mutex_lock_nested(&dir->i_mutex, 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);
|
||||
+ }
|
||||
+ mutex_unlock(&dir->i_mutex);
|
||||
+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 };
|
||||
+
|
||||
+ mutex_unlock(&dir->i_mutex);
|
||||
+ ovl_workdir_cleanup_recurse(&path, level + 1);
|
||||
+ mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
|
||||
+ ovl_cleanup(dir, dentry);
|
||||
+ }
|
||||
+}
|
||||
--- a/fs/overlayfs/super.c
|
||||
+++ b/fs/overlayfs/super.c
|
||||
@@ -784,7 +784,7 @@ retry:
|
||||
goto out_dput;
|
||||
|
||||
retried = true;
|
||||
- ovl_cleanup(dir, work);
|
||||
+ ovl_workdir_cleanup(dir, mnt, work, 0);
|
||||
dput(work);
|
||||
goto retry;
|
||||
}
|
@ -0,0 +1,53 @@ |
||||
From 7cb35119d067191ce9ebc380a599db0b03cbd9d9 Mon Sep 17 00:00:00 2001
|
||||
From: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Date: Thu, 1 Sep 2016 11:12:00 +0200
|
||||
Subject: [PATCH] ovl: listxattr: use strnlen()
|
||||
|
||||
Be defensive about what underlying fs provides us in the returned xattr
|
||||
list buffer. If it's not properly null terminated, bail out with a warning
|
||||
insead of BUG.
|
||||
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
---
|
||||
fs/overlayfs/inode.c | 17 ++++++++++-------
|
||||
1 file changed, 10 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/fs/overlayfs/inode.c
|
||||
+++ b/fs/overlayfs/inode.c
|
||||
@@ -277,7 +277,8 @@ ssize_t ovl_listxattr(struct dentry *den
|
||||
struct path realpath;
|
||||
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
|
||||
ssize_t res;
|
||||
- int off;
|
||||
+ size_t len;
|
||||
+ char *s;
|
||||
|
||||
res = vfs_listxattr(realpath.dentry, list, size);
|
||||
if (res <= 0 || size == 0)
|
||||
@@ -287,17 +288,19 @@ ssize_t ovl_listxattr(struct dentry *den
|
||||
return res;
|
||||
|
||||
/* filter out private xattrs */
|
||||
- for (off = 0; off < res;) {
|
||||
- char *s = list + off;
|
||||
- size_t slen = strlen(s) + 1;
|
||||
+ for (s = list, len = res; len;) {
|
||||
+ size_t slen = strnlen(s, len) + 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)) {
|
||||
res -= slen;
|
||||
- memmove(s, s + slen, res - off);
|
||||
+ memmove(s, s + slen, len);
|
||||
} else {
|
||||
- off += slen;
|
||||
+ s += slen;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@ |
||||
From e1ff3dd1ae52cef5b5373c8cc4ad949c2c25a71c Mon Sep 17 00:00:00 2001
|
||||
From: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Date: Mon, 5 Sep 2016 13:55:20 +0200
|
||||
Subject: [PATCH] ovl: fix workdir creation
|
||||
|
||||
Workdir creation fails in latest kernel.
|
||||
|
||||
Fix by allowing EOPNOTSUPP as a valid return value from
|
||||
vfs_removexattr(XATTR_NAME_POSIX_ACL_*). Upper filesystem may not support
|
||||
ACL and still be perfectly able to support overlayfs.
|
||||
|
||||
Reported-by: Martin Ziegler <ziegler@uni-freiburg.de>
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
||||
Fixes: c11b9fdd6a61 ("ovl: remove posix_acl_default from workdir")
|
||||
Cc: <stable@vger.kernel.org>
|
||||
---
|
||||
fs/overlayfs/super.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/fs/overlayfs/super.c
|
||||
+++ b/fs/overlayfs/super.c
|
||||
@@ -794,11 +794,11 @@ retry:
|
||||
goto out_dput;
|
||||
|
||||
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
|
||||
- if (err && err != -ENODATA)
|
||||
+ if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
||||
goto out_dput;
|
||||
|
||||
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_ACCESS);
|
||||
- if (err && err != -ENODATA)
|
||||
+ if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
||||
goto out_dput;
|
||||
|
||||
/* Clear any inherited mode bits */
|
Loading…
Reference in new issue