From e694872b52348ae7cd7e5a2df7a24b94f58acfd4 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 3 Apr 2014 18:14:51 +0100 Subject: [PATCH] add ubi flashing tool Signed-off-by: John Crispin --- .gitignore | 1 + CMakeLists.txt | 7 + libubi/libubi-tiny.c | 315 +++++++++ libubi/libubi-tiny.h | 27 + libubi/libubi.c | 1401 ++++++++++++++++++++++++++++++++++++++ libubi/libubi.h | 499 ++++++++++++++ libubi/libubi_int.h | 131 ++++ libubi/ubi-media.h | 378 ++++++++++ libubi/ubi-user.h | 418 ++++++++++++ libubi/ubiutils-common.c | 211 ++++++ ubi.c | 274 ++++++++ 11 files changed, 3662 insertions(+) create mode 100644 libubi/libubi-tiny.c create mode 100644 libubi/libubi-tiny.h create mode 100644 libubi/libubi.c create mode 100644 libubi/libubi.h create mode 100644 libubi/libubi_int.h create mode 100644 libubi/ubi-media.h create mode 100644 libubi/ubi-user.h create mode 100644 libubi/ubiutils-common.c create mode 100644 ubi.c diff --git a/.gitignore b/.gitignore index c7ff049..5631098 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +ubi block fs-state .* diff --git a/CMakeLists.txt b/CMakeLists.txt index b81d66c..4845954 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,3 +29,10 @@ ADD_EXECUTABLE(block block.c libblkid-tiny/squashfs.c) TARGET_LINK_LIBRARIES(block uci ubox blobmsg_json) INSTALL(TARGETS block RUNTIME DESTINATION sbin) + +ADD_EXECUTABLE(ubi ubi.c + libubi/libubi.c + libubi/libubi-tiny.c + libubi/ubiutils-common.c) + +INSTALL(TARGETS ubi RUNTIME DESTINATION sbin) diff --git a/libubi/libubi-tiny.c b/libubi/libubi-tiny.c new file mode 100644 index 0000000..d726801 --- /dev/null +++ b/libubi/libubi-tiny.c @@ -0,0 +1,315 @@ +/* + * 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 + */ + +/* + * An utility to delete UBI devices (detach MTD devices from UBI). + * + * Author: Artem Bityutskiy + */ + +#define PROGRAM_NAME "ubidetach" +#define VERSION "owrt-fstools" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libubi-tiny.h" + +#define DEFAULT_CTRL_DEV "/dev/ubi_ctrl" + +static int ubi_write(char *node, int fd, const void *buf, int len) +{ + int ret; + + while (len) { + ret = write(fd, buf, len); + if (ret < 0) { + if (errno == EINTR) { + fprintf(stderr, "do not interrupt me!"); + continue; + } + fprintf(stderr, "cannot write %d bytes to volume \"%s\"", len, node); + return -1; + } + + if (ret == 0) { + fprintf(stderr, "cannot write %d bytes to volume \"%s\"", len, node); + return -1; + } + len -= ret; + buf += ret; + } + + return 0; +} + +static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info, char *node, char *img, int skip) +{ + int err, fd, ifd; + long long bytes; + char *buf; + struct stat st; + + buf = malloc(vol_info->leb_size); + if (!buf) { + fprintf(stderr, "cannot allocate %d bytes of memory", vol_info->leb_size); + return -1; + } + err = stat(img, &st); + if (err < 0) { + fprintf(stderr, "stat failed on \"%s\"", img); + goto out_free; + } + + bytes = st.st_size - skip; + + if (bytes > vol_info->rsvd_bytes) { + fprintf(stderr, "\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)", + img, bytes, node, vol_info->rsvd_bytes); + goto out_free; + } + + fd = open(node, O_RDWR); + if (fd == -1) { + fprintf(stderr, "cannot open UBI volume \"%s\"", node); + goto out_free; + } + + ifd = open(img, O_RDONLY); + if (ifd == -1) { + fprintf(stderr, "cannot open \"%s\"", img); + goto out_close1; + } + + if (skip && lseek(ifd, skip, SEEK_CUR) == -1) { + fprintf(stderr, "lseek input by %d failed", skip); + goto out_close; + } + + err = ubi_update_start(libubi, fd, bytes); + if (err) { + fprintf(stderr, "cannot start volume \"%s\" update", node); + goto out_close; + } + + while (bytes) { + ssize_t ret; + int to_copy = vol_info->leb_size; + if (to_copy > bytes) + to_copy = bytes; + + ret = read(ifd, buf, to_copy); + if (ret <= 0) { + if (errno == EINTR) { + fprintf(stderr, "do not interrupt me!"); + continue; + } else { + fprintf(stderr, "cannot read %d bytes from \"%s\"", + to_copy, img); + goto out_close; + } + } + + err = ubi_write(node, fd, buf, ret); + if (err) + goto out_close; + bytes -= ret; + } + + close(ifd); + close(fd); + free(buf); + return 0; + +out_close: + close(ifd); +out_close1: + close(fd); +out_free: + free(buf); + return -1; +} + +int ubiattach(libubi_t libubi, char *mtd) +{ + struct ubi_attach_request req = { + .dev_num = UBI_DEV_NUM_AUTO, + .mtd_num = -1, + .vid_hdr_offset = 0, + .max_beb_per1024 = 0, + .mtd_dev_node = mtd, + }; + int err = ubi_attach(libubi, DEFAULT_CTRL_DEV, &req); + + if (err) { + fprintf(stderr, "cannot attach \"%s\"", mtd); + return err; + } + + return 0; +} + +int ubidetach(libubi_t libubi, char *mtd) +{ + return ubi_detach(libubi, DEFAULT_CTRL_DEV, mtd); +} + +int ubirsvol(libubi_t libubi, char *node, char *name, int bytes) +{ + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + int err = ubi_get_dev_info(libubi, node, &dev_info); + + if (err) { + fprintf(stderr, "cannot get information about UBI device \"%s\"", + node); + return -1; + } + err = ubi_get_vol_info1_nm(libubi, dev_info.dev_num, name, &vol_info); + if (err) { + fprintf(stderr, "cannot find UBI volume \"%s\"", name); + return -1; + } + + err = ubi_rsvol(libubi, node, vol_info.vol_id, bytes); + if (err) { + fprintf(stderr, "cannot UBI resize volume"); + return -1; + } + + return 0; +} + +int ubirmvol(libubi_t libubi, char *node, char *name) +{ + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + int err = ubi_get_dev_info(libubi, node, &dev_info); + + if (err) { + fprintf(stderr, "cannot get information about UBI device \"%s\"", + node); + return -1; + } + + err = ubi_get_vol_info1_nm(libubi, dev_info.dev_num, name, &vol_info); + if (err) { + fprintf(stderr, "cannot find UBI volume \"%s\"", name); + return -1; + } + + err = ubi_rmvol(libubi, node, vol_info.vol_id); + if (err) { + fprintf(stderr, "cannot UBI remove volume"); + return -1; + } + + return 0; +} + +int ubimkvol(libubi_t libubi, char *node, char *name, int maxavs) +{ + struct ubi_dev_info dev_info; + struct ubi_vol_info vol_info; + struct ubi_mkvol_request req; + int err = ubi_get_dev_info(libubi, node, &dev_info); + + if (err) { + fprintf(stderr, "cannot get information about UBI device \"%s\"", + node); + return -1; + } + + if (dev_info.avail_bytes == 0) { + fprintf(stderr, "UBI device does not have free logical eraseblocks"); + return -1; + } + + if (maxavs) + printf("Set volume size to %lld\n", dev_info.avail_bytes); + + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + req.bytes = dev_info.avail_bytes; + req.vol_type = UBI_DYNAMIC_VOLUME; + req.name = name; + + err = ubi_mkvol(libubi, node, &req); + if (err < 0) { + fprintf(stderr, "cannot UBI create volume"); + return -1; + } + + /* Print information about the created device */ + err = ubi_get_vol_info1(libubi, dev_info.dev_num, req.vol_id, &vol_info); + if (err) { + fprintf(stderr, "cannot get information about newly created UBI volume"); + return -1; + } + + printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs); + ubiutils_print_bytes(vol_info.rsvd_bytes, 0); + printf("), LEB size "); + ubiutils_print_bytes(vol_info.leb_size, 1); + printf(", %s, name \"%s\", alignment %d\n", + req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static", + vol_info.name, vol_info.alignment); + + return 0; +} + +int ubiupdatevol(libubi_t libubi, char *node, char *file) +{ + struct ubi_vol_info vol_info; + int err = ubi_get_vol_info(libubi, node, &vol_info); + + if (err) { + fprintf(stderr, "cannot get information about UBI volume \"%s\"", + node); + return -1; + } + + return update_volume(libubi, &vol_info, node, file, 0); +} + +int ubitruncatevol(libubi_t libubi, char *node) +{ + int err, fd; + + fd = open(node, O_RDWR); + if (fd == -1) { + fprintf(stderr, "cannot open \"%s\"", node); + return -1; + } + + err = ubi_update_start(libubi, fd, 0); + if (err) { + fprintf(stderr, "cannot truncate volume \"%s\"", node); + close(fd); + return -1; + } + + close(fd); + return 0; +} diff --git a/libubi/libubi-tiny.h b/libubi/libubi-tiny.h new file mode 100644 index 0000000..0ede6cd --- /dev/null +++ b/libubi/libubi-tiny.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LIBUBI_TINY_H__ +#define _LIBUBI_TINY_H__ + +#include "libubi.h" + +int ubiattach(libubi_t libubi, char *mtd); +int ubidetach(libubi_t libubi, char *mtd); +int ubirsvol(libubi_t libubi, char *node, char *name, int bytes); +int ubirmvol(libubi_t libubi, char *node, char *name); +int ubimkvol(libubi_t libubi, char *node, char *name, int maxavs); +int ubiupdatevol(libubi_t libubi, char *node, char *file); +int ubitruncatevol(libubi_t libubi, char *node); + +#endif diff --git a/libubi/libubi.c b/libubi/libubi.c new file mode 100644 index 0000000..3494f9d --- /dev/null +++ b/libubi/libubi.c @@ -0,0 +1,1401 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * 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 + * + * UBI (Unsorted Block Images) library. + */ + +#define PROGRAM_NAME "libubi" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" +#include "libubi_int.h" + +/** + * 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_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) + return -1; + + rd = read(fd, buf, sizeof(buf)); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (rd == sizeof(buf)) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } + buf[rd] = '\0'; + + 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)) { + sys_errmsg("close failed on \"%s\"", file); + return -1; + } + + return 0; + +out_error: + close(fd); + return -1; +} + +/** + * 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; + + if (read_positive_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; +} + +/** + * 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; + } + + if (rd == buf_len) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } + + ((char *)buf)[rd] = '\0'; + + /* 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 succes, 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; + errmsg("\"%s\" does not have major:minor format", file); + return -1; + } + + if (*major < 0 || *minor < 0) { + errno = EINVAL; + errmsg("bad major:minor %d:%d in \"%s\"", + *major, *minor, file); + return -1; + } + + return 0; +} + +/** + * 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); +} + +/** + * 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) +{ + char file[strlen(patt) + 100]; + + sprintf(file, patt, dev_num, vol_id); + return read_positive_int(file, value); +} + +/** + * 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) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_positive_ll(file, value); +} + +/** + * 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]; + + sprintf(file, patt, dev_num, vol_id); + return read_positive_ll(file, value); +} + +/** + * 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); +} + +/** + * 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]; + + sprintf(file, lib->dev_dev, dev_num); + return read_major(file, major, minor); +} + +/** + * 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); +} + +/** + * 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) +{ + struct stat st; + struct ubi_info info; + int i, fd, major, minor; + char file[strlen(lib->ubi_vol) + 100]; + + if (stat(node, &st)) { + sys_errmsg("cannot get information about \"%s\"", + node); + return -1; + } + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + errmsg("\"%s\" is not a character device", node); + return -1; + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + if (minor == 0) { + errno = EINVAL; + errmsg("\"%s\" is not a volume character device", node); + return -1; + } + + if (ubi_get_info((libubi_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; + return -1; + } + + if (major1 == major) + break; + } + + if (i > info.highest_dev_num) { + errno = ENODEV; + return -1; + } + + /* Make sure this UBI volume exists */ + sprintf(file, lib->ubi_vol, i, minor - 1); + fd = open(file, O_RDONLY); + if (fd == -1) { + errno = ENODEV; + return -1; + } + + *dev_num = i; + *vol_id = minor - 1; + errno = 0; + return 0; +} + +/** + * 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 %0 in case of success and %-1 in case of failure. + */ +static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) +{ + struct stat st; + struct ubi_info info; + int i, major, minor; + + if (stat(node, &st)) { + sys_errmsg("cannot get information about \"%s\"", node); + return -1; + } + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + errmsg("\"%s\" is not a character device", node); + return -1; + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + if (minor != 0) { + errno = EINVAL; + errmsg("\"%s\" is not an UBI character device", node); + return -1; + } + + if (ubi_get_info((libubi_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; + return -1; + } + + if (major1 == major) { + if (minor1 != 0) { + errmsg("UBI character device minor number is " + "%d, but must be 0", minor1); + errno = EINVAL; + return -1; + } + errno = 0; + *dev_num = i; + return 0; + } + } + + errno = ENODEV; + return -1; +} + +int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num) +{ + struct ubi_info info; + int i, ret, mtd_num1; + struct libubi *lib = desc; + + if (ubi_get_info(desc, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1); + if (ret) { + if (errno == ENOENT) + continue; + return -1; + } + + if (mtd_num1 == mtd_num) { + errno = 0; + *dev_num = i; + return 0; + } + } + + errno = 0; + return -1; +} + +libubi_t libubi_open(void) +{ + int fd, version; + struct libubi *lib; + + lib = calloc(1, sizeof(struct libubi)); + if (!lib) + return NULL; + + lib->sysfs_ctrl = mkpath("/sys", SYSFS_CTRL); + if (!lib->sysfs_ctrl) + goto out_error; + + lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV); + if (!lib->ctrl_dev) + goto out_error; + + lib->sysfs_ubi = mkpath("/sys", SYSFS_UBI); + if (!lib->sysfs_ubi) + goto out_error; + + /* Make sure UBI is present */ + fd = open(lib->sysfs_ubi, O_RDONLY); + if (fd == -1) { + errno = 0; + goto out_error; + } + + if (close(fd)) { + sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi); + goto out_error; + } + + 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->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM); + if (!lib->dev_mtd_num) + 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; + + lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); + if (!lib->vol_name) + goto out_error; + + if (read_positive_int(lib->ubi_version, &version)) + goto out_error; + if (version != LIBUBI_UBI_VERSION) { + errmsg("this library was made for UBI version %d, but UBI " + "version %d is detected\n", LIBUBI_UBI_VERSION, version); + goto out_error; + } + + return lib; + +out_error: + libubi_close((libubi_t)lib); + return NULL; +} + +void libubi_close(libubi_t desc) +{ + struct libubi *lib = (struct libubi *)desc; + + 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_mtd_num); + 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); +} + +/** + * do_attach - perform the actual attach operation. + * @node: name of the UBI control character device node + * @r: attach request + * + * This function performs the actual UBI attach operation. Returns %0 in case of + * success and %-1 in case of failure. @r->ubi_num contains newly created UBI + * device number. + */ +static int do_attach(const char *node, const struct ubi_attach_req *r) +{ + int fd, ret; + + fd = open(node, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", node); + return -1; + } + ret = ioctl(fd, UBI_IOCATT, r); + close(fd); + if (ret == -1) + return -1; + +#ifdef UDEV_SETTLE_HACK +// if (system("udevsettle") == -1) +// return -1; + usleep(100000); +#endif + return ret; +} + +#ifndef MTD_CHAR_MAJOR +/* + * This is taken from kernel and is unlikely to change anytime + * soon. + */ +#define MTD_CHAR_MAJOR 90 +#endif + +/** + * mtd_node_to_num - converts device node to MTD number. + * @mtd_dev_node: path to device node to convert + * + * This function converts given @mtd_dev_node to MTD device number. + * @mtd_dev_node should contain path to the MTD device node. Returns MTD device + * number in case of success and %-1 in case of failure (errno is set). + */ +static int mtd_node_to_num(const char *mtd_dev_node) +{ + int major, minor; + struct stat sb; + + if (stat(mtd_dev_node, &sb) < 0) { + sys_errmsg("cannot stat \"%s\"", mtd_dev_node); + return -1; + } + + if (!S_ISCHR(sb.st_mode)) { + errno = EINVAL; + sys_errmsg("\"%s\" is not a character device", + mtd_dev_node); + return -1; + } + + major = major(sb.st_rdev); + minor = minor(sb.st_rdev); + + if (major != MTD_CHAR_MAJOR) { + errno = EINVAL; + sys_errmsg("\"%s\" is not an MTD device", mtd_dev_node); + return -1; + } + + return minor / 2; +} + +int ubi_attach(libubi_t desc, const char *node, struct ubi_attach_request *req) +{ + struct ubi_attach_req r; + int ret; + + (void)desc; + + if (req->mtd_dev_node) { + /* + * User has passed path to device node. Lets find out MTD + * device number of the device and update req->mtd_num with it + */ + req->mtd_num = mtd_node_to_num(req->mtd_dev_node); + if (req->mtd_num == -1) + return -1; + } + + memset(&r, 0, sizeof(struct ubi_attach_req)); + r.ubi_num = req->dev_num; + r.mtd_num = req->mtd_num; + r.vid_hdr_offset = req->vid_hdr_offset; + + if (req->max_beb_per1024) { + /* + * We first have to check if the running kernel supports the + * 'max_beb_per1024' parameter. To do this, we invoke the + * "attach" ioctl 2 times: first with incorrect value %-1 of + * 'max_beb_per1024'. + * + * If the ioctl succeeds, it means that the kernel doesn't + * support the feature and just ignored our 'max_beb_per1024' + * value. + * + * If the ioctl returns -EINVAL, we assume this is because + * 'max_beb_per1024' was set to -1, and we invoke the ioctl for + * the second time with the 'max_beb_per1024' value. + */ + r.max_beb_per1024 = -1; + ret = do_attach(node, &r); + if (ret == 0) { + req->dev_num = r.ubi_num; + /* + * The call succeeded. It means that the kernel ignored + * 'max_beb_per1024' parameter. + */ + return 1; + } else if (errno != EINVAL) + return ret; + } + + r.max_beb_per1024 = req->max_beb_per1024; + + ret = do_attach(node, &r); + if (ret == 0) + req->dev_num = r.ubi_num; + + return ret; +} + +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) +{ + int ret, ubi_dev; + + ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); + if (ret == -1) { + errno = ENODEV; + return ret; + } + + return ubi_remove_dev(desc, node, ubi_dev); +} + +int ubi_detach(libubi_t desc, const char *node, const char *mtd_dev_node) +{ + int mtd_num; + + if (!mtd_dev_node) { + errno = EINVAL; + return -1; + } + + mtd_num = mtd_node_to_num(mtd_dev_node); + if (mtd_num == -1) + return -1; + + return ubi_detach_mtd(desc, node, mtd_num); +} + +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) +{ + int fd, ret; + + desc = desc; + + fd = open(node, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", node); + return -1; + } + ret = ioctl(fd, UBI_IOCDET, &ubi_dev); + if (ret == -1) + goto out_close; + +#ifdef UDEV_SETTLE_HACK +// if (system("udevsettle") == -1) +// return -1; + usleep(100000); +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_probe_node(libubi_t desc, const char *node) +{ + struct stat st; + struct ubi_info info; + int i, fd, major, minor; + struct libubi *lib = (struct libubi *)desc; + char file[strlen(lib->ubi_vol) + 100]; + + if (stat(node, &st)) { + sys_errmsg("cannot get information about \"%s\"", node); + return -1; + } + + 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 (ubi_get_info((libubi_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) + goto out_not_ubi; + return -1; + } + + if (major1 == major) + break; + } + + if (i > info.highest_dev_num) + goto out_not_ubi; + + if (minor == 0) + return 1; + + /* This is supposdely an UBI volume device node */ + sprintf(file, lib->ubi_vol, i, minor - 1); + fd = open(file, O_RDONLY); + if (fd == -1) + goto out_not_ubi; + + return 2; + +out_not_ubi: + errmsg("\"%s\" has major:minor %d:%d, but this does not correspond to " + "any existing UBI device or volume", node, major, minor); + errno = ENODEV; + return -1; +} + +int ubi_get_info(libubi_t desc, struct ubi_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, 0, sizeof(struct ubi_info)); + + if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) { + /* + * Older UBI versions did not have control device, so we do not + * panic here for compatibility reasons. May be few years later + * we could return -1 here, but for now just set major:minor to + * -1. + */ + info->ctrl_major = info->ctrl_minor = -1; + } + + /* + * 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; + + info->lowest_dev_num = INT_MAX; + while (1) { + int dev_num, ret; + char tmp_buf[256]; + + errno = 0; + dirent = readdir(sysfs_ubi); + if (!dirent) + break; + + if (strlen(dirent->d_name) >= 255) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_ubi, dirent->d_name); + errno = EINVAL; + goto out_close; + } + + ret = sscanf(dirent->d_name, UBI_DEV_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 (!dirent && errno) { + sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + goto out_close; + } + + if (closedir(sysfs_ubi)) { + sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + return -1; + } + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; + + if (read_positive_int(lib->ubi_version, &info->version)) + return -1; + + return 0; + +out_close: + closedir(sysfs_ubi); + return -1; +} + +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) +{ + int fd, ret; + struct ubi_mkvol_req r; + size_t n; + + memset(&r, 0, sizeof(struct ubi_mkvol_req)); + + 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; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", node); + return -1; + } + ret = ioctl(fd, UBI_IOCMKVOL, &r); + if (ret == -1) { + close(fd); + return ret; + } + + close(fd); + req->vol_id = r.vol_id; + +#ifdef UDEV_SETTLE_HACK +// if (system("udevsettle") == -1) +// return -1; + usleep(100000); +#endif + + return 0; +} + +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +{ + int fd, ret; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", node); + return -1; + } + + ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); + if (ret == -1) { + close(fd); + return ret; + } + + close(fd); + +#ifdef UDEV_SETTLE_HACK +// if (system("udevsettle") == -1) +// return -1; + usleep(100000); +#endif + + return 0; +} + +int ubi_rnvols(libubi_t desc, const char *node, struct ubi_rnvol_req *rnvol) +{ + int fd, ret; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCRNVOL, rnvol); + if (ret == -1) { + close(fd); + return ret; + } + + close(fd); + +#ifdef UDEV_SETTLE_HACK +// if (system("udevsettle") == -1) +// return -1; + usleep(100000); +#endif + + return 0; +} + +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +{ + int fd, ret; + struct ubi_rsvol_req req; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) { + sys_errmsg("cannot open \"%s\"", node); + return -1; + } + req.bytes = bytes; + req.vol_id = vol_id; + + ret = ioctl(fd, UBI_IOCRSVOL, &req); + close(fd); + return ret; +} + +int ubi_update_start(libubi_t desc, int fd, long long bytes) +{ + desc = desc; + if (ioctl(fd, UBI_IOCVOLUP, &bytes)) + return -1; + return 0; +} + +int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes) +{ + struct ubi_leb_change_req req; + + desc = desc; + memset(&req, 0, sizeof(struct ubi_leb_change_req)); + req.lnum = lnum; + req.bytes = bytes; + req.dtype = 3; + + if (ioctl(fd, UBI_IOCEBCH, &req)) + return -1; + return 0; +} + +int ubi_dev_present(libubi_t desc, int dev_num) +{ + struct stat st; + struct libubi *lib = (struct libubi *)desc; + char file[strlen(lib->ubi_dev) + 50]; + + sprintf(file, lib->ubi_dev, dev_num); + return !stat(file, &st); +} + +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; + + memset(info, 0, sizeof(struct ubi_dev_info)); + info->dev_num = dev_num; + + if (!ubi_dev_present(desc, dev_num)) + return -1; + + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return -1; + + info->lowest_vol_id = INT_MAX; + + while (1) { + int vol_id, ret, devno; + char tmp_buf[256]; + + errno = 0; + dirent = readdir(sysfs_ubi); + if (!dirent) + break; + + if (strlen(dirent->d_name) >= 255) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_ubi, dirent->d_name); + goto out_close; + } + + ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf); + if (ret == 2 && devno == dev_num) { + info->vol_count += 1; + if (vol_id > info->highest_vol_id) + info->highest_vol_id = vol_id; + if (vol_id < info->lowest_vol_id) + info->lowest_vol_id = vol_id; + } + } + + if (!dirent && errno) { + sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + goto out_close; + } + + if (closedir(sysfs_ubi)) { + sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + return -1; + } + if (info->lowest_vol_id == INT_MAX) + info->lowest_vol_id = 0; + + if (dev_get_major(lib, dev_num, &info->major, &info->minor)) + return -1; + + if (dev_read_int(lib->dev_mtd_num, dev_num, &info->mtd_num)) + return -1; + if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs)) + return -1; + if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs)) + 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->leb_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; + + info->avail_bytes = (long long)info->avail_lebs * info->leb_size; + info->total_bytes = (long long)info->total_lebs * info->leb_size; + + return 0; + +out_close: + closedir(sysfs_ubi); + return -1; +} + +int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) +{ + int err, dev_num = 0; + struct libubi *lib = (struct libubi *)desc; + + err = ubi_probe_node(desc, node); + if (err != 1) { + if (err == 2) + errno = ENODEV; + return -1; + } + + if (dev_node2num(lib, node, &dev_num)) + return -1; + + 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 (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) + return -1; + + ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); + if (ret < 0) + return -1; + + 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; + } + + 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_lebs); + if (ret) + return -1; + ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_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 = (long long)info->leb_size * info->rsvd_lebs; + + ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, + UBI_VOL_NAME_MAX + 2); + if (ret < 0) + return -1; + + info->name[ret - 1] = '\0'; + return 0; +} + +int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) +{ + int err, vol_id = 0, dev_num = 0; + struct libubi *lib = (struct libubi *)desc; + + err = ubi_probe_node(desc, node); + if (err != 2) { + if (err == 1) + errno = ENODEV; + return -1; + } + + if (vol_node2nums(lib, node, &dev_num, &vol_id)) + return -1; + + return ubi_get_vol_info1(desc, dev_num, vol_id, info); +} + +int ubi_get_vol_info1_nm(libubi_t desc, int dev_num, const char *name, + struct ubi_vol_info *info) +{ + int i, err; + unsigned int nlen = strlen(name); + struct ubi_dev_info dev_info; + + if (nlen == 0) { + errmsg("bad \"name\" input parameter"); + errno = EINVAL; + return -1; + } + + err = ubi_get_dev_info1(desc, dev_num, &dev_info); + if (err) + return err; + + for (i = dev_info.lowest_vol_id; + i <= dev_info.highest_vol_id; i++) { + err = ubi_get_vol_info1(desc, dev_num, i, info); + if (err == -1) { + if (errno == ENOENT) + continue; + return -1; + } + + if (nlen == strlen(info->name) && !strcmp(name, info->name)) + return 0; + } + + errno = ENOENT; + return -1; +} + +int ubi_set_property(int fd, uint8_t property, uint64_t value) +{ + struct ubi_set_vol_prop_req r; + + memset(&r, 0, sizeof(struct ubi_set_vol_prop_req)); + r.property = property; + r.value = value; + + return ioctl(fd, UBI_IOCSETVOLPROP, &r); +} + +int ubi_leb_unmap(int fd, int lnum) +{ + return ioctl(fd, UBI_IOCEBUNMAP, &lnum); +} + +int ubi_is_mapped(int fd, int lnum) +{ + return ioctl(fd, UBI_IOCEBISMAP, &lnum); +} diff --git a/libubi/libubi.h b/libubi/libubi.h new file mode 100644 index 0000000..fe2f7e0 --- /dev/null +++ b/libubi/libubi.h @@ -0,0 +1,499 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * 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 + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_H__ +#define __LIBUBI_H__ + +#include +#include +#include +#include +#include "ubi-user.h" +#include "ubi-media.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* UBI version libubi is made for */ +#define LIBUBI_UBI_VERSION 1 + +/* Maximum physical eraseblock size in bytes */ +#define UBI_MAX_PEB_SZ (2*1024*1024) + +#define errmsg(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) +#define sys_errmsg(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) + +long long ubiutils_get_bytes(const char *str); +void ubiutils_print_bytes(long long bytes, int bracket); +void ubiutils_print_text(FILE *stream, const char *text, int width); +int ubiutils_srand(void); + + +#define simple_strtoX(func, type) \ +static inline type simple_##func(const char *snum, int *error) \ +{ \ + char *endptr; \ + type ret = func(snum, &endptr, 0); \ + \ + if (error && (!*snum || *endptr)) { \ + errmsg("%s: unable to parse the number '%s'", #func, snum); \ + *error = 1; \ + } \ + \ + return ret; \ +} +simple_strtoX(strtol, long int) +simple_strtoX(strtoll, long long int) +simple_strtoX(strtoul, unsigned long int) +simple_strtoX(strtoull, unsigned long long int) + + +/* UBI library descriptor */ +typedef void * libubi_t; + +/** + * struct ubi_attach_request - MTD device attachment request. + * @dev_num: number to assign to the newly created UBI device + * (%UBI_DEV_NUM_AUTO should be used to automatically assign the + * number) + * @mtd_num: MTD device number to attach (used if @mtd_dev_node is %NULL) + * @mtd_dev_node: path to MTD device node to attach + * @vid_hdr_offset: VID header offset (%0 means default offset and this is what + * most of the users want) + * @max_beb_per1024: Maximum expected bad eraseblocks per 1024 eraseblocks + */ +struct ubi_attach_request +{ + int dev_num; + int mtd_num; + const char *mtd_dev_node; + int vid_hdr_offset; + int max_beb_per1024; +}; + +/** + * 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; + int alignment; + long long bytes; + int vol_type; + const char *name; +}; + +/** + * 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 + * @ctrl_major: major number of the UBI control device + * @ctrl_minor: minor number of the UBI control device + */ +struct ubi_info +{ + int dev_count; + int lowest_dev_num; + int highest_dev_num; + int version; + int ctrl_major; + int ctrl_minor; +}; + +/** + * struct ubi_dev_info - UBI device information. + * @dev_num: UBI device number + * @mtd_num: MTD device number on top of which this UBI device is working + * @vol_count: count of volumes on this UBI device + * @lowest_vol_id: lowest volume ID + * @highest_vol_id: highest volume ID + * @major: major number of corresponding character device + * @minor: minor number of corresponding character device + * @total_lebs: total number of logical eraseblocks on this UBI device + * @avail_lebs: how many logical eraseblocks are not used and available for new + * volumes + * @total_bytes: @total_lebs * @leb_size + * @avail_bytes: @avail_lebs * @leb_size + * @bad_count: count of bad physical eraseblocks + * @leb_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 +{ + int dev_num; + int mtd_num; + int vol_count; + int lowest_vol_id; + int highest_vol_id; + int major; + int minor; + int total_lebs; + int avail_lebs; + long long total_bytes; + long long avail_bytes; + int bad_count; + int leb_size; + long long max_ec; + int bad_rsvd; + int max_vol_count; + int min_io_size; +}; + +/** + * struct ubi_vol_info - UBI volume information. + * @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 + * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @alignment: alignment 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_lebs: how many logical eraseblocks are reserved for this volume + * @leb_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 type; + int alignment; + long long data_bytes; + long long rsvd_bytes; + int rsvd_lebs; + int leb_size; + int corrupted; + char name[UBI_VOL_NAME_MAX + 1]; +}; + +/** + * 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. In case of + * failure, errno contains the error code or zero if UBI is not present in the + * system. + */ +libubi_t libubi_open(void); + +/** + * libubi_close - close UBI library. + * @desc: UBI library descriptor + */ +void libubi_close(libubi_t desc); + +/** + * ubi_get_info - get general UBI information. + * @desc: UBI library descriptor + * @info: pointer to the &struct ubi_info object to fill + * + * This function fills the passed @info object with general UBI information and + * returns %0 in case of success and %-1 in case of failure. + */ +int ubi_get_info(libubi_t desc, struct ubi_info *info); + +/** + * mtd_num2ubi_dev - find UBI device by attached MTD device. + * @@desc: UBI library descriptor + * @mtd_num: MTD device number + * @dev_num: UBI device number is returned here + * + * This function finds UBI device to which MTD device @mtd_num is attached. + * Returns %0 if the UBI device was found and %-1 if not. + */ +int mtd_num2ubi_dev(libubi_t desc, int mtd_num, int *dev_num); + +/** + * ubi_attach - attach an MTD device by its node path or bt MTD device number + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @req: MTD attach request + * + * This function creates new UBI device by attaching an MTD device described by + * @req. If @req->mtd_dev_node is given it should contain path to the MTD + * device node. Otherwise @req->mtd_num will be used. + * + * Returns %0 in case of success, %-1 in case of failure (errno is set) and %1 + * if parameter @req->max_beb_per1024 was ignored by kernel (because the kernel + * is old and does not support this feature, which was added in 3.7). The newly + * created UBI device number is returned in @req->dev_num. In the MTD device + * was specified by its device node path, the MTD device number is returned in + * @req->mtd_num. + */ +int ubi_attach(libubi_t desc, const char *node, struct ubi_attach_request *req); + +/** + * ubi_detach_mtd - detach an MTD device. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @mtd_num: MTD device number to detach + * + * This function detaches MTD device number @mtd_num from UBI, which means the + * corresponding UBI device is removed. Returns zero in case of success and %-1 + * in case of failure. + */ +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num); + +/** + * ubi_detach - detach an MTD device by its node path. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @mtd_dev_node: path to an MTD device node + * + * This function detaches an MTD device @mtd_dev_node from UBI. Returns zero in + * case of success and %-1 in case of failure. + */ +int ubi_detach(libubi_t desc, const char *node, const char *mtd_dev_node); + +/** + * ubi_remove_dev - remove an UBI device. + * @desc: UBI library descriptor + * @node: name of the UBI control character device node + * @ubi_dev: UBI device number to remove + * + * This function removes UBI device number @ubi_dev and returns zero in case of + * success and %-1 in case of failure. + */ +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev); + +/** + * 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 + * + * 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 + * returned in @req->vol_id. + */ +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 + * + * This function removes volume @vol_id from UBI device @node and returns %0 in + * case of success and %-1 in case of failure. + */ +int ubi_rmvol(libubi_t desc, const char *node, int vol_id); + + +/** + * ubi_rnvols - rename UBI volumes. + * @desc: UBI library descriptor + * @node: name of the UBI character device to remove a volume from + * @rnvol: description of volumes to rename + * + * This function removes volume @vol_id from UBI device @node and returns %0 in + * case of success and %-1 in case of failure. + */ +int ubi_rnvols(libubi_t desc, const char *node, struct ubi_rnvol_req *rnvol); + +/** + * 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 + * + * 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_probe_node - test UBI node. + * @desc: UBI library descriptor + * @node: the node to test + * + * 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 device or volume node (errno is ENODEV in this case) or + * if an error occurred. + */ +int ubi_probe_node(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. If the UBI device + * corresponding to @node does not exist, errno is set to @ENODEV. + */ +int ubi_get_dev_info(libubi_t desc, const char *node, + struct ubi_dev_info *info); + +/** + * ubi_dev_present - check whether an UBI device is present. + * @desc: UBI library descriptor + * @dev_num: UBI device number to check + * + * This function returns %1 if UBI device is present and %0 if not. + */ +int ubi_dev_present(libubi_t desc, int dev_num); + +/** + * 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 + * + * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI + * device number, not UBI character device. If the UBI device @dev_num does not + * exist, errno is set to @ENODEV. + */ +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 + * + * This function fills the passed @info object with UBI volume information and + * returns %0 in case of success and %-1 in case of failure. If the UBI volume + * corresponding to @node does not exist, errno is set to @ENODEV. + */ +int ubi_get_vol_info(libubi_t desc, const char *node, + struct ubi_vol_info *info); + +/** + * 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 + * + * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI + * volume ID, not UBI volume character device. If the UBI device @dev_num does + * not exist, or if the UBI volume @vol_id does not exist, errno is set to + * @ENODEV. + */ +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info); + +/** + * ubi_get_vol_info1_nm - get UBI volume information by volume name. + * @desc: UBI library descriptor + * @dev_num: UBI device number + * @name: name 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 name, not UBI volume ID. If the UBI device @dev_num does not exist, + * or if the UBI volume @name does not exist, errno is set to @ENODEV. + */ +int ubi_get_vol_info1_nm(libubi_t desc, int dev_num, const char *name, + struct ubi_vol_info *info); + +/** + * ubi_update_start - start UBI volume update. + * @desc: UBI library descriptor + * @fd: volume character device 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. The caller is assumed to write @bytes data to the + * volume @fd afterward. + */ +int ubi_update_start(libubi_t desc, int fd, long long bytes); + +/** + * ubi_leb_change_start - start atomic LEB change. + * @desc: UBI library descriptor + * @fd: volume character device file descriptor + * @lnum: LEB number to change + * @bytes: how many bytes of new data will be written to the LEB + * + * This function initiates atomic LEB change operation and returns %0 in case + * of success and %-1 in case of error. he caller is assumed to write @bytes + * data to the volume @fd afterward. + */ +int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes); + +/** + * ubi_set_property - set volume propety. + * @fd: volume character device file descriptor + * @property: the property to change (%UBI_VOL_PROP_DIRECT_WRITE, etc) + * @value: new value of the changed property + * + * This function changes a property of a volume. Returns zero in case of + * success and a negative error code in case of error. + */ +int ubi_set_property(int fd, uint8_t property, uint64_t value); + +/** + * ubi_leb_unmap - unmap a logical eraseblock. + * @fd: volume character device file descriptor + * @lnum: logical eraseblock to unmap + * + * This function unmaps LEB @lnum and returns zero in case of success and a + * negative error code in case of error. + */ +int ubi_leb_unmap(int fd, int lnum); + +/** + * ubi_is_mapped - check if logical eraseblock is mapped. + * @fd: volume character device file descriptor + * @lnum: logical eraseblock number + * + * This function checks if logical eraseblock @lnum is mapped to a physical + * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily + * mean it will still be un-mapped after the UBI device is re-attached. The + * logical eraseblock may become mapped to the physical eraseblock it was last + * mapped to. + * + * This function returns %1 if the LEB is mapped, %0 if not, and %-1 in case of + * failure. If the volume is damaged because of an interrupted update errno + * set with %EBADF error code. + */ +int ubi_is_mapped(int fd, int lnum); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_H__ */ diff --git a/libubi/libubi_int.h b/libubi/libubi_int.h new file mode 100644 index 0000000..c3aa37a --- /dev/null +++ b/libubi/libubi_int.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * 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 + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_INT_H__ +#define __LIBUBI_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 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 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 DEV_AVAIL_EBS "avail_eraseblocks" +#define DEV_TOTAL_EBS "total_eraseblocks" +#define DEV_BAD_COUNT "bad_peb_count" +#define DEV_EB_SIZE "eraseblock_size" +#define DEV_MAX_EC "max_ec" +#define DEV_MAX_RSVD "reserved_for_bad" +#define DEV_MAX_VOLS "max_vol_count" +#define DEV_MIN_IO_SIZE "min_io_size" +#define DEV_MTD_NUM "mtd_num" + +#define UBI_VOL_NAME_PATT "ubi%d_%d" +#define VOL_TYPE "type" +#define VOL_DEV "dev" +#define VOL_ALIGNMENT "alignment" +#define VOL_DATA_BYTES "data_bytes" +#define VOL_RSVD_EBS "reserved_ebs" +#define VOL_EB_SIZE "usable_eb_size" +#define VOL_CORRUPTED "corrupted" +#define VOL_NAME "name" + +/** + * libubi - UBI library description data structure. + * @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 + * @dev_mtd_num: MTD device number + * @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; + char *dev_dev; + char *dev_avail_ebs; + char *dev_total_ebs; + char *dev_bad_count; + char *dev_eb_size; + char *dev_max_ec; + char *dev_bad_rsvd; + char *dev_max_vols; + char *dev_min_io_size; + char *dev_mtd_num; + char *ubi_vol; + char *vol_type; + char *vol_dev; + char *vol_alignment; + char *vol_data_bytes; + char *vol_rsvd_ebs; + char *vol_eb_size; + char *vol_corrupted; + char *vol_name; + char *vol_max_count; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_INT_H__ */ diff --git a/libubi/ubi-media.h b/libubi/ubi-media.h new file mode 100644 index 0000000..08bec3e --- /dev/null +++ b/libubi/ubi-media.h @@ -0,0 +1,378 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. + */ + +#ifndef __UBI_MEDIA_H__ +#define __UBI_MEDIA_H__ + +#include + +/* The version of UBI images supported by this implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 + +/* + * Volume type constants used in the volume identifier header. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Volume flags used in the volume table record. + * + * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume + * + * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume + * table. UBI automatically re-sizes the volume which has this flag and makes + * the volume to be of largest possible size. This means that if after the + * initialization UBI finds out that there are available physical eraseblocks + * present on the device, it automatically appends all of them to the volume + * (the physical eraseblocks reserved for bad eraseblocks handling and other + * reserved physical eraseblocks are not taken). So, if there is a volume with + * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical + * eraseblocks will be zero after UBI is loaded, because all of them will be + * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared + * after the volume had been initialized. + * + * The auto-resize feature is useful for device production purposes. For + * example, different NAND flash chips may have different amount of initial bad + * eraseblocks, depending of particular chip instance. Manufacturers of NAND + * chips usually guarantee that the amount of initial bad eraseblocks does not + * exceed certain percent, e.g. 2%. When one creates an UBI image which will be + * flashed to the end devices in production, he does not know the exact amount + * of good physical eraseblocks the NAND chip on the device will have, but this + * number is required to calculate the volume sized and put them to the volume + * table of the UBI image. In this case, one of the volumes (e.g., the one + * which will store the root file system) is marked as "auto-resizable", and + * UBI will adjust its size on the first boot if needed. + * + * Note, first UBI reserves some amount of physical eraseblocks for bad + * eraseblock handling, and then re-sizes the volume, not vice-versa. This + * means that the pool of reserved physical eraseblocks will always be present. + */ +enum { + UBI_VTBL_AUTORESIZE_FLG = 0x01, +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't allow the wear-leveling + * sub-system to move them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32)) + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: version of UBI implementation which is supposed to accept this + * UBI image + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start + * @image_seq: image sequence number + * @padding2: reserved for future, zeroes + * @hdr_crc: erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater than the current UBI + * version, the image is rejected. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * physical eraseblock. These values have to be the same for all physical + * eraseblocks. + * + * The @image_seq field is used to validate a UBI image that has been prepared + * for a UBI device. The @image_seq value can be any value, but it must be the + * same on all eraseblocks. UBI will ensure that all new erase counter headers + * also contain this value, and will check the value when scanning at start-up. + * One way to make use of @image_seq is to increase its value by one every time + * an image is flashed over an existing image, then, if the flashing does not + * complete, UBI will detect the error when scanning. + */ +struct ubi_ec_hdr { + __be32 magic; + __u8 version; + __u8 padding1[3]; + __be64 ec; /* Warning: the current limit is 31-bit anyway! */ + __be32 vid_hdr_offset; + __be32 data_offset; + __be32 image_seq; + __u8 padding2[32]; + __be32 hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: if this logical eraseblock was copied from another physical + * eraseblock (for wear-leveling reasons) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: ID of this volume + * @lnum: logical eraseblock number + * @padding1: reserved for future, zeroes + * @data_size: how many bytes of data this logical eraseblock contains + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this physical eraseblock are not + * used + * @data_crc: CRC checksum of the data stored in this logical eraseblock + * @padding2: reserved for future, zeroes + * @sqnum: sequence number + * @padding3: reserved for future, zeroes + * @hdr_crc: volume identifier header CRC checksum + * + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (sequence number) is used to distinguish between older and newer versions of + * logical eraseblocks. + * + * There are 2 situations when there may be more than one physical eraseblock + * corresponding to the same logical eraseblock, i.e., having the same @vol_id + * and @lnum values in the volume identifier header. Suppose we have a logical + * eraseblock L and it is mapped to the physical eraseblock P. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. + * + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. + * + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * checksum is correct, this physical eraseblock is selected (P1). Otherwise + * the older one (P) is selected. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for various internal + * UBI purposes. In this implementation there is only one internal volume - the + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. + * + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * data of the physical eraseblock was moved by the wear-leveling sub-system, + * then the wear-leveling sub-system calculates the data CRC and stores it in + * the @data_crc field. And of course, the @copy_flag is %in this case. + * + * The @data_size field is used only for static volumes because UBI has to know + * how many bytes of data are stored in this eraseblock. For dynamic volumes, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains data size. + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + */ +struct ubi_vid_hdr { + __be32 magic; + __u8 version; + __u8 vol_type; + __u8 copy_flag; + __u8 compat; + __be32 vol_id; + __be32 lnum; + __be32 leb_ver; + __be32 data_size; + __be32 used_ebs; + __be32 data_pad; + __be32 data_crc; + __u8 padding2[4]; + __be64 sqnum; + __u8 padding3[12]; + __be32 hdr_crc; +} __attribute__ ((packed)); + +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 + +/* + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* The layout volume contains the volume table */ + +#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC +#define UBI_LAYOUT_VOLUME_ALIGN 1 +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "layout volume" +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of the volume table record */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) + +/* Size of the volume table record without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32)) + +/** + * struct ubi_vtbl_record - a record in the volume table. + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length + * @name: the volume name + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + * @padding: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vtbl_record + * objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * the physical eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vtbl_record { + __be32 reserved_pebs; + __be32 alignment; + __be32 data_pad; + __u8 vol_type; + __u8 upd_marker; + __be16 name_len; + __u8 name[UBI_VOL_NAME_MAX+1]; + __u8 flags; + __u8 padding[23]; + __be32 crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_MEDIA_H__ */ diff --git a/libubi/ubi-user.h b/libubi/ubi-user.h new file mode 100644 index 0000000..1c06d88 --- /dev/null +++ b/libubi/ubi-user.h @@ -0,0 +1,418 @@ +/* + * Copyright © International Business Machines Corp., 2006 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_USER_H__ +#define __UBI_USER_H__ + +/* + * UBI device creation (the same as MTD device attachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI + * control device. The caller has to properly fill and pass + * &struct ubi_attach_req object - UBI will attach the MTD device specified in + * the request and return the newly created UBI device number as the ioctl + * return value. + * + * UBI device deletion (the same as MTD device detachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI + * control device. + * + * UBI volume creation + * ~~~~~~~~~~~~~~~~~~~ + * + * UBI volumes are created via the %UBI_IOCMKVOL ioctl command of UBI character + * device. A &struct ubi_mkvol_req object has to be properly filled and a + * pointer to it has to be passed to the ioctl. + * + * UBI volume deletion + * ~~~~~~~~~~~~~~~~~~~ + * + * To delete a volume, the %UBI_IOCRMVOL ioctl command of the UBI character + * device should be used. A pointer to the 32-bit volume ID hast to be passed + * to the ioctl. + * + * UBI volume re-size + * ~~~~~~~~~~~~~~~~~~ + * + * To re-size a volume, the %UBI_IOCRSVOL ioctl command of the UBI character + * device should be used. A &struct ubi_rsvol_req object has to be properly + * filled and a pointer to it has to be passed to the ioctl. + * + * UBI volumes re-name + * ~~~~~~~~~~~~~~~~~~~ + * + * To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command + * of the UBI character device should be used. A &struct ubi_rnvol_req object + * has to be properly filled and a pointer to it has to be passed to the ioctl. + * + * UBI volume update + * ~~~~~~~~~~~~~~~~~ + * + * Volume update should be done via the %UBI_IOCVOLUP ioctl command of the + * corresponding UBI volume character device. A pointer to a 64-bit update + * size should be passed to the ioctl. After this, UBI expects user to write + * this number of bytes to the volume character device. The update is finished + * when the claimed number of bytes is passed. So, the volume update sequence + * is something like: + * + * fd = open("/dev/my_volume"); + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); + * + * Logical eraseblock erase + * ~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To erase a logical eraseblock, the %UBI_IOCEBER ioctl command of the + * corresponding UBI volume character device should be used. This command + * unmaps the requested logical eraseblock, makes sure the corresponding + * physical eraseblock is successfully erased, and returns. + * + * Atomic logical eraseblock change + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Atomic logical eraseblock change operation is called using the %UBI_IOCEBCH + * ioctl command of the corresponding UBI volume character device. A pointer to + * a &struct ubi_leb_change_req object has to be passed to the ioctl. Then the + * user is expected to write the requested amount of bytes (similarly to what + * should be done in case of the "volume update" ioctl). + * + * Logical eraseblock map + * ~~~~~~~~~~~~~~~~~~~~~ + * + * To map a logical eraseblock to a physical eraseblock, the %UBI_IOCEBMAP + * ioctl command should be used. A pointer to a &struct ubi_map_req object is + * expected to be passed. The ioctl maps the requested logical eraseblock to + * a physical eraseblock and returns. Only non-mapped logical eraseblocks can + * be mapped. If the logical eraseblock specified in the request is already + * mapped to a physical eraseblock, the ioctl fails and returns error. + * + * Logical eraseblock unmap + * ~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To unmap a logical eraseblock to a physical eraseblock, the %UBI_IOCEBUNMAP + * ioctl command should be used. The ioctl unmaps the logical eraseblocks, + * schedules corresponding physical eraseblock for erasure, and returns. Unlike + * the "LEB erase" command, it does not wait for the physical eraseblock being + * erased. Note, the side effect of this is that if an unclean reboot happens + * after the unmap ioctl returns, you may find the LEB mapped again to the same + * physical eraseblock after the UBI is run again. + * + * Check if logical eraseblock is mapped + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To check if a logical eraseblock is mapped to a physical eraseblock, the + * %UBI_IOCEBISMAP ioctl command should be used. It returns %0 if the LEB is + * not mapped, and %1 if it is mapped. + * + * Set an UBI volume property + * ~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To set an UBI volume property the %UBI_IOCSETPROP ioctl command should be + * used. A pointer to a &struct ubi_set_vol_prop_req object is expected to be + * passed. The object describes which property should be set, and to which value + * it should be set. + */ + +/* + * When a new UBI volume or UBI device is created, users may either specify the + * volume/device number they want to create or to let UBI automatically assign + * the number using these constants. + */ +#define UBI_VOL_NUM_AUTO (-1) +#define UBI_DEV_NUM_AUTO (-1) + +/* Maximum volume name length */ +#define UBI_MAX_VOLUME_NAME 127 + +/* ioctl commands of UBI character devices */ + +#define UBI_IOC_MAGIC 'o' + +/* Create an UBI volume */ +#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) +/* Remove an UBI volume */ +#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) +/* Re-size an UBI volume */ +#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) +/* Re-name volumes */ +#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req) + +/* ioctl commands of the UBI control character device */ + +#define UBI_CTRL_IOC_MAGIC 'o' + +/* Attach an MTD device */ +#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) +/* Detach an MTD device */ +#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) + +/* ioctl commands of UBI volume character devices */ + +#define UBI_VOL_IOC_MAGIC 'O' + +/* Start UBI volume update */ +#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) +/* LEB erasure command, used for debugging, disabled by default */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) +/* Atomic LEB change command */ +#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) +/* Map LEB command */ +#define UBI_IOCEBMAP _IOW(UBI_VOL_IOC_MAGIC, 3, struct ubi_map_req) +/* Unmap LEB command */ +#define UBI_IOCEBUNMAP _IOW(UBI_VOL_IOC_MAGIC, 4, int32_t) +/* Check if LEB is mapped command */ +#define UBI_IOCEBISMAP _IOR(UBI_VOL_IOC_MAGIC, 5, int32_t) +/* Set an UBI volume property */ +#define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \ + struct ubi_set_vol_prop_req) + +/* Maximum MTD device name length supported by UBI */ +#define MAX_UBI_MTD_NAME_LEN 127 + +/* Maximum amount of UBI volumes that can be re-named at one go */ +#define UBI_MAX_RNVOL 32 + +/* + * UBI volume type constants. + * + * @UBI_DYNAMIC_VOLUME: dynamic volume + * @UBI_STATIC_VOLUME: static volume + */ +enum { + UBI_DYNAMIC_VOLUME = 3, + UBI_STATIC_VOLUME = 4, +}; + +/* + * UBI set volume property ioctl constants. + * + * @UBI_VOL_PROP_DIRECT_WRITE: allow (any non-zero value) or disallow (value 0) + * user to directly write and erase individual + * eraseblocks on dynamic volumes + */ +enum { + UBI_VOL_PROP_DIRECT_WRITE = 1, +}; + +/** + * struct ubi_attach_req - attach MTD device request. + * @ubi_num: UBI device number to create + * @mtd_num: MTD device number to attach + * @vid_hdr_offset: VID header offset (use defaults if %0) + * @max_beb_per1024: maximum expected number of bad PEB per 1024 PEBs + * @padding: reserved for future, not used, has to be zeroed + * + * This data structure is used to specify MTD device UBI has to attach and the + * parameters it has to use. The number which should be assigned to the new UBI + * device is passed in @ubi_num. UBI may automatically assign the number if + * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in + * @ubi_num. + * + * Most applications should pass %0 in @vid_hdr_offset to make UBI use default + * offset of the VID header within physical eraseblocks. The default offset is + * the next min. I/O unit after the EC header. For example, it will be offset + * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or + * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. + * + * But in rare cases, if this optimizes things, the VID header may be placed to + * a different offset. For example, the boot-loader might do things faster if + * the VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. + * As the boot-loader would not normally need to read EC headers (unless it + * needs UBI in RW mode), it might be faster to calculate ECC. This is weird + * example, but it real-life example. So, in this example, @vid_hdr_offer would + * be 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes + * aligned, which is OK, as UBI is clever enough to realize this is 4th + * sub-page of the first page and add needed padding. + * + * The @max_beb_per1024 is the maximum amount of bad PEBs UBI expects on the + * UBI device per 1024 eraseblocks. This value is often given in an other form + * in the NAND datasheet (min NVB i.e. minimal number of valid blocks). The + * maximum expected bad eraseblocks per 1024 is then: + * 1024 * (1 - MinNVB / MaxNVB) + * Which gives 20 for most NAND devices. This limit is used in order to derive + * amount of eraseblock UBI reserves for handling new bad blocks. If the device + * has more bad eraseblocks than this limit, UBI does not reserve any physical + * eraseblocks for new bad eraseblocks, but attempts to use available + * eraseblocks (if any). The accepted range is 0-768. If 0 is given, the + * default kernel value of %CONFIG_MTD_UBI_BEB_LIMIT will be used. + */ +struct ubi_attach_req { + int32_t ubi_num; + int32_t mtd_num; + int32_t vid_hdr_offset; + int16_t max_beb_per1024; + int8_t padding[10]; +}; + +/** + * struct ubi_mkvol_req - volume description data structure used in + * volume creation requests. + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding1: reserved for future, not used, has to be zeroed + * @name_len: volume name length + * @padding2: reserved for future, not used, has to be zeroed + * @name: volume name + * + * This structure is used by user-space programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical + * eraseblock. This means, that the size of logical eraseblocks will be aligned + * to this number, i.e., + * (UBI device logical eraseblock size) mod (@alignment) = 0. + * + * To put it differently, the logical eraseblock of this volume may be slightly + * shortened in order to make it properly aligned. The alignment has to be + * multiple of the flash minimal input/output unit, or %1 to utilize the entire + * available space of logical eraseblocks. + * + * The @alignment field may be useful, for example, when one wants to maintain + * a block device on top of an UBI volume. In this case, it is desirable to fit + * an integer number of blocks in logical eraseblocks of this UBI volume. With + * alignment it is possible to update this volume using plane UBI volume image + * BLOBs, without caring about how to properly align them. + */ +struct ubi_mkvol_req { + int32_t vol_id; + int32_t alignment; + int64_t bytes; + int8_t vol_type; + int8_t padding1; + int16_t name_len; + int8_t padding2[4]; + char name[UBI_MAX_VOLUME_NAME + 1]; +} __attribute__((packed)); + +/** + * struct ubi_rsvol_req - a data structure used in volume re-size requests. + * @vol_id: ID of the volume to re-size + * @bytes: new size of the volume in bytes + * + * Re-sizing is possible for both dynamic and static volumes. But while dynamic + * volumes may be re-sized arbitrarily, static volumes cannot be made to be + * smaller than the number of bytes they bear. To arbitrarily shrink a static + * volume, it must be wiped out first (by means of volume update operation with + * zero number of bytes). + */ +struct ubi_rsvol_req { + int64_t bytes; + int32_t vol_id; +} __attribute__((packed)); + +/** + * struct ubi_rnvol_req - volumes re-name request. + * @count: count of volumes to re-name + * @padding1: reserved for future, not used, has to be zeroed + * @vol_id: ID of the volume to re-name + * @name_len: name length + * @padding2: reserved for future, not used, has to be zeroed + * @name: new volume name + * + * UBI allows to re-name up to %32 volumes at one go. The count of volumes to + * re-name is specified in the @count field. The ID of the volumes to re-name + * and the new names are specified in the @vol_id and @name fields. + * + * The UBI volume re-name operation is atomic, which means that should power cut + * happen, the volumes will have either old name or new name. So the possible + * use-cases of this command is atomic upgrade. Indeed, to upgrade, say, volumes + * A and B one may create temporary volumes %A1 and %B1 with the new contents, + * then atomically re-name A1->A and B1->B, in which case old %A and %B will + * be removed. + * + * If it is not desirable to remove old A and B, the re-name request has to + * contain 4 entries: A1->A, A->A1, B1->B, B->B1, in which case old A1 and B1 + * become A and B, and old A and B will become A1 and B1. + * + * It is also OK to request: A1->A, A1->X, B1->B, B->Y, in which case old A1 + * and B1 become A and B, and old A and B become X and Y. + * + * In other words, in case of re-naming into an existing volume name, the + * existing volume is removed, unless it is re-named as well at the same + * re-name request. + */ +struct ubi_rnvol_req { + int32_t count; + int8_t padding1[12]; + struct { + int32_t vol_id; + int16_t name_len; + int8_t padding2[2]; + char name[UBI_MAX_VOLUME_NAME + 1]; + } ents[UBI_MAX_RNVOL]; +} __attribute__((packed)); + +/** + * struct ubi_leb_change_req - a data structure used in atomic LEB change + * requests. + * @lnum: logical eraseblock number to change + * @bytes: how many bytes will be written to the logical eraseblock + * @dtype: pass "3" for better compatibility with old kernels + * @padding: reserved for future, not used, has to be zeroed + * + * The @dtype field used to inform UBI about what kind of data will be written + * to the LEB: long term (value 1), short term (value 2), unknown (value 3). + * UBI tried to pick a PEB with lower erase counter for short term data and a + * PEB with higher erase counter for long term data. But this was not really + * used because users usually do not know this and could easily mislead UBI. We + * removed this feature in May 2012. UBI currently just ignores the @dtype + * field. But for better compatibility with older kernels it is recommended to + * set @dtype to 3 (unknown). + */ +struct ubi_leb_change_req { + int32_t lnum; + int32_t bytes; + int8_t dtype; /* obsolete, do not use! */ + int8_t padding[7]; +} __attribute__((packed)); + +/** + * struct ubi_map_req - a data structure used in map LEB requests. + * @dtype: pass "3" for better compatibility with old kernels + * @lnum: logical eraseblock number to unmap + * @padding: reserved for future, not used, has to be zeroed + */ +struct ubi_map_req { + int32_t lnum; + int8_t dtype; /* obsolete, do not use! */ + int8_t padding[3]; +} __attribute__((packed)); + + +/** + * struct ubi_set_vol_prop_req - a data structure used to set an UBI volume + * property. + * @property: property to set (%UBI_VOL_PROP_DIRECT_WRITE) + * @padding: reserved for future, not used, has to be zeroed + * @value: value to set + */ +struct ubi_set_vol_prop_req { + uint8_t property; + uint8_t padding[7]; + uint64_t value; +} __attribute__((packed)); + +#endif /* __UBI_USER_H__ */ diff --git a/libubi/ubiutils-common.c b/libubi/ubiutils-common.c new file mode 100644 index 0000000..2271927 --- /dev/null +++ b/libubi/ubiutils-common.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2007, 2008 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. + */ + +/* + * This file contains various common stuff used by UBI utilities. + * + * Authors: Artem Bityutskiy + * Adrian Hunter + */ + +#define PROGRAM_NAME "ubiutils" + +#include +#include +#include +#include +#include +#include +#include +#include "libubi.h" + +/** + * get_multiplier - convert size specifier to an integer multiplier. + * @str: the size specifier string + * + * This function parses the @str size specifier, which may be one of + * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive + * size multiplier in case of success and %-1 in case of failure. + */ +static int get_multiplier(const char *str) +{ + if (!str) + return 1; + + /* Remove spaces before the specifier */ + while (*str == ' ' || *str == '\t') + str += 1; + + if (!strcmp(str, "KiB")) + return 1024; + if (!strcmp(str, "MiB")) + return 1024 * 1024; + if (!strcmp(str, "GiB")) + return 1024 * 1024 * 1024; + + return -1; +} + +/** + * ubiutils_get_bytes - convert a string containing amount of bytes into an + * integer + * @str: string to convert + * + * This function parses @str which may have one of 'KiB', 'MiB', or 'GiB' + * size specifiers. Returns positive amount of bytes in case of success and %-1 + * in case of failure. + */ +long long ubiutils_get_bytes(const char *str) +{ + char *endp; + long long bytes = strtoull(str, &endp, 0); + + if (endp == str || bytes < 0) { + fprintf(stderr, "incorrect amount of bytes: \"%s\"\n", str); + return -1; + } + + if (*endp != '\0') { + int mult = get_multiplier(endp); + + if (mult == -1) { + fprintf(stderr, "bad size specifier: \"%s\" - " + "should be 'KiB', 'MiB' or 'GiB'\n", endp); + return -1; + } + bytes *= mult; + } + + return bytes; +} + +/** + * 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 && bytes != 0) + printf("%s%.1f KiB", p, (double)bytes / 1024); + else + return; + + if (bracket) + printf(")"); +} + +/** + * ubiutils_print_text - print text and fold it. + * @stream: file stream to print to + * @text: text to print + * @width: maximum allowed text width + * + * Print text and fold it so that each line would not have more then @width + * characters. + */ +void ubiutils_print_text(FILE *stream, const char *text, int width) +{ + int pos, bpos = 0; + const char *p; + char line[1024]; + + if (width > 1023) { + fprintf(stream, "%s\n", text); + return; + } + p = text; + pos = 0; + while (p[pos]) { + while (!isspace(p[pos])) { + line[pos] = p[pos]; + if (!p[pos]) + break; + ++pos; + if (pos == width) { + line[pos] = '\0'; + fprintf(stream, "%s\n", line); + p += pos; + pos = 0; + } + } + while (pos < width) { + line[pos] = p[pos]; + if (!p[pos]) { + bpos = pos; + break; + } + if (isspace(p[pos])) + bpos = pos; + ++pos; + } + line[bpos] = '\0'; + fprintf(stream, "%s\n", line); + p += bpos; + pos = 0; + while (p[pos] && isspace(p[pos])) + ++p; + } +} + +/** + * ubiutils_srand - randomly seed the standard pseudo-random generator. + * + * This helper function seeds the standard libc pseudo-random generator with a + * more or less random value to make sure the 'rand()' call does not return the + * same sequence every time UBI utilities run. Returns zero in case of success + * and a %-1 in case of error. + */ +int ubiutils_srand(void) +{ + struct timeval tv; + struct timezone tz; + unsigned int seed; + + /* + * Just assume that a combination of the PID + current time is a + * reasonably random number. + */ + if (gettimeofday(&tv, &tz)) + return -1; + + seed = (unsigned int)tv.tv_sec; + seed += (unsigned int)tv.tv_usec; + seed *= getpid(); + seed %= RAND_MAX; + srand(seed); + return 0; +} diff --git a/ubi.c b/ubi.c new file mode 100644 index 0000000..b2d7299 --- /dev/null +++ b/ubi.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2014 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libubi/libubi-tiny.h" + +static int mtd_find_index(char *name) +{ + FILE *fp = fopen("/proc/mtd", "r"); + char line[256]; + char *index = NULL; + + if (!fp) + return -1; + + while (!index && fgets(line, sizeof(line), fp)) { + if (strstr(line, name)) { + char *eol = strstr(line, ":"); + + if (!eol) + continue; + + *eol = '\0'; + index = &line[3]; + } + } + + fclose(fp); + + if (!index) + return -1; + + return atoi(index); +} + +static int mtd_find(char *name, char *ret) +{ + int index = mtd_find_index(name); + if (index < 0) + return -1; + + sprintf(ret, "/dev/mtd%d", index); + + return 0; +} + +static int ubi_find(libubi_t libubi, char *name, char *ret) +{ + int index = mtd_find_index(name); + int ubi = 0; + + if (index < 0) + return -1; + + if (mtd_num2ubi_dev(libubi, index, &ubi)) { + fprintf(stderr, "failed to get ubi node for %s\n", name); + return -1; + } + sprintf(ret, "/dev/ubi%d", ubi); + + return 0; +} + +static int ubi_find_mtd(libubi_t libubi, char *name, char *ret) +{ + struct ubi_dev_info info; + + if (ubi_find(libubi, name, ret)) + return -1; + + if (ubi_get_dev_info(libubi, ret, &info)) + return -1; + + sprintf(ret, "/dev/mtd%d", info.mtd_num); + + return 0; +} + +static int volume_find(libubi_t libubi, char *name, char *ret) +{ + int index = mtd_find_index(name); + struct ubi_vol_info vol; + int ubi = 0; + + if (index < 0) + return -1; + + if (mtd_num2ubi_dev(libubi, index, &ubi)) { + fprintf(stderr, "failed to get ubi node for %s\n", name); + return -1; + } + + if (ubi_get_vol_info1_nm(libubi, ubi, name, &vol)) { + fprintf(stderr, "failed to get ubi volume info for %s\n", name); + return -1; + } + + sprintf(ret, "/dev/ubi%d_%d", ubi, vol.vol_id); + + return 0; +} + +static int main_image(char *partition, char *image, char *overlay) +{ + libubi_t libubi; + int err; + char mtd[64]; + char part[64]; + char node[64]; + char volume[64]; + char _data[64]; + char *data = NULL; + + if (mtd_find(partition, part)) { + fprintf(stderr, "failed to find mtd partition %s\n", partition); + return -1; + } + if (overlay && !mtd_find(overlay, _data)) + data = _data; + + libubi = libubi_open(); + if (!libubi) { + fprintf(stderr, "cannot open libubi"); + return -1; + } + + if (ubi_find_mtd(libubi, partition, mtd)) { + fprintf(stderr, "failed to find mtd parent %s\n", partition); + return -1; + } + + if (ubi_find(libubi, partition, node)) { + fprintf(stderr, "failed to find ubi volume %s\n", partition); + return -1; + } + + if (volume_find(libubi, partition, volume)) { + fprintf(stderr, "failed to find ubi volume %s\n", partition); + return -1; + } + + err = ubidetach(libubi, mtd); + if (err) { + fprintf(stderr, "cannot detach \"%s\"", mtd); + return -1; + } + + err = ubiattach(libubi, mtd); + if (err) { + fprintf(stderr, "cannot detach \"%s\"", mtd); + return -1; + } + + if (data) { + err = ubirmvol(libubi, node, overlay); + if (err) { + fprintf(stderr, "cannot remove \"%s\"", node); + return -1; + } + } + + err = ubiupdatevol(libubi, volume, image); + if (err) { + fprintf(stderr, "cannot update \"%s\"", volume); + return -1; + } + + if (overlay) { + err = ubimkvol(libubi, node, overlay, 1); + if (err) { + fprintf(stderr, "cannot make \"%s\"", node); + return -1; + } + } + + libubi_close(libubi); + + return err; +} + +static int main_info(void) +{ + struct ubi_info info; + libubi_t libubi; + int i; + + libubi = libubi_open(); + if (!libubi) { + fprintf(stderr, "cannot open libubi"); + return -1; + } + + if (ubi_get_info(libubi, &info)) { + fprintf(stderr, "failed to get info\n"); + return -1; + } + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + struct ubi_dev_info dinfo; + char ubi[64]; + int j; + + sprintf(ubi, "/dev/ubi%d", i); + if (ubi_get_dev_info(libubi, ubi, &dinfo)) + continue; + printf("device - %s\n size: %lldBytes\n bad blocks: %d\n", + &ubi[5], dinfo.total_bytes, dinfo.bad_count); + for (j = dinfo.lowest_vol_id; j <= dinfo.highest_vol_id; j++) { + struct ubi_vol_info vinfo; + + sprintf(ubi, "/dev/ubi%d_%d", i, j); + if (ubi_get_vol_info(libubi, ubi, &vinfo)) + continue; + printf(" volume - %s\n", &ubi[5]); + printf("\tname: %s\n", vinfo.name); + printf("\tsize: %lld\n", vinfo.data_bytes); + } + } + + libubi_close(libubi); + + return 0; +} + +static int print_usage(void) +{ + printf("ubi info\n"); + printf("ubi kernel \n"); + printf("ubi rootfs \n"); + printf("ubi overlay \n"); + + return -1; +} + +int main(int argc, char **argv) +{ + if (argc > 1 && !strcmp(argv[1], "info")) + return main_info(); + + if (argc < 3) + return print_usage(); + + if (!strcmp(argv[1], "kernel")) { + return main_image("kernel", argv[2], NULL); + + } else if (!strcmp(argv[1], "rootfs")) { + return main_image("rootfs", argv[2], NULL); + + } else if (!strcmp(argv[1], "overlay")) { + return main_image("rootfs", argv[2], "rootfs_data"); + } + + return -1; +} + -- 2.25.1