diff options
-rw-r--r-- | ubi-utils/.gitignore | 1 | ||||
-rw-r--r-- | ubi-utils/Makefile | 10 | ||||
-rw-r--r-- | ubi-utils/include/libmtd.h | 86 | ||||
-rw-r--r-- | ubi-utils/src/libmtd.c | 788 | ||||
-rw-r--r-- | ubi-utils/src/libmtd_int.h | 88 | ||||
-rw-r--r-- | ubi-utils/src/libmtd_legacy.c | 354 | ||||
-rw-r--r-- | ubi-utils/src/libubi.c | 35 | ||||
-rw-r--r-- | ubi-utils/src/mtdinfo.c | 328 | ||||
-rw-r--r-- | ubi-utils/src/ubiformat.c | 103 | ||||
-rw-r--r-- | ubi-utils/src/ubinfo.c | 22 |
10 files changed, 1656 insertions, 159 deletions
diff --git a/ubi-utils/.gitignore b/ubi-utils/.gitignore index 4155cd1..66e766c 100644 --- a/ubi-utils/.gitignore +++ b/ubi-utils/.gitignore @@ -8,3 +8,4 @@ /ubirename /ubirmvol /ubiupdatevol +/mtdinfo diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index dcff7e3..b5388ec 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -6,12 +6,12 @@ KERNELHDR := ../include SUBDIRS = old-utils -#CFLAGS += -Werror +# CFLAGS += -Werror CPPFLAGS += -Iinclude -Isrc -I$(KERNELHDR) LIBS = libubi libmtd libubigen libiniparser libscan TARGETS = ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ - ubidetach ubinize ubiformat ubirename + ubidetach ubinize ubiformat ubirename mtdinfo VPATH = src @@ -30,13 +30,17 @@ $(BUILDDIR)/ubinize: $(addprefix $(BUILDDIR)/,\ ubinize.o common.o crc32.o libiniparser.a libubigen.a) # $(CC) $(CFLAGS) $(filter %.o, $^) -L. -liniparser -lubigen -o $@ +$(BUILDDIR)/mtdinfo: $(addprefix $(BUILDDIR)/,\ + libmtd.a libubigen.a crc32.o common.o) +# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lubigen -o $@ + $(BUILDDIR)/ubiformat: $(addprefix $(BUILDDIR)/,\ ubiformat.o common.o crc32.o libmtd.a libscan.a libubi.a libubigen.a) # $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lscan -lubi -lubigen -o $@ $(BUILDDIR)/libubi.a: $(BUILDDIR)/libubi.o -$(BUILDDIR)/libmtd.a: $(BUILDDIR)/libmtd.o +$(BUILDDIR)/libmtd.a: $(BUILDDIR)/libmtd.o $(BUILDDIR)/libmtd_legacy.o $(BUILDDIR)/libubigen.a: $(BUILDDIR)/libubigen.o diff --git a/ubi-utils/include/libmtd.h b/ubi-utils/include/libmtd.h index 6a93e5f..8d5bc4f 100644 --- a/ubi-utils/include/libmtd.h +++ b/ubi-utils/include/libmtd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2008, 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,9 +27,28 @@ extern "C" { #endif +/* Maximum MTD device name length */ +#define MTD_NAME_MAX 127 /* Maximum MTD device type string length */ #define MTD_TYPE_MAX 64 +/* MTD library descriptor */ +typedef void * libmtd_t; + +/** + * @dev_count: count of MTD devices in system + * @lowest_dev_num: lowest MTD device number + * @highest_dev_num: highest MTD device number + * @sysfs_supported: non-zero if sysfs is supported by MTD + */ +struct mtd_info +{ + int dev_count; + int lowest_dev_num; + int highest_dev_num; + unsigned int sysfs_supported:1; +}; + /** * struct mtd_dev_info - information about an MTD device. * @dev_num: MTD device number @@ -37,11 +56,14 @@ extern "C" { * @minor: minor number of corresponding character device * @type: flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h) * @type_str: static R/O flash type string + * @name: device name * @size: device size in bytes * @eb_cnt: count of eraseblocks * @eb_size: eraseblock size * @min_io_size: minimum input/output unit size * @subpage_size: sub-page size + * @oob_size: OOB size (zero if the device does not have OOB area) + * @region_cnt: count of additional erase regions * @writable: zero if the device is read-only * @bb_allowed: non-zero if the MTD device may have bad eraseblocks */ @@ -51,26 +73,69 @@ struct mtd_dev_info int major; int minor; int type; - const char type_str[MTD_TYPE_MAX]; + const char type_str[MTD_TYPE_MAX + 1]; + const char name[MTD_NAME_MAX + 1]; long long size; int eb_cnt; int eb_size; int min_io_size; int subpage_size; + int oob_size; + int region_cnt; unsigned int writable:1; unsigned int bb_allowed:1; }; /** + * libmtd_open - open MTD library. + * + * This function initializes and opens the MTD library and returns MTD library + * descriptor in case of success and %NULL in case of failure. In case of + * failure, errno contains zero if MTD is not present in the system, or + * contains the error code if a real error happened. + */ +libmtd_t libmtd_open(void); + +/** + * libmtd_close - close MTD library. + * @desc: MTD library descriptor + */ +void libmtd_close(libmtd_t desc); + +/** + * mtd_get_info - get general MTD information. + * @desc: MTD library descriptor + * @info: the MTD device information is returned here + * + * This function fills the passed @info object with general MTD information and + * returns %0 in case of success and %-1 in case of failure. If MTD subsystem is + * not present in the system, errno is set to @ENODEV. + */ +int mtd_get_info(libmtd_t desc, struct mtd_info *info); + +/** * mtd_get_dev_info - get information about an MTD device. + * @desc: MTD library descriptor * @node: name of the MTD device node * @mtd: the MTD device information is returned here * * This function gets information about MTD device defined by the @node device * node file and saves this information in the @mtd object. Returns %0 in case - * of success and %-1 in case of failure. + * of success and %-1 in case of failure. If MTD subsystem is not present in the + * system, or the MTD device does not exist, errno is set to @ENODEV. */ -int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd); +int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd); + +/** + * mtd_get_dev_info1 - get information about an MTD device. + * @desc: MTD library descriptor + * @dev_num: MTD device number to fetch information about + * @mtd: the MTD device information is returned here + * + * This function is identical to 'mtd_get_dev_info()' except that it accepts + * MTD device number, not MTD character device. + */ +int mtd_get_dev_info1(libmtd_t desc, int dev_num, struct mtd_dev_info *mtd); /** * mtd_erase - erase an eraseblock. @@ -78,7 +143,7 @@ int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd); * @fd: MTD device node file descriptor * @eb: eraseblock to erase * - * This function erases eraseblock @eb of MTD device decribed by @fd. Returns + * This function erases eraseblock @eb of MTD device described by @fd. Returns * %0 in case of success and %-1 in case of failure. */ int mtd_erase(const struct mtd_dev_info *mtd, int fd, int eb); @@ -137,6 +202,17 @@ int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs, int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, void *buf, int len); +/** + * mtd_probe_node - test MTD node. + * @desc: MTD library descriptor + * @node: the node to test + * + * This function tests whether @node is an MTD device node and returns %1 if it + * is, and %-1 if it is not (errno is ENODEV in this case) or if an error + * occurred. + */ +int mtd_probe_node(libmtd_t desc, const char *node); + #ifdef __cplusplus } #endif diff --git a/ubi-utils/src/libmtd.c b/ubi-utils/src/libmtd.c index faa958f..046beea 100644 --- a/ubi-utils/src/libmtd.c +++ b/ubi-utils/src/libmtd.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2008 Nokia Corporation + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (C) 2009 Nokia Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,127 +21,696 @@ * MTD library. */ +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> -#include <unistd.h> -#include <fcntl.h> - #include <mtd/mtd-user.h> + #include <libmtd.h> +#include "libmtd_int.h" #include "common.h" -#define PROGRAM_NAME "libmtd" -#define MTD_DEV_MAJOR 90 +/** + * 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) +{ + char *n; + int len1 = strlen(path); + int len2 = strlen(name); + + n = malloc(len1 + len2 + 2); + if (!n) { + sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); + return NULL; + } + + memcpy(n, path, len1); + if (n[len1 - 1] != '/') + n[len1++] = '/'; + + memcpy(n + len1, name, len2 + 1); + return n; +} + +/** + * 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; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, buf_len); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + + /* Make sure all data is read */ + tmp1 = read(fd, &tmp, 1); + if (tmp1 == 1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (tmp1) { + errmsg("file \"%s\" contains too much data (> %d bytes)", + file, buf_len); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) { + sys_errmsg("close failed on \"%s\"", file); + return -1; + } + + return rd; + +out_error: + close(fd); + return -1; +} + +/** + * 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 success, and %-1 in case of failure. + */ +static int read_major(const char *file, int *major, int *minor) +{ + int ret; + char buf[50]; + + ret = read_data(file, buf, 50); + if (ret < 0) + return ret; + + ret = sscanf(buf, "%d:%d\n", major, minor); + if (ret != 2) { + errno = EINVAL; + return errmsg("\"%s\" does not have major:minor format", file); + } + + if (*major < 0 || *minor < 0) { + errno = EINVAL; + return errmsg("bad major:minor %d:%d in \"%s\"", + *major, *minor, file); + } + + return 0; +} + +/** + * dev_get_major - get major and minor numbers of an MTD device. + * @lib: libmtd descriptor + * @dev_num: MTD device number + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of success and %-1 in case of failure. + */ +static int dev_get_major(struct libmtd *lib, int dev_num, int *major, int *minor) +{ + char file[strlen(lib->mtd_dev) + 50]; + + sprintf(file, lib->mtd_dev, dev_num); + return read_major(file, major, minor); +} -int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd) +/** + * dev_read_data - read data from an MTD device's sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD device number + * @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 dev_read_data(const char *patt, int dev_num, void *buf, int buf_len) +{ + char file[strlen(patt) + 100]; + + sprintf(file, patt, dev_num); + return read_data(file, buf, buf_len); +} + +/** + * read_hex_ll - read a hex '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 hexadecimal + * '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_hex_ll(const char *file, long long *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, 50); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (rd == 50) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } + + if (sscanf(buf, "%llx\n", value) != 1) { + errmsg("cannot read integer from \"%s\"\n", file); + errno = EINVAL; + goto out_error; + } + + if (*value < 0) { + errmsg("negative value %lld in \"%s\"", *value, file); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) + return sys_errmsg("close failed on \"%s\"", file); + + return 0; + +out_error: + close(fd); + return -1; +} + +/** + * read_pos_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_pos_ll(const char *file, long long *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, 50); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (rd == 50) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } + + if (sscanf(buf, "%lld\n", value) != 1) { + errmsg("cannot read integer from \"%s\"\n", file); + errno = EINVAL; + goto out_error; + } + + if (*value < 0) { + errmsg("negative value %lld in \"%s\"", *value, file); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) + return sys_errmsg("close failed on \"%s\"", file); + + return 0; + +out_error: + close(fd); + return -1; +} + +/** + * read_hex_int - read an 'int' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function is the same as 'read_pos_ll()', but it reads an 'int' + * value, not 'long long'. + */ +static int read_hex_int(const char *file, int *value) +{ + long long res; + + if (read_hex_ll(file, &res)) + return -1; + + /* Make sure the value has correct range */ + if (res > INT_MAX || res < INT_MIN) { + errmsg("value %lld read from file \"%s\" is out of range", + res, file); + errno = EINVAL; + return -1; + } + + *value = res; + return 0; +} + +/** + * read_pos_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_pos_ll()', but it reads an 'int' + * value, not 'long long'. + */ +static int read_pos_int(const char *file, int *value) +{ + long long res; + + if (read_pos_ll(file, &res)) + return -1; + + /* 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; + } + + *value = res; + return 0; +} + +/** + * dev_read_hex_int - read an hex 'int' value from an MTD device sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD 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_hex_int(const char *patt, int dev_num, int *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_hex_int(file, value); +} + +/** + * dev_read_pos_int - read a positive 'int' value from an MTD device sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD 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_pos_int(const char *patt, int dev_num, int *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_pos_int(file, value); +} + +/** + * dev_read_pos_ll - read a positive 'long long' value from an MTD device sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD 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_pos_ll(const char *patt, int dev_num, long long *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_pos_ll(file, value); +} + +/** + * type_str2int - convert MTD device type to integer. + * @str: MTD device type string to convert + * + * This function converts MTD device type string @str, read from sysfs, into an + * integer. + */ +static int type_str2int(const char *str) +{ + if (!strcmp(str, "nand")) + return MTD_NANDFLASH; + if (!strcmp(str, "nor")) + return MTD_NORFLASH; + if (!strcmp(str, "rom")) + return MTD_ROM; + if (!strcmp(str, "absent")) + return MTD_ABSENT; + if (!strcmp(str, "dataflash")) + return MTD_DATAFLASH; + if (!strcmp(str, "ram")) + return MTD_RAM; + if (!strcmp(str, "ubi")) + return MTD_UBIVOLUME; + return -1; +} + +/** + * dev_node2num - find UBI device number by its character device node. + * @lib: MTD library descriptor + * @node: name of the MTD device node + * @dev_num: MTD device number is returned here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_node2num(struct libmtd *lib, const char *node, int *dev_num) { struct stat st; - struct mtd_info_user ui; - int fd, ret; - loff_t offs = 0; + int i, major, minor; + struct mtd_info info; if (stat(node, &st)) - return sys_errmsg("cannot open \"%s\"", node); + return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { + errmsg("\"%s\" is not a character device", node); errno = EINVAL; - return errmsg("\"%s\" is not a character device", node); + return -1; } - mtd->major = major(st.st_rdev); - mtd->minor = minor(st.st_rdev); + major = major(st.st_rdev); + minor = minor(st.st_rdev); - if (mtd->major != MTD_DEV_MAJOR) { - errno = EINVAL; - return errmsg("\"%s\" has major number %d, MTD devices have " - "major %d", node, mtd->major, MTD_DEV_MAJOR); + if (mtd_get_info((libmtd_t *)lib, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + if (!errno) + break; + return -1; + } + + if (major1 == major && minor1 == minor) { + errno = 0; + *dev_num = i; + return 0; + } } - mtd->dev_num = mtd->minor / 2; - mtd->writable = !(mtd->minor & 1); + errno = ENODEV; + return -1; +} - fd = open(node, O_RDWR); - if (fd == -1) - return sys_errmsg("cannot open \"%s\"", node); +libmtd_t libmtd_open(void) +{ + int fd; + struct libmtd *lib; + + lib = calloc(1, sizeof(struct libmtd)); + if (!lib) + return NULL; + + lib->sysfs_mtd = mkpath("/sys", SYSFS_MTD); + if (!lib->sysfs_mtd) + goto out_error; + + lib->mtd = mkpath(lib->sysfs_mtd, MTD_NAME_PATT); + if (!lib->mtd) + goto out_error; + + /* Make sure MTD is recent enough and supports sysfs */ + fd = open(lib->sysfs_mtd, O_RDONLY); + if (fd == -1) { + free(lib->mtd); + free(lib->sysfs_mtd); + if (errno != ENOENT || legacy_libmtd_open()) { + free(lib); + return NULL; + } + lib->mtd_name = lib->mtd = lib->sysfs_mtd = NULL; + return lib; + } - if (ioctl(fd, MEMGETINFO, &ui)) { - sys_errmsg("MEMGETINFO ioctl request failed"); - goto out_close; + if (close(fd)) { + sys_errmsg("close failed on \"%s\"", lib->sysfs_mtd); + goto out_error; } - ret = ioctl(fd, MEMGETBADBLOCK, &offs); - if (ret == -1) { - if (errno != EOPNOTSUPP) { - sys_errmsg("MEMGETBADBLOCK ioctl failed"); - goto out_close; + lib->mtd_name = mkpath(lib->mtd, MTD_NAME); + if (!lib->mtd_name) + goto out_error; + + lib->mtd_dev = mkpath(lib->mtd, MTD_DEV); + if (!lib->mtd_dev) + goto out_error; + + lib->mtd_type = mkpath(lib->mtd, MTD_TYPE); + if (!lib->mtd_type) + goto out_error; + + lib->mtd_eb_size = mkpath(lib->mtd, MTD_EB_SIZE); + if (!lib->mtd_eb_size) + goto out_error; + + lib->mtd_size = mkpath(lib->mtd, MTD_SIZE); + if (!lib->mtd_size) + goto out_error; + + lib->mtd_min_io_size = mkpath(lib->mtd, MTD_MIN_IO_SIZE); + if (!lib->mtd_min_io_size) + goto out_error; + + lib->mtd_subpage_size = mkpath(lib->mtd, MTD_SUBPAGE_SIZE); + if (!lib->mtd_subpage_size) + goto out_error; + + lib->mtd_oob_size = mkpath(lib->mtd, MTD_OOB_SIZE); + if (!lib->mtd_oob_size) + goto out_error; + + lib->mtd_region_cnt = mkpath(lib->mtd, MTD_REGION_CNT); + if (!lib->mtd_region_cnt) + goto out_error; + + lib->mtd_flags = mkpath(lib->mtd, MTD_FLAGS); + if (!lib->mtd_flags) + goto out_error; + + lib->sysfs_supported = 1; + return lib; + +out_error: + libmtd_close((libmtd_t)lib); + return NULL; +} + +void libmtd_close(libmtd_t desc) +{ + struct libmtd *lib = (struct libmtd *)desc; + + free(lib->mtd_flags); + free(lib->mtd_region_cnt); + free(lib->mtd_oob_size); + free(lib->mtd_subpage_size); + free(lib->mtd_min_io_size); + free(lib->mtd_size); + free(lib->mtd_eb_size); + free(lib->mtd_type); + free(lib->mtd_dev); + free(lib->mtd_name); + free(lib->mtd); + free(lib->sysfs_mtd); + free(lib); +} + +int mtd_get_info(libmtd_t desc, struct mtd_info *info) +{ + DIR *sysfs_mtd; + struct dirent *dirent; + struct libmtd *lib = (struct libmtd *)desc; + + memset(info, '\0', sizeof(struct mtd_info)); + + if (!lib->sysfs_supported) + return legacy_mtd_get_info(info); + + info->sysfs_supported = 1; + + /* + * We have to scan the MTD sysfs directory to identify how many MTD + * devices are present. + */ + sysfs_mtd = opendir(lib->sysfs_mtd); + if (!sysfs_mtd) { + if (errno == ENOENT) { + errno = ENODEV; + return -1; } - errno = 0; - mtd->bb_allowed = 0; - } else - mtd->bb_allowed = 1; - - mtd->type = ui.type; - mtd->size = ui.size; - mtd->eb_size = ui.erasesize; - mtd->min_io_size = ui.writesize; - - if (mtd->min_io_size <= 0) { - errmsg("mtd%d (%s) has insane min. I/O unit size %d", - mtd->dev_num, node, mtd->min_io_size); - goto out_close; + return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); } - if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { - errmsg("mtd%d (%s) has insane eraseblock size %d", - mtd->dev_num, node, mtd->eb_size); - goto out_close; + + info->lowest_dev_num = INT_MAX; + while (1) { + int dev_num, ret; + char tmp_buf[256]; + + errno = 0; + dirent = readdir(sysfs_mtd); + if (!dirent) + break; + + if (strlen(dirent->d_name) >= 255) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_mtd, dirent->d_name); + errno = EINVAL; + goto out_close; + } + + ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", + &dev_num, tmp_buf); + 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; + } } - if (mtd->size <= 0 || mtd->size < mtd->eb_size) { - errmsg("mtd%d (%s) has insane size %lld", - mtd->dev_num, node, mtd->size); + + if (!dirent && errno) { + sys_errmsg("readdir failed on \"%s\"", lib->sysfs_mtd); goto out_close; } - mtd->eb_cnt = mtd->size / mtd->eb_size; - switch(mtd->type) { - case MTD_ABSENT: - errmsg("mtd%d (%s) is removable and is not present", - mtd->dev_num, node); - goto out_close; - case MTD_RAM: - strcpy(mtd->type_str, "RAM-based"); - break; - case MTD_ROM: - strcpy(mtd->type_str, "ROM"); - break; - case MTD_NORFLASH: - strcpy(mtd->type_str, "NOR"); - break; - case MTD_NANDFLASH: - strcpy(mtd->type_str, "NAND"); - break; - case MTD_DATAFLASH: - strcpy(mtd->type_str, "DataFlash"); - break; - case MTD_UBIVOLUME: - strcpy(mtd->type_str, "UBI-emulated MTD"); - break; - default: - strcpy(mtd->type_str, "Unknown flash type"); - break; - } - - if (ui.flags & MTD_WRITEABLE) - mtd->writable = 1; + if (closedir(sysfs_mtd)) + return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); + + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; - close(fd); return 0; out_close: - close(fd); + closedir(sysfs_mtd); return -1; } +int mtd_get_dev_info1(libmtd_t desc, int dev_num, struct mtd_dev_info *mtd) +{ + int ret; + struct stat st; + struct libmtd *lib = (struct libmtd *)desc; + + memset(mtd, '\0', sizeof(struct mtd_dev_info)); + mtd->dev_num = dev_num; + + if (!lib->sysfs_supported) + return legacy_get_dev_info1(dev_num, mtd); + else { + char file[strlen(lib->mtd) + 10]; + + sprintf(file, lib->mtd, dev_num); + if (stat(file, &st)) { + if (errno == ENOENT) + errno = ENODEV; + return -1; + } + } + + if (dev_get_major(lib, dev_num, &mtd->major, &mtd->minor)) + return -1; + + ret = dev_read_data(lib->mtd_name, dev_num, &mtd->name, + MTD_NAME_MAX); + if (ret < 0) + return -1; + ((char *)mtd->name)[ret - 1] = '\0'; + + ret = dev_read_data(lib->mtd_type, dev_num, &mtd->type_str, + MTD_TYPE_MAX); + if (ret < 0) + return -1; + ((char *)mtd->type_str)[ret - 1] = '\0'; + + if (dev_read_pos_int(lib->mtd_eb_size, dev_num, &mtd->eb_size)) + return -1; + if (dev_read_pos_ll(lib->mtd_size, dev_num, &mtd->size)) + return -1; + if (dev_read_pos_int(lib->mtd_min_io_size, dev_num, &mtd->min_io_size)) + return -1; + if (dev_read_pos_int(lib->mtd_subpage_size, dev_num, &mtd->subpage_size)) + return -1; + if (dev_read_pos_int(lib->mtd_oob_size, dev_num, &mtd->oob_size)) + return -1; + if (dev_read_pos_int(lib->mtd_region_cnt, dev_num, &mtd->region_cnt)) + return -1; + if (dev_read_hex_int(lib->mtd_flags, dev_num, &ret)) + return -1; + mtd->writable = !!(ret & MTD_WRITEABLE); + + mtd->eb_cnt = mtd->size / mtd->eb_size; + mtd->type = type_str2int(mtd->type_str); + mtd->bb_allowed = !!(mtd->type == MTD_NANDFLASH); + + return 0; +} + +int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd) +{ + int dev_num; + struct libmtd *lib = (struct libmtd *)desc; + + if (!lib->sysfs_supported) + return legacy_get_dev_info(node, mtd); + + if (dev_node2num(lib, node, &dev_num)) + return -1; + + return mtd_get_dev_info1(desc, dev_num, mtd); +} + int mtd_erase(const struct mtd_dev_info *mtd, int fd, int eb) { struct erase_info_user ei; @@ -252,7 +822,7 @@ int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, errno = EINVAL; return -1; } -#if 0 + if (offs % mtd->subpage_size) { errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", offs, mtd->dev_num, mtd->subpage_size); @@ -265,7 +835,6 @@ int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, errno = EINVAL; return -1; } -#endif /* Seek to the beginning of the eraseblock */ seek = (off_t)eb * mtd->eb_size + offs; @@ -280,3 +849,48 @@ int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, return 0; } + +int mtd_probe_node(libmtd_t desc, const char *node) +{ + struct stat st; + struct mtd_info info; + int i, major, minor; + struct libmtd *lib = (struct libmtd *)desc; + + if (stat(node, &st)) + return sys_errmsg("cannot get information about \"%s\"", node); + + if (!S_ISCHR(st.st_mode)) { + errmsg("\"%s\" is not a character device", node); + errno = EINVAL; + return -1; + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + if (mtd_get_info((libmtd_t *)lib, &info)) + return -1; + + if (!lib->sysfs_supported) + return 0; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + if (!errno) + break; + return -1; + } + + if (major1 == major && minor1 == minor) + return 1; + } + + errno = 0; + return -1; +} diff --git a/ubi-utils/src/libmtd_int.h b/ubi-utils/src/libmtd_int.h new file mode 100644 index 0000000..7de4b42 --- /dev/null +++ b/ubi-utils/src/libmtd_int.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * MTD library. + */ + +#ifndef __LIBMTD_INT_H__ +#define __LIBMTD_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define PROGRAM_NAME "libmtd" + +#define SYSFS_MTD "class/mtd" +#define MTD_NAME_PATT "mtd%d" +#define MTD_DEV "dev" +#define MTD_NAME "name" +#define MTD_TYPE "type" +#define MTD_EB_SIZE "erasesize" +#define MTD_SIZE "size" +#define MTD_MIN_IO_SIZE "writesize" +#define MTD_SUBPAGE_SIZE "subpagesize" +#define MTD_OOB_SIZE "oobsize" +#define MTD_REGION_CNT "numeraseregions" +#define MTD_FLAGS "flags" + +/** + * libmtd - MTD library description data structure. + * @sysfs_mtd: MTD directory in sysfs + * @mtd: MTD device sysfs directory pattern + * @mtd_dev: MTD device major/minor numbers file pattern + * @mtd_name: MTD device name file pattern + * @mtd_type: MTD device type file pattern + * @mtd_eb_size: MTD device eraseblock size file pattern + * @mtd_size: MTD device size file pattern + * @mtd_min_io_size: minimum I/O unit size file pattern + * @mtd_subpage_size: sub-page size file pattern + * @mtd_oob_size: MTD device OOB size file pattern + * @mtd_region_cnt: count of additional erase regions file pattern + * @mtd_flags: MTD device flags file pattern + * @sysfs_supported: non-zero if sysfs is supported by MTD + */ +struct libmtd +{ + char *sysfs_mtd; + char *mtd; + char *mtd_dev; + char *mtd_name; + char *mtd_type; + char *mtd_eb_size; + char *mtd_size; + char *mtd_min_io_size; + char *mtd_subpage_size; + char *mtd_oob_size; + char *mtd_region_cnt; + char *mtd_flags; + unsigned int sysfs_supported:1; +}; + +int legacy_libmtd_open(void); +int legacy_mtd_get_info(struct mtd_info *info); +int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd); +int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBMTD_INT_H__ */ diff --git a/ubi-utils/src/libmtd_legacy.c b/ubi-utils/src/libmtd_legacy.c new file mode 100644 index 0000000..cd474b1 --- /dev/null +++ b/ubi-utils/src/libmtd_legacy.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * This file is part of the MTD library. Implements pre-2.6.30 kernels support, + * where MTD did not have sysfs interface. The main limitation of the old + * kernels was that the sub-page size was not exported to user-space, so it was + * not possible to get sub-page size. + */ + +#include <limits.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <mtd/mtd-user.h> + +#include <libmtd.h> +#include "libmtd_int.h" +#include "common.h" + +#define MTD_PROC_FILE "/proc/mtd" +#define MTD_DEV_PATT "/dev/mtd%d" +#define MTD_DEV_MAJOR 90 + +#define PROC_MTD_FIRST "dev: size erasesize name\n" +#define PROC_MTD_FIRST_LEN (sizeof(PROC_MTD_FIRST) - 1) +#define PROC_MTD_MAX_LEN 4096 +#define PROC_MTD_PATT "mtd%d: %llx %x" + +/** + * struct proc_parse_info - /proc/mtd parsing information. + * @dev_num: MTD device number + * @size: device size + * @eb_size: eraseblock size + * @name: device name + * @buf: contents of /proc/mtd + * @data_size: how much data was read into @buf + * @pos: next string in @buf to parse + */ +struct proc_parse_info +{ + int dev_num; + long long size; + char name[MTD_NAME_MAX + 1]; + int eb_size; + char *buf; + int data_size; + char *next; +}; + +static int proc_parse_start(struct proc_parse_info *pi) +{ + int fd, ret; + + fd = open(MTD_PROC_FILE, O_RDONLY); + if (fd == -1) + return -1; + + pi->buf = malloc(PROC_MTD_MAX_LEN); + if (!pi->buf) { + sys_errmsg("cannot allocate %d bytes of memory", + PROC_MTD_MAX_LEN); + goto out_close; + } + + ret = read(fd, pi->buf, PROC_MTD_MAX_LEN); + if (ret == -1) { + sys_errmsg("cannot read \"%s\"", MTD_PROC_FILE); + goto out_free; + } + + if (ret < PROC_MTD_FIRST_LEN || + memcmp(pi->buf, PROC_MTD_FIRST, PROC_MTD_FIRST_LEN)) { + errmsg("\"%s\" does not start with \"%s\"", MTD_PROC_FILE, + PROC_MTD_FIRST); + goto out_free; + } + + pi->data_size = ret; + pi->next = pi->buf + PROC_MTD_FIRST_LEN; + + close(fd); + return 0; + +out_free: + free(pi->buf); +out_close: + close(fd); + return -1; +} + +static int proc_parse_next(struct proc_parse_info *pi) +{ + int ret, len, pos = pi->next - pi->buf; + char *p, *p1; + + if (pos >= pi->data_size) { + free(pi->buf); + return 0; + } + + ret = sscanf(pi->next, PROC_MTD_PATT, &pi->dev_num, &pi->size, + &pi->eb_size); + if (ret != 3) + return errmsg("\"%s\" pattern not found", PROC_MTD_PATT); + + p = memchr(pi->next, '\"', pi->data_size - pos); + if (!p) + return errmsg("opening \" not fount"); + p += 1; + pos = p - pi->buf; + if (pos >= pi->data_size) + return errmsg("opening \" not fount"); + + p1 = memchr(p, '\"', pi->data_size - pos); + if (!p1) + return errmsg("closing \" not fount"); + pos = p1 - pi->buf; + if (pos >= pi->data_size) + return errmsg("closing \" not fount"); + + len = p1 - p; + if (len > MTD_NAME_MAX) + return errmsg("too long mtd%d device name", pi->dev_num); + + memcpy(pi->name, p, len); + pi->name[len] = '\0'; + + if (p1[1] != '\n') + return errmsg("opening \"\n\" not fount"); + pi->next = p1 + 2; + return 1; +} + +/** + * legacy_libmtd_open - legacy version of 'libmtd_open()'. + * + * This function is just checks that MTD is present in the system. Returns + * zero in case of success and %-1 in case of failure. In case of failure, + * errno contains zero if MTD is not present in the system, or contains the + * error code if a real error happened. This is similar to the 'libmtd_open()' + * return conventions. + */ +int legacy_libmtd_open(void) +{ + int fd; + + fd = open(MTD_PROC_FILE, O_RDONLY); + if (fd == -1) { + if (errno == ENOENT) + errno = 0; + return -1; + } + + close(fd); + return 0; +} + +/** + * legacy_mtd_get_info - legacy version of 'mtd_get_info()'. + * @info: the MTD device information is returned here + * + * This function is similar to 'mtd_get_info()' and has the same conventions. + */ +int legacy_mtd_get_info(struct mtd_info *info) +{ + int ret; + struct proc_parse_info pi; + + ret = proc_parse_start(&pi); + if (ret) + return -1; + + info->lowest_dev_num = INT_MAX; + while (proc_parse_next(&pi)) { + info->dev_count += 1; + if (pi.dev_num > info->highest_dev_num) + info->highest_dev_num = pi.dev_num; + if (pi.dev_num < info->lowest_dev_num) + info->lowest_dev_num = pi.dev_num; + } + + return 0; +} + +/** + * legacy_get_dev_info - legacy version of 'mtd_get_dev_info()'. + * @node: name of the MTD device node + * @mtd: the MTD device information is returned here + * + * This function is similar to 'mtd_get_dev_info()' and has the same + * conventions. + */ +int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd) +{ + struct stat st; + struct mtd_info_user ui; + int fd, ret; + loff_t offs = 0; + struct proc_parse_info pi; + + if (stat(node, &st)) + return sys_errmsg("cannot open \"%s\"", node); + + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + return errmsg("\"%s\" is not a character device", node); + } + + memset(mtd, '\0', sizeof(struct mtd_dev_info)); + mtd->major = major(st.st_rdev); + mtd->minor = minor(st.st_rdev); + + if (mtd->major != MTD_DEV_MAJOR) { + errno = EINVAL; + return errmsg("\"%s\" has major number %d, MTD devices have " + "major %d", node, mtd->major, MTD_DEV_MAJOR); + } + + mtd->dev_num = mtd->minor / 2; + + fd = open(node, O_RDWR); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + + if (ioctl(fd, MEMGETINFO, &ui)) { + sys_errmsg("MEMGETINFO ioctl request failed"); + goto out_close; + } + + ret = ioctl(fd, MEMGETBADBLOCK, &offs); + if (ret == -1) { + if (errno != EOPNOTSUPP) { + sys_errmsg("MEMGETBADBLOCK ioctl failed"); + goto out_close; + } + errno = 0; + mtd->bb_allowed = 0; + } else + mtd->bb_allowed = 1; + + mtd->type = ui.type; + mtd->size = ui.size; + mtd->eb_size = ui.erasesize; + mtd->min_io_size = ui.writesize; + + if (mtd->min_io_size <= 0) { + errmsg("mtd%d (%s) has insane min. I/O unit size %d", + mtd->dev_num, node, mtd->min_io_size); + goto out_close; + } + if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { + errmsg("mtd%d (%s) has insane eraseblock size %d", + mtd->dev_num, node, mtd->eb_size); + goto out_close; + } + if (mtd->size <= 0 || mtd->size < mtd->eb_size) { + errmsg("mtd%d (%s) has insane size %lld", + mtd->dev_num, node, mtd->size); + goto out_close; + } + mtd->eb_cnt = mtd->size / mtd->eb_size; + + switch(mtd->type) { + case MTD_ABSENT: + errmsg("mtd%d (%s) is removable and is not present", + mtd->dev_num, node); + goto out_close; + case MTD_RAM: + strcpy((char *)mtd->type_str, "ram"); + break; + case MTD_ROM: + strcpy((char *)mtd->type_str, "rom"); + break; + case MTD_NORFLASH: + strcpy((char *)mtd->type_str, "nor"); + break; + case MTD_NANDFLASH: + strcpy((char *)mtd->type_str, "nand"); + break; + case MTD_DATAFLASH: + strcpy((char *)mtd->type_str, "dataflash"); + break; + case MTD_UBIVOLUME: + strcpy((char *)mtd->type_str, "ubi"); + break; + default: + goto out_close; + } + + if (ui.flags & MTD_WRITEABLE) + mtd->writable = 1; + mtd->subpage_size = mtd->min_io_size; + + close(fd); + + /* + * Unfortunately, the device name is not available via ioctl, and + * we have to parse /proc/mtd to get it. + */ + ret = proc_parse_start(&pi); + if (ret) + return -1; + + while (proc_parse_next(&pi)) { + if (pi.dev_num == mtd->dev_num) { + strcpy((char *)mtd->name, pi.name); + return 0; + } + } + + errmsg("mtd%d not found in \"%s\"", mtd->dev_num, MTD_PROC_FILE); + errno = ENOENT; + return -1; + +out_close: + close(fd); + return -1; +} + +/** + * legacy_get_dev_info1 - legacy version of 'mtd_get_dev_info1()'. + * @node: name of the MTD device node + * @mtd: the MTD device information is returned here + * + * This function is similar to 'mtd_get_dev_info1()' and has the same + * conventions. + */ +int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd) +{ + char node[sizeof(MTD_DEV_PATT) + 20]; + + sprintf(node, MTD_DEV_PATT, dev_num); + return legacy_get_dev_info(node, mtd); +} diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index 5c8ce9e..5b22e21 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -24,12 +24,12 @@ #include <stdio.h> #include <string.h> #include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> #include <dirent.h> #include <unistd.h> -#include <sys/ioctl.h> #include <limits.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> #include <libubi.h> #include "libubi_int.h" #include "common.h" @@ -94,7 +94,6 @@ static int read_positive_ll(const char *file, long long *value) } 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; @@ -227,7 +226,7 @@ static int read_major(const char *file, int *major, int *minor) /** * 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 + * @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. @@ -422,9 +421,9 @@ static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, * dev_node2num - find UBI device number by its character device node. * @lib: UBI library descriptor * @node: UBI character device node name + * @dev_num: UBI device number is returned here * - * This function returns positive UBI device number in case of success and %-1 - * in case of failure. + * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) { @@ -433,8 +432,7 @@ static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) int i, major, minor; if (stat(node, &st)) - return sys_errmsg("cannot get information about \"%s\"", - node); + return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errno = EINVAL; @@ -749,8 +747,7 @@ int ubi_probe_node(libubi_t desc, const char *node) char file[strlen(lib->ubi_vol) + 100]; if (stat(node, &st)) - return sys_errmsg("cannot get information about \"%s\"", - node); + return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errmsg("\"%s\" is not a character device", node); @@ -837,9 +834,10 @@ int ubi_get_info(libubi_t desc, struct ubi_info *info) if (!dirent) break; - if (strlen(dirent->d_name) > 256) { + if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_ubi, dirent->d_name); + errno = EINVAL; goto out_close; } @@ -896,6 +894,7 @@ int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); r.name_len = n; + desc = desc; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); @@ -1036,10 +1035,8 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) memset(info, '\0', sizeof(struct ubi_dev_info)); info->dev_num = dev_num; - if (!dev_present(lib, dev_num)) { - errno = ENODEV; + if (!dev_present(lib, dev_num)) return -1; - } sysfs_ubi = opendir(lib->sysfs_ubi); if (!sysfs_ubi) @@ -1056,7 +1053,7 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) if (!dirent) break; - if (strlen(dirent->d_name) > 256) { + if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_ubi, dirent->d_name); goto out_close; @@ -1120,7 +1117,8 @@ int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) err = ubi_probe_node(desc, node); if (err != 1) { - errno = ENODEV; + if (err == 2) + errno = ENODEV; return -1; } @@ -1196,7 +1194,8 @@ int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) err = ubi_probe_node(desc, node); if (err != 2) { - errno = ENODEV; + if (err == 1) + errno = ENODEV; return -1; } diff --git a/ubi-utils/src/mtdinfo.c b/ubi-utils/src/mtdinfo.c new file mode 100644 index 0000000..849d165 --- /dev/null +++ b/ubi-utils/src/mtdinfo.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2009 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 + */ + +/* + * An utility to get MTD information. + * + * Author: Artem Bityutskiy + */ + +#include <stdint.h> +#include <stdio.h> +#include <getopt.h> +#include <stdlib.h> +#include <string.h> +#include <mtd/mtd-user.h> + +#include <libubigen.h> +#include <libmtd.h> +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "mtdinfo" + +/* The variables below are set by command line arguments */ +struct args { + int mtdn; + unsigned int all:1; + unsigned int ubinfo:1; + const char *node; +}; + +static struct args args = { + .mtdn = -1, + .ubinfo = 0, + .all = 0, + .node = NULL, +}; + +static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool to print MTD information."; + +static const char *optionsstr = +"-m, --mtdn=<MTD device number> MTD device number to get information about\n" +"-u, --ubi-info print what would UBI layout be if it was put\n" +" on this MTD device\n" +"-a, --all print information about all MTD devices\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char *usage = +"Usage 1: " PROGRAM_NAME " [-m <MTD device number>] [-u] [-h] [-V] [--mtdn <MTD device number>]\n" +"\t\t[--ubi-info] [--help] [--version]\n" +"Usage 2: " PROGRAM_NAME " <MTD device node file name> [-u] [-h] [-V] [--ubi-info] [--help]\n" +"\t\t[--version]\n" +"Example 1: " PROGRAM_NAME " - (no arguments) print general MTD information\n" +"Example 2: " PROGRAM_NAME " -m 1 - print information about MTD device number 1\n" +"Example 3: " PROGRAM_NAME " /dev/mtd0 - print information MTD device /dev/mtd0\n" +"Example 4: " PROGRAM_NAME " /dev/mtd0 -u - print information MTD device /dev/mtd0\n" +"\t\t\t\tand include UBI layout information\n" +"Example 5: " PROGRAM_NAME " -a - print information about all MTD devices\n" +"\t\t\tand include UBI layout information\n"; + +static const struct option long_options[] = { + { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, + { .name = "ubi-info", .has_arg = 0, .flag = NULL, .val = 'u' }, + { .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[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "am:uhV", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'a': + args.all = 1; + break; + + case 'u': + args.ubinfo = 1; + break; + + case 'm': + args.mtdn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.mtdn < 0) + return errmsg("bad MTD device number: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc - 1) + args.node = argv[optind]; + else if (optind < argc) + return errmsg("more then one MTD device specified (use -h for help)"); + + if (args.all && (args.node || args.mtdn != -1)) { + args.mtdn = -1; + args.node = NULL; + } + + return 0; +} + +static int translate_dev(libmtd_t libmtd, const char *node) +{ + int err; + struct mtd_dev_info mtd; + + err = mtd_get_dev_info(libmtd, node, &mtd); + if (err) { + if (errno == ENODEV) + return errmsg("\"%s\" does not correspond to any " + "existing MTD device", node); + return sys_errmsg("cannot get information about MTD " + "device \"%s\"", node); + } + + args.mtdn = mtd.dev_num; + return 0; +} + +static int print_dev_info(libmtd_t libmtd, const struct mtd_info *mtd_info, int mtdn) +{ + int err; + struct mtd_dev_info mtd; + struct ubigen_info ui; + + err = mtd_get_dev_info1(libmtd, mtdn, &mtd); + if (err) { + if (errno == ENODEV) + return errmsg("mtd%d does not correspond to any " + "existing MTD device", mtdn); + return sys_errmsg("cannot get information about MTD device %d", + mtdn); + } + + printf("mtd%d\n", mtd.dev_num); + printf("Name: %s\n", mtd.name); + printf("Type: %s\n", mtd.type_str); + printf("Eraseblock size: "); + ubiutils_print_bytes(mtd.eb_size, 0); + printf("\n"); + printf("Amount of eraseblocks: %d (", mtd.eb_cnt); + ubiutils_print_bytes(mtd.size, 0); + printf(")\n"); + printf("Minimum input/output unit size: %d %s\n", + mtd.min_io_size, mtd.min_io_size > 1 ? "bytes" : "byte"); + if (mtd_info->sysfs_supported) + printf("Sub-page size: %d %s\n", + mtd.subpage_size, + mtd.subpage_size > 1 ? "bytes" : "byte"); + else if (mtd.type == MTD_NANDFLASH) + printf("Sub-page size: unknown\n"); + + if (mtd.oob_size > 0) + printf("OOB size: %d bytes\n", + mtd.oob_size); + if (mtd.region_cnt > 0) + printf("Additional erase regions: %d\n", mtd.oob_size); + if (mtd_info->sysfs_supported) + printf("Character device major/minor: %d:%d\n", + mtd.major, mtd.minor); + printf("Bad blocks are allowed: %s\n", + mtd.bb_allowed ? "true" : "false"); + printf("Device is writable: %s\n", + mtd.writable ? "true" : "false"); + + if (!args.ubinfo) + goto out; + + if (!mtd_info->sysfs_supported) { + errmsg("cannot provide UBI info, becasue sub-page size is " + "not known"); + goto out; + } + + ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, + 0, 1); + printf("Default UBI VID header offset: %d\n", ui.vid_hdr_offs); + printf("Default UBI data offset: %d\n", ui.data_offs); + printf("Default UBI LEB size: "); + ubiutils_print_bytes(ui.leb_size, 0); + printf("\n"); + printf("Maximum UBI volumes count: %d\n", ui.max_volumes); + +out: + printf("\n"); + return 0; +} + +static int print_general_info(libmtd_t libmtd, const struct mtd_info *mtd_info, + int all) +{ + int i, err, first = 1; + struct mtd_dev_info mtd; + + printf("Count of MTD devices: %d\n", mtd_info->dev_count); + if (mtd_info->dev_count == 0) + return 0; + + printf("Present MTD devices: "); + for (i = mtd_info->lowest_dev_num; + i <= mtd_info->highest_dev_num; i++) { + err = mtd_get_dev_info1(libmtd, i, &mtd); + if (err == -1) { + if (errno == ENODEV) + continue; + + printf("\n"); + return sys_errmsg("libmtd failed get MTD device %d " + "information", i); + } + + if (!first) + printf(", mtd%d", i); + else { + printf("mtd%d", i); + first = 0; + } + } + printf("\n"); + printf("Sysfs interface supported: %s\n", + mtd_info->sysfs_supported ? "yes" : "no"); + + if (!all) + return 0; + + first = 1; + printf("\n"); + + for (i = mtd_info->lowest_dev_num; + i <= mtd_info->highest_dev_num; i++) { + err = print_dev_info(libmtd, mtd_info, i); + if (err) + return err; + } + + return 0; +} + +int main(int argc, char * const argv[]) +{ + int err; + libmtd_t libmtd; + struct mtd_info mtd_info; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libmtd = libmtd_open(); + if (libmtd == NULL) { + if (errno == 0) + return errmsg("MTD is not present in the system"); + return sys_errmsg("cannot open libmtd"); + } + + err = mtd_get_info(libmtd, &mtd_info); + if (err) { + if (errno == ENODEV) + return errmsg("MTD is not present"); + return sys_errmsg("cannot get MTD information"); + } + + if (args.node) { + /* + * A character device was specified, translate this to MTD + * device number. + */ + err = translate_dev(libmtd, args.node); + if (err) + goto out_libmtd; + } + + if (args.mtdn == -1) + err = print_general_info(libmtd, &mtd_info, args.all); + else + err = print_dev_info(libmtd, &mtd_info, args.mtdn); + if (err) + goto out_libmtd; + + libmtd_close(libmtd); + return 0; + +out_libmtd: + libmtd_close(libmtd); + return -1; +} diff --git a/ubi-utils/src/ubiformat.c b/ubi-utils/src/ubiformat.c index 1c201a6..b9d3f79 100644 --- a/ubi-utils/src/ubiformat.c +++ b/ubi-utils/src/ubiformat.c @@ -45,7 +45,7 @@ #include "crc32.h" #include "common.h" -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" #define PROGRAM_NAME "ubiformat" /* The variables below are set by command line arguments */ @@ -55,6 +55,7 @@ struct args { unsigned int verbose:1; unsigned int override_ec:1; unsigned int novtbl:1; + unsigned int manual_subpage; int subpage_size; int vid_hdr_offs; int ubi_ver; @@ -633,9 +634,9 @@ static int format(const struct mtd_dev_info *mtd, const struct ubigen_info *ui, write_size, eb); if (errno != EIO) { - if (args.subpage_size != mtd->min_io_size) - normsg("may be %d is incorrect?", - args.subpage_size); + if (!args.subpage_size != mtd->min_io_size) + normsg("may be sub-page size is " + "incorrect?"); goto out_free; } @@ -682,59 +683,91 @@ out_free: int main(int argc, char * const argv[]) { int err, verbose; + libmtd_t libmtd; + struct mtd_info mtd_info; struct mtd_dev_info mtd; libubi_t libubi; struct ubigen_info ui; struct ubi_scan_info *si; + libmtd = libmtd_open(); + if (!libmtd) + return errmsg("MTD subsystem is not present"); + err = parse_opt(argc, argv); if (err) - return -1; + goto out_close_mtd; - err = mtd_get_dev_info(args.node, &mtd); - if (err) - return errmsg("cannot get information about \"%s\"", args.node); + err = mtd_get_info(libmtd, &mtd_info); + if (err) { + if (errno == ENODEV) + errmsg("MTD is not present"); + sys_errmsg("cannot get MTD information"); + goto out_close_mtd; + } - args.node_fd = open(args.node, O_RDWR); - if (args.node_fd == -1) - return sys_errmsg("cannot open \"%s\"", args.node); + err = mtd_get_dev_info(libmtd, args.node, &mtd); + if (err) { + sys_errmsg("cannot get information about \"%s\"", args.node); + goto out_close_mtd; + } + + if (!mtd_info.sysfs_supported) { + /* + * Linux kernels older than 2.6.30 did not support sysfs + * interface, and it is impossible to find out sub-page + * size in these kernels. This is why users should + * provide -s option. + */ + if (args.subpage_size == 0) { + warnmsg("your MTD system is old and it is impossible " + "to detect sub-page size. Use -s to get rid " + "of this warning"); + normsg("assume sub-page to be %d", mtd.subpage_size); + } else { + mtd.subpage_size = args.subpage_size; + args.manual_subpage = 1; + } + } else if (args.subpage_size && args.subpage_size != mtd.subpage_size) { + mtd.subpage_size = args.subpage_size; + args.manual_subpage = 1; + } - if (args.subpage_size == 0) - args.subpage_size = mtd.min_io_size; - else { + if (args.manual_subpage) { + /* Do some sanity check */ if (args.subpage_size > mtd.min_io_size) { errmsg("sub-page cannot be larger than min. I/O unit"); - goto out; + goto out_close; } if (mtd.min_io_size % args.subpage_size) { - errmsg("min. I/O unit size should be multiple of sub-page size"); - goto out; + errmsg("min. I/O unit size should be multiple of " + "sub-page size"); + goto out_close; } } + args.node_fd = open(args.node, O_RDWR); + if (args.node_fd == -1) { + sys_errmsg("cannot open \"%s\"", args.node); + goto out_close_mtd; + } + /* Validate VID header offset if it was specified */ if (args.vid_hdr_offs != 0) { if (args.vid_hdr_offs % 8) { errmsg("VID header offset has to be multiple of min. I/O unit size"); - goto out; + goto out_close; } if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) { errmsg("bad VID header offset"); - goto out; + goto out_close; } } - /* - * Because of MTD interface limitations 'mtd_get_dev_info()' cannot get - * sub-page so we force the user to pass it via the command line. Let's - * hope the user passed us something sane. - */ - mtd.subpage_size = args.subpage_size; - if (!mtd.writable) { errmsg("mtd%d (%s) is a read-only device", mtd.dev_num, args.node); - goto out; + goto out_close; } /* Make sure this MTD device is not attached to UBI */ @@ -747,14 +780,14 @@ int main(int argc, char * const argv[]) if (!err) { errmsg("please, first detach mtd%d (%s) from ubi%d", mtd.dev_num, args.node, ubi_dev_num); - goto out; + goto out_close; } } if (!args.quiet) { normsg_cont("mtd%d (%s), size ", mtd.dev_num, mtd.type_str); ubiutils_print_bytes(mtd.size, 1); - printf(", %d eraseblocks of ", mtd.eb_size); + printf(", %d eraseblocks of ", mtd.eb_cnt); ubiutils_print_bytes(mtd.eb_size, 1); printf(", min. I/O size %d bytes\n", mtd.min_io_size); } @@ -768,7 +801,7 @@ int main(int argc, char * const argv[]) err = ubi_scan(&mtd, args.node_fd, &si, verbose); if (err) { errmsg("failed to scan mtd%d (%s)", mtd.dev_num, args.node); - goto out; + goto out_close; } if (si->good_cnt == 0) { @@ -777,7 +810,8 @@ int main(int argc, char * const argv[]) } if (si->good_cnt < 2 && (!args.novtbl || args.image)) { - errmsg("too few non-bad eraseblocks (%d) on mtd%d", si->good_cnt, mtd.dev_num); + errmsg("too few non-bad eraseblocks (%d) on mtd%d", + si->good_cnt, mtd.dev_num); goto out_free; } @@ -842,7 +876,7 @@ int main(int argc, char * const argv[]) if (!args.quiet && args.override_ec) normsg("use erase counter %lld for all eraseblocks", args.ec); - ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, args.subpage_size, + ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, args.vid_hdr_offs, args.ubi_ver); if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { @@ -883,11 +917,14 @@ int main(int argc, char * const argv[]) ubi_scan_free(si); close(args.node_fd); + libmtd_close(libmtd); return 0; out_free: ubi_scan_free(si); -out: +out_close: close(args.node_fd); +out_close_mtd: + libmtd_close(libmtd); return -1; } diff --git a/ubi-utils/src/ubinfo.c b/ubi-utils/src/ubinfo.c index 036ed6c..8c5a1a9 100644 --- a/ubi-utils/src/ubinfo.c +++ b/ubi-utils/src/ubinfo.c @@ -30,7 +30,7 @@ #include <libubi.h> #include "common.h" -#define PROGRAM_VERSION "1.0" +#define PROGRAM_VERSION "1.1" #define PROGRAM_NAME "ubinfo" /* The variables below are set by command line arguments */ @@ -131,7 +131,7 @@ static int parse_opt(int argc, char * const argv[]) if (optind == argc - 1) args.node = argv[optind]; else if (optind < argc) - return errmsg("more then one UBI devices specified (use -h for help)"); + return errmsg("more then one UBI device specified (use -h for help)"); return 0; } @@ -216,9 +216,11 @@ static int print_dev_info(libubi_t libubi, int dev_num, int all) if (err) return sys_errmsg("cannot get information about UBI device %d", dev_num); - printf("ubi%d:\n", dev_info.dev_num); + 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.leb_size); + printf("Logical eraseblock size: "); + ubiutils_print_bytes(dev_info.leb_size, 0); + printf("\n"); printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs); ubiutils_print_bytes(dev_info.total_bytes, 0); @@ -232,7 +234,8 @@ static int print_dev_info(libubi_t libubi, int dev_num, int all) 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("Minimum input/output unit size: %d %s\n", + dev_info.min_io_size, dev_info.min_io_size > 1 ? "bytes" : "byte"); printf("Character device major/minor: %d:%d\n", dev_info.major, dev_info.minor); @@ -317,6 +320,7 @@ static int print_general_info(libubi_t libubi, int all) if (errno == ENOENT) continue; + printf("\n"); return sys_errmsg("libubi failed to probe UBI device %d", i); } @@ -339,15 +343,7 @@ static int print_general_info(libubi_t libubi, int all) 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; - - return sys_errmsg("libubi failed to probe UBI device %d", i); - } first = 0; - err = print_dev_info(libubi, i, all); if (err) return err; |