* [LTP] [PATCH v2 1/3] lib/tst_device.c: factor out btrfs-specific logic from tst_find_backing_dev
2025-02-11 21:42 [LTP] [PATCH v2 0/3] tst_device: add support for overlayfs Jeff Moyer
@ 2025-02-11 21:42 ` Jeff Moyer
2025-02-11 23:52 ` Petr Vorel
2025-02-11 21:42 ` [LTP] [PATCH v2 2/3] lib/tst_device.c: check for BTRFS_SUPER_MAGIC instead of device major of 0 Jeff Moyer
2025-02-11 21:42 ` [LTP] [PATCH v2 3/3] tst_find_backing_dev(): add support for overlayfs Jeff Moyer
2 siblings, 1 reply; 9+ messages in thread
From: Jeff Moyer @ 2025-02-11 21:42 UTC (permalink / raw)
To: ltp
In preparation for handling overlayfs (which also has a major device
number of 0), factor out the btrfs logic.
Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
---
lib/tst_device.c | 104 +++++++++++++++++++++++++----------------------
1 file changed, 55 insertions(+), 49 deletions(-)
diff --git a/lib/tst_device.c b/lib/tst_device.c
index 723f6ca06..70234a83c 100644
--- a/lib/tst_device.c
+++ b/lib/tst_device.c
@@ -520,20 +520,68 @@ unsigned long tst_dev_bytes_written(const char *dev)
return dev_bytes_written;
}
+static void btrfs_get_uevent_path(char *tmp_path, char *uevent_path)
+{
+ int fd;
+ struct btrfs_ioctl_fs_info_args args = {0};
+ char btrfs_uuid_str[UUID_STR_SZ];
+ struct dirent *d;
+ char bdev_path[PATH_MAX];
+ DIR *dir;
+
+ tst_resm(TINFO, "Use BTRFS specific strategy");
+
+ fd = SAFE_OPEN(NULL, tmp_path, O_DIRECTORY);
+ if (!ioctl(fd, BTRFS_IOC_FS_INFO, &args)) {
+ sprintf(btrfs_uuid_str,
+ UUID_FMT,
+ args.fsid[0], args.fsid[1],
+ args.fsid[2], args.fsid[3],
+ args.fsid[4], args.fsid[5],
+ args.fsid[6], args.fsid[7],
+ args.fsid[8], args.fsid[9],
+ args.fsid[10], args.fsid[11],
+ args.fsid[12], args.fsid[13],
+ args.fsid[14], args.fsid[15]);
+ sprintf(bdev_path,
+ "/sys/fs/btrfs/%s/devices", btrfs_uuid_str);
+ } else {
+ if (errno == ENOTTY)
+ tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl failed. Is %s on a tmpfs?", tmp_path);
+
+ tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl on %s failed.", tmp_path);
+ }
+ SAFE_CLOSE(NULL, fd);
+
+ dir = SAFE_OPENDIR(NULL, bdev_path);
+ while ((d = SAFE_READDIR(NULL, dir))) {
+ if (d->d_name[0] != '.')
+ break;
+ }
+
+ uevent_path[0] = '\0';
+
+ if (d) {
+ sprintf(uevent_path, "%s/%s/uevent",
+ bdev_path, d->d_name);
+ } else {
+ tst_brkm(TBROK | TERRNO, NULL, "No backing device found while looking in %s.", bdev_path);
+ }
+
+ if (SAFE_READDIR(NULL, dir))
+ tst_resm(TINFO, "Warning: used first of multiple backing device.");
+
+ SAFE_CLOSEDIR(NULL, dir);
+}
+
__attribute__((nonnull))
void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
{
struct stat buf;
- struct btrfs_ioctl_fs_info_args args = {0};
- struct dirent *d;
char uevent_path[PATH_MAX+PATH_MAX+10]; //10 is for the static uevent path
char dev_name[NAME_MAX];
- char bdev_path[PATH_MAX];
char tmp_path[PATH_MAX];
- char btrfs_uuid_str[UUID_STR_SZ];
- DIR *dir;
unsigned int dev_major, dev_minor;
- int fd;
if (stat(path, &buf) < 0)
tst_brkm(TWARN | TERRNO, NULL, "stat() failed");
@@ -548,49 +596,7 @@ void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
*dev = '\0';
if (dev_major == 0) {
- tst_resm(TINFO, "Use BTRFS specific strategy");
-
- fd = SAFE_OPEN(NULL, tmp_path, O_DIRECTORY);
- if (!ioctl(fd, BTRFS_IOC_FS_INFO, &args)) {
- sprintf(btrfs_uuid_str,
- UUID_FMT,
- args.fsid[0], args.fsid[1],
- args.fsid[2], args.fsid[3],
- args.fsid[4], args.fsid[5],
- args.fsid[6], args.fsid[7],
- args.fsid[8], args.fsid[9],
- args.fsid[10], args.fsid[11],
- args.fsid[12], args.fsid[13],
- args.fsid[14], args.fsid[15]);
- sprintf(bdev_path,
- "/sys/fs/btrfs/%s/devices", btrfs_uuid_str);
- } else {
- if (errno == ENOTTY)
- tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl failed. Is %s on a tmpfs?", path);
-
- tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl on %s failed.", tmp_path);
- }
- SAFE_CLOSE(NULL, fd);
-
- dir = SAFE_OPENDIR(NULL, bdev_path);
- while ((d = SAFE_READDIR(NULL, dir))) {
- if (d->d_name[0] != '.')
- break;
- }
-
- uevent_path[0] = '\0';
-
- if (d) {
- sprintf(uevent_path, "%s/%s/uevent",
- bdev_path, d->d_name);
- } else {
- tst_brkm(TBROK | TERRNO, NULL, "No backing device found while looking in %s.", bdev_path);
- }
-
- if (SAFE_READDIR(NULL, dir))
- tst_resm(TINFO, "Warning: used first of multiple backing device.");
-
- SAFE_CLOSEDIR(NULL, dir);
+ btrfs_get_uevent_path(tmp_path, uevent_path);
} else {
tst_resm(TINFO, "Use uevent strategy");
sprintf(uevent_path,
--
2.43.5
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 9+ messages in thread* [LTP] [PATCH v2 2/3] lib/tst_device.c: check for BTRFS_SUPER_MAGIC instead of device major of 0
2025-02-11 21:42 [LTP] [PATCH v2 0/3] tst_device: add support for overlayfs Jeff Moyer
2025-02-11 21:42 ` [LTP] [PATCH v2 1/3] lib/tst_device.c: factor out btrfs-specific logic from tst_find_backing_dev Jeff Moyer
@ 2025-02-11 21:42 ` Jeff Moyer
2025-02-11 23:52 ` Petr Vorel
2025-02-11 21:42 ` [LTP] [PATCH v2 3/3] tst_find_backing_dev(): add support for overlayfs Jeff Moyer
2 siblings, 1 reply; 9+ messages in thread
From: Jeff Moyer @ 2025-02-11 21:42 UTC (permalink / raw)
To: ltp
stat() may return a major number of 0 in st_dev for any number of
pseudo file systems. Check for the exact file system instead. There
should be no change in behavior with this patch.
Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
---
lib/tst_device.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/lib/tst_device.c b/lib/tst_device.c
index 70234a83c..744e08a68 100644
--- a/lib/tst_device.c
+++ b/lib/tst_device.c
@@ -17,6 +17,8 @@
#include <sys/sysmacros.h>
#include <linux/btrfs.h>
#include <linux/limits.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
#include "lapi/syscalls.h"
#include "test.h"
#include "safe_macros.h"
@@ -546,9 +548,6 @@ static void btrfs_get_uevent_path(char *tmp_path, char *uevent_path)
sprintf(bdev_path,
"/sys/fs/btrfs/%s/devices", btrfs_uuid_str);
} else {
- if (errno == ENOTTY)
- tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl failed. Is %s on a tmpfs?", tmp_path);
-
tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl on %s failed.", tmp_path);
}
SAFE_CLOSE(NULL, fd);
@@ -578,6 +577,7 @@ __attribute__((nonnull))
void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
{
struct stat buf;
+ struct statfs fsbuf;
char uevent_path[PATH_MAX+PATH_MAX+10]; //10 is for the static uevent path
char dev_name[NAME_MAX];
char tmp_path[PATH_MAX];
@@ -595,8 +595,13 @@ void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
dev_minor = minor(buf.st_dev);
*dev = '\0';
- if (dev_major == 0) {
+ if (statfs(path, &fsbuf) < 0)
+ tst_brkm(TWARN | TERRNO, NULL, "statfs() failed");
+
+ if (fsbuf.f_type == BTRFS_SUPER_MAGIC) {
btrfs_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 {
tst_resm(TINFO, "Use uevent strategy");
sprintf(uevent_path,
--
2.43.5
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [LTP] [PATCH v2 2/3] lib/tst_device.c: check for BTRFS_SUPER_MAGIC instead of device major of 0
2025-02-11 21:42 ` [LTP] [PATCH v2 2/3] lib/tst_device.c: check for BTRFS_SUPER_MAGIC instead of device major of 0 Jeff Moyer
@ 2025-02-11 23:52 ` Petr Vorel
2025-02-12 17:05 ` Jeff Moyer
0 siblings, 1 reply; 9+ messages in thread
From: Petr Vorel @ 2025-02-11 23:52 UTC (permalink / raw)
To: Jeff Moyer; +Cc: ltp
Hi Jeff,
> stat() may return a major number of 0 in st_dev for any number of
> pseudo file systems. Check for the exact file system instead. There
> should be no change in behavior with this patch.
> Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
> ---
> lib/tst_device.c | 13 +++++++++----
> 1 file changed, 9 insertions(+), 4 deletions(-)
> diff --git a/lib/tst_device.c b/lib/tst_device.c
> index 70234a83c..744e08a68 100644
> --- a/lib/tst_device.c
> +++ b/lib/tst_device.c
> @@ -17,6 +17,8 @@
> #include <sys/sysmacros.h>
> #include <linux/btrfs.h>
> #include <linux/limits.h>
> +#include <sys/vfs.h>
This just includes <sys/statfs.h>, could you please use it (if it's really
needed).
> +#include <linux/magic.h>
Please don't use it. We have TST_BTRFS_MAGIC and TST_OVERLAYFS_MAGIC in
tst_fs.h. If you ask why we mirror magic it was (probably) for compatibility
with very old kernel headers.
> #include "lapi/syscalls.h"
> #include "test.h"
> #include "safe_macros.h"
> @@ -546,9 +548,6 @@ static void btrfs_get_uevent_path(char *tmp_path, char *uevent_path)
> sprintf(bdev_path,
> "/sys/fs/btrfs/%s/devices", btrfs_uuid_str);
> } else {
> - if (errno == ENOTTY)
> - tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl failed. Is %s on a tmpfs?", tmp_path);
> -
> tst_brkm(TBROK | TERRNO, NULL, "BTRFS ioctl on %s failed.", tmp_path);
> }
> SAFE_CLOSE(NULL, fd);
> @@ -578,6 +577,7 @@ __attribute__((nonnull))
> void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
> {
> struct stat buf;
> + struct statfs fsbuf;
> char uevent_path[PATH_MAX+PATH_MAX+10]; //10 is for the static uevent path
> char dev_name[NAME_MAX];
> char tmp_path[PATH_MAX];
> @@ -595,8 +595,13 @@ void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
> dev_minor = minor(buf.st_dev);
> *dev = '\0';
> - if (dev_major == 0) {
> + if (statfs(path, &fsbuf) < 0)
> + tst_brkm(TWARN | TERRNO, NULL, "statfs() failed");
Please use SAFE_STATFS() here.
> +
> + if (fsbuf.f_type == BTRFS_SUPER_MAGIC) {
We have TST_BTRFS_MAGIC tst_fs.h.
Kind regards,
Petr
> btrfs_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 {
> tst_resm(TINFO, "Use uevent strategy");
> sprintf(uevent_path,
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [LTP] [PATCH v2 2/3] lib/tst_device.c: check for BTRFS_SUPER_MAGIC instead of device major of 0
2025-02-11 23:52 ` Petr Vorel
@ 2025-02-12 17:05 ` Jeff Moyer
2025-02-12 20:10 ` Petr Vorel
0 siblings, 1 reply; 9+ messages in thread
From: Jeff Moyer @ 2025-02-12 17:05 UTC (permalink / raw)
To: Petr Vorel; +Cc: ltp
Hi, Petr,
Thanks a lot for the review. I just have one question, below:
Petr Vorel <pvorel@suse.cz> writes:
>> @@ -595,8 +595,13 @@ void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
>> dev_minor = minor(buf.st_dev);
>> *dev = '\0';
>
>> - if (dev_major == 0) {
>> + if (statfs(path, &fsbuf) < 0)
>> + tst_brkm(TWARN | TERRNO, NULL, "statfs() failed");
> Please use SAFE_STATFS() here.
SAFE_STATFS appears to be part of the new safe macros
(tst_safe_macros_inline.h), whereas tst_device.c includes the older
macros via #include "safe_macros.h". How would you like me to proceed?
I could add a SAFE_STATFS to the old macros, or I could submit a prep
patch that converts tst_device to the new macros. Or something else?
Thanks!
Jeff
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [LTP] [PATCH v2 2/3] lib/tst_device.c: check for BTRFS_SUPER_MAGIC instead of device major of 0
2025-02-12 17:05 ` Jeff Moyer
@ 2025-02-12 20:10 ` Petr Vorel
0 siblings, 0 replies; 9+ messages in thread
From: Petr Vorel @ 2025-02-12 20:10 UTC (permalink / raw)
To: Jeff Moyer; +Cc: ltp
Hi Jeff,
> Hi, Petr,
> Thanks a lot for the review. I just have one question, below:
> Petr Vorel <pvorel@suse.cz> writes:
> >> @@ -595,8 +595,13 @@ void tst_find_backing_dev(const char *path, char *dev, size_t dev_size)
> >> dev_minor = minor(buf.st_dev);
> >> *dev = '\0';
> >> - if (dev_major == 0) {
> >> + if (statfs(path, &fsbuf) < 0)
> >> + tst_brkm(TWARN | TERRNO, NULL, "statfs() failed");
> > Please use SAFE_STATFS() here.
> SAFE_STATFS appears to be part of the new safe macros
> (tst_safe_macros_inline.h), whereas tst_device.c includes the older
> macros via #include "safe_macros.h". How would you like me to proceed?
> I could add a SAFE_STATFS to the old macros, or I could submit a prep
> patch that converts tst_device to the new macros. Or something else?
I'm sorry to overlook this. Your original is perfect then.
Kind regards,
Petr
> Thanks!
> Jeff
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 9+ messages in thread
* [LTP] [PATCH v2 3/3] tst_find_backing_dev(): add support for overlayfs
2025-02-11 21:42 [LTP] [PATCH v2 0/3] tst_device: add support for overlayfs Jeff Moyer
2025-02-11 21:42 ` [LTP] [PATCH v2 1/3] lib/tst_device.c: factor out btrfs-specific logic from tst_find_backing_dev Jeff Moyer
2025-02-11 21:42 ` [LTP] [PATCH v2 2/3] lib/tst_device.c: check for BTRFS_SUPER_MAGIC instead of device major of 0 Jeff Moyer
@ 2025-02-11 21:42 ` Jeff Moyer
2025-02-12 0:19 ` Petr Vorel
2 siblings, 1 reply; 9+ messages in thread
From: Jeff Moyer @ 2025-02-11 21:42 UTC (permalink / raw)
To: ltp
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.
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.
---
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);
+ 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.",
+ 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.
+ *
+ * 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.
+ */
+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) {
+ 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 {
--
2.43.5
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [LTP] [PATCH v2 3/3] tst_find_backing_dev(): add support for overlayfs
2025-02-11 21:42 ` [LTP] [PATCH v2 3/3] tst_find_backing_dev(): add support for overlayfs Jeff Moyer
@ 2025-02-12 0:19 ` Petr Vorel
0 siblings, 0 replies; 9+ messages in thread
From: Petr Vorel @ 2025-02-12 0:19 UTC (permalink / raw)
To: Jeff Moyer; +Cc: ltp
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 {
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 9+ messages in thread