mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-18 08:35:08 +08:00
overlayfs update for 6.6
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE9zuTYTs0RXF+Ke33EVvVyTe/1WoFAmTu0QoACgkQEVvVyTe/ 1WpbzBAAjIZXzhn8KldDpG0muw9JKaSOxM45uhZE1s/2uKsVCyp4k3lubTbxxYO1 S9rUjhF2gSJFOfuSOK/XXEKXyu4MGT7iy7pKswu0k8+AHDDRBksPXJKA/AkhLPUr vX1pU6aWw2OSn1xdhIgY+F4DveyzYQL/CEoUzFyRPxSB0G/yjktRAjdZ2HL4cAvN eVXPyTj0bd4LVj1ITla4uj8DbgivrqmRJbZ9bKnSRE8GXWBriJhV//M2Q3QRno+W 04TtAvyh+klQeqZFVOQ0reZUFZzYBBZZTmqoFiUzTny7oljWl5F0+JfJOHhRGknG LYZCia34+T6TZPhOnZzT/szTDoXVvNJhEf+vBQCqhaCugqJc/2uJdw9CW8ZcDvA9 ZNOMxEbXE4VgGjJ0HM6MoDMUoIEUiNWEnXWEaKyCAfOPqgYwPy+QeDO4JtBPQpRn fwZx7Xpc1FLpTc9feHxzox9o81S8rPRMycUBg2c3KZB6TFnYNDxWIIo365naMCzz A8IDVGf+gd+S4NaZvh9FUijciIslYfyFgqwQERZmJnpDk1d1NyeUC7Nn7EkmUpyp guRaC+rUcqYP4CpuSHTCPle94qHqiAkbsKSJWebZ2M1j9fjZ+okPw0k83Nih79vu vRhs70Ah51v1lpBb0mlDjsV3vKm3Apv8nMJKZvVuC+Cw6Qiob5s= =F4Hi -----END PGP SIGNATURE----- Merge tag 'ovl-update-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs Pull overlayfs updates from Amir Goldstein: - add verification feature needed by composefs (Alexander Larsson) - improve integration of overlayfs and fanotify (Amir Goldstein) - fortify some overlayfs code (Andrea Righi) * tag 'ovl-update-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs: ovl: validate superblock in OVL_FS() ovl: make consistent use of OVL_FS() ovl: Kconfig: introduce CONFIG_OVERLAY_FS_DEBUG ovl: auto generate uuid for new overlay filesystems ovl: store persistent uuid/fsid with uuid=on ovl: add support for unique fsid per instance ovl: support encoding non-decodable file handles ovl: Handle verity during copy-up ovl: Validate verity xattr when resolving lowerdata ovl: Add versioned header for overlay.metacopy xattr ovl: Add framework for verity support
This commit is contained in:
commit
63580f669d
@ -326,6 +326,8 @@ the file has fs-verity enabled. This can perform better than
|
|||||||
FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require
|
FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require
|
||||||
opening the file, and opening verity files can be expensive.
|
opening the file, and opening verity files can be expensive.
|
||||||
|
|
||||||
|
.. _accessing_verity_files:
|
||||||
|
|
||||||
Accessing verity files
|
Accessing verity files
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
@ -405,6 +405,53 @@ when a "metacopy" file in one of the lower layers above it, has a "redirect"
|
|||||||
to the absolute path of the "lower data" file in the "data-only" lower layer.
|
to the absolute path of the "lower data" file in the "data-only" lower layer.
|
||||||
|
|
||||||
|
|
||||||
|
fs-verity support
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
During metadata copy up of a lower file, if the source file has
|
||||||
|
fs-verity enabled and overlay verity support is enabled, then the
|
||||||
|
digest of the lower file is added to the "trusted.overlay.metacopy"
|
||||||
|
xattr. This is then used to verify the content of the lower file
|
||||||
|
each the time the metacopy file is opened.
|
||||||
|
|
||||||
|
When a layer containing verity xattrs is used, it means that any such
|
||||||
|
metacopy file in the upper layer is guaranteed to match the content
|
||||||
|
that was in the lower at the time of the copy-up. If at any time
|
||||||
|
(during a mount, after a remount, etc) such a file in the lower is
|
||||||
|
replaced or modified in any way, access to the corresponding file in
|
||||||
|
overlayfs will result in EIO errors (either on open, due to overlayfs
|
||||||
|
digest check, or from a later read due to fs-verity) and a detailed
|
||||||
|
error is printed to the kernel logs. For more details of how fs-verity
|
||||||
|
file access works, see :ref:`Documentation/filesystems/fsverity.rst
|
||||||
|
<accessing_verity_files>`.
|
||||||
|
|
||||||
|
Verity can be used as a general robustness check to detect accidental
|
||||||
|
changes in the overlayfs directories in use. But, with additional care
|
||||||
|
it can also give more powerful guarantees. For example, if the upper
|
||||||
|
layer is fully trusted (by using dm-verity or something similar), then
|
||||||
|
an untrusted lower layer can be used to supply validated file content
|
||||||
|
for all metacopy files. If additionally the untrusted lower
|
||||||
|
directories are specified as "Data-only", then they can only supply
|
||||||
|
such file content, and the entire mount can be trusted to match the
|
||||||
|
upper layer.
|
||||||
|
|
||||||
|
This feature is controlled by the "verity" mount option, which
|
||||||
|
supports these values:
|
||||||
|
|
||||||
|
- "off":
|
||||||
|
The metacopy digest is never generated or used. This is the
|
||||||
|
default if verity option is not specified.
|
||||||
|
- "on":
|
||||||
|
Whenever a metacopy files specifies an expected digest, the
|
||||||
|
corresponding data file must match the specified digest. When
|
||||||
|
generating a metacopy file the verity digest will be set in it
|
||||||
|
based on the source file (if it has one).
|
||||||
|
- "require":
|
||||||
|
Same as "on", but additionally all metacopy files must specify a
|
||||||
|
digest (or EIO is returned on open). This means metadata copy up
|
||||||
|
will only be used if the data file has fs-verity enabled,
|
||||||
|
otherwise a full copy-up is used.
|
||||||
|
|
||||||
Sharing and copying layers
|
Sharing and copying layers
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
@ -610,6 +657,31 @@ can be useful in case the underlying disk is copied and the UUID of this copy
|
|||||||
is changed. This is only applicable if all lower/upper/work directories are on
|
is changed. This is only applicable if all lower/upper/work directories are on
|
||||||
the same filesystem, otherwise it will fallback to normal behaviour.
|
the same filesystem, otherwise it will fallback to normal behaviour.
|
||||||
|
|
||||||
|
|
||||||
|
UUID and fsid
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The UUID of overlayfs instance itself and the fsid reported by statfs(2) are
|
||||||
|
controlled by the "uuid" mount option, which supports these values:
|
||||||
|
|
||||||
|
- "null":
|
||||||
|
UUID of overlayfs is null. fsid is taken from upper most filesystem.
|
||||||
|
- "off":
|
||||||
|
UUID of overlayfs is null. fsid is taken from upper most filesystem.
|
||||||
|
UUID of underlying layers is ignored.
|
||||||
|
- "on":
|
||||||
|
UUID of overlayfs is generated and used to report a unique fsid.
|
||||||
|
UUID is stored in xattr "trusted.overlay.uuid", making overlayfs fsid
|
||||||
|
unique and persistent. This option requires an overlayfs with upper
|
||||||
|
filesystem that supports xattrs.
|
||||||
|
- "auto": (default)
|
||||||
|
UUID is taken from xattr "trusted.overlay.uuid" if it exists.
|
||||||
|
Upgrade to "uuid=on" on first time mount of new overlay filesystem that
|
||||||
|
meets the prerequites.
|
||||||
|
Downgrade to "uuid=null" for existing overlay filesystems that were never
|
||||||
|
mounted with "uuid=on".
|
||||||
|
|
||||||
|
|
||||||
Volatile mount
|
Volatile mount
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -124,3 +124,12 @@ config OVERLAY_FS_METACOPY
|
|||||||
that doesn't support this feature will have unexpected results.
|
that doesn't support this feature will have unexpected results.
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
|
config OVERLAY_FS_DEBUG
|
||||||
|
bool "Overlayfs: turn on extra debugging checks"
|
||||||
|
default n
|
||||||
|
depends on OVERLAY_FS
|
||||||
|
help
|
||||||
|
Say Y here to enable extra debugging checks in overlayfs.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
@ -416,7 +416,7 @@ struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
|
|||||||
if (is_upper)
|
if (is_upper)
|
||||||
fh->fb.flags |= OVL_FH_FLAG_PATH_UPPER;
|
fh->fb.flags |= OVL_FH_FLAG_PATH_UPPER;
|
||||||
fh->fb.len = sizeof(fh->fb) + buflen;
|
fh->fb.len = sizeof(fh->fb) + buflen;
|
||||||
if (ofs->config.uuid)
|
if (ovl_origin_uuid(ofs))
|
||||||
fh->fb.uuid = *uuid;
|
fh->fb.uuid = *uuid;
|
||||||
|
|
||||||
return fh;
|
return fh;
|
||||||
@ -544,6 +544,7 @@ struct ovl_copy_up_ctx {
|
|||||||
bool origin;
|
bool origin;
|
||||||
bool indexed;
|
bool indexed;
|
||||||
bool metacopy;
|
bool metacopy;
|
||||||
|
bool metacopy_digest;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ovl_link_up(struct ovl_copy_up_ctx *c)
|
static int ovl_link_up(struct ovl_copy_up_ctx *c)
|
||||||
@ -641,8 +642,20 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (c->metacopy) {
|
if (c->metacopy) {
|
||||||
err = ovl_check_setxattr(ofs, temp, OVL_XATTR_METACOPY,
|
struct path lowerdatapath;
|
||||||
NULL, 0, -EOPNOTSUPP);
|
struct ovl_metacopy metacopy_data = OVL_METACOPY_INIT;
|
||||||
|
|
||||||
|
ovl_path_lowerdata(c->dentry, &lowerdatapath);
|
||||||
|
if (WARN_ON_ONCE(lowerdatapath.dentry == NULL))
|
||||||
|
return -EIO;
|
||||||
|
err = ovl_get_verity_digest(ofs, &lowerdatapath, &metacopy_data);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (metacopy_data.digest_algo)
|
||||||
|
c->metacopy_digest = true;
|
||||||
|
|
||||||
|
err = ovl_set_metacopy_xattr(ofs, temp, &metacopy_data);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -751,9 +764,15 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
|
|||||||
if (err)
|
if (err)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (!c->metacopy)
|
|
||||||
ovl_set_upperdata(d_inode(c->dentry));
|
|
||||||
inode = d_inode(c->dentry);
|
inode = d_inode(c->dentry);
|
||||||
|
if (c->metacopy_digest)
|
||||||
|
ovl_set_flag(OVL_HAS_DIGEST, inode);
|
||||||
|
else
|
||||||
|
ovl_clear_flag(OVL_HAS_DIGEST, inode);
|
||||||
|
ovl_clear_flag(OVL_VERIFIED_DIGEST, inode);
|
||||||
|
|
||||||
|
if (!c->metacopy)
|
||||||
|
ovl_set_upperdata(inode);
|
||||||
ovl_inode_update(inode, temp);
|
ovl_inode_update(inode, temp);
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (S_ISDIR(inode->i_mode))
|
||||||
ovl_set_flag(OVL_WHITEOUTS, inode);
|
ovl_set_flag(OVL_WHITEOUTS, inode);
|
||||||
@ -813,6 +832,12 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_fput;
|
goto out_fput;
|
||||||
|
|
||||||
|
if (c->metacopy_digest)
|
||||||
|
ovl_set_flag(OVL_HAS_DIGEST, d_inode(c->dentry));
|
||||||
|
else
|
||||||
|
ovl_clear_flag(OVL_HAS_DIGEST, d_inode(c->dentry));
|
||||||
|
ovl_clear_flag(OVL_VERIFIED_DIGEST, d_inode(c->dentry));
|
||||||
|
|
||||||
if (!c->metacopy)
|
if (!c->metacopy)
|
||||||
ovl_set_upperdata(d_inode(c->dentry));
|
ovl_set_upperdata(d_inode(c->dentry));
|
||||||
ovl_inode_update(d_inode(c->dentry), dget(temp));
|
ovl_inode_update(d_inode(c->dentry), dget(temp));
|
||||||
@ -907,7 +932,7 @@ out:
|
|||||||
static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
|
static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
|
|
||||||
if (!ofs->config.metacopy)
|
if (!ofs->config.metacopy)
|
||||||
return false;
|
return false;
|
||||||
@ -918,6 +943,19 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
|
|||||||
if (flags && ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)))
|
if (flags && ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* Fall back to full copy if no fsverity on source data and we require verity */
|
||||||
|
if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
|
||||||
|
struct path lowerdata;
|
||||||
|
|
||||||
|
ovl_path_lowerdata(dentry, &lowerdata);
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(lowerdata.dentry == NULL) ||
|
||||||
|
ovl_ensure_verity_loaded(&lowerdata) ||
|
||||||
|
!fsverity_active(d_inode(lowerdata.dentry))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -984,6 +1022,8 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
|
ovl_clear_flag(OVL_HAS_DIGEST, d_inode(c->dentry));
|
||||||
|
ovl_clear_flag(OVL_VERIFIED_DIGEST, d_inode(c->dentry));
|
||||||
ovl_set_upperdata(d_inode(c->dentry));
|
ovl_set_upperdata(d_inode(c->dentry));
|
||||||
out_free:
|
out_free:
|
||||||
kfree(capability);
|
kfree(capability);
|
||||||
@ -1078,7 +1118,7 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
|
|||||||
* not very important to optimize this case, so do lazy lowerdata lookup
|
* not very important to optimize this case, so do lazy lowerdata lookup
|
||||||
* before any copy up, so we can do it before taking ovl_inode_lock().
|
* before any copy up, so we can do it before taking ovl_inode_lock().
|
||||||
*/
|
*/
|
||||||
err = ovl_maybe_lookup_lowerdata(dentry);
|
err = ovl_verify_lowerdata(dentry);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -174,28 +174,37 @@ static int ovl_connect_layer(struct dentry *dentry)
|
|||||||
* U = upper file handle
|
* U = upper file handle
|
||||||
* L = lower file handle
|
* L = lower file handle
|
||||||
*
|
*
|
||||||
* (*) Connecting an overlay dir from real lower dentry is not always
|
* (*) Decoding a connected overlay dir from real lower dentry is not always
|
||||||
* possible when there are redirects in lower layers and non-indexed merge dirs.
|
* possible when there are redirects in lower layers and non-indexed merge dirs.
|
||||||
* To mitigate those case, we may copy up the lower dir ancestor before encode
|
* To mitigate those case, we may copy up the lower dir ancestor before encode
|
||||||
* a lower dir file handle.
|
* of a decodable file handle for non-upper dir.
|
||||||
*
|
*
|
||||||
* Return 0 for upper file handle, > 0 for lower file handle or < 0 on error.
|
* Return 0 for upper file handle, > 0 for lower file handle or < 0 on error.
|
||||||
*/
|
*/
|
||||||
static int ovl_check_encode_origin(struct dentry *dentry)
|
static int ovl_check_encode_origin(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
|
bool decodable = ofs->config.nfs_export;
|
||||||
|
|
||||||
|
/* Lower file handle for non-upper non-decodable */
|
||||||
|
if (!ovl_dentry_upper(dentry) && !decodable)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Upper file handle for pure upper */
|
/* Upper file handle for pure upper */
|
||||||
if (!ovl_dentry_lower(dentry))
|
if (!ovl_dentry_lower(dentry))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Upper file handle for non-indexed upper.
|
|
||||||
*
|
|
||||||
* Root is never indexed, so if there's an upper layer, encode upper for
|
* Root is never indexed, so if there's an upper layer, encode upper for
|
||||||
* root.
|
* root.
|
||||||
*/
|
*/
|
||||||
if (ovl_dentry_upper(dentry) &&
|
if (dentry == dentry->d_sb->s_root)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Upper decodable file handle for non-indexed upper.
|
||||||
|
*/
|
||||||
|
if (ovl_dentry_upper(dentry) && decodable &&
|
||||||
!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
|
!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -205,7 +214,7 @@ static int ovl_check_encode_origin(struct dentry *dentry)
|
|||||||
* ovl_connect_layer() will try to make origin's layer "connected" by
|
* ovl_connect_layer() will try to make origin's layer "connected" by
|
||||||
* copying up a "connectable" ancestor.
|
* copying up a "connectable" ancestor.
|
||||||
*/
|
*/
|
||||||
if (d_is_dir(dentry) && ovl_upper_mnt(ofs))
|
if (d_is_dir(dentry) && ovl_upper_mnt(ofs) && decodable)
|
||||||
return ovl_connect_layer(dentry);
|
return ovl_connect_layer(dentry);
|
||||||
|
|
||||||
/* Lower file handle for indexed and non-upper dir/non-dir */
|
/* Lower file handle for indexed and non-upper dir/non-dir */
|
||||||
@ -435,7 +444,7 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb,
|
|||||||
struct dentry *real,
|
struct dentry *real,
|
||||||
const struct ovl_layer *layer)
|
const struct ovl_layer *layer)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
struct dentry *index = NULL;
|
struct dentry *index = NULL;
|
||||||
struct dentry *this = NULL;
|
struct dentry *this = NULL;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
@ -656,7 +665,7 @@ static struct dentry *ovl_get_dentry(struct super_block *sb,
|
|||||||
struct ovl_path *lowerpath,
|
struct ovl_path *lowerpath,
|
||||||
struct dentry *index)
|
struct dentry *index)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
const struct ovl_layer *layer = upper ? &ofs->layers[0] : lowerpath->layer;
|
const struct ovl_layer *layer = upper ? &ofs->layers[0] : lowerpath->layer;
|
||||||
struct dentry *real = upper ?: (index ?: lowerpath->dentry);
|
struct dentry *real = upper ?: (index ?: lowerpath->dentry);
|
||||||
|
|
||||||
@ -681,7 +690,7 @@ static struct dentry *ovl_get_dentry(struct super_block *sb,
|
|||||||
static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
|
static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
|
||||||
struct ovl_fh *fh)
|
struct ovl_fh *fh)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct dentry *upper;
|
struct dentry *upper;
|
||||||
|
|
||||||
@ -701,7 +710,7 @@ static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
|
|||||||
static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
|
static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
|
||||||
struct ovl_fh *fh)
|
struct ovl_fh *fh)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
struct ovl_path origin = { };
|
struct ovl_path origin = { };
|
||||||
struct ovl_path *stack = &origin;
|
struct ovl_path *stack = &origin;
|
||||||
struct dentry *dentry = NULL;
|
struct dentry *dentry = NULL;
|
||||||
@ -876,3 +885,8 @@ const struct export_operations ovl_export_operations = {
|
|||||||
.get_name = ovl_get_name,
|
.get_name = ovl_get_name,
|
||||||
.get_parent = ovl_get_parent,
|
.get_parent = ovl_get_parent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* encode_fh() encodes non-decodable file handles with nfs_export=off */
|
||||||
|
const struct export_operations ovl_export_fid_operations = {
|
||||||
|
.encode_fh = ovl_encode_fh,
|
||||||
|
};
|
||||||
|
@ -115,8 +115,8 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
|
|||||||
if (allow_meta) {
|
if (allow_meta) {
|
||||||
ovl_path_real(dentry, &realpath);
|
ovl_path_real(dentry, &realpath);
|
||||||
} else {
|
} else {
|
||||||
/* lazy lookup of lowerdata */
|
/* lazy lookup and verify of lowerdata */
|
||||||
err = ovl_maybe_lookup_lowerdata(dentry);
|
err = ovl_verify_lowerdata(dentry);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -159,8 +159,8 @@ static int ovl_open(struct inode *inode, struct file *file)
|
|||||||
struct path realpath;
|
struct path realpath;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* lazy lookup of lowerdata */
|
/* lazy lookup and verify lowerdata */
|
||||||
err = ovl_maybe_lookup_lowerdata(dentry);
|
err = ovl_verify_lowerdata(dentry);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ static const char *ovl_get_link(struct dentry *dentry,
|
|||||||
|
|
||||||
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
|
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
|
||||||
if (ofs->config.userxattr)
|
if (ofs->config.userxattr)
|
||||||
return strncmp(name, OVL_XATTR_USER_PREFIX,
|
return strncmp(name, OVL_XATTR_USER_PREFIX,
|
||||||
@ -696,7 +696,7 @@ int ovl_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||||||
int ovl_update_time(struct inode *inode, int flags)
|
int ovl_update_time(struct inode *inode, int flags)
|
||||||
{
|
{
|
||||||
if (flags & S_ATIME) {
|
if (flags & S_ATIME) {
|
||||||
struct ovl_fs *ofs = inode->i_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(inode->i_sb);
|
||||||
struct path upperpath = {
|
struct path upperpath = {
|
||||||
.mnt = ovl_upper_mnt(ofs),
|
.mnt = ovl_upper_mnt(ofs),
|
||||||
.dentry = ovl_upperdentry_dereference(OVL_I(inode)),
|
.dentry = ovl_upperdentry_dereference(OVL_I(inode)),
|
||||||
@ -1291,7 +1291,7 @@ struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir)
|
|||||||
static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
|
static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
|
||||||
struct dentry *lower, bool index)
|
struct dentry *lower, bool index)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
|
||||||
/* No, if pure upper */
|
/* No, if pure upper */
|
||||||
if (!lower)
|
if (!lower)
|
||||||
@ -1311,7 +1311,7 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* No, if non-indexed upper with NFS export */
|
/* No, if non-indexed upper with NFS export */
|
||||||
if (sb->s_export_op && upper)
|
if (ofs->config.nfs_export && upper)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Otherwise, hash by lower inode for fsnotify */
|
/* Otherwise, hash by lower inode for fsnotify */
|
||||||
|
@ -25,7 +25,7 @@ struct ovl_lookup_data {
|
|||||||
bool stop;
|
bool stop;
|
||||||
bool last;
|
bool last;
|
||||||
char *redirect;
|
char *redirect;
|
||||||
bool metacopy;
|
int metacopy;
|
||||||
/* Referring to last redirect xattr */
|
/* Referring to last redirect xattr */
|
||||||
bool absolute_redirect;
|
bool absolute_redirect;
|
||||||
};
|
};
|
||||||
@ -171,8 +171,9 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
|
|||||||
* layer where file handle will be decoded.
|
* layer where file handle will be decoded.
|
||||||
* In case of uuid=off option just make sure that stored uuid is null.
|
* In case of uuid=off option just make sure that stored uuid is null.
|
||||||
*/
|
*/
|
||||||
if (ofs->config.uuid ? !uuid_equal(&fh->fb.uuid, &mnt->mnt_sb->s_uuid) :
|
if (ovl_origin_uuid(ofs) ?
|
||||||
!uuid_is_null(&fh->fb.uuid))
|
!uuid_equal(&fh->fb.uuid, &mnt->mnt_sb->s_uuid) :
|
||||||
|
!uuid_is_null(&fh->fb.uuid))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
bytes = (fh->fb.len - offsetof(struct ovl_fb, fid));
|
bytes = (fh->fb.len - offsetof(struct ovl_fb, fid));
|
||||||
@ -270,7 +271,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
|
|||||||
d->stop = true;
|
d->stop = true;
|
||||||
goto put_and_out;
|
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)
|
if (err < 0)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
@ -889,8 +890,58 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ovl_maybe_validate_verity(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
|
struct inode *inode = d_inode(dentry);
|
||||||
|
struct path datapath, metapath;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!ofs->config.verity_mode ||
|
||||||
|
!ovl_is_metacopy_dentry(dentry) ||
|
||||||
|
ovl_test_flag(OVL_VERIFIED_DIGEST, inode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ovl_test_flag(OVL_HAS_DIGEST, inode)) {
|
||||||
|
if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
|
||||||
|
pr_warn_ratelimited("metacopy file '%pd' has no digest specified\n",
|
||||||
|
dentry);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ovl_path_lowerdata(dentry, &datapath);
|
||||||
|
if (!datapath.dentry)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
ovl_path_real(dentry, &metapath);
|
||||||
|
if (!metapath.dentry)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
err = ovl_inode_lock_interruptible(inode);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (!ovl_test_flag(OVL_VERIFIED_DIGEST, inode)) {
|
||||||
|
const struct cred *old_cred;
|
||||||
|
|
||||||
|
old_cred = ovl_override_creds(dentry->d_sb);
|
||||||
|
|
||||||
|
err = ovl_validate_verity(ofs, &metapath, &datapath);
|
||||||
|
if (err == 0)
|
||||||
|
ovl_set_flag(OVL_VERIFIED_DIGEST, inode);
|
||||||
|
|
||||||
|
revert_creds(old_cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
ovl_inode_unlock(inode);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Lazy lookup of lowerdata */
|
/* Lazy lookup of lowerdata */
|
||||||
int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
|
static int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct inode *inode = d_inode(dentry);
|
struct inode *inode = d_inode(dentry);
|
||||||
const char *redirect = ovl_lowerdata_redirect(inode);
|
const char *redirect = ovl_lowerdata_redirect(inode);
|
||||||
@ -935,12 +986,23 @@ out_err:
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ovl_verify_lowerdata(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = ovl_maybe_lookup_lowerdata(dentry);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return ovl_maybe_validate_verity(dentry);
|
||||||
|
}
|
||||||
|
|
||||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct ovl_entry *oe = NULL;
|
struct ovl_entry *oe = NULL;
|
||||||
const struct cred *old_cred;
|
const struct cred *old_cred;
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
struct ovl_entry *poe = OVL_E(dentry->d_parent);
|
struct ovl_entry *poe = OVL_E(dentry->d_parent);
|
||||||
struct ovl_entry *roe = OVL_E(dentry->d_sb->s_root);
|
struct ovl_entry *roe = OVL_E(dentry->d_sb->s_root);
|
||||||
struct ovl_path *stack = NULL, *origin_path = NULL;
|
struct ovl_path *stack = NULL, *origin_path = NULL;
|
||||||
@ -955,6 +1017,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
int err;
|
int err;
|
||||||
bool uppermetacopy = false;
|
bool uppermetacopy = false;
|
||||||
|
int metacopy_size = 0;
|
||||||
struct ovl_lookup_data d = {
|
struct ovl_lookup_data d = {
|
||||||
.sb = dentry->d_sb,
|
.sb = dentry->d_sb,
|
||||||
.name = dentry->d_name,
|
.name = dentry->d_name,
|
||||||
@ -963,7 +1026,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
.stop = false,
|
.stop = false,
|
||||||
.last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
|
.last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
|
||||||
.redirect = NULL,
|
.redirect = NULL,
|
||||||
.metacopy = false,
|
.metacopy = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dentry->d_name.len > ofs->namelen)
|
if (dentry->d_name.len > ofs->namelen)
|
||||||
@ -999,6 +1062,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
|
|
||||||
if (d.metacopy)
|
if (d.metacopy)
|
||||||
uppermetacopy = true;
|
uppermetacopy = true;
|
||||||
|
metacopy_size = d.metacopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.redirect) {
|
if (d.redirect) {
|
||||||
@ -1076,6 +1140,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
origin = this;
|
origin = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!upperdentry && !d.is_dir && !ctr && d.metacopy)
|
||||||
|
metacopy_size = d.metacopy;
|
||||||
|
|
||||||
if (d.metacopy && ctr) {
|
if (d.metacopy && ctr) {
|
||||||
/*
|
/*
|
||||||
* Do not store intermediate metacopy dentries in
|
* Do not store intermediate metacopy dentries in
|
||||||
@ -1120,7 +1187,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
|
|
||||||
/* Defer lookup of lowerdata in data-only layers to first access */
|
/* Defer lookup of lowerdata in data-only layers to first access */
|
||||||
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
|
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
|
||||||
d.metacopy = false;
|
d.metacopy = 0;
|
||||||
ctr++;
|
ctr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1211,10 +1278,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
upperredirect = NULL;
|
upperredirect = NULL;
|
||||||
goto out_free_oe;
|
goto out_free_oe;
|
||||||
}
|
}
|
||||||
err = ovl_check_metacopy_xattr(ofs, &upperpath);
|
err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out_free_oe;
|
goto out_free_oe;
|
||||||
uppermetacopy = err;
|
uppermetacopy = err;
|
||||||
|
metacopy_size = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upperdentry || ctr) {
|
if (upperdentry || ctr) {
|
||||||
@ -1236,6 +1304,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
goto out_free_oe;
|
goto out_free_oe;
|
||||||
if (upperdentry && !uppermetacopy)
|
if (upperdentry && !uppermetacopy)
|
||||||
ovl_set_flag(OVL_UPPERDATA, inode);
|
ovl_set_flag(OVL_UPPERDATA, inode);
|
||||||
|
|
||||||
|
if (metacopy_size > OVL_METACOPY_MIN_SIZE)
|
||||||
|
ovl_set_flag(OVL_HAS_DIGEST, inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode));
|
ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode));
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/uuid.h>
|
#include <linux/uuid.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/fsverity.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
#include <linux/posix_acl.h>
|
#include <linux/posix_acl.h>
|
||||||
#include <linux/posix_acl_xattr.h>
|
#include <linux/posix_acl_xattr.h>
|
||||||
@ -36,6 +37,7 @@ enum ovl_xattr {
|
|||||||
OVL_XATTR_IMPURE,
|
OVL_XATTR_IMPURE,
|
||||||
OVL_XATTR_NLINK,
|
OVL_XATTR_NLINK,
|
||||||
OVL_XATTR_UPPER,
|
OVL_XATTR_UPPER,
|
||||||
|
OVL_XATTR_UUID,
|
||||||
OVL_XATTR_METACOPY,
|
OVL_XATTR_METACOPY,
|
||||||
OVL_XATTR_PROTATTR,
|
OVL_XATTR_PROTATTR,
|
||||||
};
|
};
|
||||||
@ -49,6 +51,8 @@ enum ovl_inode_flag {
|
|||||||
OVL_UPPERDATA,
|
OVL_UPPERDATA,
|
||||||
/* Inode number will remain constant over copy up. */
|
/* Inode number will remain constant over copy up. */
|
||||||
OVL_CONST_INO,
|
OVL_CONST_INO,
|
||||||
|
OVL_HAS_DIGEST,
|
||||||
|
OVL_VERIFIED_DIGEST,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ovl_entry_flag {
|
enum ovl_entry_flag {
|
||||||
@ -64,12 +68,25 @@ enum {
|
|||||||
OVL_REDIRECT_ON,
|
OVL_REDIRECT_ON,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OVL_UUID_OFF,
|
||||||
|
OVL_UUID_NULL,
|
||||||
|
OVL_UUID_AUTO,
|
||||||
|
OVL_UUID_ON,
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
OVL_XINO_OFF,
|
OVL_XINO_OFF,
|
||||||
OVL_XINO_AUTO,
|
OVL_XINO_AUTO,
|
||||||
OVL_XINO_ON,
|
OVL_XINO_ON,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OVL_VERITY_OFF,
|
||||||
|
OVL_VERITY_ON,
|
||||||
|
OVL_VERITY_REQUIRE,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
|
* The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
|
||||||
* where:
|
* where:
|
||||||
@ -126,6 +143,26 @@ struct ovl_fh {
|
|||||||
#define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \
|
#define OVL_FH_FID_OFFSET (OVL_FH_WIRE_OFFSET + \
|
||||||
offsetof(struct ovl_fb, fid))
|
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];
|
extern const char *const ovl_xattr_table[][2];
|
||||||
static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
|
static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
|
||||||
{
|
{
|
||||||
@ -430,6 +467,8 @@ bool ovl_already_copied_up(struct dentry *dentry, int flags);
|
|||||||
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
|
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
|
||||||
enum ovl_xattr ox);
|
enum ovl_xattr ox);
|
||||||
bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path);
|
bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path);
|
||||||
|
bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
|
||||||
|
const struct path *upperpath);
|
||||||
|
|
||||||
static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs,
|
static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs,
|
||||||
struct dentry *upperdentry)
|
struct dentry *upperdentry)
|
||||||
@ -452,9 +491,20 @@ bool ovl_need_index(struct dentry *dentry);
|
|||||||
int ovl_nlink_start(struct dentry *dentry);
|
int ovl_nlink_start(struct dentry *dentry);
|
||||||
void ovl_nlink_end(struct dentry *dentry);
|
void ovl_nlink_end(struct dentry *dentry);
|
||||||
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
|
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);
|
||||||
|
int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d,
|
||||||
|
struct ovl_metacopy *metacopy);
|
||||||
bool ovl_is_metacopy_dentry(struct dentry *dentry);
|
bool ovl_is_metacopy_dentry(struct dentry *dentry);
|
||||||
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding);
|
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding);
|
||||||
|
int ovl_ensure_verity_loaded(struct path *path);
|
||||||
|
int ovl_get_verity_xattr(struct ovl_fs *ofs, const struct path *path,
|
||||||
|
u8 *digest_buf, int *buf_length);
|
||||||
|
int ovl_validate_verity(struct ovl_fs *ofs,
|
||||||
|
struct path *metapath,
|
||||||
|
struct path *datapath);
|
||||||
|
int ovl_get_verity_digest(struct ovl_fs *ofs, struct path *src,
|
||||||
|
struct ovl_metacopy *metacopy);
|
||||||
int ovl_sync_status(struct ovl_fs *ofs);
|
int ovl_sync_status(struct ovl_fs *ofs);
|
||||||
|
|
||||||
static inline void ovl_set_flag(unsigned long flag, struct inode *inode)
|
static inline void ovl_set_flag(unsigned long flag, struct inode *inode)
|
||||||
@ -494,6 +544,17 @@ static inline bool ovl_redirect_dir(struct ovl_fs *ofs)
|
|||||||
return ofs->config.redirect_mode == OVL_REDIRECT_ON;
|
return ofs->config.redirect_mode == OVL_REDIRECT_ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool ovl_origin_uuid(struct ovl_fs *ofs)
|
||||||
|
{
|
||||||
|
return ofs->config.uuid != OVL_UUID_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool ovl_has_fsid(struct ovl_fs *ofs)
|
||||||
|
{
|
||||||
|
return ofs->config.uuid == OVL_UUID_ON ||
|
||||||
|
ofs->config.uuid == OVL_UUID_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* With xino=auto, we do best effort to keep all inodes on same st_dev and
|
* With xino=auto, we do best effort to keep all inodes on same st_dev and
|
||||||
* d_ino consistent with st_ino.
|
* d_ino consistent with st_ino.
|
||||||
@ -574,7 +635,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh);
|
|||||||
struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
|
struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
|
||||||
struct dentry *origin, bool verify);
|
struct dentry *origin, bool verify);
|
||||||
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
|
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
|
||||||
int ovl_maybe_lookup_lowerdata(struct dentry *dentry);
|
int ovl_verify_lowerdata(struct dentry *dentry);
|
||||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
bool ovl_lower_positive(struct dentry *dentry);
|
bool ovl_lower_positive(struct dentry *dentry);
|
||||||
@ -759,6 +820,7 @@ int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
|
|||||||
|
|
||||||
/* export.c */
|
/* export.c */
|
||||||
extern const struct export_operations ovl_export_operations;
|
extern const struct export_operations ovl_export_operations;
|
||||||
|
extern const struct export_operations ovl_export_fid_operations;
|
||||||
|
|
||||||
/* super.c */
|
/* super.c */
|
||||||
int ovl_fill_super(struct super_block *sb, struct fs_context *fc);
|
int ovl_fill_super(struct super_block *sb, struct fs_context *fc);
|
||||||
|
@ -10,8 +10,9 @@ struct ovl_config {
|
|||||||
char *workdir;
|
char *workdir;
|
||||||
bool default_permissions;
|
bool default_permissions;
|
||||||
int redirect_mode;
|
int redirect_mode;
|
||||||
|
int verity_mode;
|
||||||
bool index;
|
bool index;
|
||||||
bool uuid;
|
int uuid;
|
||||||
bool nfs_export;
|
bool nfs_export;
|
||||||
int xino;
|
int xino;
|
||||||
bool metacopy;
|
bool metacopy;
|
||||||
@ -81,6 +82,7 @@ struct ovl_fs {
|
|||||||
const struct cred *creator_cred;
|
const struct cred *creator_cred;
|
||||||
bool tmpfile;
|
bool tmpfile;
|
||||||
bool noxattr;
|
bool noxattr;
|
||||||
|
bool nofh;
|
||||||
/* Did we take the inuse lock? */
|
/* Did we take the inuse lock? */
|
||||||
bool upperdir_locked;
|
bool upperdir_locked;
|
||||||
bool workdir_locked;
|
bool workdir_locked;
|
||||||
@ -115,8 +117,13 @@ static inline struct mnt_idmap *ovl_upper_mnt_idmap(struct ovl_fs *ofs)
|
|||||||
return mnt_idmap(ovl_upper_mnt(ofs));
|
return mnt_idmap(ovl_upper_mnt(ofs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern struct file_system_type ovl_fs_type;
|
||||||
|
|
||||||
static inline struct ovl_fs *OVL_FS(struct super_block *sb)
|
static inline struct ovl_fs *OVL_FS(struct super_block *sb)
|
||||||
{
|
{
|
||||||
|
if (IS_ENABLED(CONFIG_OVERLAY_FS_DEBUG))
|
||||||
|
WARN_ON_ONCE(sb->s_type != &ovl_fs_type);
|
||||||
|
|
||||||
return (struct ovl_fs *)sb->s_fs_info;
|
return (struct ovl_fs *)sb->s_fs_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ enum {
|
|||||||
Opt_userxattr,
|
Opt_userxattr,
|
||||||
Opt_xino,
|
Opt_xino,
|
||||||
Opt_metacopy,
|
Opt_metacopy,
|
||||||
|
Opt_verity,
|
||||||
Opt_volatile,
|
Opt_volatile,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,6 +65,24 @@ static const struct constant_table ovl_parameter_bool[] = {
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct constant_table ovl_parameter_uuid[] = {
|
||||||
|
{ "off", OVL_UUID_OFF },
|
||||||
|
{ "null", OVL_UUID_NULL },
|
||||||
|
{ "auto", OVL_UUID_AUTO },
|
||||||
|
{ "on", OVL_UUID_ON },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *ovl_uuid_mode(struct ovl_config *config)
|
||||||
|
{
|
||||||
|
return ovl_parameter_uuid[config->uuid].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ovl_uuid_def(void)
|
||||||
|
{
|
||||||
|
return OVL_UUID_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct constant_table ovl_parameter_xino[] = {
|
static const struct constant_table ovl_parameter_xino[] = {
|
||||||
{ "off", OVL_XINO_OFF },
|
{ "off", OVL_XINO_OFF },
|
||||||
{ "auto", OVL_XINO_AUTO },
|
{ "auto", OVL_XINO_AUTO },
|
||||||
@ -101,6 +120,23 @@ static int ovl_redirect_mode_def(void)
|
|||||||
OVL_REDIRECT_NOFOLLOW;
|
OVL_REDIRECT_NOFOLLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct constant_table ovl_parameter_verity[] = {
|
||||||
|
{ "off", OVL_VERITY_OFF },
|
||||||
|
{ "on", OVL_VERITY_ON },
|
||||||
|
{ "require", OVL_VERITY_REQUIRE },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *ovl_verity_mode(struct ovl_config *config)
|
||||||
|
{
|
||||||
|
return ovl_parameter_verity[config->verity_mode].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ovl_verity_mode_def(void)
|
||||||
|
{
|
||||||
|
return OVL_VERITY_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
#define fsparam_string_empty(NAME, OPT) \
|
#define fsparam_string_empty(NAME, OPT) \
|
||||||
__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
|
__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
|
||||||
|
|
||||||
@ -111,11 +147,12 @@ const struct fs_parameter_spec ovl_parameter_spec[] = {
|
|||||||
fsparam_flag("default_permissions", Opt_default_permissions),
|
fsparam_flag("default_permissions", Opt_default_permissions),
|
||||||
fsparam_enum("redirect_dir", Opt_redirect_dir, ovl_parameter_redirect_dir),
|
fsparam_enum("redirect_dir", Opt_redirect_dir, ovl_parameter_redirect_dir),
|
||||||
fsparam_enum("index", Opt_index, ovl_parameter_bool),
|
fsparam_enum("index", Opt_index, ovl_parameter_bool),
|
||||||
fsparam_enum("uuid", Opt_uuid, ovl_parameter_bool),
|
fsparam_enum("uuid", Opt_uuid, ovl_parameter_uuid),
|
||||||
fsparam_enum("nfs_export", Opt_nfs_export, ovl_parameter_bool),
|
fsparam_enum("nfs_export", Opt_nfs_export, ovl_parameter_bool),
|
||||||
fsparam_flag("userxattr", Opt_userxattr),
|
fsparam_flag("userxattr", Opt_userxattr),
|
||||||
fsparam_enum("xino", Opt_xino, ovl_parameter_xino),
|
fsparam_enum("xino", Opt_xino, ovl_parameter_xino),
|
||||||
fsparam_enum("metacopy", Opt_metacopy, ovl_parameter_bool),
|
fsparam_enum("metacopy", Opt_metacopy, ovl_parameter_bool),
|
||||||
|
fsparam_enum("verity", Opt_verity, ovl_parameter_verity),
|
||||||
fsparam_flag("volatile", Opt_volatile),
|
fsparam_flag("volatile", Opt_volatile),
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -572,6 +609,9 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|||||||
config->metacopy = result.uint_32;
|
config->metacopy = result.uint_32;
|
||||||
ctx->set.metacopy = true;
|
ctx->set.metacopy = true;
|
||||||
break;
|
break;
|
||||||
|
case Opt_verity:
|
||||||
|
config->verity_mode = result.uint_32;
|
||||||
|
break;
|
||||||
case Opt_volatile:
|
case Opt_volatile:
|
||||||
config->ovl_volatile = true;
|
config->ovl_volatile = true;
|
||||||
break;
|
break;
|
||||||
@ -622,7 +662,7 @@ static void ovl_free(struct fs_context *fc)
|
|||||||
static int ovl_reconfigure(struct fs_context *fc)
|
static int ovl_reconfigure(struct fs_context *fc)
|
||||||
{
|
{
|
||||||
struct super_block *sb = fc->root->d_sb;
|
struct super_block *sb = fc->root->d_sb;
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
struct super_block *upper_sb;
|
struct super_block *upper_sb;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -679,7 +719,7 @@ int ovl_init_fs_context(struct fs_context *fc)
|
|||||||
|
|
||||||
ofs->config.redirect_mode = ovl_redirect_mode_def();
|
ofs->config.redirect_mode = ovl_redirect_mode_def();
|
||||||
ofs->config.index = ovl_index_def;
|
ofs->config.index = ovl_index_def;
|
||||||
ofs->config.uuid = true;
|
ofs->config.uuid = ovl_uuid_def();
|
||||||
ofs->config.nfs_export = ovl_nfs_export_def;
|
ofs->config.nfs_export = ovl_nfs_export_def;
|
||||||
ofs->config.xino = ovl_xino_def();
|
ofs->config.xino = ovl_xino_def();
|
||||||
ofs->config.metacopy = ovl_metacopy_def;
|
ofs->config.metacopy = ovl_metacopy_def;
|
||||||
@ -762,6 +802,23 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
|
|||||||
config->ovl_volatile = false;
|
config->ovl_volatile = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!config->upperdir && config->uuid == OVL_UUID_ON) {
|
||||||
|
pr_info("option \"uuid=on\" requires an upper fs, falling back to uuid=null.\n");
|
||||||
|
config->uuid = OVL_UUID_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resolve verity -> metacopy dependency */
|
||||||
|
if (config->verity_mode && !config->metacopy) {
|
||||||
|
/* Don't allow explicit specified conflicting combinations */
|
||||||
|
if (set.metacopy) {
|
||||||
|
pr_err("conflicting options: metacopy=off,verity=%s\n",
|
||||||
|
ovl_verity_mode(config));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* Otherwise automatically enable metacopy. */
|
||||||
|
config->metacopy = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is to make the logic below simpler. It doesn't make any other
|
* This is to make the logic below simpler. It doesn't make any other
|
||||||
* difference, since redirect_dir=on is only used for upper.
|
* difference, since redirect_dir=on is only used for upper.
|
||||||
@ -769,13 +826,18 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
|
|||||||
if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
|
if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
|
||||||
config->redirect_mode = OVL_REDIRECT_ON;
|
config->redirect_mode = OVL_REDIRECT_ON;
|
||||||
|
|
||||||
/* Resolve metacopy -> redirect_dir dependency */
|
/* Resolve verity -> metacopy -> redirect_dir dependency */
|
||||||
if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) {
|
if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) {
|
||||||
if (set.metacopy && set.redirect) {
|
if (set.metacopy && set.redirect) {
|
||||||
pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
|
pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
|
||||||
ovl_redirect_mode(config));
|
ovl_redirect_mode(config));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (config->verity_mode && set.redirect) {
|
||||||
|
pr_err("conflicting options: verity=%s,redirect_dir=%s\n",
|
||||||
|
ovl_verity_mode(config), ovl_redirect_mode(config));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
if (set.redirect) {
|
if (set.redirect) {
|
||||||
/*
|
/*
|
||||||
* There was an explicit redirect_dir=... that resulted
|
* There was an explicit redirect_dir=... that resulted
|
||||||
@ -812,7 +874,7 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolve nfs_export -> !metacopy dependency */
|
/* Resolve nfs_export -> !metacopy && !verity dependency */
|
||||||
if (config->nfs_export && config->metacopy) {
|
if (config->nfs_export && config->metacopy) {
|
||||||
if (set.nfs_export && set.metacopy) {
|
if (set.nfs_export && set.metacopy) {
|
||||||
pr_err("conflicting options: nfs_export=on,metacopy=on\n");
|
pr_err("conflicting options: nfs_export=on,metacopy=on\n");
|
||||||
@ -825,6 +887,14 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
|
|||||||
*/
|
*/
|
||||||
pr_info("disabling nfs_export due to metacopy=on\n");
|
pr_info("disabling nfs_export due to metacopy=on\n");
|
||||||
config->nfs_export = false;
|
config->nfs_export = false;
|
||||||
|
} else if (config->verity_mode) {
|
||||||
|
/*
|
||||||
|
* There was an explicit verity=.. that resulted
|
||||||
|
* in this conflict.
|
||||||
|
*/
|
||||||
|
pr_info("disabling nfs_export due to verity=%s\n",
|
||||||
|
ovl_verity_mode(config));
|
||||||
|
config->nfs_export = false;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* There was an explicit nfs_export=on that resulted
|
* There was an explicit nfs_export=on that resulted
|
||||||
@ -836,7 +906,7 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Resolve userxattr -> !redirect && !metacopy dependency */
|
/* Resolve userxattr -> !redirect && !metacopy && !verity dependency */
|
||||||
if (config->userxattr) {
|
if (config->userxattr) {
|
||||||
if (set.redirect &&
|
if (set.redirect &&
|
||||||
config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
|
config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
|
||||||
@ -848,6 +918,11 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
|
|||||||
pr_err("conflicting options: userxattr,metacopy=on\n");
|
pr_err("conflicting options: userxattr,metacopy=on\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (config->verity_mode) {
|
||||||
|
pr_err("conflicting options: userxattr,verity=%s\n",
|
||||||
|
ovl_verity_mode(config));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Silently disable default setting of redirect and metacopy.
|
* Silently disable default setting of redirect and metacopy.
|
||||||
* This shall be the default in the future as well: these
|
* This shall be the default in the future as well: these
|
||||||
@ -872,7 +947,7 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
|
|||||||
int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct super_block *sb = dentry->d_sb;
|
struct super_block *sb = dentry->d_sb;
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer;
|
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer;
|
||||||
const struct ovl_layer *data_layers = &ofs->layers[nr_merged_lower];
|
const struct ovl_layer *data_layers = &ofs->layers[nr_merged_lower];
|
||||||
|
|
||||||
@ -895,8 +970,8 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
|||||||
ovl_redirect_mode(&ofs->config));
|
ovl_redirect_mode(&ofs->config));
|
||||||
if (ofs->config.index != ovl_index_def)
|
if (ofs->config.index != ovl_index_def)
|
||||||
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
|
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
|
||||||
if (!ofs->config.uuid)
|
if (ofs->config.uuid != ovl_uuid_def())
|
||||||
seq_puts(m, ",uuid=off");
|
seq_printf(m, ",uuid=%s", ovl_uuid_mode(&ofs->config));
|
||||||
if (ofs->config.nfs_export != ovl_nfs_export_def)
|
if (ofs->config.nfs_export != ovl_nfs_export_def)
|
||||||
seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
|
seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
|
||||||
"on" : "off");
|
"on" : "off");
|
||||||
@ -909,5 +984,8 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
|||||||
seq_puts(m, ",volatile");
|
seq_puts(m, ",volatile");
|
||||||
if (ofs->config.userxattr)
|
if (ofs->config.userxattr)
|
||||||
seq_puts(m, ",userxattr");
|
seq_puts(m, ",userxattr");
|
||||||
|
if (ofs->config.verity_mode != ovl_verity_mode_def())
|
||||||
|
seq_printf(m, ",verity=%s",
|
||||||
|
ovl_verity_mode(&ofs->config));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
|
|||||||
const struct inode *inode)
|
const struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dentry *real = NULL, *lower;
|
struct dentry *real = NULL, *lower;
|
||||||
|
int err;
|
||||||
|
|
||||||
/* It's an overlay file */
|
/* It's an overlay file */
|
||||||
if (inode && d_inode(dentry) == inode)
|
if (inode && d_inode(dentry) == inode)
|
||||||
@ -58,7 +59,9 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
|
|||||||
* uprobes on offset within the file, so lowerdata should be available
|
* uprobes on offset within the file, so lowerdata should be available
|
||||||
* when setting the uprobe.
|
* when setting the uprobe.
|
||||||
*/
|
*/
|
||||||
ovl_maybe_lookup_lowerdata(dentry);
|
err = ovl_verify_lowerdata(dentry);
|
||||||
|
if (err)
|
||||||
|
goto bug;
|
||||||
lower = ovl_dentry_lowerdata(dentry);
|
lower = ovl_dentry_lowerdata(dentry);
|
||||||
if (!lower)
|
if (!lower)
|
||||||
goto bug;
|
goto bug;
|
||||||
@ -182,7 +185,7 @@ static void ovl_destroy_inode(struct inode *inode)
|
|||||||
|
|
||||||
static void ovl_put_super(struct super_block *sb)
|
static void ovl_put_super(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
|
||||||
if (ofs)
|
if (ofs)
|
||||||
ovl_free_fs(ofs);
|
ovl_free_fs(ofs);
|
||||||
@ -191,7 +194,7 @@ static void ovl_put_super(struct super_block *sb)
|
|||||||
/* Sync real dirty inodes in upper filesystem (if it exists) */
|
/* Sync real dirty inodes in upper filesystem (if it exists) */
|
||||||
static int ovl_sync_fs(struct super_block *sb, int wait)
|
static int ovl_sync_fs(struct super_block *sb, int wait)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
struct super_block *upper_sb;
|
struct super_block *upper_sb;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -239,8 +242,9 @@ static int ovl_sync_fs(struct super_block *sb, int wait)
|
|||||||
*/
|
*/
|
||||||
static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
|
static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct super_block *sb = dentry->d_sb;
|
||||||
struct dentry *root_dentry = dentry->d_sb->s_root;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
struct dentry *root_dentry = sb->s_root;
|
||||||
struct path path;
|
struct path path;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -250,6 +254,8 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||||||
if (!err) {
|
if (!err) {
|
||||||
buf->f_namelen = ofs->namelen;
|
buf->f_namelen = ofs->namelen;
|
||||||
buf->f_type = OVERLAYFS_SUPER_MAGIC;
|
buf->f_type = OVERLAYFS_SUPER_MAGIC;
|
||||||
|
if (ovl_has_fsid(ofs))
|
||||||
|
buf->f_fsid = uuid_to_fsid(sb->s_uuid.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@ -397,6 +403,7 @@ static int ovl_lower_dir(const char *name, struct path *path,
|
|||||||
pr_warn("fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n",
|
pr_warn("fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n",
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
|
ofs->nofh |= !fh_type;
|
||||||
/*
|
/*
|
||||||
* Decoding origin file handle is required for persistent st_ino.
|
* Decoding origin file handle is required for persistent st_ino.
|
||||||
* Without persistent st_ino, xino=auto falls back to xino=off.
|
* Without persistent st_ino, xino=auto falls back to xino=off.
|
||||||
@ -770,6 +777,10 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||||||
ofs->config.index = false;
|
ofs->config.index = false;
|
||||||
pr_warn("...falling back to index=off.\n");
|
pr_warn("...falling back to index=off.\n");
|
||||||
}
|
}
|
||||||
|
if (ovl_has_fsid(ofs)) {
|
||||||
|
ofs->config.uuid = OVL_UUID_NULL;
|
||||||
|
pr_warn("...falling back to uuid=null.\n");
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* xattr support is required for persistent st_ino.
|
* xattr support is required for persistent st_ino.
|
||||||
* Without persistent st_ino, xino=auto falls back to xino=off.
|
* Without persistent st_ino, xino=auto falls back to xino=off.
|
||||||
@ -815,6 +826,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|||||||
ofs->config.index = false;
|
ofs->config.index = false;
|
||||||
pr_warn("upper fs does not support file handles, falling back to index=off.\n");
|
pr_warn("upper fs does not support file handles, falling back to index=off.\n");
|
||||||
}
|
}
|
||||||
|
ofs->nofh |= !fh_type;
|
||||||
|
|
||||||
/* Check if upper fs has 32bit inode numbers */
|
/* Check if upper fs has 32bit inode numbers */
|
||||||
if (fh_type != FILEID_INO32_GEN)
|
if (fh_type != FILEID_INO32_GEN)
|
||||||
@ -1416,9 +1428,12 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
|||||||
if (!ovl_upper_mnt(ofs))
|
if (!ovl_upper_mnt(ofs))
|
||||||
sb->s_flags |= SB_RDONLY;
|
sb->s_flags |= SB_RDONLY;
|
||||||
|
|
||||||
if (!ofs->config.uuid && ofs->numfs > 1) {
|
if (!ovl_origin_uuid(ofs) && ofs->numfs > 1) {
|
||||||
pr_warn("The uuid=off requires a single fs for lower and upper, falling back to uuid=on.\n");
|
pr_warn("The uuid=off requires a single fs for lower and upper, falling back to uuid=null.\n");
|
||||||
ofs->config.uuid = true;
|
ofs->config.uuid = OVL_UUID_NULL;
|
||||||
|
} else if (ovl_has_fsid(ofs) && ovl_upper_mnt(ofs)) {
|
||||||
|
/* Use per instance persistent uuid/fsid */
|
||||||
|
ovl_init_uuid_xattr(sb, ofs, &ctx->upper);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ovl_force_readonly(ofs) && ofs->config.index) {
|
if (!ovl_force_readonly(ofs) && ofs->config.index) {
|
||||||
@ -1449,8 +1464,15 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
|||||||
ofs->config.nfs_export = false;
|
ofs->config.nfs_export = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Support encoding decodable file handles with nfs_export=on
|
||||||
|
* and encoding non-decodable file handles with nfs_export=off
|
||||||
|
* if all layers support file handles.
|
||||||
|
*/
|
||||||
if (ofs->config.nfs_export)
|
if (ofs->config.nfs_export)
|
||||||
sb->s_export_op = &ovl_export_operations;
|
sb->s_export_op = &ovl_export_operations;
|
||||||
|
else if (!ofs->nofh)
|
||||||
|
sb->s_export_op = &ovl_export_fid_operations;
|
||||||
|
|
||||||
/* Never override disk quota limits or use reserved space */
|
/* Never override disk quota limits or use reserved space */
|
||||||
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
|
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
|
||||||
@ -1479,7 +1501,7 @@ out_err:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct file_system_type ovl_fs_type = {
|
struct file_system_type ovl_fs_type = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "overlay",
|
.name = "overlay",
|
||||||
.init_fs_context = ovl_init_fs_context,
|
.init_fs_context = ovl_init_fs_context,
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
#include <linux/xattr.h>
|
#include <linux/xattr.h>
|
||||||
#include <linux/exportfs.h>
|
#include <linux/exportfs.h>
|
||||||
|
#include <linux/file.h>
|
||||||
#include <linux/fileattr.h>
|
#include <linux/fileattr.h>
|
||||||
#include <linux/uuid.h>
|
#include <linux/uuid.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
@ -18,25 +19,25 @@
|
|||||||
|
|
||||||
int ovl_want_write(struct dentry *dentry)
|
int ovl_want_write(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
return mnt_want_write(ovl_upper_mnt(ofs));
|
return mnt_want_write(ovl_upper_mnt(ofs));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ovl_drop_write(struct dentry *dentry)
|
void ovl_drop_write(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
mnt_drop_write(ovl_upper_mnt(ofs));
|
mnt_drop_write(ovl_upper_mnt(ofs));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dentry *ovl_workdir(struct dentry *dentry)
|
struct dentry *ovl_workdir(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
return ofs->workdir;
|
return ofs->workdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct cred *ovl_override_creds(struct super_block *sb)
|
const struct cred *ovl_override_creds(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
|
||||||
return override_creds(ofs->creator_cred);
|
return override_creds(ofs->creator_cred);
|
||||||
}
|
}
|
||||||
@ -62,7 +63,7 @@ int ovl_can_decode_fh(struct super_block *sb)
|
|||||||
|
|
||||||
struct dentry *ovl_indexdir(struct super_block *sb)
|
struct dentry *ovl_indexdir(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
|
||||||
return ofs->indexdir;
|
return ofs->indexdir;
|
||||||
}
|
}
|
||||||
@ -70,7 +71,7 @@ struct dentry *ovl_indexdir(struct super_block *sb)
|
|||||||
/* Index all files on copy up. For now only enabled for NFS export */
|
/* Index all files on copy up. For now only enabled for NFS export */
|
||||||
bool ovl_index_all(struct super_block *sb)
|
bool ovl_index_all(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
|
||||||
return ofs->config.nfs_export && ofs->config.index;
|
return ofs->config.nfs_export && ofs->config.index;
|
||||||
}
|
}
|
||||||
@ -78,7 +79,7 @@ bool ovl_index_all(struct super_block *sb)
|
|||||||
/* Verify lower origin on lookup. For now only enabled for NFS export */
|
/* Verify lower origin on lookup. For now only enabled for NFS export */
|
||||||
bool ovl_verify_lower(struct super_block *sb)
|
bool ovl_verify_lower(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(sb);
|
||||||
|
|
||||||
return ofs->config.nfs_export && ofs->config.index;
|
return ofs->config.nfs_export && ofs->config.index;
|
||||||
}
|
}
|
||||||
@ -203,7 +204,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
|
|||||||
|
|
||||||
void ovl_path_upper(struct dentry *dentry, struct path *path)
|
void ovl_path_upper(struct dentry *dentry, struct path *path)
|
||||||
{
|
{
|
||||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||||
|
|
||||||
path->mnt = ovl_upper_mnt(ofs);
|
path->mnt = ovl_upper_mnt(ofs);
|
||||||
path->dentry = ovl_dentry_upper(dentry);
|
path->dentry = ovl_dentry_upper(dentry);
|
||||||
@ -675,6 +676,65 @@ bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, const struct path *path)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load persistent uuid from xattr into s_uuid if found, or store a new
|
||||||
|
* random generated value in s_uuid and in xattr.
|
||||||
|
*/
|
||||||
|
bool ovl_init_uuid_xattr(struct super_block *sb, struct ovl_fs *ofs,
|
||||||
|
const struct path *upperpath)
|
||||||
|
{
|
||||||
|
bool set = false;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
/* Try to load existing persistent uuid */
|
||||||
|
res = ovl_path_getxattr(ofs, upperpath, OVL_XATTR_UUID, sb->s_uuid.b,
|
||||||
|
UUID_SIZE);
|
||||||
|
if (res == UUID_SIZE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (res != -ENODATA)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* With uuid=auto, if uuid xattr is found, it will be used.
|
||||||
|
* If uuid xattrs is not found, generate a persistent uuid only on mount
|
||||||
|
* of new overlays where upper root dir is not yet marked as impure.
|
||||||
|
* An upper dir is marked as impure on copy up or lookup of its subdirs.
|
||||||
|
*/
|
||||||
|
if (ofs->config.uuid == OVL_UUID_AUTO) {
|
||||||
|
res = ovl_path_getxattr(ofs, upperpath, OVL_XATTR_IMPURE, NULL,
|
||||||
|
0);
|
||||||
|
if (res > 0) {
|
||||||
|
/* Any mount of old overlay - downgrade to uuid=null */
|
||||||
|
ofs->config.uuid = OVL_UUID_NULL;
|
||||||
|
return true;
|
||||||
|
} else if (res == -ENODATA) {
|
||||||
|
/* First mount of new overlay - upgrade to uuid=on */
|
||||||
|
ofs->config.uuid = OVL_UUID_ON;
|
||||||
|
} else if (res < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate overlay instance uuid */
|
||||||
|
uuid_gen(&sb->s_uuid);
|
||||||
|
|
||||||
|
/* Try to store persistent uuid */
|
||||||
|
set = true;
|
||||||
|
res = ovl_setxattr(ofs, upperpath->dentry, OVL_XATTR_UUID, sb->s_uuid.b,
|
||||||
|
UUID_SIZE);
|
||||||
|
if (res == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
memset(sb->s_uuid.b, 0, UUID_SIZE);
|
||||||
|
ofs->config.uuid = OVL_UUID_NULL;
|
||||||
|
pr_warn("failed to %s uuid (%pd2, err=%i); falling back to uuid=null.\n",
|
||||||
|
set ? "set" : "get", upperpath->dentry, res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
|
bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
|
||||||
enum ovl_xattr ox)
|
enum ovl_xattr ox)
|
||||||
{
|
{
|
||||||
@ -697,6 +757,7 @@ bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, const struct path *path,
|
|||||||
#define OVL_XATTR_IMPURE_POSTFIX "impure"
|
#define OVL_XATTR_IMPURE_POSTFIX "impure"
|
||||||
#define OVL_XATTR_NLINK_POSTFIX "nlink"
|
#define OVL_XATTR_NLINK_POSTFIX "nlink"
|
||||||
#define OVL_XATTR_UPPER_POSTFIX "upper"
|
#define OVL_XATTR_UPPER_POSTFIX "upper"
|
||||||
|
#define OVL_XATTR_UUID_POSTFIX "uuid"
|
||||||
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
|
#define OVL_XATTR_METACOPY_POSTFIX "metacopy"
|
||||||
#define OVL_XATTR_PROTATTR_POSTFIX "protattr"
|
#define OVL_XATTR_PROTATTR_POSTFIX "protattr"
|
||||||
|
|
||||||
@ -711,6 +772,7 @@ const char *const ovl_xattr_table[][2] = {
|
|||||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_IMPURE),
|
OVL_XATTR_TAB_ENTRY(OVL_XATTR_IMPURE),
|
||||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_NLINK),
|
OVL_XATTR_TAB_ENTRY(OVL_XATTR_NLINK),
|
||||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UPPER),
|
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UPPER),
|
||||||
|
OVL_XATTR_TAB_ENTRY(OVL_XATTR_UUID),
|
||||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
|
OVL_XATTR_TAB_ENTRY(OVL_XATTR_METACOPY),
|
||||||
OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR),
|
OVL_XATTR_TAB_ENTRY(OVL_XATTR_PROTATTR),
|
||||||
};
|
};
|
||||||
@ -1054,8 +1116,12 @@ err:
|
|||||||
return -EIO;
|
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;
|
int res;
|
||||||
|
|
||||||
@ -1063,7 +1129,8 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path)
|
|||||||
if (!S_ISREG(d_inode(path->dentry)->i_mode))
|
if (!S_ISREG(d_inode(path->dentry)->i_mode))
|
||||||
return 0;
|
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 < 0) {
|
||||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1077,12 +1144,48 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path)
|
|||||||
goto out;
|
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:
|
out:
|
||||||
pr_warn_ratelimited("failed to get metacopy (%i)\n", res);
|
pr_warn_ratelimited("failed to get metacopy (%i)\n", res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d, struct ovl_metacopy *metacopy)
|
||||||
|
{
|
||||||
|
size_t len = metacopy->len;
|
||||||
|
|
||||||
|
/* If no flags or digest fall back to empty metacopy file */
|
||||||
|
if (metacopy->version == 0 && metacopy->flags == 0 && metacopy->digest_algo == 0)
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
return ovl_check_setxattr(ofs, d, OVL_XATTR_METACOPY,
|
||||||
|
metacopy, len, -EOPNOTSUPP);
|
||||||
|
}
|
||||||
|
|
||||||
bool ovl_is_metacopy_dentry(struct dentry *dentry)
|
bool ovl_is_metacopy_dentry(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct ovl_entry *oe = OVL_E(dentry);
|
struct ovl_entry *oe = OVL_E(dentry);
|
||||||
@ -1145,6 +1248,112 @@ err_free:
|
|||||||
return ERR_PTR(res);
|
return ERR_PTR(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Call with mounter creds as it may open the file */
|
||||||
|
int ovl_ensure_verity_loaded(struct path *datapath)
|
||||||
|
{
|
||||||
|
struct inode *inode = d_inode(datapath->dentry);
|
||||||
|
struct file *filp;
|
||||||
|
|
||||||
|
if (!fsverity_active(inode) && IS_VERITY(inode)) {
|
||||||
|
/*
|
||||||
|
* If this inode was not yet opened, the verity info hasn't been
|
||||||
|
* loaded yet, so we need to do that here to force it into memory.
|
||||||
|
*/
|
||||||
|
filp = kernel_file_open(datapath, O_RDONLY, inode, current_cred());
|
||||||
|
if (IS_ERR(filp))
|
||||||
|
return PTR_ERR(filp);
|
||||||
|
fput(filp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ovl_validate_verity(struct ovl_fs *ofs,
|
||||||
|
struct path *metapath,
|
||||||
|
struct path *datapath)
|
||||||
|
{
|
||||||
|
struct ovl_metacopy metacopy_data;
|
||||||
|
u8 actual_digest[FS_VERITY_MAX_DIGEST_SIZE];
|
||||||
|
int xattr_digest_size, digest_size;
|
||||||
|
int xattr_size, err;
|
||||||
|
u8 verity_algo;
|
||||||
|
|
||||||
|
if (!ofs->config.verity_mode ||
|
||||||
|
/* Verity only works on regular files */
|
||||||
|
!S_ISREG(d_inode(metapath->dentry)->i_mode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
xattr_size = ovl_check_metacopy_xattr(ofs, metapath, &metacopy_data);
|
||||||
|
if (xattr_size < 0)
|
||||||
|
return xattr_size;
|
||||||
|
|
||||||
|
if (!xattr_size || !metacopy_data.digest_algo) {
|
||||||
|
if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
|
||||||
|
pr_warn_ratelimited("metacopy file '%pd' has no digest specified\n",
|
||||||
|
metapath->dentry);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
xattr_digest_size = ovl_metadata_digest_size(&metacopy_data);
|
||||||
|
|
||||||
|
err = ovl_ensure_verity_loaded(datapath);
|
||||||
|
if (err < 0) {
|
||||||
|
pr_warn_ratelimited("lower file '%pd' failed to load fs-verity info\n",
|
||||||
|
datapath->dentry);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
digest_size = fsverity_get_digest(d_inode(datapath->dentry), actual_digest,
|
||||||
|
&verity_algo, NULL);
|
||||||
|
if (digest_size == 0) {
|
||||||
|
pr_warn_ratelimited("lower file '%pd' has no fs-verity digest\n", datapath->dentry);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xattr_digest_size != digest_size ||
|
||||||
|
metacopy_data.digest_algo != verity_algo ||
|
||||||
|
memcmp(metacopy_data.digest, actual_digest, xattr_digest_size) != 0) {
|
||||||
|
pr_warn_ratelimited("lower file '%pd' has the wrong fs-verity digest\n",
|
||||||
|
datapath->dentry);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ovl_get_verity_digest(struct ovl_fs *ofs, struct path *src,
|
||||||
|
struct ovl_metacopy *metacopy)
|
||||||
|
{
|
||||||
|
int err, digest_size;
|
||||||
|
|
||||||
|
if (!ofs->config.verity_mode || !S_ISREG(d_inode(src->dentry)->i_mode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = ovl_ensure_verity_loaded(src);
|
||||||
|
if (err < 0) {
|
||||||
|
pr_warn_ratelimited("lower file '%pd' failed to load fs-verity info\n",
|
||||||
|
src->dentry);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
digest_size = fsverity_get_digest(d_inode(src->dentry),
|
||||||
|
metacopy->digest, &metacopy->digest_algo, NULL);
|
||||||
|
if (digest_size == 0 ||
|
||||||
|
WARN_ON_ONCE(digest_size > FS_VERITY_MAX_DIGEST_SIZE)) {
|
||||||
|
if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
|
||||||
|
pr_warn_ratelimited("lower file '%pd' has no fs-verity digest\n",
|
||||||
|
src->dentry);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
metacopy->len += digest_size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ovl_sync_status() - Check fs sync status for volatile mounts
|
* ovl_sync_status() - Check fs sync status for volatile mounts
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user