diff mbox

New ubitunefs command to adjust tunable filesystem parameters on ubifs

Message ID 1293790128-4986-1-git-send-email-stefani@seibold.net
State New, archived
Headers show

Commit Message

Stefani Seibold Dec. 31, 2010, 10:08 a.m. UTC
From: Stefani Seibold <stefani@seibold.net>

ubitunefs allows the system administrator to adjust various tunable
filesystem  parameters on Linux ubifs filesystems. The current values of these
parameters can be displayed by using the -v or calling without any option.

Currently the ubitunefs supports the following parameters:

-x, --compr=<none|lzo|zlib>    compression type
-R, --reserved=SIZE            how much space should be reserved for super-user

Maybe it will be nice to adjust also the uid and gid of the reserved owner,
or the increase the max. size of the ubi filesystem. This will be very simple
to implement.

Signed-off-by: Stefani Seibold <stefani@seibold.net>
---
 ubi-utils/Makefile        |    5 +-
 ubi-utils/src/ubitunefs.c |  494 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 498 insertions(+), 1 deletions(-)
 create mode 100644 ubi-utils/src/ubitunefs.c

Comments

Artem Bityutskiy Jan. 16, 2011, 9:44 p.m. UTC | #1
On Fri, 2010-12-31 at 11:08 +0100, stefani@seibold.net wrote:
> From: Stefani Seibold <stefani@seibold.net>
> 
> ubitunefs allows the system administrator to adjust various tunable
> filesystem  parameters on Linux ubifs filesystems. The current values of these
> parameters can be displayed by using the -v or calling without any option.
> 
> Currently the ubitunefs supports the following parameters:
> 
> -x, --compr=<none|lzo|zlib>    compression type
> -R, --reserved=SIZE            how much space should be reserved for super-user
> 
> Maybe it will be nice to adjust also the uid and gid of the reserved owner,
> or the increase the max. size of the ubi filesystem. This will be very simple
> to implement.
> 
> Signed-off-by: Stefani Seibold <stefani@seibold.net>

Stefani,

thanks for the patch. I have not gone through it, but could we please
keep UBI and UBIFS things separate. The "ubi-utils" directory is for
UBI, could we please avoid putting this UBIFS tool there? Instead, would
it please be possible to:
  A. re-name the "mkfs.ubifs" directory into "ubifs-utils", and put your
     new tool there.
  B. re-name the tool to something which suggests a bit better that this
     is about UBIFS (not UBI), e.g.
        * tuneubifs (similar to tune2fs)
        * ubifsctl (similar to btrfsctl)
Artem Bityutskiy Jan. 16, 2011, 9:49 p.m. UTC | #2
On Fri, 2010-12-31 at 11:08 +0100, stefani@seibold.net wrote:
> +"-d, --devn=<UBI device number>  UBI device number to get information about\n"
> +"-n, --vol_id=<volume ID>        ID of UBI volume to print information about\n"
> +"-N, --name=<volume name>        name of UBI volume to print information about\n" 

These commentaries should be tweaked I think.
diff mbox

Patch

diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile
index df81cd9..35260be 100644
--- a/ubi-utils/Makefile
+++ b/ubi-utils/Makefile
@@ -8,7 +8,7 @@  KERNELHDR := ../include
 CPPFLAGS += -Iinclude -Isrc -I$(KERNELHDR)
 
 LIBS = libubi libubigen libiniparser libscan
-TARGETS = ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
+TARGETS = ubitunefs ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
           ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol
 
 VPATH = src
@@ -37,6 +37,9 @@  $(BUILDDIR)/ubiformat: $(addprefix $(BUILDDIR)/,\
 	ubiformat.o ubiutils-common.o libscan.a libubi.a libubigen.a)
 #	$(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lscan -lubi -lubigen -o $@
 
+$(BUILDDIR)/ubitunefs: $(addprefix $(BUILDDIR)/,\
+	ubitunefs.o ubiutils-common.o libscan.a libubi.a libubigen.a)
+
 $(BUILDDIR)/libubi.a: $(BUILDDIR)/libubi.o
 
 $(BUILDDIR)/libubigen.a: $(BUILDDIR)/libubigen.o
diff --git a/ubi-utils/src/ubitunefs.c b/ubi-utils/src/ubitunefs.c
new file mode 100644
index 0000000..f8b6161
--- /dev/null
+++ b/ubi-utils/src/ubitunefs.c
@@ -0,0 +1,494 @@ 
+/*
+ * 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@seibold.net>
+ *         in order of NSN Nokia Siemens Networks Ulm/Germany
+ * 	   based on work by Artem Bityutskiy
+ *
+ */
+
+#define PROGRAM_VERSION "0.3"
+#define PROGRAM_NAME    "ubitunefs"
+
+#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 "../mkfs.ubifs/ubifs-media.h"
+#include "../mkfs.ubifs/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 get information about\n"
+"-n, --vol_id=<volume ID>        ID of UBI volume to print information about\n"
+"-N, --name=<volume name>        name of UBI volume to print information about\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;
+}