[LTP] [PATCH v6 1/2] tst_find_backing_dev: Get dev name from /sys/dev/block/*/uevent

Alessandro Carminati
2022-11-09T19:43:58Z

In some minimal Linux, the /dev/root can be missing. The consequence of
this is that mountinfo doesn't contain the correct information. btrfs
file systems are yet another point of trouble for this function.

The unevent file in sysfs is another method to retrieve device info
using the sysfs.

btrfs file systems are special from the device name retrieval, and in
place of use of the minor/major they are approached by using the uuid.
In the end, btrfs strategy is a slightly modified version of the same
unevent strategy.

Non btrfs look in "/sys/dev/block/%d:%d/uevent" major, minor
btrfs look in /sys/fs/btrfs/%s/devices/%s/uevent, uuid, devname

The btrfs handling requires BTRFS specific ioctl for finding the
file system uuid, and for this reason, btrfs/ioctl.h is needed.

Signed-off-by: Richard Palethorpe <rpalethorpe@suse.com>
Signed-off-by: Alessandro Carminati <alessandro.carminati@gmail.com>
---
lib/tst_device.c | 95 +++++++++++++++++++++++++++++++++---------------
1 file changed, 66 insertions(+), 29 deletions(-)

diff --git a/lib/tst_device.c b/lib/tst_device.c
index 8419b80c3..85acdc4f2 100644
--- a/lib/tst_device.c
+++ b/lib/tst_device.c
@@ -33,6 +33,8 @@
#include <stdint.h>
#include <inttypes.h>
#include <sys/sysmacros.h>
+#include <linux/btrfs.h>
+#include <linux/limits.h>
#include "lapi/syscalls.h"
#include "test.h"
#include "safe_macros.h"
@@ -45,6 +47,8 @@

#define DEV_FILE "test_dev.img"
#define DEV_SIZE_MB 300u
+#define UUID_STR_SZ 37
+#define UUID_FMT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"

static char dev_path[1024];
static int device_acquired;
@@ -519,48 +523,81 @@ static int count_match_len(const char *first, const char *second)
void tst_find_backing_dev(const char *path, char *dev)
{
struct stat buf;
- FILE *file;
- char line[PATH_MAX];
- char *pre = NULL;
- char *next = NULL;
- unsigned int dev_major, dev_minor, line_mjr, line_mnr;
- unsigned int len, best_match_len = 1;
- char mnt_point[PATH_MAX];
+ 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");
+ strncpy(tmp_path, path, PATH_MAX-1);
+ tmp_path[PATH_MAX-1] = '\0';
+ if (S_ISREG(buf.st_mode))
+ dirname(tmp_path);

dev_major = major(buf.st_dev);
dev_minor = minor(buf.st_dev);
- file = SAFE_FOPEN(NULL, "/proc/self/mountinfo", "r");
*dev = '\0';

- while (fgets(line, sizeof(line), file)) {
- if (sscanf(line, "%*d %*d %d:%d %*s %s",
- &line_mjr, &line_mnr, mnt_point) != 3)
- continue;
-
- pre = strstr(line, " - ");
- pre = strtok_r(pre, " ", &next);
- pre = strtok_r(NULL, " ", &next);
- pre = strtok_r(NULL, " ", &next);
-
- if (line_mjr == dev_major && line_mnr == dev_minor) {
- strcpy(dev, pre);
- break;
+ 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 failed with %d.", errno);
}
-
- len = count_match_len(path, mnt_point);
- if (len > best_match_len) {
- strcpy(dev, pre);
- best_match_len = len;
+ 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 backining 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);
+ } else {
+
+ tst_resm(TINFO, "Use uevent strategy");
+ sprintf(uevent_path,
+ "/sys/dev/block/%d:%d/uevent", dev_major, dev_minor);
}

- SAFE_FCLOSE(NULL, file);
+ if (!access(uevent_path, R_OK)) {
+ FILE_LINES_SCANF(NULL, uevent_path, "DEVNAME=%s", dev_name);

- if (!*dev)
- tst_brkm(TBROK, NULL, "Cannot find block device for %s", path);
+ if (dev_name[0])
+ sprintf(dev, "/dev/%s", dev_name);
+ } else {
+ tst_brkm(TBROK, NULL, "uevent file (%s) access failed", uevent_path);
+ }

if (stat(dev, &buf) < 0)
tst_brkm(TWARN | TERRNO, NULL, "stat(%s) failed", dev);