@@ -276,25 +276,90 @@ static int pack_uuid(const char *uuid_str, char *uuid)
return 0;
}
+#define MAJ_MIN_COL 3
+#define MOUNT_SOURCE_COL 10
+
+static int get_blk_dev(dev_t dev, char *path, size_t size)
+{
+ /*
+ * Looking in /dev/block/<major>:<minor> does not suffice for some
+ * special file systems like btrfs or overlayfs, because their st_dev
+ * values do not correspond to any named block device.
+ *
+ * Instead parse /proc/self/mountinfo for the correct source device.
+ */
+ int ret = -1;
+ unsigned int maj = major(dev);
+ unsigned int min = minor(dev);
+ char line[LINE_MAX];
+ char maj_min_val[LINE_MAX];
+ size_t print_res = 0;
+ FILE *mountinfo = fopen("/proc/self/mountinfo", "r");
+
+ if (!mountinfo)
+ return -1;
+
+ // comparison string for the column parsing below
+ print_res = snprintf(maj_min_val, LINE_MAX, "%u:%u", maj, min);
+
+ if (print_res >= LINE_MAX)
+ {
+ // buffer to small
+ ret = -2;
+ goto out;
+ }
+
+ while (fgets(line, LINE_MAX, mountinfo) != NULL)
+ {
+ size_t column = 0;
+ char *token, *tmp_line = line;
+
+ while ( (token = strtok(tmp_line, " ")) )
+ {
+ if (tmp_line)
+ tmp_line = NULL;
+ column++;
+
+ if (column == MAJ_MIN_COL)
+ {
+ if (strcmp(token, maj_min_val) != 0)
+ // not the device we're looking for
+ break;
+ }
+ else if (column == MOUNT_SOURCE_COL)
+ {
+ print_res = snprintf(path, size, "%s", token);
+ log_debug("dev: %u:%u -> %s\n", maj, min, token);
+ ret = print_res < size ? 0 : -2;
+ goto out;
+ }
+ }
+ }
+
+ // not found or read/parse error
+ ret = -3;
+out:
+ if (mountinfo)
+ fclose(mountinfo);
+
+ return ret;
+}
+
static int get_uuid(struct stat *st, char *uuid)
{
- uint32_t dev;
- unsigned minor, major;
- char path[PATH_MAX], _uuid[37];
+ char blkdev[PATH_MAX], cmdline[PATH_MAX], _uuid[37];
FILE *fp;
size_t len;
if (uuid_str)
return pack_uuid(uuid_str, uuid);
- dev = st->st_dev;
- major = (dev & 0xfff00) >> 8;
- minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
+ if (get_blk_dev(st->st_dev, blkdev, PATH_MAX) != 0)
+ goto err;
- log_debug("dev: %u:%u\n", major, minor);
- sprintf(path, "blkid -s UUID -o value /dev/block/%u:%u", major, minor);
+ snprintf(cmdline, PATH_MAX, "blkid -s UUID -o value %s", blkdev);
- fp = popen(path, "r");
+ fp = popen(cmdline, "r");
if (!fp)
goto err;
@@ -305,7 +370,7 @@ static int get_uuid(struct stat *st, char *uuid)
return pack_uuid(_uuid, uuid);
err:
- log_err("Failed to read UUID. Root access might require.\n");
+ log_err("Failed to read UUID. Root access might be required.\n");
return -1;
}