From c036f9958b3719a949999ca4fe9866ba56b3f713 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 22 Dec 2007 15:43:19 +0200 Subject: ubi-utils: introduce ubinfo utility Add new handy UBI utility which prints various type of UBI information. This commit also includes a lot of fixes and cleanups in libubi, and other utilities. It was quite complex to separate this all out and I figured that nobody anyway would really need this, and decided to save my time for more useful things. Signed-off-by: Artem Bityutskiy --- ubi-utils/Makefile | 7 +- ubi-utils/inc/libubi.h | 172 ++--- ubi-utils/src/common.c | 35 ++ ubi-utils/src/common.h | 17 +- ubi-utils/src/libubi.c | 1486 ++++++++++++++++++++++++-------------------- ubi-utils/src/libubi_int.h | 86 +-- ubi-utils/src/ubimkvol.c | 88 +-- ubi-utils/src/ubinfo.c | 461 ++++++++++++++ ubi-utils/src/ubirmvol.c | 66 +- 9 files changed, 1505 insertions(+), 913 deletions(-) create mode 100644 ubi-utils/src/ubinfo.c diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index 1a3b2b9..ee4956f 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -2,7 +2,7 @@ # Makefile for ubi-utils # -OPTFLAGS := -O2 -g -Wall +OPTFLAGS := -O2 -Wall KERNELHDR := ../include DESTDIR := /usr/local SBINDIR=/usr/sbin @@ -15,7 +15,7 @@ CFLAGS := -I./inc -I./src -I$(KERNELHDR) $(OPTFLAGS) -Werror \ PERLPROGS = mkpfi ubicrc32.pl TARGETS = ubiupdatevol ubimkvol ubirmvol pfiflash pddcustomize ubimirror \ - bin2nand nand2bin ubigen mkbootenv unubi pfi2bin ubicrc32 + bin2nand nand2bin ubigen mkbootenv unubi pfi2bin ubicrc32 ubinfo vpath %.c ./src @@ -33,6 +33,9 @@ IGNORE=${wildcard .*.c.dep} clean: rm -rf *.o $(TARGETS) .*.c.dep +ubinfo: ubinfo.o common.o libubi.o + $(CC) $(LDFLAGS) -o $@ $^ + ubiupdatevol: ubiupdatevol.o error.o libubi.o $(CC) $(LDFLAGS) -o $@ $^ diff --git a/ubi-utils/inc/libubi.h b/ubi-utils/inc/libubi.h index d39c1b9..21be68a 100644 --- a/ubi-utils/inc/libubi.h +++ b/ubi-utils/inc/libubi.h @@ -40,7 +40,13 @@ typedef void * libubi_t; /** * struct ubi_mkvol_request - volume creation request. - * */ + * @vol_id: ID to assign to the new volume (%UBI_VOL_NUM_AUTO should be used to + * automatically assign ID) + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @name: volume name + */ struct ubi_mkvol_request { int vol_id; @@ -52,11 +58,12 @@ struct ubi_mkvol_request /** * struct ubi_info - general UBI information. - * - * @dev_count count of UBI devices in system - * @lowest_dev_num lowest UBI device number - * @highest_dev_num highest UBI device number - * @version UBI version + * @dev_count: count of UBI devices in system + * @lowest_dev_num: lowest UBI device number + * @highest_dev_num: highest UBI device number + * @version: UBI version + * @ctrl_major: major number of the UBI control device + * @ctrl_minor: minor number of the UBI control device */ struct ubi_info { @@ -64,26 +71,29 @@ struct ubi_info int lowest_dev_num; int highest_dev_num; int version; + int ctrl_major; + int ctrl_minor; }; /** * struct ubi_dev_info - UBI device information. - * - * @vol_count count of volumes on this UBI device - * @lowest_vol_num lowest volume number - * @highest_vol_num highest volume number - * @total_ebs total number of eraseblocks on this UBI device - * @avail_ebs how many eraseblocks are not used and available for new - * volumes - * @total_bytes @total_ebs * @eb_size - * @avail_bytes @avail_ebs * @eb_size - * @bad_count count of bad eraseblocks - * @eb_size size of UBI eraseblock - * @max_ec current highest erase counter value - * @bad_rsvd how many physical eraseblocks of the underlying flash - * device are reserved for bad eraseblocks handling - * @max_vol_count maximum count of volumes on this UBI device - * @min_io_size minimum input/output size of the UBI device + * @vol_count: count of volumes on this UBI device + * @lowest_vol_num: lowest volume number + * @highest_vol_num: highest volume number + * @major: major number of corresponding character device + * @minor: minor number of corresponding character device + * @total_ebs: total number of logical eraseblocks on this UBI device + * @avail_ebs: how many logical eraseblocks are not used and available for new + * volumes + * @total_bytes: @total_ebs * @eb_size + * @avail_bytes: @avail_ebs * @eb_size + * @bad_count: count of bad physical eraseblocks + * @eb_size: logical eraseblock size + * @max_ec: current highest erase counter value + * @bad_rsvd: how many physical eraseblocks of the underlying flash device are + * reserved for bad eraseblocks handling + * @max_vol_count: maximum possible number of volumes on this UBI device + * @min_io_size: minimum input/output unit size of the UBI device */ struct ubi_dev_info { @@ -91,6 +101,8 @@ struct ubi_dev_info int vol_count; int lowest_vol_num; int highest_vol_num; + int major; + int minor; int total_ebs; int avail_ebs; long long total_bytes; @@ -105,24 +117,31 @@ struct ubi_dev_info /** * struct ubi_vol_info - UBI volume information. - * - * @dev_num UBI device number the volume resides on - * @vol_id ID of this volume - * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) - * @alignment alignemnt of this volume - * @data_bytes how many data bytes are stored on this volume (equivalent to - * @rsvd_bytes for dynamic volumes) - * @rsvd_bytes how many bytes are reserved for this volume - * @rsvd_ebs how many eraseblocks are reserved for this volume - * @eb_size logical eraseblock size of this volume (may be less then - * device's logical eraseblock size due to alignment) - * @corrupted the volume is corrupted if this flag is not zero - * @name volume name (null-terminated) + * @dev_num: UBI device number the volume resides on + * @vol_id: ID of this volume + * @major: major number of corresponding volume character device + * @minor: minor number of corresponding volume character device + * @dev_major: major number of corresponding UBI device character device + * @dev_minor: minor number of corresponding UBI device character device + * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @alignment: alignemnt of this volume + * @data_bytes: how many data bytes are stored on this volume (equivalent to + * @rsvd_bytes for dynamic volumes) + * @rsvd_bytes: how many bytes are reserved for this volume + * @rsvd_ebs: how many logical eraseblocks are reserved for this volume + * @eb_size: logical eraseblock size of this volume (may be less then + * device's logical eraseblock size due to alignment) + * @corrupted: non-zero if the volume is corrupted + * @name: volume name (null-terminated) */ struct ubi_vol_info { int dev_num; int vol_id; + int major; + int minor; + int dev_major; + int dev_minor; int type; int alignment; long long data_bytes; @@ -135,24 +154,21 @@ struct ubi_vol_info /** * libubi_open - open UBI library. - * * This function initializes and opens the UBI library and returns UBI library * descriptor in case of success and %NULL in case of failure. */ libubi_t libubi_open(void); /** - * libubi_close - close UBI library - * + * libubi_close - close UBI library. * @desc UBI library descriptor */ void libubi_close(libubi_t desc); /** * ubi_get_info - get general UBI information. - * - * @info pointer to the &struct ubi_info object to fill - * @desc UBI library descriptor + * @info: pointer to the &struct ubi_info object to fill + * @desc: UBI library descriptor * * This function fills the passed @info object with general UBI information and * returns %0 in case of success and %-1 in case of failure. @@ -161,10 +177,9 @@ int ubi_get_info(libubi_t desc, struct ubi_info *info); /** * ubi_mkvol - create an UBI volume. - * - * @desc UBI library descriptor - * @node name of the UBI character device to create a volume at - * @req UBI volume creation request (defined at ) + * @desc: UBI library descriptor + * @node: name of the UBI character device to create a volume at + * @req: UBI volume creation request * * This function creates a UBI volume as described at @req and returns %0 in * case of success and %-1 in case of failure. The assigned volume ID is @@ -174,10 +189,9 @@ int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req); /** * ubi_rmvol - remove a UBI volume. - * - * @desc UBI library descriptor - * @node name of the UBI character device to remove a volume from - * @vol_id ID of the volume to remove + * @desc: UBI library descriptor + * @node: name of the UBI character device to remove a volume from + * @vol_id: ID of the volume to remove * * This function removes volume @vol_id from UBI device @node and returns %0 in * case of success and %-1 in case of failure. @@ -186,23 +200,33 @@ int ubi_rmvol(libubi_t desc, const char *node, int vol_id); /** * ubi_rsvol - re-size UBI volume. - * - * @desc UBI library descriptor - * @node name of the UBI character device owning the volume which should be - * re-sized - * @vol_id volume ID to re-size - * @bytes new volume size in bytes + * @desc: UBI library descriptor + * @node: name of the UBI character device owning the volume which should be + * re-sized + * @vol_id: volume ID to re-size + * @bytes: new volume size in bytes * * This function returns %0 in case of success and %-1 in case of error. */ int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes); /** - * ubi_get_dev_info - get UBI device information. + * ubi_node_type - test UBI node type. + * @desc: UBI library descriptor + * @node: the node to test * - * @desc UBI library descriptor - * @node name of the UBI character device to fetch information about - * @info pointer to the &struct ubi_dev_info object to fill + * This function tests whether @node is a UBI device or volume node and returns + * %1 if this is an UBI device node, %2 if this is a volume node, and %-1 if + * this is not an UBI node or if an error occurred (the latter is indicated by + * a non-zero errno). + */ +int ubi_node_type(libubi_t desc, const char *node); + +/** + * ubi_get_dev_info - get UBI device information. + * @desc: UBI library descriptor + * @node: name of the UBI character device to fetch information about + * @info: pointer to the &struct ubi_dev_info object to fill * * This function fills the passed @info object with UBI device information and * returns %0 in case of success and %-1 in case of failure. @@ -212,10 +236,9 @@ int ubi_get_dev_info(libubi_t desc, const char *node, /** * ubi_get_dev_info1 - get UBI device information. - * - * @desc UBI library descriptor - * @dev_num UBI device number to fetch information about - * @info pointer to the &struct ubi_dev_info object to fill + * @desc: UBI library descriptor + * @dev_num: UBI device number to fetch information about + * @info: pointer to the &struct ubi_dev_info object to fill * * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI * device number, not UBI character device. @@ -224,10 +247,9 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info); /** * ubi_get_vol_info - get UBI volume information. - * - * @desc UBI library descriptor - * @node name of the UBI volume character device to fetch information about - * @info pointer to the &struct ubi_vol_info object to fill + * @desc: UBI library descriptor + * @node: name of the UBI volume character device to fetch information about + * @info: pointer to the &struct ubi_vol_info object to fill * * This function fills the passed @info object with UBI volume information and * returns %0 in case of success and %-1 in case of failure. @@ -237,11 +259,10 @@ int ubi_get_vol_info(libubi_t desc, const char *node, /** * ubi_get_vol_info1 - get UBI volume information. - * - * @desc UBI library descriptor - * @dev_num UBI device number - * @vol_id ID of the UBI volume to fetch information about - * @info pointer to the &struct ubi_vol_info object to fill + * @desc: UBI library descriptor + * @dev_num: UBI device number + * @vol_id: ID of the UBI volume to fetch information about + * @info: pointer to the &struct ubi_vol_info object to fill * * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI * volume number, not UBI volume character device. @@ -251,10 +272,9 @@ int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, /** * ubi_update_start - start UBI volume update. - * - * @desc UBI library descriptor - * @fd volume character devie file descriptor - * @bytes how many bytes will be written to the volume + * @desc: UBI library descriptor + * @fd: volume character devie file descriptor + * @bytes: how many bytes will be written to the volume * * This function initiates UBI volume update and returns %0 in case of success * and %-1 in case of error. diff --git a/ubi-utils/src/common.c b/ubi-utils/src/common.c index 4a56128..761d47d 100644 --- a/ubi-utils/src/common.c +++ b/ubi-utils/src/common.c @@ -20,6 +20,7 @@ * This file contains various common stuff used by UBI utilities. */ +#include #include /** @@ -50,3 +51,37 @@ int ubiutils_get_multiplier(const char *str) return -1; } + +/** + * ubiutils_print_bytes - print bytes. + * @bytes: variable to print + * @bracket: whether brackets have to be put or not + * + * This is a helper function which prints amount of bytes in a human-readable + * form, i.e., it prints the exact amount of bytes following by the approximate + * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes + * is. + */ +void ubiutils_print_bytes(long long bytes, int bracket) +{ + const char *p; + + if (bracket) + p = " ("; + else + p = ", "; + + printf("%lld bytes", bytes); + + if (bytes > 1024 * 1024 * 1024) + printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024)); + else if (bytes > 1024 * 1024) + printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024)); + else if (bytes > 1024) + printf("%s%.1f KiB", p, (double)bytes / 1024); + else + return; + + if (bracket) + printf(")"); +} diff --git a/ubi-utils/src/common.h b/ubi-utils/src/common.h index c20d308..0e33dd1 100644 --- a/ubi-utils/src/common.h +++ b/ubi-utils/src/common.h @@ -19,19 +19,28 @@ #ifndef __UBI_UTILS_COMMON_H__ #define __UBI_UTILS_COMMON_H__ +#ifdef __cplusplus +extern "C" { +#endif + /* Maximum device node name length */ #define MAX_NODE_LEN 255 /* Error messages */ -#define errmsg(fmt, ...) do { \ - fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__); \ +#define errmsg(fmt, ...) do { \ + fprintf(stderr, PROGRAM_NAME " error: " fmt "\n", ##__VA_ARGS__); \ } while(0) /* Warnings */ -#define warnmsg(fmt, ...) do { \ - fprintf(stderr, "Warning: " fmt "\n", ##__VA_ARGS__); \ +#define warnmsg(fmt, ...) do { \ + fprintf(stderr, PROGRAM_NAME " warning: " fmt "\n", ##__VA_ARGS__); \ } while(0) int ubiutils_get_multiplier(const char *str); +void ubiutils_print_bytes(long long bytes, int bracket); + +#ifdef __cplusplus +} +#endif #endif /* !__UBI_UTILS_COMMON_H__ */ diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index 4d2b725..ecc12b4 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -34,894 +34,1016 @@ #include "libubi.h" #include "libubi_int.h" -libubi_t libubi_open(void) +/** + * mkpath - compose full path from 2 given components. + * @path: the first component + * @name: the second component + * + * This function returns the resulting path in case of success and %NULL in + * case of failure. + */ +static char *mkpath(const char *path, const char *name) { - int fd, version; - struct libubi *lib; + char *n; + int len1 = strlen(path); + int len2 = strlen(name); - lib = calloc(1, sizeof(struct libubi)); - if (!lib) + n = malloc(len1 + len2 + 2); + if (!n) { + errmsg("cannot allocate %d bytes", len1 + len2 + 2); + perror("malloc"); return NULL; + } - /* TODO: this must be discovered instead */ - lib->sysfs = strdup("/sys"); - if (!lib->sysfs) - goto error; + memcpy(n, path, len1); + if (n[len1 - 1] != '/') + n[len1++] = '/'; - lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); - if (!lib->sysfs_ubi) - goto error; + memcpy(n + len1, name, len2 + 1); + return n; +} - /* Make sure UBI is present */ - fd = open(lib->sysfs_ubi, O_RDONLY); +/** + * read_positive_ll - read a positive 'long long' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function reads file @file and interprets its contents as a positive + * 'long long' integer. If this is not true, it fails with %EINVAL error code. + * Returns %0 in case of success and %-1 in case of failure. + */ +static int read_positive_ll(const char *file, long long *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); if (fd == -1) - goto error; - close(fd); + return -1; - lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); - if (!lib->ubi_dev) - goto error; + rd = read(fd, buf, 50); + if (rd == -1) { + errmsg("cannot read \"%s\"", file); + perror("read"); + goto out_error; + } + if (rd == 50) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } - lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); - if (!lib->ubi_version) - goto error; + if (sscanf(buf, "%lld\n", value) != 1) { + /* This must be a UBI bug */ + errmsg("cannot read integer from \"%s\"\n", file); + errno = EINVAL; + goto out_error; + } - lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); - if (!lib->dev_dev) - goto error; + if (*value < 0) { + errmsg("negative value %lld in \"%s\"", *value, file); + errno = EINVAL; + goto out_error; + } - lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); - if (!lib->dev_avail_ebs) - goto error; + if (close(fd)) { + errmsg("close failed on \"%s\"", file); + perror("close"); + return -1; + } - lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); - if (!lib->dev_total_ebs) - goto error; + return 0; - lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); - if (!lib->dev_bad_count) - goto error; +out_error: + close(fd); + return -1; +} - lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); - if (!lib->dev_eb_size) - goto error; +/** + * read_positive_int - read a positive 'int' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function is the same as 'read_positive_ll()', but it reads an 'int' + * value, not 'long long'. + */ +static int read_positive_int(const char *file, int *value) +{ + long long res; - lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); - if (!lib->dev_max_ec) - goto error; + if (read_positive_ll(file, &res)) + return -1; - lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); - if (!lib->dev_bad_rsvd) - goto error; + /* Make sure the value is not too big */ + if (res > INT_MAX) { + errmsg("value %lld read from file \"%s\" is out of range", + res, file); + errno = EINVAL; + return -1; + } - lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); - if (!lib->dev_max_vols) - goto error; + *value = res; + return 0; +} - lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); - if (!lib->dev_min_io_size) - goto error; +/** + * read_data - read data from a file. + * @file: the file to read from + * @buf: the buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. Note, if the file contains more then @buf_len bytes of + * date, this function fails with %EINVAL error code. + */ +static int read_data(const char *file, void *buf, int buf_len) +{ + int fd, rd, tmp, tmp1; - lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); - if (!lib->ubi_vol) - goto error; + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; - lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); - if (!lib->vol_type) - goto error; + rd = read(fd, buf, buf_len); + if (rd == -1) { + errmsg("cannot read \"%s\"", file); + perror("read"); + goto out_error; + } - lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); - if (!lib->vol_dev) - goto error; + /* Make sure all data is read */ + tmp1 = read(fd, &tmp, 1); + if (tmp1 == 1) { + errmsg("cannot read \"%s\"", file); + perror("read"); + goto out_error; + } + if (tmp1) { + errmsg("file \"%s\" contains too much data (> %d bytes)", + file, buf_len); + errno = EINVAL; + goto out_error; + } - lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); - if (!lib->vol_alignment) - goto error; + if (close(fd)) { + errmsg("close failed on \"%s\"", file); + perror("close"); + return -1; + } - lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); - if (!lib->vol_data_bytes) - goto error; + return rd; - lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); - if (!lib->vol_rsvd_ebs) - goto error; +out_error: + close(fd); + return -1; +} - lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); - if (!lib->vol_eb_size) - goto error; +/** + * read_major - read major and minor numbers from a file. + * @file: name of the file to read from + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns % in case of succes, and %-1 in case of failure. + */ +static int read_major(const char *file, int *major, int *minor) +{ + int ret; + char buf[50]; - lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); - if (!lib->vol_corrupted) - goto error; + ret = read_data(file, buf, 50); + if (ret < 0) + return ret; - lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); - if (!lib->vol_name) - goto error; + ret = sscanf(buf, "%d:%d\n", major, minor); + if (ret != 2) { + errmsg("\"%s\" does not have major:minor format", file); + errno = EINVAL; + return -1; + } - if (read_int(lib->ubi_version, &version)) - goto error; - if (version != LIBUBI_UBI_VERSION) { - fprintf(stderr, "LIBUBI: this library was made for UBI version " - "%d, but UBI version %d is detected\n", - LIBUBI_UBI_VERSION, version); - goto error; + if (*major < 0 || *minor < 0) { + errmsg("bad major:minor %d:%d in \"%s\"", + *major, *minor, file); + errno = EINVAL; + return -1; } - return lib; + return 0; +} -error: - free(lib->vol_corrupted); - free(lib->vol_eb_size); - free(lib->vol_rsvd_ebs); - free(lib->vol_data_bytes); - free(lib->vol_alignment); - free(lib->vol_dev); - free(lib->vol_type); - free(lib->ubi_vol); - free(lib->dev_min_io_size); - free(lib->dev_max_vols); - free(lib->dev_bad_rsvd); - free(lib->dev_max_ec); - free(lib->dev_eb_size); - free(lib->dev_bad_count); - free(lib->dev_total_ebs); - free(lib->dev_avail_ebs); - free(lib->dev_dev); - free(lib->ubi_version); - free(lib->ubi_dev); - free(lib->sysfs_ubi); - free(lib->sysfs); - free(lib); - return NULL; +/** + * dev_read_int - read a positive 'int' value from an UBI device sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_int(const char *patt, int dev_num, int *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_positive_int(file, value); } -void libubi_close(libubi_t desc) +/** + * vol_read_int - read a positive 'int' value from an UBI volume sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) { - struct libubi *lib = (struct libubi *)desc; + char file[strlen(patt) + 100]; - free(lib->vol_name); - free(lib->vol_corrupted); - free(lib->vol_eb_size); - free(lib->vol_rsvd_ebs); - free(lib->vol_data_bytes); - free(lib->vol_alignment); - free(lib->vol_dev); - free(lib->vol_type); - free(lib->ubi_vol); - free(lib->dev_min_io_size); - free(lib->dev_max_vols); - free(lib->dev_bad_rsvd); - free(lib->dev_max_ec); - free(lib->dev_eb_size); - free(lib->dev_bad_count); - free(lib->dev_total_ebs); - free(lib->dev_avail_ebs); - free(lib->dev_dev); - free(lib->ubi_version); - free(lib->ubi_dev); - free(lib->sysfs_ubi); - free(lib->sysfs); - free(lib); + sprintf(file, patt, dev_num, vol_id); + return read_positive_int(file, value); } -int ubi_get_info(libubi_t desc, struct ubi_info *info) +/** + * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_ll(const char *patt, int dev_num, long long *value) { - DIR *sysfs_ubi; - struct dirent *dirent; - struct libubi *lib = (struct libubi *)desc; + char file[strlen(patt) + 50]; - memset(info, '\0', sizeof(struct ubi_info)); + sprintf(file, patt, dev_num); + return read_positive_ll(file, value); +} - /* - * We have to scan the UBI sysfs directory to identify how many UBI - * devices are present. - */ - sysfs_ubi = opendir(lib->sysfs_ubi); - if (!sysfs_ubi) - return -1; +/** + * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_ll(const char *patt, int dev_num, int vol_id, + long long *value) +{ + char file[strlen(patt) + 100]; - info->lowest_dev_num = INT_MAX; - while ((dirent = readdir(sysfs_ubi))) { - char *name = &dirent->d_name[0]; - int dev_num, ret; + sprintf(file, patt, dev_num, vol_id); + return read_positive_ll(file, value); +} - ret = sscanf(name, UBI_DEV_NAME_PATT, &dev_num); - if (ret == 1) { - info->dev_count += 1; - if (dev_num > info->highest_dev_num) - info->highest_dev_num = dev_num; - if (dev_num < info->lowest_dev_num) - info->lowest_dev_num = dev_num; - } +/** + * vol_read_data - read data from an UBI volume's sysfs file. + * @patt: file pattern to read from + * @dev_num: UBI device number + * @vol_id: volume ID + * @buf: buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, + int buf_len) +{ + char file[strlen(patt) + 100]; + + sprintf(file, patt, dev_num, vol_id); + return read_data(file, buf, buf_len); +} + +/** + * dent_is_dir - check if a file is a directory. + * @dir: the base directory path of the file + * @name: file name + * + * This function returns %1 if file @name in directory @dir is a directoru, and + * %0 if not. + */ +static int dent_is_dir(const char *dir, const char *name) +{ + int ret; + struct stat st; + char full_path[strlen(dir) + strlen(name) + 2]; + + sprintf(full_path, "%s/%s", dir, name); + ret = lstat(full_path, &st); + if (ret) { + errmsg("lstat failed on \"%s\"", full_path); + perror("lstat"); + return -1; } - if (info->lowest_dev_num == INT_MAX) - info->lowest_dev_num = 0; + return !!S_ISDIR(st.st_mode); +} - if (read_int(lib->ubi_version, &info->version)) - goto close; +/** + * dev_get_major - get major and minor numbers of an UBI device. + * @lib: libubi descriptor + * @dev_num: UBI device number + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor) +{ + char file[strlen(lib->dev_dev) + 50]; - return closedir(sysfs_ubi); + sprintf(file, lib->dev_dev, dev_num); + return read_major(file, major, minor); +} -close: - closedir(sysfs_ubi); - return -1; +/** + * vol_get_major - get major and minor numbers of an UBI volume. + * @lib: libubi descriptor + * @dev_num: UBI device number + * @vol_id: volume ID + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int vol_get_major(struct libubi *lib, int dev_num, int vol_id, + int *major, int *minor) +{ + char file[strlen(lib->vol_dev) + 100]; + + sprintf(file, lib->vol_dev, dev_num, vol_id); + return read_major(file, major, minor); } -int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) +/** + * vol_node2nums - find UBI device number and volume ID by volume device node + * file. + * @lib: UBI library descriptor + * @node: UBI character device node name + * @dev_num: UBI device number is returned here + * @vol_id: volume ID is returned hers + * + * This function returns zero in case of succes and %-1 in case of failure. + */ +static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, + int *vol_id) { - int fd, ret; - struct ubi_mkvol_req r; - size_t n; + struct stat st; + struct ubi_info info; + int i, fd, major, minor; + char file[strlen(lib->ubi_vol) + 100]; - desc = desc; - r.vol_id = req->vol_id; - r.alignment = req->alignment; - r.bytes = req->bytes; - r.vol_type = req->vol_type; + if (lstat(node, &st)) + return -1; - n = strlen(req->name); - if (n > UBI_MAX_VOLUME_NAME) + if (!S_ISCHR(st.st_mode)) { + errmsg("\"%s\" is not a character device", node); + errno = EINVAL; return -1; + } - strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); - r.name_len = n; + major = major(st.st_rdev); + minor = minor(st.st_rdev); - fd = open(node, O_RDONLY); - if (fd == -1) + if (minor == 0) { + errmsg("\"%s\" is not a volume character device", node); + errno = -EINVAL; return -1; + } - ret = ioctl(fd, UBI_IOCMKVOL, &r); - if (!ret) - req->vol_id = r.vol_id; + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; - close(fd); + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; -#ifdef UDEV_SETTLE_HACK - if (system("udevsettle") == -1) + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } + + if (major1 == major) + break; + } + + if (i > info.highest_dev_num) { + errno = ENOENT; return -1; -#endif + } - return ret; + /* Make sure this UBI volume exists */ + sprintf(file, lib->ubi_vol, i, minor - 1); + fd = open(file, O_RDONLY); + if (fd == -1) { + errno = ENOENT; + return -1; + } + + *dev_num = i; + *vol_id = minor - 1; + return 0; } -int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +/** + * dev_node2num - find UBI device number by its character device node. + * @lib: UBI library descriptor + * @node: UBI character device node name + * + * This function returns positive UBI device number in case of success and %-1 + * in case of failure. + */ +static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) { - int fd, ret; + struct stat stat; + struct ubi_info info; + int i, major, minor; - desc = desc; - fd = open(node, O_RDONLY); - if (fd == -1) + if (lstat(node, &stat)) return -1; - ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); - close(fd); - -#ifdef UDEV_SETTLE_HACK - if (system("udevsettle") == -1) + if (!S_ISCHR(stat.st_mode)) { + errmsg("\"%s\" is not a character device", node); + errno = EINVAL; return -1; -#endif + } - return ret; -} + major = major(stat.st_rdev); + minor = minor(stat.st_rdev); -int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) -{ - int fd, ret; - struct ubi_rsvol_req req; + if (minor != 0) { + errmsg("\"%s\" is not an UBI character device", node); + errno = -EINVAL; + return -1; + } - desc = desc; - fd = open(node, O_RDONLY); - if (fd == -1) + if (ubi_get_info((libubi_t *)lib, &info)) return -1; - req.bytes = bytes; - req.vol_id = vol_id; + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; - ret = ioctl(fd, UBI_IOCRSVOL, &req); - close(fd); - return ret; -} + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } -int ubi_update_start(libubi_t desc, int fd, long long bytes) -{ - desc = desc; - if (ioctl(fd, UBI_IOCVOLUP, &bytes)) - return -1; - return 0; + if (major1 == major) { + if (minor1 != 0) { + errmsg("UBI character device minor number is " + "%d, but must be 0", minor1); + errno = EINVAL; + return -1; + } + *dev_num = i; + return 0; + } + } + + errno = ENOENT; + return -1; } -int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) +libubi_t libubi_open(void) { - int dev_num; - struct libubi *lib = (struct libubi *)desc; + int fd, version; + struct libubi *lib; - dev_num = find_dev_num(lib, node); - if (dev_num == -1) - return -1; + lib = calloc(1, sizeof(struct libubi)); + if (!lib) + return NULL; - return ubi_get_dev_info1(desc, dev_num, info); -} + /* TODO: this must be discovered instead */ + lib->sysfs = strdup("/sys"); + if (!lib->sysfs) + goto out_error; -int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) -{ - DIR *sysfs_ubi; - struct dirent *dirent; - struct libubi *lib = (struct libubi *)desc; + lib->sysfs_ctrl = mkpath(lib->sysfs, SYSFS_CTRL); + if (!lib->sysfs_ctrl) + goto out_error; - memset(info, '\0', sizeof(struct ubi_dev_info)); - info->dev_num = dev_num; + lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV); + if (!lib->ctrl_dev) + goto out_error; - sysfs_ubi = opendir(lib->sysfs_ubi); - if (!sysfs_ubi) - return -1; + lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); + if (!lib->sysfs_ubi) + goto out_error; - info->lowest_vol_num = INT_MAX; - while ((dirent = readdir(sysfs_ubi))) { - char *name = &dirent->d_name[0]; - int vol_id, ret, devno; + /* Make sure UBI is present */ + fd = open(lib->sysfs_ubi, O_RDONLY); + if (fd == -1) { + errmsg("cannot open \"%s\", UBI does not seem to exist in system", + lib->sysfs_ubi); + goto out_error; + } - ret = sscanf(name, UBI_VOL_NAME_PATT, &devno, &vol_id); - if (ret == 2 && devno == dev_num) { - info->vol_count += 1; - if (vol_id > info->highest_vol_num) - info->highest_vol_num = vol_id; - if (vol_id < info->lowest_vol_num) - info->lowest_vol_num = vol_id; - } + if (close(fd)) { + errmsg("close failed on \"%s\"", lib->sysfs_ubi); + perror("close"); + goto out_error; } - closedir(sysfs_ubi); + lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); + if (!lib->ubi_dev) + goto out_error; + + lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); + if (!lib->ubi_version) + goto out_error; + + lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); + if (!lib->dev_dev) + goto out_error; + + lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); + if (!lib->dev_avail_ebs) + goto out_error; + + lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); + if (!lib->dev_total_ebs) + goto out_error; + + lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); + if (!lib->dev_bad_count) + goto out_error; + + lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); + if (!lib->dev_eb_size) + goto out_error; + + lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); + if (!lib->dev_max_ec) + goto out_error; + + lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); + if (!lib->dev_bad_rsvd) + goto out_error; + + lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); + if (!lib->dev_max_vols) + goto out_error; + + lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); + if (!lib->dev_min_io_size) + goto out_error; + + lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); + if (!lib->ubi_vol) + goto out_error; + + lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); + if (!lib->vol_type) + goto out_error; + + lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); + if (!lib->vol_dev) + goto out_error; + + lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); + if (!lib->vol_alignment) + goto out_error; + + lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); + if (!lib->vol_data_bytes) + goto out_error; + + lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); + if (!lib->vol_rsvd_ebs) + goto out_error; + + lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); + if (!lib->vol_eb_size) + goto out_error; + + lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); + if (!lib->vol_corrupted) + goto out_error; - if (info->lowest_vol_num == INT_MAX) - info->lowest_vol_num = 0; + lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); + if (!lib->vol_name) + goto out_error; - if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs)) - return -1; - if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs)) - return -1; - if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) - return -1; - if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size)) - return -1; - if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) - return -1; - if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) - return -1; - if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) - return -1; - if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) - return -1; + if (read_positive_int(lib->ubi_version, &version)) + goto out_error; + if (version != LIBUBI_UBI_VERSION) { + fprintf(stderr, "LIBUBI: this library was made for UBI version " + "%d, but UBI version %d is detected\n", + LIBUBI_UBI_VERSION, version); + goto out_error; + } - info->avail_bytes = info->avail_ebs * info->eb_size; - info->total_bytes = info->total_ebs * info->eb_size; + return lib; - return 0; +out_error: + libubi_close((libubi_t)lib); + return NULL; } -int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) +void libubi_close(libubi_t desc) { - int vol_id, dev_num; struct libubi *lib = (struct libubi *)desc; - dev_num = find_dev_num_vol(lib, node); - if (dev_num == -1) - return -1; - - vol_id = find_vol_num(lib, dev_num, node); - if (vol_id == -1) - return -1; - - return ubi_get_vol_info1(desc, dev_num, vol_id, info); + free(lib->vol_name); + free(lib->vol_corrupted); + free(lib->vol_eb_size); + free(lib->vol_rsvd_ebs); + free(lib->vol_data_bytes); + free(lib->vol_alignment); + free(lib->vol_dev); + free(lib->vol_type); + free(lib->ubi_vol); + free(lib->dev_min_io_size); + free(lib->dev_max_vols); + free(lib->dev_bad_rsvd); + free(lib->dev_max_ec); + free(lib->dev_eb_size); + free(lib->dev_bad_count); + free(lib->dev_total_ebs); + free(lib->dev_avail_ebs); + free(lib->dev_dev); + free(lib->ubi_version); + free(lib->ubi_dev); + free(lib->sysfs_ubi); + free(lib->ctrl_dev); + free(lib->sysfs_ctrl); + free(lib->sysfs); + free(lib); } -int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, - struct ubi_vol_info *info) +int ubi_node_type(libubi_t desc, const char *node) { - int ret; + struct stat st; + struct ubi_info info; + int i, fd, major, minor; struct libubi *lib = (struct libubi *)desc; - char buf[50]; - - memset(info, '\0', sizeof(struct ubi_vol_info)); - info->dev_num = dev_num; - info->vol_id = vol_id; + char file[strlen(lib->ubi_vol) + 100]; - ret = vol_read_data(lib->vol_type, dev_num, vol_id, &buf[0], 50); - if (ret < 0) + if (lstat(node, &st)) return -1; - if (strncmp(&buf[0], "static\n", ret) == 0) - info->type = UBI_STATIC_VOLUME; - else if (strncmp(&buf[0], "dynamic\n", ret) == 0) - info->type = UBI_DYNAMIC_VOLUME; - else { - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + if (!S_ISCHR(st.st_mode)) { + errmsg("\"%s\" is not a character device", node); errno = EINVAL; return -1; } - ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, - &info->alignment); - if (ret) - return -1; - ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, - &info->data_bytes); - if (ret) - return -1; - ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs); - if (ret) - return -1; - ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size); - if (ret) - return -1; - ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, - &info->corrupted); - if (ret) - return -1; - info->rsvd_bytes = info->eb_size * info->rsvd_ebs; + major = major(st.st_rdev); + minor = minor(st.st_rdev); - ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, - UBI_VOL_NAME_MAX + 2); - if (ret < 0) + if (ubi_get_info((libubi_t *)lib, &info)) return -1; - info->name[ret - 1] = '\0'; - return 0; -} - -/** - * read_int - read an 'int' value from a file. - * - * @file the file to read from - * @value the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int read_int(const char *file, int *value) -{ - int fd, rd; - char buf[50]; - - fd = open(file, O_RDONLY); - if (fd == -1) - return -1; + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; - rd = read(fd, &buf[0], 50); - if (rd == -1) - goto error; + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } - if (sscanf(&buf[0], "%d\n", value) != 1) { - /* This must be a UBI bug */ - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - goto error; + if (major1 == major) + break; } - close(fd); - return 0; - -error: - close(fd); - return -1; -} - -/** - * dev_read_int - read an 'int' value from an UBI device's sysfs file. - * - * @patt the file pattern to read from - * @dev_num UBI device number - * @value the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int dev_read_int(const char *patt, int dev_num, int *value) -{ - int fd, rd; - char buf[50]; - char file[strlen(patt) + 50]; - - sprintf(&file[0], patt, dev_num); - fd = open(&file[0], O_RDONLY); - if (fd == -1) + if (i > info.highest_dev_num) { + /* + * The character device node does not correspond to any + * existing UBI device or volume, but we do not want to return + * any error number in this case, to indicate the fact that it + * could be a UBI device/volume, but it doesn't. + */ + errno = 0; return -1; + } - rd = read(fd, &buf[0], 50); - if (rd == -1) - goto error; + if (minor == 0) + return 1; - if (sscanf(&buf[0], "%d\n", value) != 1) { - /* This must be a UBI bug */ - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - goto error; + /* This is supposdely an UBI volume device node */ + sprintf(file, lib->ubi_vol, i, minor - 1); + fd = open(file, O_RDONLY); + if (fd == -1) { + errno = 0; + return -1; } - close(fd); - return 0; - -error: - close(fd); - return -1; + return 2; } -/** - * dev_read_ll - read a 'long long' value from an UBI device's sysfs file. - * - * @patt the file pattern to read from - * @dev_num UBI device number - * @value the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int dev_read_ll(const char *patt, int dev_num, long long *value) +int ubi_get_info(libubi_t desc, struct ubi_info *info) { - int fd, rd; - char buf[50]; - char file[strlen(patt) + 50]; + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; - sprintf(&file[0], patt, dev_num); - fd = open(&file[0], O_RDONLY); - if (fd == -1) - return -1; + memset(info, '\0', sizeof(struct ubi_info)); - rd = read(fd, &buf[0], 50); - if (rd == -1) - goto error; + if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) + return -1; - if (sscanf(&buf[0], "%lld\n", value) != 1) { - /* This must be a UBI bug */ - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - goto error; + /* + * We have to scan the UBI sysfs directory to identify how many UBI + * devices are present. + */ + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) { + errmsg("cannot open %s", lib->sysfs_ubi); + perror("opendir"); + return -1; } - close(fd); - return 0; + info->lowest_dev_num = INT_MAX; + while ((dirent = readdir(sysfs_ubi))) { + int dev_num, ret; -error: - close(fd); - return -1; -} + /* + * Make sure this direntry is a directory and not a symlink - + * Linux puts symlinks to UBI volumes on this UBI device to the + * same sysfs directory. + */ + if (!dent_is_dir(lib->sysfs_ubi, dirent->d_name)) + continue; -/** - * dev_read_data - read data from an UBI device's sysfs file. - * - * @patt the file pattern to read from - * @dev_num UBI device number - * @buf buffer to read data to - * @buf_len buffer length - * - * This function returns number of read bytes in case of success and %-1 in - * case of failure. - */ -static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len) -{ - int fd, rd; - char file[strlen(patt) + 50]; + ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT, &dev_num); + if (ret == 1) { + info->dev_count += 1; + if (dev_num > info->highest_dev_num) + info->highest_dev_num = dev_num; + if (dev_num < info->lowest_dev_num) + info->lowest_dev_num = dev_num; + } + } - sprintf(&file[0], patt, dev_num); - fd = open(&file[0], O_RDONLY); - if (fd == -1) - return -1; + if (!dirent && errno) { + errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + perror("readdir"); + goto out_close; + } - rd = read(fd, buf, buf_len); - if (rd == -1) { - close(fd); + if (closedir(sysfs_ubi)) { + errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + perror("closedir"); return -1; } - close(fd); - return rd; -} - -/** - * vol_read_int - read an 'int' value from an UBI volume's sysfs file. - * - * @patt the file pattern to read from - * @dev_num UBI device number - * @vol_id volume identifier - * @value the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) -{ - int fd, rd; - char buf[50]; - char file[strlen(patt) + 100]; + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; - sprintf(&file[0], patt, dev_num, vol_id); - fd = open(&file[0], O_RDONLY); - if (fd == -1) + if (read_positive_int(lib->ubi_version, &info->version)) return -1; - rd = read(fd, &buf[0], 50); - if (rd == -1) - goto error; - - if (sscanf(&buf[0], "%d\n", value) != 1) { - /* This must be a UBI bug */ - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - goto error; - } - - close(fd); return 0; -error: - close(fd); +out_close: + closedir(sysfs_ubi); return -1; } -/** - * vol_read_ll - read a 'long long' value from an UBI volume's sysfs file. - * - * @patt the file pattern to read from - * @dev_num UBI device number - * @vol_id volume identifier - * @value the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int vol_read_ll(const char *patt, int dev_num, int vol_id, - long long *value) +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) { - int fd, rd; - char buf[50]; - char file[strlen(patt) + 100]; + int fd, ret; + struct ubi_mkvol_req r; + size_t n; + + memset(&r, sizeof(struct ubi_mkvol_req), '\0'); + + desc = desc; + r.vol_id = req->vol_id; + r.alignment = req->alignment; + r.bytes = req->bytes; + r.vol_type = req->vol_type; + + n = strlen(req->name); + if (n > UBI_MAX_VOLUME_NAME) + return -1; + + strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); + r.name_len = n; - sprintf(&file[0], patt, dev_num, vol_id); - fd = open(&file[0], O_RDONLY); + fd = open(node, O_RDONLY); if (fd == -1) return -1; - rd = read(fd, &buf[0], 50); - if (rd == -1) - goto error; - - if (sscanf(&buf[0], "%lld\n", value) != 1) { - /* This must be a UBI bug */ - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - goto error; - } + ret = ioctl(fd, UBI_IOCMKVOL, &r); + if (ret == 0) + req->vol_id = r.vol_id; close(fd); - return 0; -error: - close(fd); - return -1; +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + + return ret; } -/** - * vol_read_data - read data from an UBI volume's sysfs file. - * - * @patt the file pattern to read from - * @dev_num UBI device number - * @vol_id volume identifier - * @buf buffer to read to - * @buf_len buffer length - * - * This function returns number of read bytes in case of success and %-1 in - * case of failure. - */ -static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, - int buf_len) +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) { - int fd, rd; - char file[strlen(patt) + 100]; + int fd, ret; - sprintf(&file[0], patt, dev_num, vol_id); - fd = open(&file[0], O_RDONLY); + desc = desc; + fd = open(node, O_RDONLY); if (fd == -1) return -1; - rd = read(fd, buf, buf_len); - if (rd == -1) { - close(fd); + ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); + close(fd); + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) return -1; - } +#endif - close(fd); - return rd; + return ret; } -/** - * mkpath - compose full path from 2 given components. - * - * @path first component - * @name second component - * - * This function returns the resulting path in case of success and %NULL in - * case of failure. - */ -static char *mkpath(const char *path, const char *name) +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) { - char *n; - int len1 = strlen(path); - int len2 = strlen(name); + int fd, ret; + struct ubi_rsvol_req req; - n = malloc(len1 + len2 + 2); - if (!n) - return NULL; + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; - memcpy(n, path, len1); - if (n[len1 - 1] != '/') - n[len1++] = '/'; + req.bytes = bytes; + req.vol_id = vol_id; - memcpy(n + len1, name, len2 + 1); - return n; + ret = ioctl(fd, UBI_IOCRSVOL, &req); + close(fd); + return ret; } -/** - * find_dev_num - find UBI device number by its character device node. - * - * @lib UBI library descriptor - * @node UBI character device node name - * - * This function returns positive UBI device number in case of success and %-1 - * in case of failure. - */ -static int find_dev_num(struct libubi *lib, const char *node) +int ubi_update_start(libubi_t desc, int fd, long long bytes) { - struct stat stat; - struct ubi_info info; - int i, major, minor; - - if (lstat(node, &stat)) - return -1; - - if (!S_ISCHR(stat.st_mode)) { - errno = EINVAL; + desc = desc; + if (ioctl(fd, UBI_IOCVOLUP, &bytes)) return -1; - } + return 0; +} - major = major(stat.st_rdev); - minor = minor(stat.st_rdev); +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; - if (minor != 0) { - errno = -EINVAL; - return -1; - } + memset(info, '\0', sizeof(struct ubi_dev_info)); + info->dev_num = dev_num; - if (ubi_get_info((libubi_t *)lib, &info)) + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) return -1; - for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { - int major1, minor1, ret; - char buf[50]; - - ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); - if (ret < 0) - return -1; + info->lowest_vol_num = INT_MAX; + while ((dirent = readdir(sysfs_ubi))) { + int vol_id, ret, devno; - ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); - if (ret != 2) { - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - return -1; + ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT, &devno, &vol_id); + if (ret == 2 && devno == dev_num) { + info->vol_count += 1; + if (vol_id > info->highest_vol_num) + info->highest_vol_num = vol_id; + if (vol_id < info->lowest_vol_num) + info->lowest_vol_num = vol_id; } - - if (minor1 == minor && major1 == major) - return i; } - errno = ENOENT; - return -1; -} - -/** - * find_dev_num_vol - find UBI device number by volume character device node. - * - * @lib UBI library descriptor - * @node UBI character device node name - * - * This function returns positive UBI device number in case of success and %-1 - * in case of failure. - */ -static int find_dev_num_vol(struct libubi *lib, const char *node) -{ - struct stat stat; - struct ubi_info info; - int i, major; - - if (lstat(node, &stat)) - return -1; + if (!dirent && errno) { + errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + perror("readdir"); + goto out_close; + } - if (!S_ISCHR(stat.st_mode)) { - errno = EINVAL; + if (closedir(sysfs_ubi)) { + errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + perror("closedir"); return -1; } - major = major(stat.st_rdev); + if (info->lowest_vol_num == INT_MAX) + info->lowest_vol_num = 0; - if (minor(stat.st_rdev) == 0) { - errno = -EINVAL; + if (dev_get_major(lib, dev_num, &info->major, &info->minor)) return -1; - } - if (ubi_get_info((libubi_t *)lib, &info)) + if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs)) + return -1; + if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs)) + return -1; + if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) + return -1; + if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size)) + return -1; + if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) + return -1; + if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) + return -1; + if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) + return -1; + if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) return -1; - for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { - int major1, minor1, ret; - char buf[50]; - - ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); - if (ret < 0) - return -1; - - ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); - if (ret != 2) { - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - return -1; - } + info->avail_bytes = info->avail_ebs * info->eb_size; + info->total_bytes = info->total_ebs * info->eb_size; - if (major1 == major) - return i; - } + return 0; - errno = ENOENT; +out_close: + closedir(sysfs_ubi); return -1; } -/** - * find_vol_num - find UBI volume number by its character device node. - * - * @lib UBI library descriptor - * @dev_num UBI device number - * @node UBI volume character device node name - * - * This function returns positive UBI volume number in case of success and %-1 - * in case of failure. - */ -static int find_vol_num(struct libubi *lib, int dev_num, const char *node) +int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) { - struct stat stat; - struct ubi_dev_info info; - int i, major, minor; + int dev_num; + struct libubi *lib = (struct libubi *)desc; - if (lstat(node, &stat)) + if (dev_node2num(lib, node, &dev_num)) return -1; - if (!S_ISCHR(stat.st_mode)) { - errno = EINVAL; + return ubi_get_dev_info1(desc, dev_num, info); +} + +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info) +{ + int ret; + struct libubi *lib = (struct libubi *)desc; + char buf[50]; + + memset(info, '\0', sizeof(struct ubi_vol_info)); + info->dev_num = dev_num; + info->vol_id = vol_id; + + if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor)) + return -1; + if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) return -1; - } - major = major(stat.st_rdev); - minor = minor(stat.st_rdev); + ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); + if (ret < 0) + return -1; - if (minor == 0) { - errno = -EINVAL; + if (strncmp(buf, "static\n", ret) == 0) + info->type = UBI_STATIC_VOLUME; + else if (strncmp(buf, "dynamic\n", ret) == 0) + info->type = UBI_DYNAMIC_VOLUME; + else { + errmsg("bad value at \"%s\"", buf); + errno = EINVAL; return -1; } - if (ubi_get_dev_info1((libubi_t *)lib, dev_num, &info)) + ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, + &info->alignment); + if (ret) + return -1; + ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, + &info->data_bytes); + if (ret) + return -1; + ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs); + if (ret) + return -1; + ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size); + if (ret) + return -1; + ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, + &info->corrupted); + if (ret) return -1; + info->rsvd_bytes = info->eb_size * info->rsvd_ebs; - for (i = info.lowest_vol_num; i <= info.highest_vol_num; i++) { - int major1, minor1, ret; - char buf[50]; + ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, + UBI_VOL_NAME_MAX + 2); + if (ret < 0) + return -1; - ret = vol_read_data(lib->vol_dev, dev_num, i, &buf[0], 50); - if (ret < 0) - return -1; + info->name[ret - 1] = '\0'; + return 0; +} - ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); - if (ret != 2) { - fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); - errno = EINVAL; - return -1; - } +int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) +{ + int vol_id, dev_num; + struct libubi *lib = (struct libubi *)desc; - if (minor1 == minor && major1 == major) - return i; - } + if (vol_node2nums(lib, node, &dev_num, &vol_id)) + return -1; - errno = ENOENT; - return -1; + return ubi_get_vol_info1(desc, dev_num, vol_id, info); } diff --git a/ubi-utils/src/libubi_int.h b/ubi-utils/src/libubi_int.h index e68b791..4c26eb5 100644 --- a/ubi-utils/src/libubi_int.h +++ b/ubi-utils/src/libubi_int.h @@ -27,16 +27,29 @@ extern "C" { #endif +/* Error messages */ +#define errmsg(fmt, ...) do { \ + fprintf(stderr, "libubi error: " fmt "\n", ##__VA_ARGS__); \ +} while(0) + /* - * UBI heavily makes use of the sysfs file system to interact with users-pace. * The below are pre-define UBI file and directory names. + * + * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'. + * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is + * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y' + * directories to '/sys/class/ubi/'. For now libubi assumes old layout. */ #define SYSFS_UBI "class/ubi" -#define UBI_DEV_NAME_PATT "ubi%d" +#define SYSFS_CTRL "class/misc/ubi_ctrl/" + +#define CTRL_DEV "dev" + #define UBI_VER "version" +#define UBI_DEV_NAME_PATT "ubi%d" + #define DEV_DEV "dev" -#define UBI_VOL_NAME_PATT "ubi%d_%d" #define DEV_AVAIL_EBS "avail_eraseblocks" #define DEV_TOTAL_EBS "total_eraseblocks" #define DEV_BAD_COUNT "bad_peb_count" @@ -45,6 +58,8 @@ extern "C" { #define DEV_MAX_RSVD "reserved_for_bad" #define DEV_MAX_VOLS "max_vol_count" #define DEV_MIN_IO_SIZE "min_io_size" + +#define UBI_VOL_NAME_PATT "ubi%d_%d" #define VOL_TYPE "type" #define VOL_DEV "dev" #define VOL_ALIGNMENT "alignment" @@ -56,34 +71,37 @@ extern "C" { /** * libubi - UBI library description data structure. - * - * @sysfs sysfs file system path - * @sysfs_ubi UBI directory in sysfs - * @ubi_dev UBI device sysfs directory pattern - * @ubi_version UBI version file sysfs path - * @dev_dev UBI device's major/minor numbers file pattern - * @dev_avail_ebs count of available eraseblocks sysfs path pattern - * @dev_total_ebs total eraseblocks count sysfs path pattern - * @dev_bad_count count of bad eraseblocks sysfs path pattern - * @dev_eb_size size of UBI device's eraseblocks sysfs path pattern - * @dev_max_ec maximum erase counter sysfs path pattern - * @dev_bad_rsvd count of physical eraseblock reserved for bad eraseblocks - * handling - * @dev_max_vols maximum volumes number count sysfs path pattern - * @dev_min_io_size minimum I/O unit size sysfs path pattern - * @ubi_vol UBI volume sysfs directory pattern - * @vol_type volume type sysfs path pattern - * @vol_dev volume's major/minor numbers file pattern - * @vol_alignment volume alignment sysfs path pattern - * @vol_data_bytes volume data size sysfs path pattern - * @vol_rsvd_ebs volume reserved size sysfs path pattern - * @vol_eb_size volume eraseblock size sysfs path pattern - * @vol_corrupted volume corruption flag sysfs path pattern - * @vol_name volume name sysfs path pattern + * @sysfs: sysfs file system path + * @sysfs_ctrl: UBI control device directory in sysfs + * @ctrl_dev: UBI control device major/minor numbers sysfs file + * @sysfs_ubi: UBI directory in sysfs + * @ubi_dev: UBI device sysfs directory pattern + * @ubi_version: UBI version file sysfs path + * @dev_dev: UBI device major/minor numbers file pattern + * @dev_avail_ebs: count of available eraseblocks sysfs path pattern + * @dev_total_ebs: total eraseblocks count sysfs path pattern + * @dev_bad_count: count of bad eraseblocks sysfs path pattern + * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern + * @dev_max_ec: maximum erase counter sysfs path pattern + * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks + * handling + * @dev_max_vols: maximum volumes number count sysfs path pattern + * @dev_min_io_size: minimum I/O unit size sysfs path pattern + * @ubi_vol: UBI volume sysfs directory pattern + * @vol_type: volume type sysfs path pattern + * @vol_dev: volume major/minor numbers file pattern + * @vol_alignment: volume alignment sysfs path pattern + * @vol_data_bytes: volume data size sysfs path pattern + * @vol_rsvd_ebs: volume reserved size sysfs path pattern + * @vol_eb_size: volume eraseblock size sysfs path pattern + * @vol_corrupted: volume corruption flag sysfs path pattern + * @vol_name: volume name sysfs path pattern */ struct libubi { char *sysfs; + char *sysfs_ctrl; + char *ctrl_dev; char *sysfs_ubi; char *ubi_dev; char *ubi_version; @@ -108,20 +126,6 @@ struct libubi char *vol_max_count; }; -static int read_int(const char *file, int *value); -static int dev_read_int(const char *patt, int dev_num, int *value); -static int dev_read_ll(const char *patt, int dev_num, long long *value); -static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len); -static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value); -static int vol_read_ll(const char *patt, int dev_num, int vol_id, - long long *value); -static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, - int buf_len); -static char *mkpath(const char *path, const char *name); -static int find_dev_num(struct libubi *lib, const char *node); -static int find_dev_num_vol(struct libubi *lib, const char *node); -static int find_vol_num(struct libubi *lib, int dev_num, const char *node); - #ifdef __cplusplus } #endif diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index 454e6fe..488dd17 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -30,7 +30,6 @@ #include #include -#include #include #include "common.h" @@ -39,7 +38,6 @@ /* The variables below is set by command line arguments */ struct args { - int devn; int vol_id; int vol_type; long long bytes; @@ -47,13 +45,12 @@ struct args { int alignment; char *name; int nlen; - char node[MAX_NODE_LEN + 1]; + char node[MAX_NODE_LEN + 2]; int maxavs; }; static struct args myargs = { .vol_type = UBI_DYNAMIC_VOLUME, - .devn = -1, .bytes = -1, .lebs = -1, .alignment = 1, @@ -83,15 +80,14 @@ static const char *optionsstr = static const char *usage = "Usage: " PROGRAM_NAME " [-h] [-a ] [-n ] [-N ]\n" "\t\t\t[-s ] [-S ] [-t ] [-V] [-m]\n" -"\t\t\t[--alignment=] [--devn=] [--vol_id=]\n" -"\t\t\t[--name=] [--size=] [--lebs=]\n" -"\t\t\t[--type=] [--help] [--version] [--maxavsize]\n\n" +"\t\t\t[--alignment=][--vol_id=] [--name=]\n" +"\t\t\t[--size=] [--lebs=] [--type=] [--help]\n" +"\t\t\t[--version] [--maxavsize]\n\n" "Example: " PROGRAM_NAME "/dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume\n" " named \"config_data\" on UBI device /dev/ubi0."; static const struct option long_options[] = { { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' }, - { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, @@ -110,8 +106,7 @@ static int parse_opt(int argc, char * const argv[], struct args *args) while (1) { int key; - key = getopt_long(argc, argv, "a:d:n:N:s:S:t:hVm", - long_options, NULL); + key = getopt_long(argc, argv, "a:n:N:s:S:t:hVm", long_options, NULL); if (key == -1) break; @@ -161,20 +156,6 @@ static int parse_opt(int argc, char * const argv[], struct args *args) } break; - case 'd': - args->devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args->devn < 0) { - errmsg("bad UBI device number: \"%s\"", optarg); - return -1; - } - - warnmsg("'-d' and '--devn' options are deprecated and will be " - "removed. Specify UBI device node name instead!\n" - "Example: " PROGRAM_NAME " /dev/ubi0, instead of " - PROGRAM_NAME " -d 0"); - sprintf(args->node, "/dev/ubi%d", args->devn); - break; - case 'n': args->vol_id = strtoul(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || args->vol_id < 0) { @@ -215,7 +196,7 @@ static int parse_opt(int argc, char * const argv[], struct args *args) return 0; } -static int param_sanity_check(struct args *args, libubi_t libubi) +static int param_sanity_check(struct args *args) { int len; @@ -225,7 +206,7 @@ static int param_sanity_check(struct args *args, libubi_t libubi) return -1; } - if (args->bytes == -1 && !args->maxavs && !args->lebs) { + if (args->bytes == -1 && !args->maxavs && args->lebs == -1) { errmsg("volume size was not specified (use -h for help)"); return -1; } @@ -242,23 +223,6 @@ static int param_sanity_check(struct args *args, libubi_t libubi) return -1; } - if (args->devn != -1) { - int err; - struct ubi_info ubi; - - err = ubi_get_info(libubi, &ubi); - if (err) { - errmsg("cannot get UBI information"); - perror("ubi_get_info"); - return -1; - } - - if (args->devn >= ubi.dev_count) { - errmsg("UBI device %d does not exist", args->devn); - return -1; - } - } - len = strlen(args->name); if (len > UBI_MAX_VOLUME_NAME) { errmsg("too long name (%d symbols), max is %d", @@ -287,27 +251,37 @@ int main(int argc, char * const argv[]) return -1; } - strncpy(myargs.node, argv[1], MAX_NODE_LEN); + if (argv[1][0] == '-') { + errmsg("UBI device was not specified (use -h for help)"); + return -1; + } + + if (argv[2][0] != '-') { + errmsg("incorrect arguments, use -h for help"); + return -1; + } + + strncpy(myargs.node, argv[1], MAX_NODE_LEN + 1); err = parse_opt(argc, (char **)argv, &myargs); if (err) return err; libubi = libubi_open(); - if (libubi == NULL) { + if (!libubi) { errmsg("cannot open libubi"); perror("libubi_open"); return -1; } - err = param_sanity_check(&myargs, libubi); + err = param_sanity_check(&myargs); if (err) goto out_libubi; err = ubi_get_dev_info(libubi, myargs.node, &dev_info); if (err) { - errmsg("cannot get information about UBI device number %d (%s)", - myargs.devn, myargs.node); + errmsg("cannot get information about UBI device number %s", + myargs.node); perror("ubi_get_dev_info"); goto out_libubi; } @@ -346,19 +320,11 @@ int main(int argc, char * const argv[]) goto out_libubi; } - printf("Volume ID is %d, size %lld LEBs (%lld bytes, ", - vol_info.vol_id, vol_info.rsvd_bytes / vol_info.eb_size, - vol_info.rsvd_bytes); - - if (vol_info.rsvd_bytes > 1024 * 1024 * 1024) - printf("%.1f GiB)", (double)vol_info.rsvd_bytes / (1024 * 1024 * 1024)); - else if (vol_info.rsvd_bytes > 1024 * 1024) - printf("%.1f MiB)", (double)vol_info.rsvd_bytes / (1024 * 1024)); - else - printf("%.1f KiB)", (double)vol_info.rsvd_bytes / 1024); - - printf(" LEB size is %d bytes (%.1f KiB), %s volume, name \"%s\", alignment %d\n", - vol_info.eb_size, (double)vol_info.eb_size / 1024, + printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_ebs); + ubiutils_print_bytes(vol_info.rsvd_bytes, 0); + printf("), LEB size "); + ubiutils_print_bytes(vol_info.eb_size, 1); + printf(", %s volume, name \"%s\", alignment %d\n", req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static", vol_info.name, vol_info.alignment); diff --git a/ubi-utils/src/ubinfo.c b/ubi-utils/src/ubinfo.c new file mode 100644 index 0000000..358f208 --- /dev/null +++ b/ubi-utils/src/ubinfo.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Artem Bityutskiy + */ + +/* + * An utility to get UBI information. + */ +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "ubinfo" + +/* The variables below is set by command line arguments */ +struct args { + int devn; + int vol_id; + int all; + char node[MAX_NODE_LEN + 2]; + int node_given; +}; + +static struct args myargs = { + .vol_id = -1, + .devn = -1, + .all = 0, + .node_given = 0, +}; + +static const char *doc = "Version " PROGRAM_VERSION "\n" + PROGRAM_NAME " - a tool to UBI information."; + +static const char *optionsstr = +"-d, --devn= UBI device number to get information about\n" +"-n, --vol_id= ID of UBI volume to print information about\n" +"-a, --all print information about all devices and volumes,\n" +" or about all volumes if the UBI device was\n" +" specified\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage 1: " PROGRAM_NAME " [-d ] [-n ] [-a] [-h] [-V] [--vol_id=]\n" +"\t\t[--devn ] [--all] [--help] [--version]\n" +"Usage 2: " PROGRAM_NAME " [-a] [-h] [-V] [--all] [--help] [--version]\n" +"Usage 3: " PROGRAM_NAME " [-h] [-V] [--help] [--version]\n\n" +"Example 1: " PROGRAM_NAME " - (no arguments) print general UBI information\n" +"Example 2: " PROGRAM_NAME " -d 1 - print information about UBI device number 1\n" +"Example 3: " PROGRAM_NAME " /dev/ubi0 -a - print information about all volumes of UBI\n" +" device /dev/ubi0\n" +"Example 4: " PROGRAM_NAME " /dev/ubi1_0 - print information about UBI volume /dev/ubi1_0\n" +"Example 5: " PROGRAM_NAME " -a - print all information\n"; + +static const struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0}, +}; + +static int parse_opt(int argc, char * const argv[], struct args *args) +{ + char *endp; + + while (1) { + int key; + + key = getopt_long(argc, argv, "an:d:hV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'a': + args->all = 1; + break; + + case 'n': + args->vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args->vol_id < 0) { + errmsg("bad volume ID: " "\"%s\"", optarg); + return -1; + } + break; + + case 'd': + args->devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args->devn < 0) { + errmsg("bad UBI device number: \"%s\"", optarg); + return -1; + } + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(0); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(0); + + case ':': + errmsg("parameter is missing"); + return -1; + + default: + fprintf(stderr, "Use -h for help\n"); + exit(-1); + } + } + + return 0; +} + +static int translate_dev(libubi_t libubi, const char *node) +{ + int err; + + if (strlen(node) > MAX_NODE_LEN) { + errmsg("too long device node name: \"%s\" (%d characters), max. is %d", + node, strlen(node), MAX_NODE_LEN); + return -1; + } + + err = ubi_node_type(libubi, node); + if (err == -1) { + if (errno) { + errmsg("unrecognized device node \"%s\"", node); + perror("ubi_node_type"); + return -1; + } + errmsg("\"%s\" does not correspond to any UBI device or volume", + node); + return -1; + } + + if (err == 1) { + struct ubi_dev_info dev_info; + + err = ubi_get_dev_info(libubi, node, &dev_info); + if (err) { + errmsg("cannot get information about UBI device \"%s\"", + node); + perror("ubi_get_dev_info"); + return -1; + } + + myargs.devn = dev_info.dev_num; + } else { + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info(libubi, node, &vol_info); + if (err) { + errmsg("cannot get information about UBI volume \"%s\"", + node); + perror("ubi_get_vol_info"); + return -1; + } + + if (myargs.vol_id != -1) { + errmsg("both volume character device node (\"%s\") and " + "volume ID (%d) are specify, use only one of them" + "(use -h for help)", node, myargs.vol_id); + return -1; + } + + myargs.devn = vol_info.dev_num; + myargs.vol_id = vol_info.vol_id; + } + + return 0; +} + +static int print_vol_info(libubi_t libubi, int dev_num, int vol_id) +{ + int err; + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info); + if (err) { + errmsg("cannot get information about UBI volume %d on ubi%d", + vol_id, dev_num); + perror("ubi_get_vol_info1"); + return -1; + } + + printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num); + printf("Type: %s\n", + vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"); + printf("Alignment: %d\n", vol_info.alignment); + + printf("Size: %d LEBs (", vol_info.rsvd_ebs); + ubiutils_print_bytes(vol_info.rsvd_bytes, 0); + printf(")\n"); + + if (vol_info.type == UBI_STATIC_VOLUME) { + printf("Data bytes: "); + ubiutils_print_bytes(vol_info.data_bytes, 1); + } + printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK"); + printf("Name: %s\n", vol_info.name); + printf("Character device major/minor: %d:%d\n", + vol_info.major, vol_info.minor); + + return 0; +} + +static int print_dev_info(libubi_t libubi, int dev_num, int all) +{ + int i, err, first = 1; + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + + err = ubi_get_dev_info1(libubi, dev_num, &dev_info); + if (err) { + errmsg("cannot get information about UBI device %d", dev_num); + perror("ubi_get_dev_info1"); + return -1; + } + + printf("ubi%d:\n", dev_info.dev_num); + printf("Volumes count: %d\n", dev_info.vol_count); + printf("Logical eraseblock size: %d\n", dev_info.eb_size); + + printf("Total amount of logical eraseblocks: %d (", dev_info.total_ebs); + ubiutils_print_bytes(dev_info.total_bytes, 0); + printf(")\n"); + + printf("Amount of available logical eraseblocks: %d", dev_info.avail_ebs); + ubiutils_print_bytes(dev_info.avail_bytes, 0); + printf(")\n"); + + printf("Maximum count of volumes %d\n", dev_info.max_vol_count); + printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count); + printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd); + printf("Current maximum erase counter value: %lld\n", dev_info.max_ec); + printf("Minimum input/output unit size: %d bytes\n", dev_info.min_io_size); + printf("Character device major/minor: %d:%d\n", + dev_info.major, dev_info.minor); + + if (dev_info.vol_count == 0) + return 0; + + printf("Present volumes: "); + for (i = dev_info.lowest_vol_num; + i <= dev_info.highest_vol_num; i++) { + err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + errmsg("libubi failed to probe volume %d on ubi%d", + i, dev_info.dev_num); + perror("ubi_get_vol_info1"); + return -1; + } + + if (!first) + printf(", %d", i); + else { + printf("%d", i); + first = 0; + } + } + printf("\n"); + + if (!all) + return 0; + + first = 1; + printf("\n"); + + for (i = dev_info.lowest_vol_num; + i <= dev_info.highest_vol_num; i++) { + if(!first) + printf("-----------------------------------\n"); + err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + errmsg("libubi failed to probe volume %d on ubi%d", + i, dev_info.dev_num); + perror("ubi_get_vol_info1"); + return -1; + } + first = 0; + + err = print_vol_info(libubi, dev_info.dev_num, i); + if (err) + return err; + } + + return 0; +} + +static int print_general_info(libubi_t libubi, int all) +{ + int i, err, first = 1; + struct ubi_info ubi_info; + struct ubi_dev_info dev_info; + + err = ubi_get_info(libubi, &ubi_info); + if (err) { + errmsg("cannot get UBI information"); + perror("ubi_get_info"); + return -1; + } + + printf("UBI version: %d\n", ubi_info.version); + printf("Count of UBI devices: %d\n", ubi_info.dev_count); + printf("UBI control device major/minor: %d:%d\n", + ubi_info.ctrl_major, ubi_info.ctrl_minor); + + if (ubi_info.dev_count == 0) + return 0; + + printf("Present UBI devices: "); + for (i = ubi_info.lowest_dev_num; + i <= ubi_info.highest_dev_num; i++) { + err = ubi_get_dev_info1(libubi, i, &dev_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + errmsg("libubi failed to probe UBI device %d", i); + perror("ubi_get_dev_info1"); + return -1; + } + + if (!first) + printf(", ubi%d", i); + else { + printf("ubi%d", i); + first = 0; + } + } + printf("\n"); + + if (!all) + return 0; + + first = 1; + printf("\n"); + + for (i = ubi_info.lowest_dev_num; + i <= ubi_info.highest_dev_num; i++) { + if(!first) + printf("\n===================================\n\n"); + err = ubi_get_dev_info1(libubi, i, &dev_info); + if (err == -1) { + if (errno == ENOENT) + continue; + + errmsg("libubi failed to probe UBI device %d", i); + perror("ubi_get_dev_info1"); + return -1; + } + first = 0; + + err = print_dev_info(libubi, i, all); + if (err) + return err; + } + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + + if (argc > 1 && argv[1][0] != '-') { + strncpy(myargs.node, argv[1], MAX_NODE_LEN + 1); + myargs.node_given = 1; + } + + if (argc > 2 && argv[2][0] != '-') { + errmsg("incorrect arguments, use -h for help"); + return -1; + } + + err = parse_opt(argc, argv, &myargs); + if (err) + return -1; + + if (argc > 1 && !myargs.node_given && myargs.devn != -1) { + errmsg("specify either device number or node file (use -h for help)"); + return -1; + } + + libubi = libubi_open(); + if (libubi == NULL) { + errmsg("cannot open libubi"); + perror("libubi_open"); + return -1; + } + + if (myargs.node_given) { + /* + * A character device was specified, translate this into UBI + * device number and volume ID. + */ + err = translate_dev(libubi, myargs.node); + if (err) + goto out_libubi; + } + + if (myargs.vol_id != -1 && myargs.devn == -1) { + errmsg("volume ID is specified, but UBI device number is not " + "(use -h for help)\n"); + goto out_libubi; + } + + if (myargs.devn != -1 && myargs.vol_id != -1) { + print_vol_info(libubi, myargs.devn, myargs.vol_id); + goto out; + } + + if (myargs.devn == -1 && myargs.vol_id == -1) + err = print_general_info(libubi, myargs.all); + else if (myargs.devn != -1 && myargs.vol_id == -1) + err = print_dev_info(libubi, myargs.devn, myargs.all); + + if (err) + goto out_libubi; + +out: + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index b6fb8ef..e9a1b41 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -30,7 +30,6 @@ #include #include -#include #include #include "common.h" @@ -39,18 +38,14 @@ /* The variables below is set by command line arguments */ struct args { - int devn; int vol_id; - char node[MAX_NODE_LEN + 1]; + char node[MAX_NODE_LEN + 2]; }; static struct args myargs = { - .devn = -1, .vol_id = -1, }; -static int param_sanity_check(struct args *args, libubi_t libubi); - static const char *doc = "Version: " PROGRAM_VERSION "\n" PROGRAM_NAME " - a tool to remove UBI volumes."; @@ -65,7 +60,6 @@ static const char *usage = " to the node file /dev/ubi0."; static const struct option long_options[] = { - { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, @@ -79,26 +73,12 @@ static int parse_opt(int argc, char * const argv[], struct args *args) while (1) { int key; - key = getopt_long(argc, argv, "d:n:hV", long_options, NULL); + key = getopt_long(argc, argv, "n:hV", long_options, NULL); if (key == -1) break; switch (key) { - case 'd': - args->devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args->devn < 0) { - errmsg("bad UBI device number: \"%s\"", optarg); - return -1; - } - - warnmsg("'-d' and '--devn' options are deprecated and will be " - "removed. Specify UBI device node name instead!\n" - "Example: " PROGRAM_NAME " /dev/ubi0, instead of " - PROGRAM_NAME " -d 0"); - sprintf(args->node, "/dev/ubi%d", args->devn); - break; - case 'n': args->vol_id = strtoul(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || args->vol_id < 0) { @@ -132,10 +112,8 @@ static int parse_opt(int argc, char * const argv[], struct args *args) return -1; } -static int param_sanity_check(struct args *args, libubi_t libubi) +static int param_sanity_check(struct args *args) { - int err; - if (strlen(args->node) > MAX_NODE_LEN) { errmsg("too long device node name: \"%s\" (%d characters), max. is %d", args->node, strlen(args->node), MAX_NODE_LEN); @@ -147,22 +125,6 @@ static int param_sanity_check(struct args *args, libubi_t libubi) return -1; } - if (args->devn != -1) { - struct ubi_info ubi; - - err = ubi_get_info(libubi, &ubi); - if (err) { - errmsg("cannot get UBI information"); - perror("ubi_get_info"); - return -1; - } - - if (args->devn >= ubi.dev_count) { - errmsg("UBI device %d does not exist", args->devn); - return -1; - } - } - return 0; } @@ -171,11 +133,7 @@ int main(int argc, char * const argv[]) int err; libubi_t libubi; - strncpy(myargs.node, argv[1], MAX_NODE_LEN); - - err = parse_opt(argc, argv, &myargs); - if (err) - return -1; + strncpy(myargs.node, argv[1], MAX_NODE_LEN + 1); if (argc < 2) { errmsg("UBI device name was not specified (use -h for help)"); @@ -187,6 +145,20 @@ int main(int argc, char * const argv[]) return -1; } + if (argv[1][0] == '-') { + errmsg("UBI device was not specified (use -h for help)"); + return -1; + } + + if (argv[2][0] != '-') { + errmsg("incorrect arguments, use -h for help"); + return -1; + } + + err = parse_opt(argc, argv, &myargs); + if (err) + return -1; + libubi = libubi_open(); if (libubi == NULL) { errmsg("cannot open libubi"); @@ -194,7 +166,7 @@ int main(int argc, char * const argv[]) return -1; } - err = param_sanity_check(&myargs, libubi); + err = param_sanity_check(&myargs); if (err) goto out_libubi; -- cgit v1.2.3