libfstools: query drivers by priority
[project/fstools.git] / libfstools / ubi.c
index 866034a7cc84e4d06e495b18d87be85ec658ca8a..9bd7b050f4784bd59b7ed3d6be3ee6e64ae26c1a 100644 (file)
  * GNU General Public License for more details.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-
-#include "libfstools.h"
-#include "volume.h"
+#include "common.h"
 
 /* fit for UBI_MAX_VOLUME_NAME and sysfs path lengths */
 #define BUFLEN         128
 
 /* could use libubi-tiny instead, but already had the code directly reading
  * from sysfs */
-const char *const ubi_dir_name = "/sys/devices/virtual/ubi";
+const char *const ubi_dir_name = "/sys/class/ubi";
 
-struct ubi_priv {
+struct ubi_volume {
+       struct volume v;
        int             ubi_num;
        int             ubi_volid;
 };
 
 static struct driver ubi_driver;
 
-static int
-read_uint_from_file(char *dirname, char *filename, unsigned int *i)
-{
-       FILE *f;
-       char fname[BUFLEN];
-       int ret = -1;
-
-       snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
-
-       f = fopen(fname, "r");
-       if (!f)
-               return ret;
-
-       if (fscanf(f, "%u", i) == 1)
-               ret = 0;
-
-       fclose(f);
-       return ret;
-}
-
-static char
-*read_string_from_file(char *dirname, char *filename)
-{
-       FILE *f;
-       char fname[BUFLEN];
-       char buf[BUFLEN];
-       int i;
-
-       snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
-
-       f = fopen(fname, "r");
-       if (!f)
-               return NULL;
-
-       if (fgets(buf, sizeof(buf), f) == NULL)
-               return NULL;
-
-       fclose(f);
-
-       /* make sure the string is \0 terminated */
-       buf[sizeof(buf) - 1] = '\0';
-
-       /* remove trailing whitespace */
-       i = strlen(buf) - 1;
-       while (i > 0 && buf[i] <= ' ')
-               buf[i--] = '\0';
-
-       return strdup(buf);
-}
-
 static unsigned int
 test_open(char *filename)
 {
@@ -98,20 +43,17 @@ test_open(char *filename)
 
 static int ubi_volume_init(struct volume *v)
 {
-       char voldir[BUFLEN], voldev[BUFLEN], *volname;
-       struct ubi_priv *p;
+       struct ubi_volume *p = container_of(v, struct ubi_volume, v);
+       char voldir[BUFLEN], voldev[BUFLEN], volname[BUFLEN];
        unsigned int volsize;
 
-       p = (struct ubi_priv*)v->priv;
-
-       snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u",
-               ubi_dir_name, p->ubi_num, p->ubi_num, p->ubi_volid);
+       snprintf(voldir, sizeof(voldir), "%s/ubi%u_%u",
+               ubi_dir_name, p->ubi_num, p->ubi_volid);
 
        snprintf(voldev, sizeof(voldev), "/dev/ubi%u_%u",
                p->ubi_num, p->ubi_volid);
 
-       volname = read_string_from_file(voldir, "name");
-       if (!volname)
+       if (!read_string_from_file(voldir, "name", volname, sizeof(volname)))
                return -1;
 
        if (read_uint_from_file(voldir, "data_bytes", &volsize))
@@ -125,66 +67,79 @@ static int ubi_volume_init(struct volume *v)
        return 0;
 }
 
-static int ubi_volume_match(struct volume *v, char *name, int ubi_num, int volid)
+static struct volume *ubi_volume_match(char *name, int ubi_num, int volid)
 {
-       char voldir[BUFLEN], volblkdev[BUFLEN], *volname;
-       struct ubi_priv *p;
+       char voldir[BUFLEN], volblkdev[BUFLEN], volname[BUFLEN];
+       struct ubi_volume *p;
 
-       snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u",
-               ubi_dir_name, ubi_num, ubi_num, volid);
+       snprintf(voldir, sizeof(voldir), "%s/ubi%u_%u",
+               ubi_dir_name, ubi_num, volid);
 
        snprintf(volblkdev, sizeof(volblkdev), "/dev/ubiblock%u_%u",
                ubi_num, volid);
 
        /* skip if ubiblock device exists */
        if (test_open(volblkdev))
-               return -1;
+               return NULL;
 
        /* todo: skip existing gluebi device for legacy support */
 
-       volname = read_string_from_file(voldir, "name");
+       if (!read_string_from_file(voldir, "name", volname, sizeof(volname))) {
+               ULOG_ERR("Couldn't read %s/name\n", voldir);
+               return NULL;
+       }
 
-       if (strncmp(name, volname, strlen(volname) + 1))
-               return -1;
+       if (strcmp(name, volname))
+               return NULL;
 
-       p = calloc(1, sizeof(struct ubi_priv));
+       p = calloc(1, sizeof(struct ubi_volume));
        if (!p)
-               return -1;
+               return NULL;
 
-       v->priv = p;
-       v->drv = &ubi_driver;
+       p->v.drv = &ubi_driver;
        p->ubi_num = ubi_num;
        p->ubi_volid = volid;
 
-       return ubi_volume_init(v);
+       return &p->v;
 }
 
-static int ubi_part_match(struct volume *v, char *name, unsigned int ubi_num)
+static struct volume *ubi_part_match(char *name, unsigned int ubi_num)
 {
-       unsigned int i, volumes_count;
+       DIR *ubi_dir;
+       struct dirent *ubi_dirent;
+       unsigned int volid;
        char devdir[BUFLEN];
+       struct volume *ret = NULL;
 
        snprintf(devdir, sizeof(devdir), "%s/ubi%u",
                ubi_dir_name, ubi_num);
 
-       if (read_uint_from_file(devdir, "volumes_count", &volumes_count))
-               return -1;
+       ubi_dir = opendir(devdir);
+       if (!ubi_dir)
+               return ret;
+
+       while ((ubi_dirent = readdir(ubi_dir)) != NULL) {
+               if (strncmp(ubi_dirent->d_name, "ubi", 3))
+                       continue;
+
+               if (sscanf(ubi_dirent->d_name, "ubi%*u_%u", &volid) != 1)
+                       continue;
 
-       for (i=0;i<volumes_count;i++) {
-               if (!ubi_volume_match(v, name, ubi_num, i)) {
-                       return 0;
-               }
+               ret = ubi_volume_match(name, ubi_num, volid);
+               if (ret)
+                       break;
        }
+       closedir(ubi_dir);
 
-       return -1;
+       return ret;
 }
 
-static int ubi_volume_find(struct volume *v, char *name)
+static struct volume *ubi_volume_find(char *name)
 {
+       struct volume *ret = NULL;
        DIR *ubi_dir;
        struct dirent *ubi_dirent;
        unsigned int ubi_num;
-       int ret = -1;
 
        if (find_filesystem("ubifs"))
                return ret;
@@ -200,10 +155,9 @@ static int ubi_volume_find(struct volume *v, char *name)
                        continue;
 
                sscanf(ubi_dirent->d_name, "ubi%u", &ubi_num);
-               if (!ubi_part_match(v, name, ubi_num)) {
-                       ret = 0;
+               ret = ubi_part_match(name, ubi_num);
+               if (ret)
                        break;
-               };
        }
        closedir(ubi_dir);
        return ret;
@@ -217,6 +171,7 @@ static int ubi_volume_identify(struct volume *v)
 
 static struct driver ubi_driver = {
        .name = "ubi",
+       .priority = 20,
        .find = ubi_volume_find,
        .init = ubi_volume_init,
        .identify = ubi_volume_identify,