On Wed, Jun 21, 2023 at 2:18 PM Alexander Larsson <alexl@xxxxxxxxxx> wrote: > > 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@xxxxxxxxxx> Looks good. Reviewed-by: Amir Goldstein <amir73il@xxxxxxxxx> > --- > fs/overlayfs/namei.c | 10 +++++----- > fs/overlayfs/overlayfs.h | 24 +++++++++++++++++++++++- > fs/overlayfs/util.c | 37 +++++++++++++++++++++++++++++++++---- > 3 files changed, 61 insertions(+), 10 deletions(-) > > diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c > index 57adf911735f..3dd480253710 100644 > --- a/fs/overlayfs/namei.c > +++ b/fs/overlayfs/namei.c > @@ -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; > diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h > index cf92a9aaf934..6d4e08df0dfe 100644 > --- a/fs/overlayfs/overlayfs.h > +++ b/fs/overlayfs/overlayfs.h > @@ -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> > @@ -140,6 +141,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) > { > @@ -490,7 +511,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); > diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c > index 7ef9e13c404a..921747223991 100644 > --- a/fs/overlayfs/util.c > +++ b/fs/overlayfs/util.c > @@ -1054,8 +1054,12 @@ int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir) > 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; > -- > 2.40.1 >