[PATCH 2/2] Add new tuneubifs
stefani at seibold.net
stefani at seibold.net
Tue Jan 18 04:04:25 EST 2011
From: Stefani Seibold <stefani at seibold.net>
This patch add the new tuneubifs utility under ubifs-utils/
Signed-off-by: Stefani Seibold <stefani at seibold.net>
---
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 <stefani at seibold.net>
+ * 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 <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdint.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <linux/types.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libubi.h>
+#include <crc32.h>
+#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> UBI device number to tune\n"
+"-n, --vol_id=<volume ID> ID of UBI volume to tune\n"
+"-N, --name=<volume name> name of UBI volume to tune\n"
+"-x, --compr=<none|lzo|zlib> 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 <UBI device number>] [-n <volume ID> | -N <volume name>]\n"
+"\t\t[-h] [-V] [--vol_id=<volume ID> | --name <volume name>]\n"
+"\t\t[--devn <UBI device number>] [--help] [--version]\n"
+"Usage 2: " PROGRAM_NAME " <UBI volume node file 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
More information about the linux-mtd
mailing list