Commit 415543d5 authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi

ovl: cleanup bad and stale index entries on mount

Bad index entries are entries whose name does not match the
origin file handle stored in trusted.overlay.origin xattr.
Bad index entries could be a result of a system power off in
the middle of copy up.

Stale index entries are entries whose origin file handle is
stale. Stale index entries could be a result of copying layers
or removing lower entries while the overlay is not mounted.
The case of copying layers should be detected earlier by the
verification of upper root dir origin and index dir origin.

Both bad and stale index entries are detected and removed
on mount.
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 359f392c
...@@ -24,7 +24,7 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644); ...@@ -24,7 +24,7 @@ module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);
MODULE_PARM_DESC(ovl_redirect_max, MODULE_PARM_DESC(ovl_redirect_max,
"Maximum length of absolute redirect xattr value"); "Maximum length of absolute redirect xattr value");
void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
{ {
int err; int err;
...@@ -39,6 +39,8 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) ...@@ -39,6 +39,8 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
pr_err("overlayfs: cleanup of '%pd2' failed (%i)\n", pr_err("overlayfs: cleanup of '%pd2' failed (%i)\n",
wdentry, err); wdentry, err);
} }
return err;
} }
struct dentry *ovl_lookup_temp(struct dentry *workdir) struct dentry *ovl_lookup_temp(struct dentry *workdir)
......
...@@ -285,17 +285,17 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, ...@@ -285,17 +285,17 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
} }
static int ovl_check_origin(struct dentry *dentry, struct dentry *upperdentry, static int ovl_check_origin(struct dentry *upperdentry,
struct path *lowerstack, unsigned int numlower,
struct path **stackp, unsigned int *ctrp) struct path **stackp, unsigned int *ctrp)
{ {
struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata;
struct vfsmount *mnt; struct vfsmount *mnt;
struct dentry *origin = NULL; struct dentry *origin = NULL;
int i; int i;
for (i = 0; i < roe->numlower; i++) { for (i = 0; i < numlower; i++) {
mnt = roe->lowerstack[i].mnt; mnt = lowerstack[i].mnt;
origin = ovl_get_origin(upperdentry, mnt); origin = ovl_get_origin(upperdentry, mnt);
if (IS_ERR(origin)) if (IS_ERR(origin))
return PTR_ERR(origin); return PTR_ERR(origin);
...@@ -307,7 +307,8 @@ static int ovl_check_origin(struct dentry *dentry, struct dentry *upperdentry, ...@@ -307,7 +307,8 @@ static int ovl_check_origin(struct dentry *dentry, struct dentry *upperdentry,
if (!origin) if (!origin)
return 0; return 0;
BUG_ON(*stackp || *ctrp); BUG_ON(*ctrp);
if (!*stackp)
*stackp = kmalloc(sizeof(struct path), GFP_TEMPORARY); *stackp = kmalloc(sizeof(struct path), GFP_TEMPORARY);
if (!*stackp) { if (!*stackp) {
dput(origin); dput(origin);
...@@ -378,6 +379,63 @@ int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, ...@@ -378,6 +379,63 @@ int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
goto out; goto out;
} }
/*
* Verify that an index entry name matches the origin file handle stored in
* OVL_XATTR_ORIGIN and that origin file handle can be decoded to lower path.
* Return 0 on match, -ESTALE on mismatch or stale origin, < 0 on error.
*/
int ovl_verify_index(struct dentry *index, struct path *lowerstack,
unsigned int numlower)
{
struct ovl_fh *fh = NULL;
size_t len;
struct path origin = { };
struct path *stack = &origin;
unsigned int ctr = 0;
int err;
if (!d_inode(index))
return 0;
err = -EISDIR;
if (d_is_dir(index))
goto fail;
err = -EINVAL;
if (index->d_name.len < sizeof(struct ovl_fh)*2)
goto fail;
err = -ENOMEM;
len = index->d_name.len / 2;
fh = kzalloc(len, GFP_TEMPORARY);
if (!fh)
goto fail;
err = -EINVAL;
if (hex2bin((u8 *)fh, index->d_name.name, len) || len != fh->len)
goto fail;
err = ovl_verify_origin_fh(index, fh);
if (err)
goto fail;
err = ovl_check_origin(index, lowerstack, numlower, &stack, &ctr);
if (!err && !ctr)
err = -ESTALE;
if (err)
goto fail;
dput(origin.dentry);
out:
kfree(fh);
return err;
fail:
pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, err=%i)\n",
index, err);
goto out;
}
/* /*
* Lookup in indexdir for the index entry of a lower real inode or a copy up * Lookup in indexdir for the index entry of a lower real inode or a copy up
* origin inode. The index entry name is the hex representation of the lower * origin inode. The index entry name is the hex representation of the lower
...@@ -541,8 +599,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -541,8 +599,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
* number - it's the same as if we held a reference * number - it's the same as if we held a reference
* to a dentry in lower layer that was moved under us. * to a dentry in lower layer that was moved under us.
*/ */
err = ovl_check_origin(dentry, upperdentry, err = ovl_check_origin(upperdentry, roe->lowerstack,
&stack, &ctr); roe->numlower, &stack, &ctr);
if (err) if (err)
goto out; goto out;
} }
......
...@@ -237,6 +237,8 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) ...@@ -237,6 +237,8 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
/* namei.c */ /* namei.c */
int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
struct dentry *origin, bool is_upper, bool set); struct dentry *origin, bool is_upper, bool set);
int ovl_verify_index(struct dentry *index, struct path *lowerstack,
unsigned int numlower);
int ovl_get_index_name(struct dentry *origin, struct qstr *name); int ovl_get_index_name(struct dentry *origin, struct qstr *name);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path); int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags);
...@@ -250,6 +252,8 @@ void ovl_cache_free(struct list_head *list); ...@@ -250,6 +252,8 @@ 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, void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
struct dentry *dentry, int level); struct dentry *dentry, int level);
int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
struct path *lowerstack, unsigned int numlower);
/* inode.c */ /* inode.c */
int ovl_setattr(struct dentry *dentry, struct iattr *attr); int ovl_setattr(struct dentry *dentry, struct iattr *attr);
...@@ -289,7 +293,7 @@ struct cattr { ...@@ -289,7 +293,7 @@ struct cattr {
int ovl_create_real(struct inode *dir, struct dentry *newdentry, int ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct cattr *attr, struct cattr *attr,
struct dentry *hardlink, bool debug); struct dentry *hardlink, bool debug);
void ovl_cleanup(struct inode *dir, struct dentry *dentry); int ovl_cleanup(struct inode *dir, struct dentry *dentry);
/* copy_up.c */ /* copy_up.c */
int ovl_copy_up(struct dentry *dentry); int ovl_copy_up(struct dentry *dentry);
......
...@@ -667,3 +667,53 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, ...@@ -667,3 +667,53 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
ovl_cleanup(dir, dentry); ovl_cleanup(dir, dentry);
} }
} }
int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
struct path *lowerstack, unsigned int numlower)
{
int err;
struct inode *dir = dentry->d_inode;
struct path path = { .mnt = mnt, .dentry = dentry };
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 *index;
if (p->name[0] == '.') {
if (p->len == 1)
continue;
if (p->len == 2 && p->name[1] == '.')
continue;
}
index = lookup_one_len(p->name, dentry, p->len);
if (IS_ERR(index)) {
err = PTR_ERR(index);
break;
}
if (ovl_verify_index(index, lowerstack, numlower)) {
err = ovl_cleanup(dir, index);
if (err)
break;
}
dput(index);
}
inode_unlock(dir);
out:
ovl_cache_free(&list);
if (err)
pr_err("overlayfs: failed index dir cleanup (%i)\n", err);
return err;
}
...@@ -1068,6 +1068,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1068,6 +1068,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
upperpath.dentry, true, true); upperpath.dentry, true, true);
if (err) if (err)
pr_err("overlayfs: failed to verify index dir origin\n"); pr_err("overlayfs: failed to verify index dir origin\n");
/* Cleanup bad/stale index entries */
if (!err)
err = ovl_indexdir_cleanup(ufs->indexdir,
ufs->upper_mnt,
stack, numlower);
} }
if (err || !ufs->indexdir) if (err || !ufs->indexdir)
pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n"); pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
......
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