Commit ad1d615c authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi

ovl: use directory index entries for consistency verification

A directory index is a directory type entry in index dir with a
"trusted.overlay.upper" xattr containing an encoded ovl_fh of the merge
directory upper dir inode.

On lookup of non-dir files, lower file is followed by origin file handle.
On lookup of dir entries, lower dir is found by name and then compared
to origin file handle. We only trust dir index if we verified that lower
dir matches origin file handle, otherwise index may be inconsistent and
we ignore it.

If we find an indexed non-upper dir or an indexed merged dir, whose
index 'upper' xattr points to a different upper dir, that means that the
lower directory may be also referenced by another upper dir via redirect,
so we fail the lookup on inconsistency error.

To be consistent with directory index entries format, the association of
index dir to upper root dir, that was stored by older kernels in
"trusted.overlay.origin" xattr is now stored in "trusted.overlay.upper"
xattr. This also serves as an indication that overlay was mounted with a
kernel that support index directory entries. For backward compatibility,
if an 'origin' xattr exists on the index dir we also verify it on mount.

Directory index entries are going to be used for NFS export.
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 86eaa130
...@@ -538,6 +538,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry, ...@@ -538,6 +538,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
struct dentry *index; struct dentry *index;
struct inode *inode; struct inode *inode;
struct qstr name; struct qstr name;
bool is_dir = d_is_dir(origin);
int err; int err;
err = ovl_get_index_name(origin, &name); err = ovl_get_index_name(origin, &name);
...@@ -561,8 +562,6 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry, ...@@ -561,8 +562,6 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
inode = d_inode(index); inode = d_inode(index);
if (d_is_negative(index)) { if (d_is_negative(index)) {
goto out_dput; goto out_dput;
} else if (upper && d_inode(upper) != inode) {
goto out_dput;
} else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) || } else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) { ((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
/* /*
...@@ -576,8 +575,25 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry, ...@@ -576,8 +575,25 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
index, d_inode(index)->i_mode & S_IFMT, index, d_inode(index)->i_mode & S_IFMT,
d_inode(origin)->i_mode & S_IFMT); d_inode(origin)->i_mode & S_IFMT);
goto fail; goto fail;
} } else if (is_dir) {
if (!upper) {
pr_warn_ratelimited("overlayfs: suspected uncovered redirected dir found (origin=%pd2, index=%pd2).\n",
origin, index);
goto fail;
}
/* Verify that dir index 'upper' xattr points to upper dir */
err = ovl_verify_upper(index, upper, false);
if (err) {
if (err == -ESTALE) {
pr_warn_ratelimited("overlayfs: suspected multiply redirected dir found (upper=%pd2, origin=%pd2, index=%pd2).\n",
upper, origin, index);
}
goto fail;
}
} else if (upper && d_inode(upper) != inode) {
goto out_dput;
}
out: out:
kfree(name.name); kfree(name.name);
return index; return index;
...@@ -646,6 +662,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -646,6 +662,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata; struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata;
struct ovl_path *stack = NULL; struct ovl_path *stack = NULL;
struct dentry *upperdir, *upperdentry = NULL; struct dentry *upperdir, *upperdentry = NULL;
struct dentry *origin = NULL;
struct dentry *index = NULL; struct dentry *index = NULL;
unsigned int ctr = 0; unsigned int ctr = 0;
struct inode *inode = NULL; struct inode *inode = NULL;
...@@ -739,7 +756,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -739,7 +756,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
/* /*
* When "verify_lower" feature is enabled, do not merge with a * When "verify_lower" feature is enabled, do not merge with a
* lower dir that does not match a stored origin xattr. * lower dir that does not match a stored origin xattr. In any
* case, only verified origin is used for index lookup.
*/ */
if (upperdentry && !ctr && ovl_verify_lower(dentry->d_sb)) { if (upperdentry && !ctr && ovl_verify_lower(dentry->d_sb)) {
err = ovl_verify_origin(upperdentry, this, false); err = ovl_verify_origin(upperdentry, this, false);
...@@ -747,6 +765,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -747,6 +765,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
dput(this); dput(this);
break; break;
} }
/* Bless lower dir as verified origin */
origin = this;
} }
stack[ctr].dentry = this; stack[ctr].dentry = this;
...@@ -780,10 +801,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -780,10 +801,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
} }
} }
/* Lookup index by lower inode and verify it matches upper inode */ /*
if (ctr && !d.is_dir && ovl_indexdir(dentry->d_sb)) { * Lookup index by lower inode and verify it matches upper inode.
struct dentry *origin = stack[0].dentry; * We only trust dir index if we verified that lower dir matches
* origin, otherwise dir index entries may be inconsistent and we
* ignore them. Always lookup index of non-dir and non-upper.
*/
if (ctr && (!upperdentry || !d.is_dir))
origin = stack[0].dentry;
if (origin && ovl_indexdir(dentry->d_sb) &&
(!d.is_dir || ovl_index_all(dentry->d_sb))) {
index = ovl_lookup_index(dentry, upperdentry, origin); index = ovl_lookup_index(dentry, upperdentry, origin);
if (IS_ERR(index)) { if (IS_ERR(index)) {
err = PTR_ERR(index); err = PTR_ERR(index);
......
...@@ -27,6 +27,7 @@ enum ovl_path_type { ...@@ -27,6 +27,7 @@ enum ovl_path_type {
#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin" #define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure" #define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink" #define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
#define OVL_XATTR_UPPER OVL_XATTR_PREFIX "upper"
enum ovl_flag { enum ovl_flag {
/* Pure upper dir that may contain non pure upper entries */ /* Pure upper dir that may contain non pure upper entries */
...@@ -266,6 +267,12 @@ static inline int ovl_verify_origin(struct dentry *upper, ...@@ -266,6 +267,12 @@ static inline int ovl_verify_origin(struct dentry *upper,
return ovl_verify_set_fh(upper, OVL_XATTR_ORIGIN, origin, false, set); return ovl_verify_set_fh(upper, OVL_XATTR_ORIGIN, origin, false, set);
} }
static inline int ovl_verify_upper(struct dentry *index,
struct dentry *upper, bool set)
{
return ovl_verify_set_fh(index, OVL_XATTR_UPPER, upper, true, set);
}
/* readdir.c */ /* readdir.c */
extern const struct file_operations ovl_dir_operations; extern const struct file_operations ovl_dir_operations;
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list); int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
......
...@@ -1078,11 +1078,23 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, ...@@ -1078,11 +1078,23 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true); ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (ofs->indexdir) { if (ofs->indexdir) {
/* Verify upper root is exclusively associated with index dir */ /*
err = ovl_verify_set_fh(ofs->indexdir, OVL_XATTR_ORIGIN, * Verify upper root is exclusively associated with index dir.
upperpath->dentry, true, true); * Older kernels stored upper fh in "trusted.overlay.origin"
* xattr. If that xattr exists, verify that it is a match to
* upper dir file handle. In any case, verify or set xattr
* "trusted.overlay.upper" to indicate that index may have
* directory entries.
*/
if (ovl_check_origin_xattr(ofs->indexdir)) {
err = ovl_verify_set_fh(ofs->indexdir, OVL_XATTR_ORIGIN,
upperpath->dentry, true, false);
if (err)
pr_err("overlayfs: failed to verify index dir 'origin' xattr\n");
}
err = ovl_verify_upper(ofs->indexdir, upperpath->dentry, true);
if (err) if (err)
pr_err("overlayfs: failed to verify index dir origin\n"); pr_err("overlayfs: failed to verify index dir 'upper' xattr\n");
/* Cleanup bad/stale/orphan index entries */ /* Cleanup bad/stale/orphan index entries */
if (!err) if (!err)
......
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