ovl: restrict lower null uuid for "xino=auto"

Commit a888db3101 ("ovl: fix regression with re-formatted lower
squashfs") attempted to fix a regression with existing setups that
use a practice that we are trying to discourage.

The discourage part was described this way in the commit message:
"To avoid the reported regression while still allowing the new features
 with single lower squashfs, do not allow decoding origin with lower null
 uuid unless user opted-in to one of the new features that require
 following the lower inode of non-dir upper (index, xino, metacopy)."

The three mentioned features are disabled by default in Kconfig, so
it was assumed that if they are enabled, the user opted-in for them.
Apparently, distros started to configure CONFIG_OVERLAY_FS_XINO_AUTO=y
some time ago, so users upgrading their kernels can still be affected
by said regression even though they never opted-in for any new feature.

To fix this, treat "xino=on" as "user opted-in", but not "xino=auto".
Since we are changing the behavior of "xino=auto" to no longer follow
to lower origin with null uuid, take this one step further and disable
xino in that corner case.  To be consistent, disable xino also in cases
of lower fs without file handle support and upper fs without xattr
support.

Update documentation w.r.t the new "xino=auto" behavior and fix the out
dated bits of documentation regarding "xino" and regarding offline
modifications to lower layers.

Link: https://lore.kernel.org/linux-unionfs/b36a429d7c563730c28d763d4d57a6fc30508a4f.1615216996.git.kevin@kevinlocke.name/
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Amir Goldstein 2021-03-09 18:26:54 +02:00 committed by Miklos Szeredi
parent e21a6c57e3
commit b0e0f69731
2 changed files with 47 additions and 20 deletions

View File

@ -40,17 +40,17 @@ On 64bit systems, even if all overlay layers are not on the same
underlying filesystem, the same compliant behavior could be achieved underlying filesystem, the same compliant behavior could be achieved
with the "xino" feature. The "xino" feature composes a unique object with the "xino" feature. The "xino" feature composes a unique object
identifier from the real object st_ino and an underlying fsid index. identifier from the real object st_ino and an underlying fsid index.
The "xino" feature uses the high inode number bits for fsid, because the
If all underlying filesystems support NFS file handles and export file underlying filesystems rarely use the high inode number bits. In case
handles with 32bit inode number encoding (e.g. ext4), overlay filesystem
will use the high inode number bits for fsid. Even when the underlying
filesystem uses 64bit inode numbers, users can still enable the "xino"
feature with the "-o xino=on" overlay mount option. That is useful for the
case of underlying filesystems like xfs and tmpfs, which use 64bit inode
numbers, but are very unlikely to use the high inode number bits. In case
the underlying inode number does overflow into the high xino bits, overlay the underlying inode number does overflow into the high xino bits, overlay
filesystem will fall back to the non xino behavior for that inode. filesystem will fall back to the non xino behavior for that inode.
The "xino" feature can be enabled with the "-o xino=on" overlay mount option.
If all underlying filesystems support NFS file handles, the value of st_ino
for overlay filesystem objects is not only unique, but also persistent over
the lifetime of the filesystem. The "-o xino=auto" overlay mount option
enables the "xino" feature only if the persistent st_ino requirement is met.
The following table summarizes what can be expected in different overlay The following table summarizes what can be expected in different overlay
configurations. configurations.
@ -66,14 +66,13 @@ Inode properties
| All layers | Y | Y | Y | Y | Y | Y | Y | Y | | All layers | Y | Y | Y | Y | Y | Y | Y | Y |
| on same fs | | | | | | | | | | on same fs | | | | | | | | |
+--------------+-----+------+-----+------+--------+--------+--------+-------+ +--------------+-----+------+-----+------+--------+--------+--------+-------+
| Layers not | N | Y | Y | N | N | Y | N | Y | | Layers not | N | N | Y | N | N | Y | N | Y |
| on same fs, | | | | | | | | | | on same fs, | | | | | | | | |
| xino=off | | | | | | | | | | xino=off | | | | | | | | |
+--------------+-----+------+-----+------+--------+--------+--------+-------+ +--------------+-----+------+-----+------+--------+--------+--------+-------+
| xino=on/auto | Y | Y | Y | Y | Y | Y | Y | Y | | xino=on/auto | Y | Y | Y | Y | Y | Y | Y | Y |
| | | | | | | | | |
+--------------+-----+------+-----+------+--------+--------+--------+-------+ +--------------+-----+------+-----+------+--------+--------+--------+-------+
| xino=on/auto,| N | Y | Y | N | N | Y | N | Y | | xino=on/auto,| N | N | Y | N | N | Y | N | Y |
| ino overflow | | | | | | | | | | ino overflow | | | | | | | | |
+--------------+-----+------+-----+------+--------+--------+--------+-------+ +--------------+-----+------+-----+------+--------+--------+--------+-------+
@ -81,7 +80,6 @@ Inode properties
/proc files, such as /proc/locks and /proc/self/fdinfo/<fd> of an inotify /proc files, such as /proc/locks and /proc/self/fdinfo/<fd> of an inotify
file descriptor. file descriptor.
Upper and Lower Upper and Lower
--------------- ---------------
@ -461,7 +459,7 @@ enough free bits in the inode number, then overlayfs will not be able to
guarantee that the values of st_ino and st_dev returned by stat(2) and the guarantee that the values of st_ino and st_dev returned by stat(2) and the
value of d_ino returned by readdir(3) will act like on a normal filesystem. value of d_ino returned by readdir(3) will act like on a normal filesystem.
E.g. the value of st_dev may be different for two objects in the same E.g. the value of st_dev may be different for two objects in the same
overlay filesystem and the value of st_ino for directory objects may not be overlay filesystem and the value of st_ino for filesystem objects may not be
persistent and could change even while the overlay filesystem is mounted, as persistent and could change even while the overlay filesystem is mounted, as
summarized in the `Inode properties`_ table above. summarized in the `Inode properties`_ table above.
@ -476,7 +474,7 @@ a crash or deadlock.
Offline changes, when the overlay is not mounted, are allowed to the Offline changes, when the overlay is not mounted, are allowed to the
upper tree. Offline changes to the lower tree are only allowed if the upper tree. Offline changes to the lower tree are only allowed if the
"metadata only copy up", "inode index", and "redirect_dir" features "metadata only copy up", "inode index", "xino" and "redirect_dir" features
have not been used. If the lower tree is modified and any of these have not been used. If the lower tree is modified and any of these
features has been used, the behavior of the overlay is undefined, features has been used, the behavior of the overlay is undefined,
though it will not result in a crash or deadlock. though it will not result in a crash or deadlock.

View File

@ -945,6 +945,16 @@ 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);
} }
/*
* Decoding origin file handle is required for persistent st_ino.
* Without persistent st_ino, xino=auto falls back to xino=off.
*/
if (ofs->config.xino == OVL_XINO_AUTO &&
ofs->config.upperdir && !fh_type) {
ofs->config.xino = OVL_XINO_OFF;
pr_warn("fs on '%s' does not support file handles, falling back to xino=off.\n",
name);
}
/* Check if lower fs has 32bit inode numbers */ /* Check if lower fs has 32bit inode numbers */
if (fh_type != FILEID_INO32_GEN) if (fh_type != FILEID_INO32_GEN)
@ -1401,9 +1411,19 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
err = ovl_do_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1); err = ovl_do_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1);
if (err) { if (err) {
ofs->noxattr = true; ofs->noxattr = true;
if (ofs->config.index || ofs->config.metacopy) {
ofs->config.index = false; ofs->config.index = false;
ofs->config.metacopy = false; ofs->config.metacopy = false;
pr_warn("upper fs does not support xattr, falling back to index=off and metacopy=off.\n"); pr_warn("upper fs does not support xattr, falling back to index=off,metacopy=off.\n");
}
/*
* xattr support is required for persistent st_ino.
* Without persistent st_ino, xino=auto falls back to xino=off.
*/
if (ofs->config.xino == OVL_XINO_AUTO) {
ofs->config.xino = OVL_XINO_OFF;
pr_warn("upper fs does not support xattr, falling back to xino=off.\n");
}
err = 0; err = 0;
} else { } else {
ovl_do_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE); ovl_do_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE);
@ -1580,7 +1600,8 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
* user opted-in to one of the new features that require following the * user opted-in to one of the new features that require following the
* lower inode of non-dir upper. * lower inode of non-dir upper.
*/ */
if (!ofs->config.index && !ofs->config.metacopy && !ofs->config.xino && if (!ofs->config.index && !ofs->config.metacopy &&
ofs->config.xino != OVL_XINO_ON &&
uuid_is_null(uuid)) uuid_is_null(uuid))
return false; return false;
@ -1609,6 +1630,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
dev_t dev; dev_t dev;
int err; int err;
bool bad_uuid = false; bool bad_uuid = false;
bool warn = false;
for (i = 0; i < ofs->numfs; i++) { for (i = 0; i < ofs->numfs; i++) {
if (ofs->fs[i].sb == sb) if (ofs->fs[i].sb == sb)
@ -1617,13 +1639,20 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) { if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) {
bad_uuid = true; bad_uuid = true;
if (ofs->config.xino == OVL_XINO_AUTO) {
ofs->config.xino = OVL_XINO_OFF;
warn = true;
}
if (ofs->config.index || ofs->config.nfs_export) { if (ofs->config.index || ofs->config.nfs_export) {
ofs->config.index = false; ofs->config.index = false;
ofs->config.nfs_export = false; ofs->config.nfs_export = false;
pr_warn("%s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n", warn = true;
}
if (warn) {
pr_warn("%s uuid detected in lower fs '%pd2', falling back to xino=%s,index=off,nfs_export=off.\n",
uuid_is_null(&sb->s_uuid) ? "null" : uuid_is_null(&sb->s_uuid) ? "null" :
"conflicting", "conflicting",
path->dentry); path->dentry, ovl_xino_str[ofs->config.xino]);
} }
} }