ovl: Add versioned header for overlay.metacopy xattr

Historically overlay.metacopy was a zero-size xattr, and it's
existence marked a metacopy file. This change adds a versioned header
with a flag field, a length and a digest. The initial use-case of this
will be for validating a fs-verity digest, but the flags field could
also be used later for other new features.

ovl_check_metacopy_xattr() now returns the size of the xattr,
emulating a size of OVL_METACOPY_MIN_SIZE for empty xattrs to
distinguish it from the no-xattr case.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
This commit is contained in:
Alexander Larsson 2023-06-21 10:32:31 +02:00 committed by Amir Goldstein
parent ae8cba4033
commit bf07089081
3 changed files with 61 additions and 10 deletions

View File

@ -25,7 +25,7 @@ struct ovl_lookup_data {
bool stop;
bool last;
char *redirect;
bool metacopy;
int metacopy;
/* Referring to last redirect xattr */
bool absolute_redirect;
};
@ -270,7 +270,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
d->stop = true;
goto put_and_out;
}
err = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path);
err = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path, NULL);
if (err < 0)
goto out_err;
@ -963,7 +963,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
.stop = false,
.last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
.redirect = NULL,
.metacopy = false,
.metacopy = 0,
};
if (dentry->d_name.len > ofs->namelen)
@ -1120,7 +1120,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
/* Defer lookup of lowerdata in data-only layers to first access */
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
d.metacopy = false;
d.metacopy = 0;
ctr++;
}
@ -1211,7 +1211,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
upperredirect = NULL;
goto out_free_oe;
}
err = ovl_check_metacopy_xattr(ofs, &upperpath);
err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL);
if (err < 0)
goto out_free_oe;
uppermetacopy = err;

View File

@ -7,6 +7,7 @@
#include <linux/kernel.h>
#include <linux/uuid.h>
#include <linux/fs.h>
#include <linux/fsverity.h>
#include <linux/namei.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
@ -132,6 +133,26 @@ struct ovl_fh {
#define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \
offsetof(struct ovl_fb, fid))
/* On-disk format for "metacopy" xattr (if non-zero size) */
struct ovl_metacopy {
u8 version; /* 0 */
u8 len; /* size of this header + used digest bytes */
u8 flags;
u8 digest_algo; /* FS_VERITY_HASH_ALG_* constant, 0 for no digest */
u8 digest[FS_VERITY_MAX_DIGEST_SIZE]; /* Only the used part on disk */
} __packed;
#define OVL_METACOPY_MAX_SIZE (sizeof(struct ovl_metacopy))
#define OVL_METACOPY_MIN_SIZE (OVL_METACOPY_MAX_SIZE - FS_VERITY_MAX_DIGEST_SIZE)
#define OVL_METACOPY_INIT { 0, OVL_METACOPY_MIN_SIZE }
static inline int ovl_metadata_digest_size(const struct ovl_metacopy *metacopy)
{
if (metacopy->len < OVL_METACOPY_MIN_SIZE)
return 0;
return (int)metacopy->len - OVL_METACOPY_MIN_SIZE;
}
extern const char *const ovl_xattr_table[][2];
static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
{
@ -458,7 +479,8 @@ bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry);
void ovl_nlink_end(struct dentry *dentry);
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path);
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path,
struct ovl_metacopy *data);
bool ovl_is_metacopy_dentry(struct dentry *dentry);
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding);
int ovl_sync_status(struct ovl_fs *ofs);

View File

@ -1054,8 +1054,12 @@ err:
return -EIO;
}
/* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path)
/*
* err < 0, 0 if no metacopy xattr, metacopy data size if xattr found.
* an empty xattr returns OVL_METACOPY_MIN_SIZE to distinguish from no xattr value.
*/
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path,
struct ovl_metacopy *data)
{
int res;
@ -1063,7 +1067,8 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path)
if (!S_ISREG(d_inode(path->dentry)->i_mode))
return 0;
res = ovl_path_getxattr(ofs, path, OVL_XATTR_METACOPY, NULL, 0);
res = ovl_path_getxattr(ofs, path, OVL_XATTR_METACOPY,
data, data ? OVL_METACOPY_MAX_SIZE : 0);
if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP)
return 0;
@ -1077,7 +1082,31 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path)
goto out;
}
return 1;
if (res == 0) {
/* Emulate empty data for zero size metacopy xattr */
res = OVL_METACOPY_MIN_SIZE;
if (data) {
memset(data, 0, res);
data->len = res;
}
} else if (res < OVL_METACOPY_MIN_SIZE) {
pr_warn_ratelimited("metacopy file '%pd' has too small xattr\n",
path->dentry);
return -EIO;
} else if (data) {
if (data->version != 0) {
pr_warn_ratelimited("metacopy file '%pd' has unsupported version\n",
path->dentry);
return -EIO;
}
if (res != data->len) {
pr_warn_ratelimited("metacopy file '%pd' has invalid xattr size\n",
path->dentry);
return -EIO;
}
}
return res;
out:
pr_warn_ratelimited("failed to get metacopy (%i)\n", res);
return res;