/* * Copyright (C) 2014 Daniel Golle * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include "libfstools.h" #include "volume.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"; 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) { FILE *f; f = fopen(filename, "r"); if (!f) return 0; fclose(f); return 1; } static int ubi_volume_init(struct volume *v) { struct ubi_volume *p = container_of(v, struct ubi_volume, v); char voldir[BUFLEN], voldev[BUFLEN], *volname; unsigned int volsize; snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u", ubi_dir_name, p->ubi_num, 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) return -1; if (read_uint_from_file(voldir, "data_bytes", &volsize)) return -1; v->name = volname; v->type = UBIVOLUME; v->size = volsize; v->blk = strdup(voldev); return 0; } static struct volume *ubi_volume_match(char *name, int ubi_num, int volid) { char voldir[BUFLEN], volblkdev[BUFLEN], *volname; struct ubi_volume *p; snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u", ubi_dir_name, ubi_num, ubi_num, volid); snprintf(volblkdev, sizeof(volblkdev), "/dev/ubiblock%u_%u", ubi_num, volid); /* skip if ubiblock device exists */ if (test_open(volblkdev)) return NULL; /* todo: skip existing gluebi device for legacy support */ volname = read_string_from_file(voldir, "name"); if (!volname) { ULOG_ERR("Couldn't read %s/name\n", voldir); return NULL; } if (strncmp(name, volname, strlen(volname) + 1)) return NULL; p = calloc(1, sizeof(struct ubi_volume)); if (!p) return NULL; p->v.drv = &ubi_driver; p->ubi_num = ubi_num; p->ubi_volid = volid; return &p->v; } static struct volume *ubi_part_match(char *name, unsigned int ubi_num) { 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); 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; ret = ubi_volume_match(name, ubi_num, volid); if (ret) break; } closedir(ubi_dir); return ret; } static struct volume *ubi_volume_find(char *name) { struct volume *ret = NULL; DIR *ubi_dir; struct dirent *ubi_dirent; unsigned int ubi_num; if (find_filesystem("ubifs")) return ret; ubi_dir = opendir(ubi_dir_name); /* check for os ubi support */ if (!ubi_dir) return ret; /* probe ubi devices and volumes */ while ((ubi_dirent = readdir(ubi_dir)) != NULL) { if (ubi_dirent->d_name[0] == '.') continue; sscanf(ubi_dirent->d_name, "ubi%u", &ubi_num); ret = ubi_part_match(name, ubi_num); if (ret) break; } closedir(ubi_dir); return ret; } static int ubi_volume_identify(struct volume *v) { /* Todo: use libblkid-tiny on the ubi chardev */ return FS_UBIFS; } static struct driver ubi_driver = { .name = "ubi", .find = ubi_volume_find, .init = ubi_volume_init, .identify = ubi_volume_identify, }; DRIVER(ubi_driver);