From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from www84.your-server.de ([213.133.104.84]) by canuck.infradead.org with esmtps (Exim 4.72 #1 (Red Hat Linux)) id 1Pf7TM-0004rZ-Fl for linux-mtd@lists.infradead.org; Tue, 18 Jan 2011 09:03:35 +0000 From: stefani@seibold.net To: dedekind1@gmail.com, linux-mtd@lists.infradead.org, dwmw2@infradead.org, Artem.Bityutskiy@nokia.com Subject: [PATCH 2/2] Add new tuneubifs Date: Tue, 18 Jan 2011 10:04:25 +0100 Message-Id: <1295341465-2468-3-git-send-email-stefani@seibold.net> In-Reply-To: <1295341465-2468-1-git-send-email-stefani@seibold.net> References: <1295341465-2468-1-git-send-email-stefani@seibold.net> Cc: Stefani Seibold List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Stefani Seibold This patch add the new tuneubifs utility under ubifs-utils/ Signed-off-by: Stefani Seibold --- ubifs-utils/.gitignore | 1 + ubifs-utils/Makefile | 5 +- ubifs-utils/tuneubifs.c | 492 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 497 insertions(+), 1 deletions(-) create mode 100644 ubifs-utils/tuneubifs.c diff --git a/ubifs-utils/.gitignore b/ubifs-utils/.gitignore index 6b0e85c..05d6d9f 100644 --- a/ubifs-utils/.gitignore +++ b/ubifs-utils/.gitignore @@ -1 +1,2 @@ /mkfs.ubifs +/tuneubifs diff --git a/ubifs-utils/Makefile b/ubifs-utils/Makefile index 499f9e5..fb81871 100644 --- a/ubifs-utils/Makefile +++ b/ubifs-utils/Makefile @@ -4,12 +4,15 @@ CPPFLAGS += $(ZLIBCPPFLAGS) $(LZOCPPFLAGS) ALL_SOURCES=*.[ch] hashtable/*.[ch] -TARGETS = mkfs.ubifs +TARGETS = tuneubifs mkfs.ubifs LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid -L$(BUILDDIR)/../ubi-utils/ -lubi LDLIBS_mkfs.ubifs += -L$(BUILDDIR)/../lib -lmtd -lcrc32 LDLIBS_mkfs.ubifs += $(ZLIBLDFLAGS) $(LZOLDFLAGS) +LDLIBS_tuneubifs = -L$(BUILDDIR)/../ubi-utils/ -lubi +LDLIBS_tuneubifs += -L$(BUILDDIR)/../lib -lmtd + include ../common.mk $(BUILDDIR)/mkfs.ubifs: $(addprefix $(BUILDDIR)/,\ diff --git a/ubifs-utils/tuneubifs.c b/ubifs-utils/tuneubifs.c new file mode 100644 index 0000000..9546281 --- /dev/null +++ b/ubifs-utils/tuneubifs.c @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2007, 2008, 2010 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 tune a UBI filesystem. + * + * Author: Stefani Seibold + * in order of NSN Nokia Siemens Networks Ulm/Germany + * based on work by Artem Bityutskiy + * + */ + +#define PROGRAM_VERSION "0.4" +#define PROGRAM_NAME "tuneubifs" + +#define _GNU_SOURCE +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "common.h" +#include "ubiutils-common.h" +#include "ubifs-media.h" +#include "defs.h" + +static void *super_buf; +struct ubi_dev_info dev_info; + +/* The variables below are set by command line arguments */ +struct args { + int devn; + int vol_id; + const char *node; + const char *vol_name; + int compr; + long long reserved; + int verbose; +}; + +static struct args args = { + .vol_id = -1, + .devn = -1, + .node = NULL, + .vol_name = NULL, + .compr = -1, + .reserved = -1, + .verbose = 0, +}; + +static const char doc[] = PROGRAM_NAME " version " PROGRAM_VERSION + " - a tool for UBI filesystem tuning."; + +static const char optionsstr[] = +"-d, --devn= UBI device number to tune\n" +"-n, --vol_id= ID of UBI volume to tune\n" +"-N, --name= name of UBI volume to tune\n" +"-x, --compr= compression type\n" +"-R, --reserved=SIZE how much space should be reserved for super-user\n" +"-v, --verbose verbose output\n" +"-h, --help print help message\n" +"-V, --version print program version"; + +static const char usage[] = +"Usage 1: " PROGRAM_NAME " [-d ] [-n | -N ]\n" +"\t\t[-h] [-V] [--vol_id= | --name ]\n" +"\t\t[--devn ] [--help] [--version]\n" +"Usage 2: " PROGRAM_NAME " [-h] [-V] [--help] [--version]\n\n"; + +static const struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { .name = "compr", .has_arg = 1, .flag = NULL, .val = 'c' }, + { .name = "reserved", .has_arg = 1, .flag = NULL, .val = 'r' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { NULL, 0, NULL, 0}, +}; + +/** + * 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++; + + if (!strcasecmp(str, "KB")) + return 1000; + if (!strcasecmp(str, "MB")) + return 1000 * 1000; + if (!strcasecmp(str, "GB")) + return 1000 * 1000 * 1000; + if (!strcasecmp(str, "KiB")) + return 1024; + if (!strcasecmp(str, "MiB")) + return 1024 * 1024; + if (!strcasecmp(str, "GiB")) + return 1024 * 1024 * 1024; + + return -1; +} + +/** + * 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. + */ +static long long get_bytes(const char *str) +{ + char *endp; + long long bytes = strtoull(str, &endp, 0); + + if (endp == str || bytes < 0) + return errmsg("incorrect amount of bytes: \"%s\"", str); + + if (*endp != '\0') { + int mult = get_multiplier(endp); + + if (mult == -1) + return errmsg("bad size specifier: \"%s\" - " + "should be 'KiB', 'MiB' or 'GiB'", endp); + bytes *= mult; + } + + return bytes; +} + +static int parse_opt(int argc, char * const argv[]) +{ + while (1) { + int key; + char *endp; + + key = getopt_long(argc, argv, "an:N:d:hVx:R:v", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'n': + args.vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.vol_id < 0) + return errmsg("bad volume ID: " "\"%s\"", optarg); + break; + + case 'N': + args.vol_name = optarg; + break; + + case 'd': + args.devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.devn < 0) + return errmsg("bad UBI device number: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(EXIT_SUCCESS); + + case 'x': + if (!strcmp(optarg, "none")) + args.compr = UBIFS_COMPR_NONE; + else + if (!strcmp(optarg, "lzo")) + args.compr = UBIFS_COMPR_LZO; + else + if (!strcmp(optarg, "zlib")) + args.compr = UBIFS_COMPR_ZLIB; + else + return errmsg("bad compr type: \"%s\"", optarg); + + break; + + case 'R': + args.reserved = get_bytes(optarg); + break; + + case 'v': + args.verbose = 1; + break; + + case ':': + return errmsg("parameter is missing"); + + default: + fprintf(stderr, "Use -h for help\n"); + return -1; + } + } + + if (optind == argc - 1) + args.node = argv[optind]; + else if (optind < argc) + return errmsg("more then one UBI device specified (use -h for help)"); + + return 0; +} + +static int translate_dev(libubi_t libubi, const char *node) +{ + int err; + + err = ubi_probe_node(libubi, node); + if (err == -1) { + if (errno != ENODEV) + return sys_errmsg("error while probing \"%s\"", node); + return errmsg("\"%s\" does not correspond to any UBI device or volume", node); + } + + if (err == 1) { + struct ubi_dev_info dev_info; + + err = ubi_get_dev_info(libubi, node, &dev_info); + if (err) + return sys_errmsg("cannot get information about UBI device \"%s\"", node); + + args.devn = dev_info.dev_num; + } else { + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info(libubi, node, &vol_info); + if (err) + return sys_errmsg("cannot get information about UBI volume \"%s\"", node); + + if (args.vol_id != -1) + return errmsg("both volume character device node (\"%s\") and " + "volume ID (%d) are specify, use only one of them" + "(use -h for help)", node, args.vol_id); + + args.devn = vol_info.dev_num; + args.vol_id = vol_info.vol_id; + } + + return 0; +} + +static int get_vol_id_by_name(libubi_t libubi, int dev_num, const char *name) +{ + int err; + struct ubi_vol_info vol_info; + + err = ubi_get_vol_info1_nm(libubi, dev_num, name, &vol_info); + if (err) + return sys_errmsg("cannot get information about volume \"%s\" on ubi%d\n", name, dev_num); + + args.vol_id = vol_info.vol_id; + + return 0; +} + +/** + * read_super - read the ubifs super block + * @fd: device node file handle + * @buf: buffer (must be at least leb_size bytes) + */ +static int read_super(int fd, void *buf) +{ + struct ubifs_sb_node *sup = buf; + off64_t pos = UBIFS_SB_LNUM * dev_info.leb_size; + + if (lseek64(fd, pos, SEEK_SET) != pos) + return sys_errmsg("failed seeking super block"); + + if (read(fd, buf, UBIFS_SB_NODE_SZ) != UBIFS_SB_NODE_SZ) + return sys_errmsg("read super block failed"); + + if (le32_to_cpu(sup->ch.magic) != UBIFS_NODE_MAGIC) + return sys_errmsg("invalid super block magic"); + + if (le32_to_cpu(sup->ch.crc) != mtd_crc32(UBIFS_CRC32_INIT, buf + 8, le32_to_cpu(sup->ch.len) - 8)) + return sys_errmsg("invalid super block crc"); + + if (le32_to_cpu(sup->leb_size) != dev_info.leb_size) + return sys_errmsg("invalid super block leb_size"); + + return 0; +} + +/** + * update_super - write the super block. + * @ubi: ubi descriptor + * @fd: device node file handle + * @buf: buffer (must be at least leb_size bytes) + */ +static int update_super(libubi_t ubi, int fd, void *buf) +{ + int len; + struct ubifs_sb_node *sup = buf; + off64_t pos = (off64_t)UBIFS_SB_LNUM * dev_info.leb_size; + int update = 0; + + if (args.compr != -1) { + if (args.compr != le16_to_cpu(sup->default_compr)) { + sup->default_compr = cpu_to_le16(args.compr); + update = 1; + } + } + + if (args.reserved != -1) { + if (args.reserved != le64_to_cpu(sup->rp_size)) { + sup->rp_size = cpu_to_le64(args.reserved); + update = 1; + } + } + + if (!update) { + puts("Nothing changed, hence super block NOT written to flash!"); + return 1; + } + + len = ALIGN(ALIGN(UBIFS_SB_NODE_SZ, 8), dev_info.min_io_size); + memset(buf + len, 0xff, dev_info.leb_size - len); + + sup->ch.crc = cpu_to_le32(mtd_crc32(UBIFS_CRC32_INIT, buf + 8, le32_to_cpu(sup->ch.len) - 8)); + + if (ubi_leb_change_start(ubi, fd, UBIFS_SB_LNUM, dev_info.leb_size, UBI_LONGTERM)) + return sys_errmsg("ubi_leb_change_start failed"); + + if (lseek64(fd, pos, SEEK_SET) != pos) + return sys_errmsg("failed seeking super block"); + + if (write(fd, buf, dev_info.leb_size) != dev_info.leb_size) + return sys_errmsg("write super block failed"); + + return 0; +} + +/** + * show_super - show the super block. + */ +static void show_super(void *buf) +{ + struct ubifs_sb_node *sup = buf; + const char *p; + + switch(le16_to_cpu(sup->default_compr)) { + case UBIFS_COMPR_NONE: + p = "none"; + break; + case UBIFS_COMPR_LZO: + p = "lzo"; + break; + case UBIFS_COMPR_ZLIB: + p = "zlib"; + break; + default: + p = "unknown"; + break; + } + + printf("UBIFS:\n"); + printf(" default compressor: %s\n", p); + printf(" reserved for root: %llu bytes\n", le64_to_cpu(sup->rp_size)); +} + +int main(int argc, char * const argv[]) +{ + int err; + libubi_t libubi; + char devname[128]; + int fd; + + err = parse_opt(argc, argv); + if (err) + return -1; + + libubi = libubi_open(); + if (!libubi) { + if (errno == 0) + return errmsg("UBI is not present in the system"); + return sys_errmsg("cannot open libubi"); + } + + if (args.node) { + /* + * A character device was specified, translate this into UBI + * device number and volume ID. + */ + err = translate_dev(libubi, args.node); + if (err) + goto out_libubi; + } + + if (args.devn == -1) { + errmsg("device number is missing (use -h for help)\n"); + goto out_libubi; + } + + err = ubi_get_dev_info1(libubi, args.devn, &dev_info); + if (err) { + errmsg("cannot get information about UBI device %d", args.devn); + goto out_libubi; + } + + if (args.vol_name) { + err = get_vol_id_by_name(libubi, args.devn, args.vol_name); + if (err) + goto out_libubi; + } + + if (args.vol_id == -1) { + errmsg("volume ID is missing (use -h for help)\n"); + goto out_libubi; + } + + if (!args.node) { + sprintf(devname, "/dev/ubi%d_%d", args.devn, args.vol_id); + args.node = devname; + } + + super_buf = malloc(dev_info.leb_size); + if (!super_buf) { + errmsg("out of memory"); + goto out_libubi; + } + + fd = open(args.node, O_RDWR|O_EXCL); + if (fd == -1) { + errmsg("cannot open the UBI volume '%s'", args.node); + goto out_libubi; + } + + err = read_super(fd, super_buf); + if (err) + goto out_libubi; + + if (args.compr != -1 || args.reserved != -1) + update_super(libubi, fd, super_buf); + else + args.verbose = 1; + + if (args.verbose) + show_super(super_buf); + + if (err) + goto out_libubi; + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} -- 1.7.4.rc2