Resolve ZFS dataset mounts through mountinfo, including legacy mountpoints
Some checks are pending
Build / build-mac (push) Waiting to run
Build / build-linux (push) Waiting to run
Build / build-arm32 (push) Waiting to run
Build / build-win (push) Waiting to run
Build / build-freebsd (push) Waiting to run
Build / build-musl (push) Waiting to run
CodeQL Advanced / Analyze (c-cpp) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run

This commit is contained in:
Andrea Mazzoleni 2026-09-25 17:46:03 +02:00
parent 1aa6d2b787
commit afba275a0e

View file

@ -971,9 +971,83 @@ bail:
return -1;
}
static int extract_zfs_mount(const char* resolved, char* dataset, size_t dataset_size, char* mount_point, size_t mount_point_size)
{
struct stat st;
if (stat(resolved, &st) != 0)
return -1;
FILE* fd = fopen("/proc/self/mountinfo", "r");
if (!fd)
return -1;
size_t best_len = 0;
while (1) {
char buf[PATH_MAX * 2 + 64];
char* id_map[8];
unsigned id_mac;
char* fs_map[8];
unsigned fs_mac;
unsigned mount_major;
unsigned mount_minor;
char* s = fgets(buf, sizeof(buf), fd);
if (s == 0)
break;
/* find the separator position */
char* separator = strstr(s, " - ");
if (!separator)
continue;
/* skip the separator */
*separator = 0;
separator += 3;
/* split the line */
id_mac = strsplit(id_map, 8, s, " \t\r\n", 0, 1);
fs_mac = strsplit(fs_map, 8, separator, " \t\r\n", 0, 1);
if (id_mac < 5 || fs_mac < 2)
continue;
/* mount point must contain the directory */
unescape_mount(id_map[4]);
const char* mp = id_map[4];
size_t mp_len = strlen(mp);
if (strncmp(resolved, mp, mp_len) != 0)
continue;
if (mp_len > 1 && resolved[mp_len] != '/' && resolved[mp_len] != 0)
continue;
/* match the device backing the path, not a hidden parent mount */
if (strcmp(fs_map[0], "zfs") != 0
|| sscanf(id_map[2], "%u:%u", &mount_major, &mount_minor) != 2
|| mount_major != major(st.st_dev) || mount_minor != minor(st.st_dev))
continue;
/* use the visible mount, including a legacy mount under a parent dataset */
if (mp_len > best_len) {
best_len = mp_len;
unescape_mount(fs_map[1]);
pathcpy(dataset, dataset_size, fs_map[1]);
pathcpy(mount_point, mount_point_size, mp);
}
}
int error = ferror(fd);
if (fclose(fd) != 0 || error)
return -1;
return best_len == 0 ? -1 : 0;
}
static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char* uuid, size_t uuid_size, char* mount_point, size_t mount_point_size)
{
char resolved[PATH_MAX];
char mounted_dataset[PATH_MAX];
char mounted_point[PATH_MAX];
const char* zfs = find_zfs();
if (!zfs) {
@ -988,13 +1062,16 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char
/* LCOV_EXCL_STOP */
}
if (extract_zfs_mount(resolved, mounted_dataset, sizeof(mounted_dataset), mounted_point, sizeof(mounted_point)) != 0)
return -1;
/* list all ZFS filesystems in one shot */
const char* argv[] = {
zfs,
"list",
"-H",
"-o",
"name,guid,mountpoint",
"name,guid",
"-t",
"filesystem",
0
@ -1007,10 +1084,10 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char
/* LCOV_EXCL_STOP */
}
size_t best_len = 0;
int found = 0;
while (1) {
char buf[PATH_MAX * 2 + 64];
char* map[3];
char* map[2];
unsigned mac;
char* s = os_fgets(buf, sizeof(buf), fp);
@ -1018,33 +1095,36 @@ static int extract_zfs(const char* dir, char* dataset, size_t dataset_size, char
break;
/* split the line */
mac = strsplit(map, 3, s, "\t\r\n", 0, 1);
mac = strsplit(map, 2, s, "\t\r\n", 0, 1);
if (mac < 3)
if (mac < 2 || strcmp(map[0], mounted_dataset) != 0)
continue;
const char* mp = map[2];
size_t mp_len = strlen(mp);
if (strncmp(resolved, mp, mp_len) != 0)
continue;
if (mp_len > 1 && resolved[mp_len] != '/' && resolved[mp_len] != 0)
continue;
/* keep the longest (innermost) match */
if (mp_len > best_len) {
best_len = mp_len;
if (dataset)
pathcpy(dataset, dataset_size, map[0]);
if (uuid)
pathcpy(uuid, uuid_size, map[1]);
if (mount_point)
pathcpy(mount_point, mount_point_size, map[2]);
}
found = 1;
if (dataset)
pathcpy(dataset, dataset_size, map[0]);
if (uuid)
pathcpy(uuid, uuid_size, map[1]);
if (mount_point)
pathcpy(mount_point, mount_point_size, mounted_point);
}
os_pclose(fp);
/* incomplete output cannot confirm the mounted dataset's GUID */
int ret = os_pclose(fp);
if (ret < 0) {
/* LCOV_EXCL_START */
log_error(errno, "Failed to close ZFS dataset list for '%s'. %s.\n", dir, strerror(errno));
return -1;
/* LCOV_EXCL_STOP */
}
if (ret != 0) {
/* LCOV_EXCL_START */
log_error(ESOFT, "Failed to list ZFS datasets for '%s' (status %d).\n", dir, ret);
return -1;
/* LCOV_EXCL_STOP */
}
if (best_len == 0) {
if (!found) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
@ -1855,6 +1935,12 @@ static int devuuid_bcachefs(uint64_t device, const char* dir, char* uuid, size_t
#if HAVE_LINUX_DEVICE
int devuuid_zfs(uint64_t device, const char* dir, char* uuid, size_t uuid_size)
{
struct statfs sfs;
/* only report ZFS lookup failures after identifying the filesystem */
if (statfs(dir, &sfs) != 0 || statfs_type(&sfs) != ZFS_SUPER_MAGIC)
return -1;
if (extract_zfs(dir, 0, 0, uuid, uuid_size, 0, 0) != 0)
return -1;