partname: Introduce fstools_partname_fallback_scan option
[project/fstools.git] / libfstools / partname.c
index 9073a0d2c9968be31d61db0ffbdfdfcdc5a6d3b4..f42322a49d5ba2d730d532c79d4475bc71ba2186 100644 (file)
@@ -6,20 +6,33 @@
 
 const char *const block_dir_name = "/sys/class/block";
 
+struct devpath {
+       char prefix[5];
+       char device[11];
+};
+
 struct partname_volume {
        struct volume v;
-       char devname[16];
-       char parent_devname[16];
+       union {
+               char devpathstr[16];
+               struct devpath devpath;
+       } dev;
+
+       union {
+               char devpathstr[16];
+               struct devpath devpath;
+       } parent_dev;
 };
 
 static struct driver partname_driver;
 
 static int partname_volume_identify(struct volume *v)
 {
+       struct partname_volume *p = container_of(v, struct partname_volume, v);
        int ret = FS_NONE;
        FILE *f;
 
-       f = fopen(v->blk, "r");
+       f = fopen(p->dev.devpathstr, "r");
        if (!f)
                return ret;
 
@@ -34,30 +47,28 @@ static int partname_volume_init(struct volume *v)
 {
        struct partname_volume *p = container_of(v, struct partname_volume, v);
        char voldir[BUFLEN];
-       char voldev[BUFLEN];
-       char pdev[BUFLEN];
        unsigned int volsize;
 
-       snprintf(voldir, sizeof(voldir), "%s/%s", block_dir_name, p->devname);
+       snprintf(voldir, sizeof(voldir), "%s/%s", block_dir_name, p->dev.devpath.device);
 
        if (read_uint_from_file(voldir, "size", &volsize))
                return -1;
 
-       snprintf(voldev, sizeof(voldev), "/dev/%s", p->devname);
-       snprintf(pdev, sizeof(pdev), "/dev/%s", p->parent_devname);
-
        v->type = BLOCKDEV;
        v->size = volsize << 9; /* size is returned in sectors of 512 bytes */
-       v->blk = strdup(voldev);
+       v->blk = p->dev.devpathstr;
 
-       return block_volume_format(v, 0, pdev);
+       return block_volume_format(v, 0, p->parent_dev.devpathstr);
 }
 
-/* from procd/utils.c -> should go to libubox */
-static char* get_cmdline_val(const char* name, char* out, int len)
+/* adapted from procd/utils.c -> should go to libubox */
+static char* get_var_from_file(const char* filename, const char* name, char* out, int len)
 {
        char line[1024], *c, *sptr;
-       int fd = open("/proc/cmdline", O_RDONLY);
+       int fd = open(filename, O_RDONLY);
+       if (fd == -1)
+               return NULL;
+
        ssize_t r = read(fd, line, sizeof(line) - 1);
        close(fd);
 
@@ -77,7 +88,7 @@ static char* get_cmdline_val(const char* name, char* out, int len)
                        continue;
 
                strncpy(out, &sep[1], len);
-               out[len-1] = 0;
+               out[len-1] = '\0';
                return out;
        }
 
@@ -104,7 +115,7 @@ static char *rootdevname(char *devpath) {
 static struct volume *partname_volume_find(char *name)
 {
        struct partname_volume *p;
-       char volnamegstr[BUFLEN];
+       char ueventgstr[BUFLEN];
        char namebuf[BUFLEN];
        char rootparam[BUFLEN];
        char *rootdev = NULL, *devname, *tmp;
@@ -112,18 +123,36 @@ static struct volume *partname_volume_find(char *name)
        bool found = false;
        glob_t gl;
 
-       if (get_cmdline_val("root", rootparam, sizeof(rootparam))) {
+       if (get_var_from_file("/proc/cmdline", "fstools_ignore_partname", rootparam, sizeof(rootparam))) {
+               if (!strcmp("1", rootparam))
+                       return NULL;
+       }
+
+       if (get_var_from_file("/proc/cmdline", "root", rootparam, sizeof(rootparam)) && rootparam[0] == '/') {
                rootdev = rootdevname(rootparam);
                /* find partition on same device as rootfs */
-               snprintf(volnamegstr, sizeof(volnamegstr), "%s/%s/*/name", block_dir_name, rootdev);
+               snprintf(ueventgstr, sizeof(ueventgstr), "%s/%s/*/uevent", block_dir_name, rootdev);
        } else {
-               /* no 'root=' kernel cmdline parameter, find on any block device */
-               snprintf(volnamegstr, sizeof(volnamegstr), "%s/*/name", block_dir_name);
+               /*
+                * Some device may contains a GPT partition named rootfs_data that may not be suitable.
+                * To save from regression with old implementation that doesn't use fstools_ignore_partname to
+                * explicitly say that that parname scan should be ignored, make explicit that scanning each
+                * partition should be done by providing fstools_partname_fallback_scan=1 and skip partname scan
+                * in every other case.
+                */
+               if (!get_var_from_file("/proc/cmdline", "fstools_partname_fallback_scan", rootparam, sizeof(rootparam)))
+                       return NULL;
+
+               if (!strcmp("1", rootparam))
+                       return NULL;
+
+               /* no useful 'root=' kernel cmdline parameter, find on any block device */
+               snprintf(ueventgstr, sizeof(ueventgstr), "%s/*/uevent", block_dir_name);
        }
 
-       if (!glob(volnamegstr, GLOB_NOESCAPE, NULL, &gl))
+       if (!glob(ueventgstr, GLOB_NOESCAPE, NULL, &gl))
                for (j = 0; j < gl.gl_pathc; j++) {
-                       if (!read_string_from_file("", gl.gl_pathv[j], namebuf, sizeof(namebuf)))
+                       if (!get_var_from_file(gl.gl_pathv[j], "PARTNAME", namebuf, sizeof(namebuf)))
                                continue;
                        if (!strncmp(namebuf, name, sizeof(namebuf))) {
                                found = 1;
@@ -136,19 +165,28 @@ static struct volume *partname_volume_find(char *name)
 
        devname = gl.gl_pathv[j];
        tmp = strrchr(devname, '/');
+       if (!tmp)
+               return NULL;
+
        *tmp = '\0';
        devname = strrchr(devname, '/') + 1;
 
        p = calloc(1, sizeof(*p));
-       strncpy(p->devname, devname, sizeof(p->devname));
+       memcpy(p->dev.devpath.prefix, "/dev/", sizeof(p->dev.devpath.prefix));
+       strncpy(p->dev.devpath.device, devname, sizeof(p->dev.devpath.device) - 1);
+       p->dev.devpath.device[sizeof(p->dev.devpath.device)-1] = '\0';
+
+       memcpy(p->parent_dev.devpath.prefix, "/dev/", sizeof(p->parent_dev.devpath.prefix));
        if (rootdev)
-               strncpy(p->parent_devname, rootdev, sizeof(p->devname));
+               strncpy(p->parent_dev.devpath.device, rootdev, sizeof(p->parent_dev.devpath.device) - 1);
        else
-               strncpy(p->parent_devname, rootdevname(devname), sizeof(p->devname));
+               strncpy(p->parent_dev.devpath.device, rootdevname(devname), sizeof(p->parent_dev.devpath.device) - 1);
+
+       p->parent_dev.devpath.device[sizeof(p->parent_dev.devpath.device)-1] = '\0';
 
-       p->devname[sizeof(p->devname)-1] = '\0';
        p->v.drv = &partname_driver;
-       p->v.name = strdup(namebuf);
+       p->v.blk = p->dev.devpathstr;
+       p->v.name = name;
 
        return &p->v;
 }