summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ubi-tests/Makefile41
-rw-r--r--tests/ubi-tests/README.udev25
-rw-r--r--tests/ubi-tests/common.c336
-rw-r--r--tests/ubi-tests/common.h103
-rw-r--r--tests/ubi-tests/integ.c783
-rw-r--r--tests/ubi-tests/io_basic.c182
-rw-r--r--tests/ubi-tests/io_paral.c251
-rw-r--r--tests/ubi-tests/io_read.c398
-rw-r--r--tests/ubi-tests/io_update.c370
-rw-r--r--tests/ubi-tests/mkvol_bad.c304
-rw-r--r--tests/ubi-tests/mkvol_basic.c253
-rw-r--r--tests/ubi-tests/mkvol_paral.c112
-rw-r--r--tests/ubi-tests/rmvol.c308
-rw-r--r--tests/ubi-tests/rsvol.c310
-rwxr-xr-xtests/ubi-tests/runtests.sh39
15 files changed, 3815 insertions, 0 deletions
diff --git a/tests/ubi-tests/Makefile b/tests/ubi-tests/Makefile
new file mode 100644
index 0000000..a2a49d0
--- /dev/null
+++ b/tests/ubi-tests/Makefile
@@ -0,0 +1,41 @@
+UBIUTILS=../../ubi-utils
+INCLUDE1=$(UBIUTILS)/inc
+INCLUDE2=../../include
+LIB=.
+
+CC := $(CROSS)gcc
+
+ALL_FILES=libubi io_update rmvol integ
+ALL_FILES+=io_paral io_read io_basic mkvol_basic mkvol_bad mkvol_paral rsvol
+
+CFLAGS += -Wall -I$(INCLUDE1) -I$(INCLUDE2) -L$(LIB) -ggdb
+
+all: $(ALL_FILES)
+
+libubi: $(UBIUTILS)/src/libubi.c $(UBIUTILS)/inc/libubi.h $(UBIUTILS)/src/libubi_int.h
+ $(CC) $(CFLAGS) -DUDEV_SETTLE_HACK -c $(UBIUTILS)/src/libubi.c -o libubi.o
+ ar cr libubi.a libubi.o
+
+io_paral: io_paral.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -lpthread -o $@
+io_update: io_update.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+io_read: io_read.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+io_basic: io_basic.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+mkvol_basic: mkvol_basic.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+mkvol_bad: mkvol_bad.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+mkvol_paral: mkvol_paral.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -lpthread -o $@
+rsvol: rsvol.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+rmvol: rmvol.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+integ: integ.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+
+clean:
+ rm -rf $(ALL_FILES) $(addsuffix .o, $(ALL_FILES))
diff --git a/tests/ubi-tests/README.udev b/tests/ubi-tests/README.udev
new file mode 100644
index 0000000..06e71d3
--- /dev/null
+++ b/tests/ubi-tests/README.udev
@@ -0,0 +1,25 @@
+There is a problem with udev: when a volume is created, there is a delay
+before corresponding /dev/ubiX_Y device node is created by udev, so some
+tests fail because of this. The symptom is error messages like
+"cannot open /dev/ubi0_0".
+
+One possible solution of this problem is to pre-create UBI device and volume
+nodes. There is even a script which may be used for this in ubi-utils/scripts/.
+But this is not enough because udev will still remove and re-create the nodes
+and tests will still fail. So you need to stop removing device nodes using
+the following udev rule:
+
+ KERNEL=="ubi*_*", ACTION=="remove", OPTIONS+="ignore_device"
+
+In our Ubuntu distribution we put that to new file:
+/etc/udev/rules.d/50-local.rules
+
+Another possibility is to call udevsettle utility in libubi after the volume
+has been created See src/libubi.c - the call is compiled in only if
+UDEV_SETTLE_HACK is defined. This is anyway an ugly hack, but works, although
+makes the tests slower. Suggestions are welcome.
+
+So, if you have udevsettel unility in your system, you do not have to do
+anyting, and the tests should work, because we compile libubi with
+UDEV_SETTLE_HACK. Otherwise, you should remove -D UDEV_SETTLE_HACK
+from the makefile and pre-create UBI device nodes.
diff --git a/tests/ubi-tests/common.c b/tests/ubi-tests/common.c
new file mode 100644
index 0000000..cb63e77
--- /dev/null
+++ b/tests/ubi-tests/common.c
@@ -0,0 +1,336 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * The stuff which is common for many tests.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#include "common.h"
+
+/**
+ * __initial_check - check that common prerequisites which are required to run
+ * tests.
+ *
+ * @test test name
+ * @argc count of command-line arguments
+ * @argv command-line arguments
+ *
+ * This function returns %0 if all is fine and test may be run and %-1 if not.
+ */
+int __initial_check(const char *test, int argc, char * const argv[])
+{
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+
+ /*
+ * All tests require UBI character device name as the first parameter,
+ * check this.
+ */
+ if (argc < 2) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "UBI character device node is not specified");
+ return -1;
+ }
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ __failed(test, __FUNCTION__, __LINE__, "libubi_open");
+ return -1;
+ }
+
+ if (ubi_get_dev_info(libubi, argv[1], &dev_info)) {
+ __failed(test, __FUNCTION__, __LINE__, "ubi_get_dev_info");
+ goto close;
+ }
+
+ if (dev_info.avail_ebs < MIN_AVAIL_EBS) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "insufficient available eraseblocks %d on UBI "
+ "device, required %d",
+ dev_info.avail_ebs, MIN_AVAIL_EBS);
+ goto close;
+ }
+
+ if (dev_info.vol_count != 0) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "device %s is not empty", argv[1]);
+ goto close;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return -1;
+}
+
+/**
+ * __err_msg - print a message to stderr.
+ *
+ * @test test name
+ * @func function name
+ * @line line number
+ * @fmt format string
+ */
+void __err_msg(const char *test, const char *func, int line,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "[%s] %s():%d: ", test, func, line);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+}
+
+/**
+ * __failed - print function fail message.
+ *
+ * @test test name
+ * @func calling function name
+ * @line line number
+ * @failed failed function name
+ */
+void __failed(const char *test, const char *func, int line,
+ const char *failed)
+{
+ fprintf(stderr, "[%s] %s():%d: function %s() failed with error %d (%s)\n",
+ test, func, line, failed, errno, strerror(errno));
+}
+
+/**
+ * __check_volume - check volume information.
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @vol_id ID of existing volume to check
+ * @req volume creation request to compare with
+ *
+ * This function checks if a volume created using @req request has exactly the
+ * requested characteristics. Returns 0 in case of success and %-1 in case of
+ * error.
+ */
+int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line, int vol_id,
+ const struct ubi_mkvol_request *req)
+{
+ int ret;
+ struct ubi_vol_info vol_info;
+ int eb_size;
+ long long rsvd_bytes;
+
+ ret = ubi_get_vol_info1(libubi, dev_info->dev_num, vol_id, &vol_info);
+ if (ret) {
+ __failed(test, func, line, "ubi_get_vol_info");
+ return -1;
+ }
+
+ if (req->alignment != vol_info.alignment) {
+ __err_msg(test, func, line,
+ "bad alignment: requested %d, got %d",
+ req->alignment, vol_info.alignment);
+ return -1;
+ }
+ if (req->vol_type != vol_info.type) {
+ __err_msg(test, func, line, "bad type: requested %d, got %d",
+ req->vol_type, vol_info.type);
+ return -1;
+ }
+ if (strlen(req->name) != strlen(&vol_info.name[0]) ||
+ strcmp(req->name, &vol_info.name[0]) != 0) {
+ __err_msg(test, func, line,
+ "bad name: requested \"%s\", got \"%s\"",
+ req->name, &vol_info.name[0]);
+ return -1;
+ }
+ if (vol_info.corrupted) {
+ __err_msg(test, func, line, "corrupted new volume");
+ return -1;
+ }
+
+ eb_size = dev_info->eb_size - (dev_info->eb_size % req->alignment);
+ if (eb_size != vol_info.eb_size) {
+ __err_msg(test, func, line,
+ "bad usable LEB size %d, should be %d",
+ vol_info.eb_size, eb_size);
+ return -1;
+ }
+
+ rsvd_bytes = req->bytes;
+ if (rsvd_bytes % eb_size)
+ rsvd_bytes += eb_size - (rsvd_bytes % eb_size);
+
+ if (rsvd_bytes != vol_info.rsvd_bytes) {
+ __err_msg(test, func, line,
+ "bad reserved bytes %lld, should be %lld",
+ vol_info.rsvd_bytes, rsvd_bytes);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * __check_vol_patt - check that volume contains certain data
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @node volume character device node
+ * @byte data pattern to check
+ *
+ * This function returns %0 if the volume contains only @byte bytes, and %-1 if
+ * not.
+ */
+int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line,
+ const char *node, uint8_t byte)
+{
+ int ret, fd;
+ long long bytes = 0;
+ struct ubi_vol_info vol_info;
+ unsigned char buf[512];
+
+ fd = open(node, O_RDONLY);
+ if (fd == -1) {
+ __failed(test, func, line, "open");
+ __err_msg(test, func, line, "cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ ret = ubi_get_vol_info(libubi, node, &vol_info);
+ if (ret) {
+ __failed(test, func, line, "ubi_get_vol_info");
+ goto close;
+ }
+
+ while (bytes < vol_info.data_bytes) {
+ int i;
+
+ memset(&buf[0], ~byte, 512);
+ ret = read(fd, &buf[0], 512);
+ if (ret == -1) {
+ __failed(test, func, line, "read");
+ __err_msg(test, func, line, "bytes = %lld, ret = %d",
+ bytes, ret);
+ goto close;
+ }
+
+ if (ret == 0 && bytes + ret < vol_info.data_bytes) {
+ __err_msg(test, func, line,
+ "EOF, but read only %lld bytes of %lld",
+ bytes + ret, vol_info.data_bytes);
+ goto close;
+ }
+
+ for (i = 0; i < ret; i++)
+ if (buf[i] != byte) {
+ __err_msg(test, func, line,
+ "byte at %lld is not %#x but %#x",
+ bytes + i, byte, (int)buf[i]);
+ goto close;
+ }
+
+ bytes += ret;
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * __update_vol_patt - update volume using a certain byte pattern
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @node volume character device node
+ * @byte data pattern to check
+ *
+ * This function returns %0 in case of success, and %-1 if in case of failure.
+ */
+int __update_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, long long bytes, uint8_t byte)
+{
+ int ret, fd;
+ long long written = 0;
+ unsigned char buf[512];
+
+ fd = open(node, O_RDWR);
+ if (fd == -1) {
+ __failed(test, func, line, "open");
+ __err_msg(test, func, line, "cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ if (ubi_update_start(libubi, fd, bytes)) {
+ __failed(test, func, line, "ubi_update_start");
+ __err_msg(test, func, line, "bytes = %lld", bytes);
+ goto close;
+ }
+
+ memset(&buf[0], byte, 512);
+
+ while (written != bytes) {
+ ret = write(fd, &buf[0], 512);
+ if (ret == -1) {
+ __failed(test, func, line, "write");
+ __err_msg(test, func, line, "written = %lld, ret = %d",
+ written, ret);
+ goto close;
+ }
+ written += ret;
+
+ if (written > bytes) {
+ __err_msg(test, func, line, "update length %lld bytes, "
+ "but %lld bytes are already written",
+ bytes, written);
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
diff --git a/tests/ubi-tests/common.h b/tests/ubi-tests/common.h
new file mode 100644
index 0000000..3e8ada8
--- /dev/null
+++ b/tests/ubi-tests/common.h
@@ -0,0 +1,103 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * The stuff which is common for many tests.
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UBI_VOLUME_PATTERN "/dev/ubi%d_%d"
+#define MIN_AVAIL_EBS 5
+#define PAGE_SIZE 4096
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#define err_msg(fmt, ...) \
+ __err_msg(TESTNAME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
+
+#define failed(name) \
+ __failed(TESTNAME, __FUNCTION__, __LINE__, name)
+
+#define initial_check(argc, argv) \
+ __initial_check(TESTNAME, argc, argv)
+
+#define check_volume(vol_id, req) \
+ __check_volume(libubi, &dev_info, TESTNAME, __FUNCTION__, \
+ __LINE__, vol_id, req)
+
+#define check_vol_patt(node, byte) \
+ __check_vol_patt(libubi, &dev_info, TESTNAME, __FUNCTION__, __LINE__, \
+ node, byte)
+
+#define update_vol_patt(node, bytes, byte) \
+ __update_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, \
+ node, bytes, byte)
+
+#define check_failed(ret, error, func, fmt, ...) ({ \
+ int __ret; \
+ \
+ if (!ret) { \
+ err_msg("%s() returned success but should have failed", func); \
+ err_msg(fmt, ##__VA_ARGS__); \
+ __ret = -1; \
+ } \
+ if (errno != (error)) { \
+ err_msg("%s failed with error %d (%s), expected %d (%s)", \
+ func, errno, strerror(errno), error, strerror(error)); \
+ err_msg(fmt, ##__VA_ARGS__); \
+ __ret = -1; \
+ } \
+ __ret = 0; \
+})
+
+/* Alignments to test, @s is eraseblock size */
+#define ALIGNMENTS(s) \
+ {3, 5, 27, 666, 512, 1024, 2048, (s)/2-3, (s)/2-2, (s)/2-1, (s)/2+1, \
+ (s)/2+2, (s)/2+3, (s)/3-3, (s)/3-2, (s)/3-1, (s)/3+1, (s)/3+2, \
+ (s)/3+3, (s)/4-3, (s)/4-2, (s)/4-1, (s)/4+1, (s)/4+2, (s)/4+3, \
+ (s)/5-3, (s)/5-2, (s)/5-1, (s)/5+1, (s)/5+2, (s)/5+3, (s)-17, (s)-9, \
+ (s)-8, (s)-6, (s)-4, (s)-1, (s)};
+
+extern void __err_msg(const char *test, const char *func, int line,
+ const char *fmt, ...);
+void __failed(const char *test, const char *func, int line,
+ const char *failed);
+int __initial_check(const char *test, int argc, char * const argv[]);
+int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line, int vol_id,
+ const struct ubi_mkvol_request *req);
+int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line,
+ const char *node, uint8_t byte);
+int __update_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, long long bytes,
+ uint8_t byte);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__COMMON_H__ */
diff --git a/tests/ubi-tests/integ.c b/tests/ubi-tests/integ.c
new file mode 100644
index 0000000..4da7121
--- /dev/null
+++ b/tests/ubi-tests/integ.c
@@ -0,0 +1,783 @@
+#define _LARGEFILE64_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "libubi.h"
+
+struct erase_block_info;
+struct volume_info;
+struct ubi_device_info;
+
+struct write_info
+{
+ struct write_info *next;
+ struct erase_block_info *erase_block;
+ int offset_within_block; /* Offset within erase block */
+ off64_t offset; /* Offset within volume */
+ int size;
+ int random_seed;
+};
+
+struct erase_block_info
+{
+ struct volume_info *volume;
+ int block_number;
+ off64_t offset; /* Offset within volume */
+ off64_t top_of_data;
+ int touched; /* Have we done anything at all with this erase block */
+ int erased; /* This erased block is currently erased */
+ struct write_info *writes;
+};
+
+struct volume_fd
+{
+ struct volume_fd *next;
+ struct volume_info *volume;
+ int fd;
+};
+
+struct volume_info
+{
+ struct volume_info *next;
+ struct ubi_device_info *ubi_device;
+ struct volume_fd *fds;
+ struct erase_block_info *erase_blocks;
+ const char *device_file_name;
+ struct ubi_vol_info info;
+};
+
+struct ubi_device_info
+{
+ struct volume_info *volumes;
+ const char *device_file_name;
+ struct ubi_dev_info info;
+};
+
+struct open_volume_fd
+{
+ struct open_volume_fd *next;
+ struct volume_fd *vol_fd;
+};
+
+#define MAX_UBI_DEVICES 64
+
+static libubi_t libubi;
+
+static struct ubi_info info;
+static struct ubi_device_info ubi_array[MAX_UBI_DEVICES];
+
+static uint64_t total_written = 0;
+static uint64_t total_space = 0;
+
+static struct open_volume_fd *open_volumes;
+static size_t open_volume_count = 0;
+
+static const char *ubi_module_load_string;
+
+static unsigned char *write_buffer = NULL;
+static unsigned char *read_buffer = NULL;
+
+static long long max_ebs_per_vol = 0; /* max number of ebs per vol (zero => no max) */
+
+static unsigned long next_seed = 1;
+
+static unsigned get_next_seed()
+{
+ next_seed = next_seed * 1103515245 + 12345;
+ return ((unsigned) (next_seed / 65536) % 32768);
+}
+
+static void error_exit(const char *msg)
+{
+ int eno = errno;
+ fprintf(stderr,"UBI Integrity Test Error: %s\n",msg);
+ if (eno) {
+ fprintf(stderr, "errno = %d\n", eno);
+ fprintf(stderr, "strerror = %s\n", strerror(eno));
+ }
+ exit(1);
+}
+
+static void *allocate(size_t n)
+{
+ void *p = malloc(n);
+ if (!p)
+ error_exit("Memory allocation failure");
+ memset(p, 0, n);
+ return p;
+}
+
+static unsigned get_random_number(unsigned n)
+{
+ uint64_t r, b;
+
+ if (n < 1)
+ return 0;
+ r = rand();
+ r *= n;
+ b = RAND_MAX;
+ b += 1;
+ r /= b;
+ return r;
+}
+
+static struct volume_fd *open_volume(struct volume_info *vol)
+{
+ struct volume_fd *s;
+ struct open_volume_fd *ofd;
+ int fd;
+
+ if (vol->fds) {
+ /* If already open dup it */
+ fd = dup(vol->fds->fd);
+ if (fd == -1)
+ error_exit("Failed to dup volume device file des");
+ } else {
+ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
+ if (fd == -1)
+ error_exit("Failed to open volume device file");
+ }
+ s = allocate(sizeof(*s));
+ s->fd = fd;
+ s->volume = vol;
+ s->next = vol->fds;
+ vol->fds = s;
+ /* Add to open volumes list */
+ ofd = allocate(sizeof(*ofd));
+ ofd->vol_fd = s;
+ ofd->next = open_volumes;
+ open_volumes = ofd;
+ open_volume_count += 1;
+ return 0;
+}
+
+static void close_volume(struct volume_fd *vol_fd)
+{
+ struct volume_fd *vfd, *vfd_last;
+ struct open_volume_fd *ofd, *ofd_last;
+ int fd = vol_fd->fd;
+
+ /* Remove from open volumes list */
+ ofd_last = NULL;
+ ofd = open_volumes;
+ while (ofd) {
+ if (ofd->vol_fd == vol_fd) {
+ if (ofd_last)
+ ofd_last->next = ofd->next;
+ else
+ open_volumes = ofd->next;
+ free(ofd);
+ open_volume_count -= 1;
+ break;
+ }
+ ofd_last = ofd;
+ ofd = ofd->next;
+ }
+ /* Remove from volume fd list */
+ vfd_last = NULL;
+ vfd = vol_fd->volume->fds;
+ while (vfd) {
+ if (vfd == vol_fd) {
+ if (vfd_last)
+ vfd_last->next = vfd->next;
+ else
+ vol_fd->volume->fds = vfd->next;
+ free(vfd);
+ break;
+ }
+ vfd_last = vfd;
+ vfd = vfd->next;
+ }
+ /* Close volume device file */
+ if (close(fd) == -1)
+ error_exit("Failed to close volume file descriptor");
+}
+
+static void set_random_data(unsigned seed, unsigned char *buf, int size)
+{
+ int i;
+ unsigned r;
+
+ r = rand();
+ srand(seed);
+ for (i = 0; i < size; ++i)
+ buf[i] = rand();
+ srand(r);
+}
+
+#if 0
+static void print_write_info(struct write_info *w)
+{
+ printf("Offset: %lld Size:%d Seed:%u\n", w->offset, w->size, w->random_seed);
+ fflush(stdout);
+}
+#endif
+
+static void check_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ struct write_info *w;
+ off64_t gap_end;
+ int eb_size = erase_block->volume->info.eb_size;
+ ssize_t bytes_read;
+
+ w = erase_block->writes;
+ gap_end = erase_block->offset + eb_size;
+ while (w) {
+ if (w->offset + w->size < gap_end) {
+ /* There is a gap. Check all 0xff */
+ off64_t gap_start = w->offset + w->size;
+ size_t size = gap_end - gap_start;
+ if (lseek64(fd, gap_start, SEEK_SET) != gap_start)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, size);
+ if (bytes_read != size)
+ error_exit("read failed in gap");
+ while (size)
+ if (read_buffer[--size] != 0xff) {
+ fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
+ fprintf(stderr, "offset = %lld\n" , (long long) gap_start);
+ fprintf(stderr, "size = %ld\n" , (long) bytes_read);
+ error_exit("verify 0xff failed");
+ }
+ }
+ if (lseek64(fd, w->offset, SEEK_SET) != w->offset)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , w->size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, w->size);
+ if (bytes_read != w->size) {
+ fprintf(stderr, "offset = %lld\n" , (long long) w->offset);
+ fprintf(stderr, "size = %ld\n" , (long) w->size);
+ fprintf(stderr, "bytes_read = %ld\n" , (long) bytes_read);
+ error_exit("read failed");
+ }
+ set_random_data(w->random_seed, write_buffer, w->size);
+ if (memcmp(read_buffer, write_buffer, w->size))
+ error_exit("verify failed");
+ gap_end = w->offset;
+ w = w->next;
+ }
+ if (gap_end > erase_block->offset) {
+ /* Check all 0xff */
+ off64_t gap_start = erase_block->offset;
+ size_t size = gap_end - gap_start;
+ if (lseek64(fd, gap_start, SEEK_SET) != gap_start)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, size);
+ if (bytes_read != size)
+ error_exit("read failed in gap");
+ while (size)
+ if (read_buffer[--size] != 0xff) {
+ fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
+ fprintf(stderr, "offset = %lld\n" , (long long) gap_start);
+ fprintf(stderr, "size = %ld\n" , (long) bytes_read);
+ error_exit("verify 0xff failed!");
+ }
+ }
+}
+
+static int write_to_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ int page_size = erase_block->volume->ubi_device->info.min_io_size;
+ int eb_size = erase_block->volume->info.eb_size;
+ int next_offset = 0;
+ int space, size;
+ off64_t offset;
+ unsigned seed;
+ struct write_info *w;
+
+ if (erase_block->writes)
+ next_offset = erase_block->writes->offset_within_block + erase_block->writes->size;
+ space = eb_size - next_offset;
+ if (space <= 0)
+ return 0; /* No space */
+ if (!get_random_number(10)) {
+ /* 1 time in 10 leave a gap */
+ next_offset += get_random_number(space);
+ next_offset = (next_offset / page_size) * page_size;
+ space = eb_size - next_offset;
+ }
+ if (get_random_number(2))
+ size = 1 * page_size;
+ else if (get_random_number(2))
+ size = 2 * page_size;
+ else if (get_random_number(2))
+ size = 3 * page_size;
+ else if (get_random_number(2))
+ size = 4 * page_size;
+ else {
+ if (get_random_number(4))
+ size = get_random_number(space);
+ else
+ size = space;
+ size = (size / page_size) * page_size;
+ }
+ if (size == 0 || size > space)
+ size = page_size;
+ if (next_offset + size > eb_size)
+ error_exit("internal error");
+ offset = erase_block->offset + next_offset;
+ if (offset < erase_block->top_of_data)
+ error_exit("internal error!");
+ if (lseek64(fd, offset, SEEK_SET) != offset)
+ error_exit("lseek64 failed");
+ /* Do write */
+ seed = get_next_seed();
+ if (!seed)
+ seed = 1;
+ set_random_data(seed, write_buffer, size);
+ if (write(fd, write_buffer, size) != size)
+ error_exit("write failed");
+ erase_block->top_of_data = offset + size;
+ /* Make write info and add to eb */
+ w = allocate(sizeof(*w));
+ w->offset_within_block = next_offset;
+ w->offset = offset;
+ w->size = size;
+ w->random_seed = seed;
+ w->next = erase_block->writes;
+ erase_block->writes = w;
+ erase_block->touched = 1;
+ erase_block->erased = 0;
+ total_written += size;
+ return 1;
+}
+
+static void erase_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ struct write_info *w;
+ uint32_t eb_no;
+ int res;
+
+ eb_no = erase_block->block_number;
+ res = ioctl(fd, UBI_IOCEBER, &eb_no);
+ if (res)
+ error_exit("Failed to erase an erase block");
+ /* Remove writes from this eb */
+ while (erase_block->writes) {
+ w = erase_block->writes;
+ erase_block->writes = erase_block->writes->next;
+ free(w);
+ }
+ erase_block->erased = 1;
+ erase_block->touched = 1;
+ erase_block->top_of_data = erase_block->offset;
+}
+
+static void operate_on_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ /*
+ Possible operations:
+ read from it and verify
+ write to it
+ erase it
+ */
+ int work_done = 1;
+ static int no_work_done_count = 0;
+
+ if (!get_random_number(10) && no_work_done_count <= 5) {
+ check_erase_block(erase_block, fd);
+ work_done = 0;
+ } else if (get_random_number(100)) {
+ if (!write_to_erase_block(erase_block, fd)) {
+ /* The erase block was full */
+ if (get_random_number(2) || no_work_done_count > 5)
+ erase_erase_block(erase_block, fd);
+ else
+ work_done = 0;
+ }
+ } else
+ erase_erase_block(erase_block, fd);
+ if (work_done)
+ no_work_done_count = 0;
+ else
+ no_work_done_count += 1;
+}
+
+static void operate_on_open_volume(struct volume_fd *vol_fd)
+{
+ /*
+ Possible operations:
+ operate on an erase block
+ close volume
+ */
+ if (get_random_number(100) == 0)
+ close_volume(vol_fd);
+ else {
+ /* Pick an erase block at random */
+ int eb_no = get_random_number(vol_fd->volume->info.rsvd_ebs);
+ operate_on_erase_block(&vol_fd->volume->erase_blocks[eb_no], vol_fd->fd);
+ }
+}
+
+static void operate_on_volume(struct volume_info *vol)
+{
+ /*
+ Possible operations:
+ open it
+ resize it (must close fd's first) <- TODO
+ delete it (must close fd's first) <- TODO
+ */
+ open_volume(vol);
+}
+
+static int ubi_major(const char *device_file_name)
+{
+ struct stat buf;
+ static int maj = 0;
+
+ if (maj)
+ return maj;
+ if (stat(device_file_name, &buf) == -1)
+ error_exit("Failed to stat ubi device file");
+ maj = major(buf.st_rdev);
+ return maj;
+}
+
+static void operate_on_ubi_device(struct ubi_device_info *ubi_device)
+{
+ /*
+ TODO:
+ Possible operations:
+ create a new volume
+ operate on existing volume
+ */
+ /*
+ Simplified operation (i.e. only have 1 volume):
+ If there are no volumes create 1 volumne
+ Then operate on the volume
+ */
+ if (ubi_device->info.vol_count == 0) {
+ /* Create the one-and-only volume we will use */
+ char dev_name[1024];
+ int i, n, maj, fd;
+ struct volume_info *s;
+ struct ubi_mkvol_request req;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1; /* TODO: What is this? */
+ req.bytes = ubi_device->info.eb_size * max_ebs_per_vol;
+ if (req.bytes == 0 || req.bytes > ubi_device->info.avail_bytes)
+ req.bytes = ubi_device->info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = "integ-test-vol";
+ if (ubi_mkvol(libubi, ubi_device->device_file_name, &req))
+ error_exit("ubi_mkvol failed");
+ s = allocate(sizeof(*s));
+ s->ubi_device = ubi_device;
+ if (ubi_get_vol_info1(libubi, ubi_device->info.dev_num, req.vol_id, &s->info))
+ error_exit("ubi_get_vol_info failed");
+ n = s->info.rsvd_ebs;
+ s->erase_blocks = allocate(sizeof(struct erase_block_info) * n);
+ for (i = 0; i < n; ++i) {
+ s->erase_blocks[i].volume = s;
+ s->erase_blocks[i].block_number = i;
+ s->erase_blocks[i].offset = i * (off64_t) s->info.eb_size;
+ s->erase_blocks[i].top_of_data = s->erase_blocks[i].offset;
+ }
+ /* FIXME: Correctly get device file name */
+ sprintf(dev_name, "%s_%d", ubi_device->device_file_name, req.vol_id);
+ s->device_file_name = strdup(dev_name);
+ ubi_device->volumes = s;
+ ubi_device->info.vol_count += 1;
+ sleep(1);
+ fd = open(s->device_file_name, O_RDONLY);
+ if (fd == -1) {
+ /* FIXME: Correctly make node */
+ maj = ubi_major(ubi_device->device_file_name);
+ sprintf(dev_name, "mknod %s c %d %d", s->device_file_name, maj, req.vol_id + 1);
+ system(dev_name);
+ } else if (close(fd) == -1)
+ error_exit("Failed to close volume device file");
+ }
+ operate_on_volume(ubi_device->volumes);
+}
+
+static void do_an_operation(void)
+{
+ int too_few = (open_volume_count < info.dev_count * 3);
+ int too_many = (open_volume_count > info.dev_count * 5);
+
+ if (too_many || (!too_few && get_random_number(1000) > 0)) {
+ /* Operate on an open volume */
+ size_t pos;
+ struct open_volume_fd *ofd;
+ pos = get_random_number(open_volume_count);
+ for (ofd = open_volumes; pos && ofd && ofd->next; --pos)
+ ofd = ofd->next;
+ operate_on_open_volume(ofd->vol_fd);
+ } else if (info.dev_count > 0) {
+ /* Operate on a ubi device */
+ size_t ubi_pos = 0;
+ if (info.dev_count > 1)
+ ubi_pos = get_random_number(info.dev_count - 1);
+ operate_on_ubi_device(&ubi_array[ubi_pos]);
+ } else
+ error_exit("Internal error");
+}
+
+static void get_ubi_devices_info(void)
+{
+ int i, ubi_pos = 0;
+ char dev_name[1024];
+ size_t buf_size = 1024 * 128;
+
+ if (ubi_get_info(libubi, &info))
+ error_exit("ubi_get_info failed");
+ if (info.dev_count > MAX_UBI_DEVICES)
+ error_exit("Too many ubi devices");
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; ++i) {
+ struct ubi_device_info *s;
+ s = &ubi_array[ubi_pos++];
+ if (ubi_get_dev_info1(libubi, i, &s->info))
+ error_exit("ubi_get_dev_info1 failed");
+ if (s->info.vol_count)
+ error_exit("There are existing volumes");
+ /* FIXME: Correctly get device file name */
+ sprintf(dev_name, "/dev/ubi%d", i);
+ s->device_file_name = strdup(dev_name);
+ if (buf_size < s->info.eb_size)
+ buf_size = s->info.eb_size;
+ if (max_ebs_per_vol && s->info.eb_size * max_ebs_per_vol < s->info.avail_bytes)
+ total_space += s->info.eb_size * max_ebs_per_vol;
+ else
+ total_space += s->info.avail_bytes;
+ }
+ write_buffer = allocate(buf_size);
+ read_buffer = allocate(buf_size);
+}
+
+static void load_ubi(void)
+{
+ system("rmmod ubi");
+ if (system(ubi_module_load_string) != 0)
+ error_exit("Failed to load UBI module");
+ sleep(1);
+}
+
+static void do_some_operations(void)
+{
+ unsigned i = 0;
+ total_written = 0;
+ printf("Total space: %llu\n", (unsigned long long) total_space);
+ while (total_written < total_space * 3) {
+ do_an_operation();
+ if (i++ % 10000 == 0)
+ printf("Total written: %llu\n", (unsigned long long) total_written);
+ }
+ printf("Total written: %llu\n", (unsigned long long) total_written);
+}
+
+static void reload_ubi(void)
+{
+ /* Remove module */
+ if (system("rmmod ubi") != 0)
+ error_exit("Failed to remove UBI module");
+ /* Install module */
+ if (system(ubi_module_load_string) != 0)
+ error_exit("Failed to load UBI module");
+ sleep(1);
+}
+
+static void check_volume(struct volume_info *vol)
+{
+ struct erase_block_info *eb = vol->erase_blocks;
+ int pos;
+ int fd;
+
+ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
+ if (fd == -1)
+ error_exit("Failed to open volume device file");
+ for (pos = 0; pos < vol->info.rsvd_ebs; ++pos)
+ check_erase_block(eb++, fd);
+ if (close(fd) == -1)
+ error_exit("Failed to close volume device file");
+}
+
+static void check_ubi_device(struct ubi_device_info *ubi_device)
+{
+ struct volume_info *vol;
+
+ vol = ubi_device->volumes;
+ while (vol) {
+ check_volume(vol);
+ vol = vol->next;
+ }
+}
+
+static void check_ubi(void)
+{
+ int i;
+
+ for (i = 0; i < info.dev_count; ++i)
+ check_ubi_device(&ubi_array[i]);
+}
+
+static int is_all_digits(const char *s)
+{
+ const char *digits = "0123456789";
+ if (!s || !*s)
+ return 0;
+ for (;*s;++s)
+ if (!strchr(digits,*s))
+ return 0;
+ return 1;
+}
+
+static int get_short_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
+{
+ const char *p = NULL;
+ int i = *pos;
+ size_t n = strlen(name);
+
+ if (strlen(argv[i]) > n)
+ p = argv[i] + n;
+ else if (++i < argc)
+ p = argv[i];
+ if (!is_all_digits(p))
+ return 1;
+ *result = atoll(p);
+ *pos = i;
+ return 0;
+}
+
+static int get_long_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
+{
+ const char *p = NULL;
+ int i = *pos;
+ size_t n = strlen(name);
+
+ if (strlen(argv[i]) > n)
+ p = argv[i] + n;
+ else if (++i < argc)
+ p = argv[i];
+ if (p && *p == '=') {
+ p += 1;
+ if (!*p && ++i < argc)
+ p = argv[i];
+ }
+ if (!is_all_digits(p))
+ return 1;
+ *result = atoll(p);
+ *pos = i;
+ return 0;
+}
+
+static int remove_all_volumes(void)
+{
+ int i;
+
+ for (i = 0; i < info.dev_count; ++i) {
+ struct ubi_device_info *ubi_device = &ubi_array[i];
+ struct volume_info *vol;
+ vol = ubi_device->volumes;
+ while (vol) {
+ int res = ubi_rmvol(libubi,
+ ubi_device->device_file_name,
+ vol->info.vol_id);
+ if (res)
+ return res;
+ vol = vol->next;
+ }
+ }
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ int i;
+ long long r, repeat = 1;
+ int initial_seed = 1, args_ok = 1;
+
+ printf("UBI Integrity Test\n");
+
+ /* Get arguments */
+ ubi_module_load_string = 0;
+ for (i = 1; i < argc; ++i) {
+ if (strncmp(argv[i], "-h", 2) == 0)
+ args_ok = 0;
+ else if (strncmp(argv[i], "--help", 6) == 0)
+ args_ok = 0;
+ else if (strncmp(argv[i], "-n", 2) == 0) {
+ if (get_short_arg(&i, "-n", &repeat, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "--repeat", 8) == 0) {
+ if (get_long_arg(&i, "--repeat", &repeat, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "-m", 2) == 0) {
+ if (get_short_arg(&i,"-m", &max_ebs_per_vol, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "--maxebs", 8) == 0) {
+ if (get_long_arg(&i, "--maxebs", &max_ebs_per_vol, argc, argv))
+ args_ok = 0;
+ } else if (!ubi_module_load_string)
+ ubi_module_load_string = argv[i];
+ else
+ args_ok = 0;
+ }
+ if (!args_ok || !ubi_module_load_string) {
+ fprintf(stderr, "Usage is: ubi_integ [<options>] <UBI Module load command>\n");
+ fprintf(stderr, " Options: \n");
+ fprintf(stderr, " -h, --help Help\n");
+ fprintf(stderr, " -n arg, --repeat=arg Repeat test arg times\n");
+ fprintf(stderr, " -m arg, --maxebs=arg Max no. of erase blocks\n");
+ return 1;
+ }
+
+ initial_seed = getpid();
+ printf("Initial seed = %u\n", (unsigned) initial_seed);
+ next_seed = initial_seed;
+ srand(initial_seed);
+ load_ubi();
+
+ libubi = libubi_open();
+ if (!libubi)
+ error_exit("Failed to open libubi");
+
+ get_ubi_devices_info();
+
+ r = 0;
+ while (repeat == 0 || r++ < repeat) {
+ printf("Cycle %lld\n", r);
+ do_some_operations();
+
+ /* Close all volumes */
+ while (open_volumes)
+ close_volume(open_volumes->vol_fd);
+
+ check_ubi();
+
+ libubi_close(libubi);
+
+ reload_ubi();
+
+ libubi = libubi_open();
+ if (!libubi)
+ error_exit("Failed to open libubi");
+
+ check_ubi();
+ }
+
+ if (remove_all_volumes())
+ error_exit("Failed to remove all volumes");
+
+ libubi_close(libubi);
+
+ printf("UBI Integrity Test completed ok\n");
+ return 0;
+}
diff --git a/tests/ubi-tests/io_basic.c b/tests/ubi-tests/io_basic.c
new file mode 100644
index 0000000..2e8809a
--- /dev/null
+++ b/tests/ubi-tests/io_basic.c
@@ -0,0 +1,182 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * Test basic UBI volume I/O capabilities.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_basic(int type);
+static int test_aligned(int type);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_basic(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_basic(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_aligned(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_aligned(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_basic - check basic volume read and update capabilities.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_basic(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_basic()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = type;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ /* Make sure newly created volume contains only 0xFF bytes */
+ if (check_vol_patt(&vol_node[0], 0xFF))
+ goto remove;
+
+ /* Write 0xA5 bytes to the volume */
+ if (update_vol_patt(&vol_node[0], dev_info.avail_bytes, 0xA5))
+ goto remove;
+ if (check_vol_patt(&vol_node[0], 0xA5))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+/**
+ * test_aligned - test volume alignment feature.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_aligned(int type)
+{
+ int i, ebsz;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_aligned()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+
+ req.vol_type = type;
+ req.name = name;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * ebsz;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ /* Make sure newly created volume contains only 0xFF bytes */
+ if (check_vol_patt(&vol_node[0], 0xFF))
+ goto remove;
+
+ /* Write 0xA5 bytes to the volume */
+ if (update_vol_patt(&vol_node[0], req.bytes, 0xA5))
+ goto remove;
+ if (check_vol_patt(&vol_node[0], 0xA5))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
diff --git a/tests/ubi-tests/io_paral.c b/tests/ubi-tests/io_paral.c
new file mode 100644
index 0000000..488a0b0
--- /dev/null
+++ b/tests/ubi-tests/io_paral.c
@@ -0,0 +1,251 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * This test does a lot of I/O to volumes in parallel.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_paral"
+#include "common.h"
+
+#define THREADS_NUM 3
+#define ITERATIONS 10
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+static int iterations = ITERATIONS;
+int total_bytes;
+
+static void * the_thread(void *ptr);
+
+static long long memory_limit(void)
+{
+ long long result = 0;
+ FILE *f;
+
+ f = fopen("/proc/meminfo", "r");
+ if (!f)
+ return 0;
+ fscanf(f, "%*s %lld", &result);
+ fclose(f);
+ return result * 1024 / 4;
+}
+
+int main(int argc, char * const argv[])
+{
+ int i, ret;
+ pthread_t threads[THREADS_NUM];
+ struct ubi_mkvol_request req;
+ long long mem_limit;
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ req.alignment = 1;
+ mem_limit = memory_limit();
+ if (mem_limit && mem_limit < dev_info.avail_bytes)
+ total_bytes = req.bytes =
+ (mem_limit / dev_info.eb_size / THREADS_NUM)
+ * dev_info.eb_size;
+ else
+ total_bytes = req.bytes =
+ ((dev_info.avail_ebs - 3) / THREADS_NUM)
+ * dev_info.eb_size;
+ for (i = 0; i < THREADS_NUM; i++) {
+ char name[100];
+
+ req.vol_id = i;
+ sprintf(&name[0], TESTNAME":%d", i);
+ req.name = &name[0];
+ req.vol_type = (i & 1) ? UBI_STATIC_VOLUME : UBI_DYNAMIC_VOLUME;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ goto remove;
+ }
+ }
+
+ /* Create one volume with static data to make WL work more */
+ req.vol_id = THREADS_NUM;
+ req.name = TESTNAME ":static";
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.bytes = 3*dev_info.eb_size;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ goto remove;
+ }
+
+ for (i = 0; i < THREADS_NUM; i++) {
+ ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i);
+ if (ret) {
+ failed("pthread_create");
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < THREADS_NUM; i++)
+ pthread_join(threads[i], NULL);
+
+ for (i = 0; i <= THREADS_NUM; i++) {
+ if (ubi_rmvol(libubi, node, i)) {
+ failed("ubi_rmvol");
+ goto remove;
+ }
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+remove:
+ for (i = 0; i <= THREADS_NUM; i++)
+ ubi_rmvol(libubi, node, i);
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * the_thread - the testing thread.
+ *
+ * @ptr thread number
+ */
+static void * the_thread(void *ptr)
+{
+ int fd, iter = iterations, vol_id = (int)ptr;
+ unsigned char *wbuf, *rbuf;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+
+ wbuf = malloc(total_bytes);
+ rbuf = malloc(total_bytes);
+ if (!wbuf || !rbuf) {
+ failed("malloc");
+ goto free;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, vol_id);
+
+ while (iter--) {
+ int i, ret, written = 0, rd = 0;
+ int bytes = (random() % (total_bytes - 1)) + 1;
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto free;
+ }
+
+ for (i = 0; i < bytes; i++)
+ wbuf[i] = random() % 255;
+ memset(rbuf, '\0', bytes);
+
+ do {
+ ret = ubi_update_start(libubi, fd, bytes);
+ if (ret && errno != EBUSY) {
+ failed("ubi_update_start");
+ err_msg("vol_id %d", vol_id);
+ goto close;
+ }
+ } while (ret);
+
+ while (written < bytes) {
+ int to_write = random() % (bytes - written);
+
+ if (to_write == 0)
+ to_write = 1;
+
+ ret = write(fd, wbuf, to_write);
+ if (ret != to_write) {
+ failed("write");
+ err_msg("failed to write %d bytes at offset %d "
+ "of volume %d", to_write, written,
+ vol_id);
+ err_msg("update: %d bytes", bytes);
+ goto close;
+ }
+
+ written += to_write;
+ }
+
+ close(fd);
+
+ fd = open(vol_node, O_RDONLY);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto free;
+ }
+
+ /* read data back and check */
+ while (rd < bytes) {
+ int to_read = random() % (bytes - rd);
+
+ if (to_read == 0)
+ to_read = 1;
+
+ ret = read(fd, rbuf, to_read);
+ if (ret != to_read) {
+ failed("read");
+ err_msg("failed to read %d bytes at offset %d "
+ "of volume %d", to_read, rd, vol_id);
+ goto close;
+ }
+
+ rd += to_read;
+ }
+
+ close(fd);
+
+ }
+
+ free(wbuf);
+ free(rbuf);
+ return NULL;
+
+close:
+ close(fd);
+free:
+ free(wbuf);
+ free(rbuf);
+ return NULL;
+}
diff --git a/tests/ubi-tests/io_read.c b/tests/ubi-tests/io_read.c
new file mode 100644
index 0000000..c5d1da7
--- /dev/null
+++ b/tests/ubi-tests/io_read.c
@@ -0,0 +1,398 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * Test UBI volume read.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_static(void);
+static int test_read(int type);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_static())
+ goto close;
+ if (test_read(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_read(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_static - test static volume-specific features.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_static(void)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_basic()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_vol_info vol_info;
+ int fd, ret;
+ char buf[20];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_STATIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto remove;
+ }
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto close;
+ }
+
+ /* Make sure new static volume contains no data */
+ if (vol_info.data_bytes != 0) {
+ err_msg("data_bytes = %lld, not zero", vol_info.data_bytes);
+ goto close;
+ }
+
+ /* Ensure read returns EOF */
+ ret = read(fd, &buf[0], 1);
+ if (ret < 0) {
+ failed("read");
+ goto close;
+ }
+ if (ret != 0) {
+ err_msg("read data from free static volume");
+ goto close;
+ }
+
+ if (ubi_update_start(libubi, fd, 10)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ ret = write(fd, &buf[0], 10);
+ if (ret < 0) {
+ failed("write");
+ goto close;
+ }
+ if (ret != 10) {
+ err_msg("written %d bytes", ret);
+ goto close;
+ }
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ failed("seek");
+ goto close;
+ }
+ ret = read(fd, &buf[0], 20);
+ if (ret < 0) {
+ failed("read");
+ goto close;
+ }
+ if (ret != 10) {
+ err_msg("read %d bytes", ret);
+ goto close;
+ }
+
+ close(fd);
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+close:
+ close(fd);
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+static int test_read1(struct ubi_vol_info *vol_info);
+
+/**
+ * test_read - test UBI volume reading from different offsets.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_read(int type)
+{
+ const char *name = TESTNAME ":test_read()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_mkvol_request req;
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int eb_size;
+ struct ubi_vol_info vol_info;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * eb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_read1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+static int test_read2(const struct ubi_vol_info *vol_info, int len);
+
+static int fd;
+
+/* Data lengthes to test, @io - minimal I/O unit size, @s - eraseblock size */
+#define LENGTHES(io, s) \
+ {1, (io), (io)+1, 2*(io), 3*(io)-1, 3*(io), \
+ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \
+ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \
+ 2*(s)+(io), 3*(s), 3*(s)+(io)};
+
+/*
+ * A helper function for test_read().
+ */
+static int test_read1(struct ubi_vol_info *vol_info)
+{
+ int i, written = 0;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int lengthes[] = LENGTHES(dev_info.min_io_size, vol_info->eb_size);
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ /* Write some pattern to the volume */
+ if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) {
+ failed("ubi_update_start");
+ err_msg("bytes = %lld", vol_info->rsvd_bytes);
+ goto close;
+ }
+
+ while (written < vol_info->rsvd_bytes) {
+ int i, ret;
+ unsigned char buf[512];
+
+ for (i = 0; i < 512; i++)
+ buf[i] = (unsigned char)(written + i);
+
+ ret = write(fd, &buf[0], 512);
+ if (ret == -1) {
+ failed("write");
+ err_msg("written = %d, ret = %d", written, ret);
+ goto close;
+ }
+ written += ret;
+ }
+
+ close(fd);
+
+ if (ubi_get_vol_info(libubi, vol_node, vol_info)) {
+ failed("ubi_get_vol_info");
+ return -1;
+ }
+
+ fd = open(vol_node, O_RDONLY);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(lengthes)/sizeof(int); i++) {
+ if (test_read2(vol_info, lengthes[i])) {
+ err_msg("length = %d", lengthes[i]);
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off);
+
+/*
+ * Offsets to test, @io - minimal I/O unit size, @s - eraseblock size, @sz -
+ * volume size.
+ */
+#define OFFSETS(io, s, sz) \
+ {0, (io)-1, (io), (io)+1, 2*(io)-1, 2*(io), 3*(io)-1, 3*(io), \
+ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \
+ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \
+ 2*(s)+(io), 3*(s), (sz)-(s)-1, (sz)-(io)-1, (sz)-PAGE_SIZE-1};
+
+/*
+ * A helper function for test_read1().
+ */
+static int test_read2(const struct ubi_vol_info *vol_info, int len)
+{
+ int i;
+ off_t offsets[] = OFFSETS(dev_info.min_io_size, vol_info->eb_size,
+ vol_info->data_bytes);
+
+ for (i = 0; i < sizeof(offsets)/sizeof(off_t); i++) {
+ if (test_read3(vol_info, len, offsets[i])) {
+ err_msg("offset = %d", offsets[i]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * A helper function for test_read2().
+ */
+static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off)
+{
+ int i, len1;
+ unsigned char ck_buf[len], buf[len];
+ off_t new_off;
+
+ if (off + len > vol_info->data_bytes)
+ len1 = vol_info->data_bytes - off;
+ else
+ len1 = len;
+
+ if (lseek(fd, off, SEEK_SET) != off) {
+ failed("seek");
+ err_msg("len = %d", len);
+ return -1;
+ }
+ if (read(fd, &buf[0], len) != len1) {
+ failed("read");
+ err_msg("len = %d", len);
+ return -1;
+ }
+
+ new_off = lseek(fd, 0, SEEK_CUR);
+ if (new_off != off + len1) {
+ if (new_off == -1)
+ failed("lseek");
+ else
+ err_msg("read %d bytes from %lld, but resulting "
+ "offset is %lld", len1, (long long) off, (long long) new_off);
+ return -1;
+ }
+
+ for (i = 0; i < len1; i++)
+ ck_buf[i] = (unsigned char)(off + i);
+
+ if (memcmp(&buf[0], &ck_buf[0], len1)) {
+ err_msg("incorrect data read from offset %lld",
+ (long long)off);
+ err_msg("len = %d", len);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/tests/ubi-tests/io_update.c b/tests/ubi-tests/io_update.c
new file mode 100644
index 0000000..2e3422a
--- /dev/null
+++ b/tests/ubi-tests/io_update.c
@@ -0,0 +1,370 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * Test UBI volume update.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_update"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_update(int type);
+static int test_update_ff(void);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_update(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_update(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_update_ff())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+static int test_update1(struct ubi_vol_info *vol_info);
+
+/**
+ * test_update - check volume update capabilities.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_update(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_update()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+ struct ubi_vol_info vol_info;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int eb_size;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * eb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_update1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+#define SEQUENCES(io, s) { \
+ {3*(s)-(io)-1, 1}, \
+ {512}, \
+ {666}, \
+ {2048}, \
+ {(io), (io), PAGE_SIZE}, \
+ {(io)+1, (io)+1, PAGE_SIZE}, \
+ {PAGE_SIZE}, \
+ {PAGE_SIZE-1}, \
+ {PAGE_SIZE+(io)}, \
+ {(s)}, \
+ {(s)-1}, \
+ {(s)+1}, \
+ {(io), (s)+1}, \
+ {(s)+(io), PAGE_SIZE}, \
+ {2*(s), PAGE_SIZE}, \
+ {PAGE_SIZE, 2*(s), 1}, \
+ {PAGE_SIZE, 2*(s)}, \
+ {2*(s)-1, 2*(s)-1}, \
+ {3*(s), PAGE_SIZE + 1}, \
+ {1, PAGE_SIZE}, \
+ {(io), (s)} \
+}
+#define SEQ_SZ 21
+
+/*
+ * test_update1 - helper function for test_update().
+ */
+static int test_update1(struct ubi_vol_info *vol_info)
+{
+ int sequences[SEQ_SZ][3] = SEQUENCES(dev_info.min_io_size,
+ vol_info->eb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ unsigned char buf[vol_info->rsvd_bytes];
+ int fd, i, j;
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ for (i = 0; i < vol_info->rsvd_bytes; i++)
+ buf[i] = (unsigned char)i;
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ for (i = 0; i < SEQ_SZ; i++) {
+ int ret, stop = 0, len;
+ off_t off = 0;
+ unsigned char buf1[vol_info->rsvd_bytes];
+
+ if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ for (j = 0; off < vol_info->rsvd_bytes; j++) {
+ if (!stop) {
+ if (sequences[i][j] != 0)
+ len = sequences[i][j];
+ else
+ stop = 1;
+ }
+
+ ret = write(fd, &buf[off], len);
+ if (ret < 0) {
+ failed("write");
+ err_msg("failed to write %d bytes at offset "
+ "%lld", len, (long long) off);
+ goto close;
+ }
+ if (off + len > vol_info->rsvd_bytes)
+ len = vol_info->rsvd_bytes - off;
+ if (ret != len) {
+ err_msg("failed to write %d bytes at offset "
+ "%lld, wrote %d", len, (long long) off, ret);
+ goto close;
+ }
+ off += len;
+ }
+
+ /* Check data */
+ if ((ret = lseek(fd, SEEK_SET, 0)) != 0) {
+ if (ret < 0)
+ failed("lseek");
+ err_msg("cannot seek to 0");
+ goto close;
+ }
+ memset(&buf1[0], 0x01, vol_info->rsvd_bytes);
+ ret = read(fd, &buf1[0], vol_info->rsvd_bytes + 1);
+ if (ret < 0) {
+ failed("read");
+ err_msg("failed to read %d bytes",
+ vol_info->rsvd_bytes + 1);
+ goto close;
+ }
+ if (ret != vol_info->rsvd_bytes) {
+ err_msg("failed to read %d bytes, read %d",
+ vol_info->rsvd_bytes, ret);
+ goto close;
+ }
+ if (memcmp(&buf[0], &buf1[0], vol_info->rsvd_bytes)) {
+ err_msg("data corruption");
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * test_update_ff - check volume with 0xFF data
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_update_ff(void)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_update()";
+ struct ubi_vol_info vol_info;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int i, fd, ret, types[2];
+ int upd_len = MIN_AVAIL_EBS * dev_info.eb_size;
+ char buf[upd_len], buf1[upd_len];
+
+ for(i = 0; i < MIN_AVAIL_EBS; i++) {
+ if (i % 1)
+ memset(&buf[0], 0xAB, upd_len);
+ else
+ memset(&buf[0], 0xFF, upd_len);
+ }
+
+ types[0] = UBI_DYNAMIC_VOLUME;
+ types[1] = UBI_STATIC_VOLUME;
+
+ for (i = 0; i < 2; i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = types[i];
+ req.name = name;
+
+ req.alignment = 1;
+ req.bytes = upd_len;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto remove;
+ }
+
+ if (ubi_update_start(libubi, fd, upd_len)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+
+ ret = write(fd, &buf[0], upd_len);
+ if (ret < 0 || ret != upd_len) {
+ failed("write");
+ err_msg("failed to write %d bytes", upd_len);
+ goto close;
+ }
+
+ /* Check data */
+ if ((ret = lseek(fd, SEEK_SET, 0)) != 0) {
+ if (ret < 0)
+ failed("lseek");
+ err_msg("cannot seek to 0");
+ goto close;
+ }
+
+ close(fd);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto remove;
+ }
+
+ memset(&buf1[0], 0x00, upd_len);
+ ret = read(fd, &buf1[0], upd_len);
+ if (ret < 0) {
+ failed("read");
+ err_msg("failed to read %d bytes", upd_len);
+ goto close;
+ }
+ if (ret != upd_len) {
+ err_msg("failed to read %d bytes, read %d",
+ upd_len, ret);
+ goto close;
+ }
+ if (memcmp(&buf[0], &buf1[0], upd_len)) {
+ err_msg("data corruption");
+ goto close;
+ }
+
+ close(fd);
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+close:
+ close(fd);
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
diff --git a/tests/ubi-tests/mkvol_bad.c b/tests/ubi-tests/mkvol_bad.c
new file mode 100644
index 0000000..023b06b
--- /dev/null
+++ b/tests/ubi-tests/mkvol_bad.c
@@ -0,0 +1,304 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * Test UBI volume creation and deletion ioctl()s with bad input and in case of
+ * incorrect usage.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_bad"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_mkvol(void);
+static int test_rmvol(void);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_mkvol())
+ goto close;
+
+ if (test_rmvol())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_mkvol - test that UBI mkvol ioctl rejects bad input parameters.
+ *
+ * This function returns %0 if the test passed and %-1 if not.
+ */
+static int test_mkvol(void)
+{
+ int ret, i;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_mkvol()";
+
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ /* Bad volume ID */
+ req.vol_id = -2;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id))
+ return -1;
+
+ req.vol_id = dev_info.max_vol_count;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id))
+ return -1;
+
+ /* Bad alignment */
+ req.vol_id = 0;
+ req.alignment = 0;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ req.alignment = -1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ req.alignment = dev_info.eb_size + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ if (dev_info.min_io_size > 1) {
+ req.alignment = dev_info.min_io_size + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+ }
+
+ /* Bad bytes */
+ req.alignment = 1;
+ req.bytes = -1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.bytes = 0;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.bytes = dev_info.avail_bytes + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.alignment = dev_info.eb_size - dev_info.min_io_size;
+ req.bytes = (dev_info.eb_size - dev_info.eb_size % req.alignment) *
+ dev_info.avail_ebs + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ /* Bad vol_type */
+ req.alignment = 1;
+ req.bytes = dev_info.eb_size;
+ req.vol_type = UBI_DYNAMIC_VOLUME + UBI_STATIC_VOLUME;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_type = %d",
+ req.vol_type))
+ return -1;
+
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+
+ /* Too long name */
+ {
+ char name[UBI_VOL_NAME_MAX + 5];
+
+ memset(&name[0], 'x', UBI_VOL_NAME_MAX + 1);
+ name[UBI_VOL_NAME_MAX + 1] = '\0';
+
+ req.name = &name[0];
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "name_len = %d",
+ UBI_VOL_NAME_MAX + 1))
+ return -1;
+ }
+
+ /* Try to create 2 volumes with the same ID and name */
+ req.name = name;
+ req.vol_id = 0;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "volume with ID 0 created twice"))
+ return -1;
+
+ req.vol_id = 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "volume with name \"%s\" created twice", name))
+ return -1;
+
+ if (ubi_rmvol(libubi, node, 0)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Try to use too much space */
+ req.vol_id = 0;
+ req.bytes = dev_info.avail_bytes;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ req.bytes = 1;
+ req.vol_id = 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "created volume of maximum size %lld, but still "
+ "can create more volumes", dev_info.avail_bytes))
+ return -1;
+
+ if (ubi_rmvol(libubi, node, 0)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Try to create too many volumes */
+ for (i = 0; i < dev_info.max_vol_count; i++) {
+ char nm[strlen(name) + 50];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = 1;
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ sprintf(&nm[0], "%s:%d", name, i);
+ req.name = &nm[0];
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ /*
+ * Note, because of gluebi we may be unable to create
+ * dev_info.max_vol_count devices (MTD restrictions).
+ */
+ if (errno == ENFILE)
+ break;
+ failed("ubi_mkvol");
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+
+ return 0;
+
+remove:
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+ return -1;
+}
+
+/**
+ * test_rmvol - test that UBI rmvol ioctl rejects bad input parameters.
+ *
+ * This function returns %0 if the test passed and %-1 if not.
+ */
+static int test_rmvol(void)
+{
+ int ret;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_rmvol()";
+
+ /* Bad vol_id */
+ ret = ubi_rmvol(libubi, node, -1);
+ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = -1"))
+ return -1;
+
+ ret = ubi_rmvol(libubi, node, dev_info.max_vol_count);
+ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = %d",
+ dev_info.max_vol_count))
+ return -1;
+
+ /* Try to remove non-existing volume */
+ ret = ubi_rmvol(libubi, node, 0);
+ if (check_failed(ret, ENODEV, "ubi_rmvol",
+ "removed non-existing volume 0"))
+ return -1;
+
+ /* Try to remove volume twice */
+ 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;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ ret = ubi_rmvol(libubi, node, req.vol_id);
+ if (check_failed(ret, ENODEV, "ubi_rmvol", "volume %d removed twice",
+ req.vol_id))
+ return -1;
+
+ return 0;
+}
diff --git a/tests/ubi-tests/mkvol_basic.c b/tests/ubi-tests/mkvol_basic.c
new file mode 100644
index 0000000..e2120e9
--- /dev/null
+++ b/tests/ubi-tests/mkvol_basic.c
@@ -0,0 +1,253 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * Test test checks basic volume creation and deletion capabilities.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int mkvol_basic(void);
+static int mkvol_alignment(void);
+static int mkvol_multiple(void);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (mkvol_basic())
+ goto close;
+
+ if (mkvol_alignment())
+ goto close;
+
+ if (mkvol_multiple())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * mkvol_alignment - create volumes with different alignments.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_alignment(void)
+{
+ struct ubi_mkvol_request req;
+ int i, vol_id, ebsz;
+ const char *name = TESTNAME ":mkvol_alignment()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+
+ /* Alignment should actually be multiple of min. I/O size */
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ /* Bear in mind alignment reduces EB size */
+ ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = dev_info.avail_ebs * ebsz;
+
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ err_msg("alignment %d", req.alignment);
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_basic - simple test that checks basic volume creation capability.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_basic(void)
+{
+ struct ubi_mkvol_request req;
+ struct ubi_vol_info vol_info;
+ int vol_id, ret;
+ const char *name = TESTNAME ":mkvol_basic()";
+
+ /* Create dynamic volume of maximum size */
+ 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;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Create static volume of maximum size */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_STATIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", vol_id);
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_multiple - test multiple volumes creation
+ *
+ * Thus function returns %0 if the test passed and %-1 if not.
+ */
+static int mkvol_multiple(void)
+{
+ struct ubi_mkvol_request req;
+ int i, ret, max = dev_info.max_vol_count;
+ const char *name = TESTNAME ":mkvol_multiple()";
+
+ /* Create maximum number of volumes */
+ for (i = 0; i < max; i++) {
+ char nm[strlen(name) + 50];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = 1;
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ sprintf(&nm[0], "%s:%d", name, i);
+ req.name = &nm[0];
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ if (errno == ENFILE) {
+ max = i;
+ break;
+ }
+ failed("ubi_mkvol");
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req)) {
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < max; i++) {
+ struct ubi_vol_info vol_info;
+
+ if (ubi_rmvol(libubi, node, i)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", i);
+ goto remove;
+ }
+ }
+
+ return 0;
+
+remove:
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+ return -1;
+}
diff --git a/tests/ubi-tests/mkvol_paral.c b/tests/ubi-tests/mkvol_paral.c
new file mode 100644
index 0000000..faf085c
--- /dev/null
+++ b/tests/ubi-tests/mkvol_paral.c
@@ -0,0 +1,112 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * This test creates and deletes volumes in parallel.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_paral"
+#include "common.h"
+
+#define THREADS_NUM 4
+#define ITERATIONS 500
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+static int iterations = ITERATIONS;
+
+static void * the_thread(void *ptr);
+
+int main(int argc, char * const argv[])
+{
+ int i, ret;
+ pthread_t threads[THREADS_NUM];
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ for (i = 0; i < THREADS_NUM; i++) {
+ ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i);
+ if (ret) {
+ failed("pthread_create");
+ goto close;
+ }
+ }
+
+ for (i = 0; i < THREADS_NUM; i++)
+ pthread_join(threads[i], NULL);
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * the_thread - the testing thread.
+ *
+ * @ptr thread number
+ */
+static void * the_thread(void *ptr)
+{
+ int n = (int)ptr, iter = iterations;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":the_thread()";
+ char nm[strlen(name) + 50];
+
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes/ITERATIONS;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ sprintf(&nm[0], "%s:%d", name, n);
+ req.name = &nm[0];
+
+ while (iter--) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return NULL;
+ }
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
diff --git a/tests/ubi-tests/rmvol.c b/tests/ubi-tests/rmvol.c
new file mode 100644
index 0000000..fb9b344
--- /dev/null
+++ b/tests/ubi-tests/rmvol.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) Nokia Corporation, 2007
+ *
+ * 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 B. Bityutskiy
+ *
+ * Test volume reference counting - create a volume, open a sysfs file
+ * belonging to the volume, delete the volume but do not close the file, make
+ * sure the file cannot be read, make sure the volume cannot be open, close the
+ * file, make sure the volume disappeard, make sure its sysfs subtree
+ * disappeared.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "libubi.h"
+#define TESTNAME "rmvol"
+#include "common.h"
+
+#define SYSFS_FILE "/sys/class/ubi/ubi%d_%d/usable_eb_size"
+
+int main(int argc, char * const argv[])
+{
+ int ret, fd;
+ char fname[sizeof(SYSFS_FILE) + 20];
+ const char *node;
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+ struct ubi_mkvol_request req;
+ char tmp[100];
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto out_libubi;
+ }
+
+ /* Create a small dynamic volume */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = dev_info.min_io_size;
+ req.bytes = dev_info.eb_size;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = "rmvol";
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ perror("ubi_mkvol");
+ goto out_libubi;
+ }
+
+ /* Open volume-related sysfs file */
+ sprintf(fname, SYSFS_FILE, dev_info.dev_num, req.vol_id);
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ failed("open");
+ perror("open");
+ goto out_rmvol;
+ }
+
+ /* Remove the volume, but do not close the file */
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ perror("ubi_rmvol");
+ goto out_close;
+ }
+
+ /* Try to read from the file, this should fail */
+ ret = read(fd, tmp, 100);
+ if (ret != -1) {
+ failed("read");
+ err_msg("read returned %d, expected -1", ret);
+ goto out_close;
+ }
+
+ /* Close the file and try to open it again, should fail */
+ close(fd);
+ fd = open(fname, O_RDONLY);
+ if (fd != -1) {
+ failed("open");
+ err_msg("opened %s again, open returned %d, expected -1",
+ fname, fd);
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_rmvol:
+ ubi_rmvol(libubi, node, req.vol_id);
+out_libubi:
+ libubi_close(libubi);
+ return 1;
+
+out_close:
+ close(fd);
+ libubi_close(libubi);
+ return 1;
+}
+
+#if 0
+/**
+ * mkvol_alignment - create volumes with different alignments.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_alignment(void)
+{
+ struct ubi_mkvol_request req;
+ int i, vol_id, ebsz;
+ const char *name = TESTNAME ":mkvol_alignment()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+
+ /* Alignment should actually be multiple of min. I/O size */
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ /* Bear in mind alignment reduces EB size */
+ ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = dev_info.avail_ebs * ebsz;
+
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ err_msg("alignment %d", req.alignment);
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_basic - simple test that checks basic volume creation capability.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_basic(void)
+{
+ struct ubi_mkvol_request req;
+ struct ubi_vol_info vol_info;
+ int vol_id, ret;
+ const char *name = TESTNAME ":mkvol_basic()";
+
+ /* Create dynamic volume of maximum size */
+ 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;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Create static volume of maximum size */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_STATIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", vol_id);
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_multiple - test multiple volumes creation
+ *
+ * Thus function returns %0 if the test passed and %-1 if not.
+ */
+static int mkvol_multiple(void)
+{
+ struct ubi_mkvol_request req;
+ int i, ret, max = dev_info.max_vol_count;
+ const char *name = TESTNAME ":mkvol_multiple()";
+
+ /* Create maximum number of volumes */
+ for (i = 0; i < max; i++) {
+ char nm[strlen(name) + 50];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = 1;
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ sprintf(&nm[0], "%s:%d", name, i);
+ req.name = &nm[0];
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ if (errno == ENFILE) {
+ max = i;
+ break;
+ }
+ failed("ubi_mkvol");
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req)) {
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < max; i++) {
+ struct ubi_vol_info vol_info;
+
+ if (ubi_rmvol(libubi, node, i)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", i);
+ goto remove;
+ }
+ }
+
+ return 0;
+
+remove:
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+ return -1;
+}
+#endif
diff --git a/tests/ubi-tests/rsvol.c b/tests/ubi-tests/rsvol.c
new file mode 100644
index 0000000..7a9e5ea
--- /dev/null
+++ b/tests/ubi-tests/rsvol.c
@@ -0,0 +1,310 @@
+/*
+ * 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 B. Bityutskiy
+ *
+ * Tes UBI volume re-size.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "rsvol"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_basic(int type);
+static int test_rsvol(int type);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_basic(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_basic(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_rsvol(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_rsvol(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_basic - check volume re-size capability.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_basic(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_basic()";
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = MIN_AVAIL_EBS * dev_info.eb_size;
+ req.vol_type = type;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ req.bytes = dev_info.eb_size;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ req.bytes = (MIN_AVAIL_EBS + 1) * dev_info.eb_size;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ req.bytes -= 1;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+static int test_rsvol1(struct ubi_vol_info *vol_info);
+
+/**
+ * test_rsvol - test UBI volume re-size.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_rsvol(int type)
+{
+ const char *name = TESTNAME "test_rsvol:()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_mkvol_request req;
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int eb_size;
+ struct ubi_vol_info vol_info;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * eb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_rsvol1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+/*
+ * Helper function for test_rsvol().
+ */
+static int test_rsvol1(struct ubi_vol_info *vol_info)
+{
+ long long bytes;
+ struct ubi_vol_info vol_info1;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ unsigned char buf[vol_info->rsvd_bytes];
+ int fd, i, ret;
+
+ /* Make the volume smaller and check basic volume I/O */
+ bytes = vol_info->rsvd_bytes - vol_info->eb_size;
+ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes - 1)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ if (ubi_get_vol_info1(libubi, vol_info->dev_num, vol_info->vol_id,
+ &vol_info1)) {
+ failed("ubi_get_vol_info");
+ return -1;
+ }
+
+ if (vol_info1.rsvd_bytes != bytes) {
+ err_msg("rsvd_bytes %lld, must be %lld",
+ vol_info1.rsvd_bytes, bytes);
+ return -1;
+ }
+
+ if (vol_info1.rsvd_ebs != vol_info->rsvd_ebs - 1) {
+ err_msg("rsvd_ebs %d, must be %d",
+ vol_info1.rsvd_ebs, vol_info->rsvd_ebs - 1);
+ return -1;
+ }
+
+ /* Write data to the volume */
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", vol_node);
+ return -1;
+ }
+
+ bytes = vol_info->rsvd_bytes - vol_info->eb_size - 1;
+ if (ubi_update_start(libubi, fd, bytes)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ for (i = 0; i < bytes; i++)
+ buf[i] = (unsigned char)i;
+
+ ret = write(fd, &buf[0], bytes);
+ if (ret != bytes) {
+ failed("write");
+ goto close;
+ }
+
+ close(fd);
+
+ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ if (ubi_rsvol(libubi, node, vol_info->vol_id,
+ vol_info->eb_size * dev_info.avail_ebs)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", vol_node);
+ return -1;
+ }
+
+ /* Read data back */
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ failed("seek");
+ goto close;
+ }
+ memset(&buf[0], 0, bytes);
+ ret = read(fd, &buf[0], bytes);
+ if (ret != bytes) {
+ failed("read");
+ goto close;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ if (buf[i] != (unsigned char)i) {
+ err_msg("bad data");
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
diff --git a/tests/ubi-tests/runtests.sh b/tests/ubi-tests/runtests.sh
new file mode 100755
index 0000000..7072e03
--- /dev/null
+++ b/tests/ubi-tests/runtests.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+ubidev="$1"
+tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update
+io_paral rmvol"
+
+if test -z "$ubidev";
+then
+ echo "Usage:"
+ echo "$0 <UBI device> <ubi module load command>"
+ exit 1
+fi
+
+ubiname=`echo $ubidev | cut -d/ -f3`
+
+major=`cat /sys/class/ubi/$ubiname/dev | cut -d: -f1`
+
+for minor in `seq 0 4`; do
+ if test ! -e ${ubidev}_${minor} ;
+ then
+ mknod ${ubidev}_${minor} c $major $(($minor + 1))
+ fi
+done
+
+if ! test -c "$ubidev";
+then
+ echo "Error: $ubidev is not character device"
+ exit 1
+fi
+
+for t in `echo $tests`;
+do
+ echo "Running $t $ubidev"
+ "./$t" "$ubidev" || exit 1
+done
+
+echo SUCCESS
+
+exit 0