mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-17 09:14:19 +08:00
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi: "Fix regressions: - missing CONFIG_EXPORTFS dependency - failure if upper fs doesn't support xattr - bad error cleanup This also adds the concept of "impure" directories complementing the "origin" marking introduced in -rc1. Together they enable getting consistent st_ino and d_ino for directory listings. And there's a bug fix and a cleanup as well" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: filter trusted xattr for non-admin ovl: mark upper merge dir with type origin entries "impure" ovl: mark upper dir with type origin entries "impure" ovl: remove unused arg from ovl_lookup_temp() ovl: handle rename when upper doesn't support xattr ovl: don't fail copy-up if upper doesn't support xattr ovl: check on mount time if upper fs supports setting xattr ovl: fix creds leak in copy up error path ovl: select EXPORTFS
This commit is contained in:
commit
d602fb6844
@ -1,5 +1,6 @@
|
||||
config OVERLAY_FS
|
||||
tristate "Overlay filesystem support"
|
||||
select EXPORTFS
|
||||
help
|
||||
An overlay filesystem combines two filesystems - an 'upper' filesystem
|
||||
and a 'lower' filesystem. When a name exists in both filesystems, the
|
||||
|
@ -300,7 +300,11 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
|
||||
return PTR_ERR(fh);
|
||||
}
|
||||
|
||||
err = ovl_do_setxattr(upper, OVL_XATTR_ORIGIN, fh, fh ? fh->len : 0, 0);
|
||||
/*
|
||||
* Do not fail when upper doesn't support xattrs.
|
||||
*/
|
||||
err = ovl_check_setxattr(dentry, upper, OVL_XATTR_ORIGIN, fh,
|
||||
fh ? fh->len : 0, 0);
|
||||
kfree(fh);
|
||||
|
||||
return err;
|
||||
@ -342,13 +346,14 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
|
||||
if (tmpfile)
|
||||
temp = ovl_do_tmpfile(upperdir, stat->mode);
|
||||
else
|
||||
temp = ovl_lookup_temp(workdir, dentry);
|
||||
err = PTR_ERR(temp);
|
||||
if (IS_ERR(temp))
|
||||
goto out1;
|
||||
|
||||
temp = ovl_lookup_temp(workdir);
|
||||
err = 0;
|
||||
if (!tmpfile)
|
||||
if (IS_ERR(temp)) {
|
||||
err = PTR_ERR(temp);
|
||||
temp = NULL;
|
||||
}
|
||||
|
||||
if (!err && !tmpfile)
|
||||
err = ovl_create_real(wdir, temp, &cattr, NULL, true);
|
||||
|
||||
if (new_creds) {
|
||||
@ -454,6 +459,11 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
ovl_path_upper(parent, &parentpath);
|
||||
upperdir = parentpath.dentry;
|
||||
|
||||
/* Mark parent "impure" because it may now contain non-pure upper */
|
||||
err = ovl_set_impure(parent, upperdir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = vfs_getattr(&parentpath, &pstat,
|
||||
STATX_ATIME | STATX_MTIME, AT_STATX_SYNC_AS_STAT);
|
||||
if (err)
|
||||
|
@ -41,7 +41,7 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
|
||||
}
|
||||
}
|
||||
|
||||
struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry)
|
||||
struct dentry *ovl_lookup_temp(struct dentry *workdir)
|
||||
{
|
||||
struct dentry *temp;
|
||||
char name[20];
|
||||
@ -68,7 +68,7 @@ static struct dentry *ovl_whiteout(struct dentry *workdir,
|
||||
struct dentry *whiteout;
|
||||
struct inode *wdir = workdir->d_inode;
|
||||
|
||||
whiteout = ovl_lookup_temp(workdir, dentry);
|
||||
whiteout = ovl_lookup_temp(workdir);
|
||||
if (IS_ERR(whiteout))
|
||||
return whiteout;
|
||||
|
||||
@ -127,17 +127,28 @@ int ovl_create_real(struct inode *dir, struct dentry *newdentry,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
|
||||
static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
|
||||
int xerr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ovl_do_setxattr(upperdentry, OVL_XATTR_OPAQUE, "y", 1, 0);
|
||||
err = ovl_check_setxattr(dentry, upper, OVL_XATTR_OPAQUE, "y", 1, xerr);
|
||||
if (!err)
|
||||
ovl_dentry_set_opaque(dentry);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
|
||||
{
|
||||
/*
|
||||
* Fail with -EIO when trying to create opaque dir and upper doesn't
|
||||
* support xattrs. ovl_rename() calls ovl_set_opaque_xerr(-EXDEV) to
|
||||
* return a specific error for noxattr case.
|
||||
*/
|
||||
return ovl_set_opaque_xerr(dentry, upperdentry, -EIO);
|
||||
}
|
||||
|
||||
/* Common operations required to be done after creation of file on upper */
|
||||
static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
|
||||
struct dentry *newdentry, bool hardlink)
|
||||
@ -162,6 +173,11 @@ static bool ovl_type_merge(struct dentry *dentry)
|
||||
return OVL_TYPE_MERGE(ovl_path_type(dentry));
|
||||
}
|
||||
|
||||
static bool ovl_type_origin(struct dentry *dentry)
|
||||
{
|
||||
return OVL_TYPE_ORIGIN(ovl_path_type(dentry));
|
||||
}
|
||||
|
||||
static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
|
||||
struct cattr *attr, struct dentry *hardlink)
|
||||
{
|
||||
@ -250,7 +266,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
||||
if (upper->d_parent->d_inode != udir)
|
||||
goto out_unlock;
|
||||
|
||||
opaquedir = ovl_lookup_temp(workdir, dentry);
|
||||
opaquedir = ovl_lookup_temp(workdir);
|
||||
err = PTR_ERR(opaquedir);
|
||||
if (IS_ERR(opaquedir))
|
||||
goto out_unlock;
|
||||
@ -382,7 +398,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
newdentry = ovl_lookup_temp(workdir, dentry);
|
||||
newdentry = ovl_lookup_temp(workdir);
|
||||
err = PTR_ERR(newdentry);
|
||||
if (IS_ERR(newdentry))
|
||||
goto out_unlock;
|
||||
@ -846,18 +862,16 @@ static int ovl_set_redirect(struct dentry *dentry, bool samedir)
|
||||
if (IS_ERR(redirect))
|
||||
return PTR_ERR(redirect);
|
||||
|
||||
err = ovl_do_setxattr(ovl_dentry_upper(dentry), OVL_XATTR_REDIRECT,
|
||||
redirect, strlen(redirect), 0);
|
||||
err = ovl_check_setxattr(dentry, ovl_dentry_upper(dentry),
|
||||
OVL_XATTR_REDIRECT,
|
||||
redirect, strlen(redirect), -EXDEV);
|
||||
if (!err) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
ovl_dentry_set_redirect(dentry, redirect);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
} else {
|
||||
kfree(redirect);
|
||||
if (err == -EOPNOTSUPP)
|
||||
ovl_clear_redirect_dir(dentry->d_sb);
|
||||
else
|
||||
pr_warn_ratelimited("overlay: failed to set redirect (%i)\n", err);
|
||||
pr_warn_ratelimited("overlay: failed to set redirect (%i)\n", err);
|
||||
/* Fall back to userspace copy-up */
|
||||
err = -EXDEV;
|
||||
}
|
||||
@ -943,6 +957,25 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
||||
old_upperdir = ovl_dentry_upper(old->d_parent);
|
||||
new_upperdir = ovl_dentry_upper(new->d_parent);
|
||||
|
||||
if (!samedir) {
|
||||
/*
|
||||
* When moving a merge dir or non-dir with copy up origin into
|
||||
* a new parent, we are marking the new parent dir "impure".
|
||||
* When ovl_iterate() iterates an "impure" upper dir, it will
|
||||
* lookup the origin inodes of the entries to fill d_ino.
|
||||
*/
|
||||
if (ovl_type_origin(old)) {
|
||||
err = ovl_set_impure(new->d_parent, new_upperdir);
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
}
|
||||
if (!overwrite && ovl_type_origin(new)) {
|
||||
err = ovl_set_impure(old->d_parent, old_upperdir);
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
}
|
||||
}
|
||||
|
||||
trap = lock_rename(new_upperdir, old_upperdir);
|
||||
|
||||
olddentry = lookup_one_len(old->d_name.name, old_upperdir,
|
||||
@ -992,7 +1025,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
||||
if (ovl_type_merge_or_lower(old))
|
||||
err = ovl_set_redirect(old, samedir);
|
||||
else if (!old_opaque && ovl_type_merge(new->d_parent))
|
||||
err = ovl_set_opaque(old, olddentry);
|
||||
err = ovl_set_opaque_xerr(old, olddentry, -EXDEV);
|
||||
if (err)
|
||||
goto out_dput;
|
||||
}
|
||||
@ -1000,7 +1033,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
||||
if (ovl_type_merge_or_lower(new))
|
||||
err = ovl_set_redirect(new, samedir);
|
||||
else if (!new_opaque && ovl_type_merge(old->d_parent))
|
||||
err = ovl_set_opaque(new, newdentry);
|
||||
err = ovl_set_opaque_xerr(new, newdentry, -EXDEV);
|
||||
if (err)
|
||||
goto out_dput;
|
||||
}
|
||||
|
@ -240,6 +240,16 @@ int ovl_xattr_get(struct dentry *dentry, const char *name,
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool ovl_can_list(const char *s)
|
||||
{
|
||||
/* List all non-trusted xatts */
|
||||
if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
|
||||
return true;
|
||||
|
||||
/* Never list trusted.overlay, list other trusted for superuser only */
|
||||
return !ovl_is_private_xattr(s) && capable(CAP_SYS_ADMIN);
|
||||
}
|
||||
|
||||
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
{
|
||||
struct dentry *realdentry = ovl_dentry_real(dentry);
|
||||
@ -263,7 +273,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
return -EIO;
|
||||
|
||||
len -= slen;
|
||||
if (ovl_is_private_xattr(s)) {
|
||||
if (!ovl_can_list(s)) {
|
||||
res -= slen;
|
||||
memmove(s, s + slen, len);
|
||||
} else {
|
||||
|
@ -169,17 +169,7 @@ invalid:
|
||||
|
||||
static bool ovl_is_opaquedir(struct dentry *dentry)
|
||||
{
|
||||
int res;
|
||||
char val;
|
||||
|
||||
if (!d_is_dir(dentry))
|
||||
return false;
|
||||
|
||||
res = vfs_getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
|
||||
if (res == 1 && val == 'y')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE);
|
||||
}
|
||||
|
||||
static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
|
||||
@ -351,6 +341,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int ctr = 0;
|
||||
struct inode *inode = NULL;
|
||||
bool upperopaque = false;
|
||||
bool upperimpure = false;
|
||||
char *upperredirect = NULL;
|
||||
struct dentry *this;
|
||||
unsigned int i;
|
||||
@ -395,6 +386,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
poe = roe;
|
||||
}
|
||||
upperopaque = d.opaque;
|
||||
if (upperdentry && d.is_dir)
|
||||
upperimpure = ovl_is_impuredir(upperdentry);
|
||||
}
|
||||
|
||||
if (!d.stop && poe->numlower) {
|
||||
@ -463,6 +456,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
revert_creds(old_cred);
|
||||
oe->opaque = upperopaque;
|
||||
oe->impure = upperimpure;
|
||||
oe->redirect = upperredirect;
|
||||
oe->__upperdentry = upperdentry;
|
||||
memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
|
||||
|
@ -24,6 +24,7 @@ enum ovl_path_type {
|
||||
#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
|
||||
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
|
||||
#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
|
||||
#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
|
||||
|
||||
/*
|
||||
* The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
|
||||
@ -203,10 +204,10 @@ struct dentry *ovl_dentry_real(struct dentry *dentry);
|
||||
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
|
||||
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
|
||||
bool ovl_dentry_is_opaque(struct dentry *dentry);
|
||||
bool ovl_dentry_is_impure(struct dentry *dentry);
|
||||
bool ovl_dentry_is_whiteout(struct dentry *dentry);
|
||||
void ovl_dentry_set_opaque(struct dentry *dentry);
|
||||
bool ovl_redirect_dir(struct super_block *sb);
|
||||
void ovl_clear_redirect_dir(struct super_block *sb);
|
||||
const char *ovl_dentry_get_redirect(struct dentry *dentry);
|
||||
void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
|
||||
void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
|
||||
@ -219,6 +220,17 @@ bool ovl_is_whiteout(struct dentry *dentry);
|
||||
struct file *ovl_path_open(struct path *path, int flags);
|
||||
int ovl_copy_up_start(struct dentry *dentry);
|
||||
void ovl_copy_up_end(struct dentry *dentry);
|
||||
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name);
|
||||
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
||||
const char *name, const void *value, size_t size,
|
||||
int xerr);
|
||||
int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry);
|
||||
|
||||
static inline bool ovl_is_impuredir(struct dentry *dentry)
|
||||
{
|
||||
return ovl_check_dir_xattr(dentry, OVL_XATTR_IMPURE);
|
||||
}
|
||||
|
||||
|
||||
/* namei.c */
|
||||
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
|
||||
@ -263,7 +275,7 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
|
||||
|
||||
/* dir.c */
|
||||
extern const struct inode_operations ovl_dir_inode_operations;
|
||||
struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry);
|
||||
struct dentry *ovl_lookup_temp(struct dentry *workdir);
|
||||
struct cattr {
|
||||
dev_t rdev;
|
||||
umode_t mode;
|
||||
|
@ -28,6 +28,7 @@ struct ovl_fs {
|
||||
/* creds of process who forced instantiation of super block */
|
||||
const struct cred *creator_cred;
|
||||
bool tmpfile;
|
||||
bool noxattr;
|
||||
wait_queue_head_t copyup_wq;
|
||||
/* sb common to all layers */
|
||||
struct super_block *same_sb;
|
||||
@ -42,6 +43,7 @@ struct ovl_entry {
|
||||
u64 version;
|
||||
const char *redirect;
|
||||
bool opaque;
|
||||
bool impure;
|
||||
bool copying;
|
||||
};
|
||||
struct rcu_head rcu;
|
||||
|
@ -891,6 +891,19 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
||||
dput(temp);
|
||||
else
|
||||
pr_warn("overlayfs: upper fs does not support tmpfile.\n");
|
||||
|
||||
/*
|
||||
* Check if upper/work fs supports trusted.overlay.*
|
||||
* xattr
|
||||
*/
|
||||
err = ovl_do_setxattr(ufs->workdir, OVL_XATTR_OPAQUE,
|
||||
"0", 1, 0);
|
||||
if (err) {
|
||||
ufs->noxattr = true;
|
||||
pr_warn("overlayfs: upper fs does not support xattr.\n");
|
||||
} else {
|
||||
vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -961,7 +974,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
||||
path_put(&workpath);
|
||||
kfree(lowertmp);
|
||||
|
||||
oe->__upperdentry = upperpath.dentry;
|
||||
if (upperpath.dentry) {
|
||||
oe->__upperdentry = upperpath.dentry;
|
||||
oe->impure = ovl_is_impuredir(upperpath.dentry);
|
||||
}
|
||||
for (i = 0; i < numlower; i++) {
|
||||
oe->lowerstack[i].dentry = stack[i].dentry;
|
||||
oe->lowerstack[i].mnt = ufs->lower_mnt[i];
|
||||
|
@ -175,6 +175,13 @@ bool ovl_dentry_is_opaque(struct dentry *dentry)
|
||||
return oe->opaque;
|
||||
}
|
||||
|
||||
bool ovl_dentry_is_impure(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
return oe->impure;
|
||||
}
|
||||
|
||||
bool ovl_dentry_is_whiteout(struct dentry *dentry)
|
||||
{
|
||||
return !dentry->d_inode && ovl_dentry_is_opaque(dentry);
|
||||
@ -191,14 +198,7 @@ bool ovl_redirect_dir(struct super_block *sb)
|
||||
{
|
||||
struct ovl_fs *ofs = sb->s_fs_info;
|
||||
|
||||
return ofs->config.redirect_dir;
|
||||
}
|
||||
|
||||
void ovl_clear_redirect_dir(struct super_block *sb)
|
||||
{
|
||||
struct ovl_fs *ofs = sb->s_fs_info;
|
||||
|
||||
ofs->config.redirect_dir = false;
|
||||
return ofs->config.redirect_dir && !ofs->noxattr;
|
||||
}
|
||||
|
||||
const char *ovl_dentry_get_redirect(struct dentry *dentry)
|
||||
@ -303,3 +303,59 @@ void ovl_copy_up_end(struct dentry *dentry)
|
||||
wake_up_locked(&ofs->copyup_wq);
|
||||
spin_unlock(&ofs->copyup_wq.lock);
|
||||
}
|
||||
|
||||
bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
|
||||
{
|
||||
int res;
|
||||
char val;
|
||||
|
||||
if (!d_is_dir(dentry))
|
||||
return false;
|
||||
|
||||
res = vfs_getxattr(dentry, name, &val, 1);
|
||||
if (res == 1 && val == 'y')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry,
|
||||
const char *name, const void *value, size_t size,
|
||||
int xerr)
|
||||
{
|
||||
int err;
|
||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
||||
|
||||
if (ofs->noxattr)
|
||||
return xerr;
|
||||
|
||||
err = ovl_do_setxattr(upperdentry, name, value, size, 0);
|
||||
|
||||
if (err == -EOPNOTSUPP) {
|
||||
pr_warn("overlayfs: cannot set %s xattr on upper\n", name);
|
||||
ofs->noxattr = true;
|
||||
return xerr;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry)
|
||||
{
|
||||
int err;
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
if (oe->impure)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Do not fail when upper doesn't support xattrs.
|
||||
* Upper inodes won't have origin nor redirect xattr anyway.
|
||||
*/
|
||||
err = ovl_check_setxattr(dentry, upperdentry, OVL_XATTR_IMPURE,
|
||||
"y", 1, 0);
|
||||
if (!err)
|
||||
oe->impure = true;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user