[LTP] [PATCH v2 3/3] tst_find_backing_dev(): add support for overlayfs
Petr Vorel
pvorel@suse.cz
Wed Feb 12 01:19:25 CET 2025
Hi Jeff,
> Add checks for overlayfs in tst_find_backing_dev. As with btrfs, only
> a single device is checked (the upper one) and returned from
> tst_find_backing_dev().
> The implementation uses both /proc/self/mountinfo and /proc/self/mounts.
> The former is used to map a device to a mountpoint, and the latter is
> used to get the file system options for the mountpoint. All of the
> information is present in mountinfo, but the file format is more complex,
> and there are no glibc helpers for parsing it.
> The '#define _GNU_SOURCE' was added for the use of the strchrnul(3)
> function.
+1 (strangely not needed for current glibc in Tumbleweed, but at least musl
requires it).
> Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
> ---
> v2: Don't use libmount. Instead, map from device number to mount-point
> using /proc/self/mountinfo, and then use the mntent.h helpers to get
> the mount options for the mountpoint from /proc/self/mounts.
Thanks a lot!
> ---
> lib/tst_device.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 134 insertions(+)
> diff --git a/lib/tst_device.c b/lib/tst_device.c
> index 744e08a68..4730396b4 100644
> --- a/lib/tst_device.c
> +++ b/lib/tst_device.c
> @@ -3,6 +3,7 @@
> * Copyright (C) 2014 Cyril Hrubis chrubis@suse.cz
> */
> +#define _GNU_SOURCE
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <sys/ioctl.h>
> @@ -573,6 +574,137 @@ static void btrfs_get_uevent_path(char *tmp_path, char *uevent_path)
> SAFE_CLOSEDIR(NULL, dir);
> }
> +static char *overlay_mount_from_dev(dev_t dev)
> +{
> + unsigned dev_major, dev_minor, mnt_major, mnt_minor;
> + FILE *fp;
> + char line[PATH_MAX];
> + char *mountpoint;
> + int ret;
> +
> + dev_major = major(dev);
> + dev_minor = minor(dev);
> +
> + fp = SAFE_FOPEN(NULL, "/proc/self/mountinfo", "r");
> + while (fgets(line, sizeof(line), fp) != NULL) {
> + ret = sscanf(line, "%*d %*d %u:%u %*s %ms",
> + &mnt_major, &mnt_minor, &mountpoint);
Can you please use SAFE_SSCANF() (our extra checks)?
> + if (ret != 3)
> + tst_brkm(TBROK, NULL,
> + "failed to parse mountinfo line: \"%s\"",
> + line);
> + if (mnt_major == dev_major && mnt_minor == dev_minor)
> + break;
> + free(mountpoint);
> + mountpoint = NULL;
> + }
> + SAFE_FCLOSE(NULL, fp);
> + if (!mountpoint)
> + tst_brkm(TBROK, NULL,
> + "Unable to find mount entry for device %u:%u\n",
> + dev_major, dev_minor);
> +
> + return mountpoint;
> +}
> +
> +static char *overlay_get_upperdir(char *mountpoint)
> +{
> + FILE *mntf;
> + struct mntent *mnt;
> + char *optstr, *optstart, *optend;
> + char *upperdir = NULL;
> +
> + mntf = setmntent("/proc/self/mounts", "r");
> + if (!mntf)
> + tst_brkm(TBROK | TERRNO, NULL, "Can't open /proc/self/mounts");
> +
> + while ((mnt = getmntent(mntf)) != NULL) {
> + if (strncmp(mnt->mnt_dir, mountpoint, strlen(mountpoint)))
> + continue;
> +
> + if (strncmp(mnt->mnt_type, "overlay", strlen("overlay")))
> + tst_brkm(TBROK, NULL,
> + "expected overlay file system on mount point "\
> + "\"%s\", but it is of type %s.",
nit: I slightly prefer not splitting string when together is not that long.
> + mountpoint, mnt->mnt_type);
> +
> + optstr = hasmntopt(mnt, "upperdir");
> + if (optstr) {
> + optstart = strchr(optstr, '=');
> + optstart++;
> + optend = strchrnul(optstr, ',');
> + upperdir = calloc(optend - optstart + 1, 1);
> + memcpy(upperdir, optstart, optend - optstart);
> + break;
> + } else {
> + tst_brkm(TBROK, NULL,
> + "mount point %s does not contain an upperdir",
> + mountpoint);
> + }
> + }
> + endmntent(mntf);
> +
> + if (!upperdir)
> + tst_brkm(TBROK, NULL,
> + "Unable to find mount point \"%s\" in mount table",
> + mountpoint);
> +
> + return upperdir;
> +}
> +
> +/*
> + * To get from a file or directory on an overlayfs to a device
> + * for an underlying mountpoint requires the following steps:
> + *
> + * 1. stat() the pathname and pick out st_dev.
> + * 2. use the st_dev to look up the mount point of the file
> + * system in /proc/self/mountinfo
> + *
> + * Because 'mountinfo' is a much more complicated file format than
> + * 'mounts', we switch to looking up the mount point in /proc/mounts,
> + * and use the mntent.h helpers to parse the entries.
+1
> + *
> + * 3. Using getmntent(), find the entry for the mount point identified
> + * in step 2.
> + * 4. Call hasmntopt() to find the upperdir option, and parse that
> + * option to get to the path name for the upper directory.
> + * 5. Call stat on the upper directory. This should now contain
> + * the major and minor number for the underlying device (assuming
> + * that there aren't nested overlay file systems).
> + * 6. Populate the uevent path.
> + *
> + * Example /proc/self/mountinfo line for overlayfs:
> + * 471 69 0:48 / /tmp rw,relatime shared:242 - overlay overlay rw,seclabel,lowerdir=/tmp,upperdir=/mnt/upper/upper,workdir=/mnt/upper/work,uuid=null
> + *
> + * See section 3.5 of the kernel's Documentation/filesystems/proc.rst file
> + * for a detailed explanation of the mountinfo format.
+1, thanks for an example and docs.
> + */
> +static void overlay_get_uevent_path(char *tmp_path, char *uevent_path)
> +{
> + int ret;
> + struct stat st;
> + char *mountpoint, *upperdir;
> +
> + tst_resm(TINFO, "Use OVERLAYFS specific strategy");
> +
> + ret = stat(tmp_path, &st);
> + if (ret)
> + tst_brkm(TBROK | TERRNO, NULL, "stat failed");
> +
> + mountpoint = overlay_mount_from_dev(st.st_dev);
> + upperdir = overlay_get_upperdir(mountpoint);
> + free(mountpoint);
> +
> + ret = stat(upperdir, &st);
> + free(upperdir);
> + if (ret)
> + tst_brkm(TBROK | TERRNO, NULL, "stat failed");
> +
> + tst_resm(TINFO, "Warning: used first of multiple backing devices.");
> + sprintf(uevent_path, "/sys/dev/block/%d:%d/uevent",
> + major(st.st_dev), minor(st.st_dev));
> +}
> +
> __attribute__((nonnull))
> void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
> {
> @@ -600,6 +732,8 @@ void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
> if (fsbuf.f_type == BTRFS_SUPER_MAGIC) {
> btrfs_get_uevent_path(tmp_path, uevent_path);
> + } else if (fsbuf.f_type == OVERLAYFS_SUPER_MAGIC) {
As I noted, we have ST_BTRFS_MAGIC and TST_OVERLAYFS_MAGIC in tst_fs.h.
And the reason is now clear, old headers we still support does not have it:
tst_device.c:735:29: error: 'OVERLAYFS_SUPER_MAGIC' undeclared (first use in this function)
https://github.com/pevik/ltp/actions/runs/13274657394/job/37061743549
With this fixed:
Reviewed-by: Petr Vorel <pvorel@suse.cz>
Kind regards,
Petr
> + overlay_get_uevent_path(tmp_path, uevent_path);
> } else if (dev_major == 0) {
> tst_brkm(TBROK, NULL, "%s resides on an unsupported pseudo-file system.", path);
> } else {
More information about the ltp
mailing list