]> www.infradead.org Git - mtd-utils.git/commitdiff
UBI-Utils: Add Artem's test programs
authorAdrian Hunter <ext-adrian.hunter@nokia.com>
Wed, 21 Mar 2007 09:55:30 +0000 (11:55 +0200)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Thu, 29 Mar 2007 14:17:36 +0000 (17:17 +0300)
Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
14 files changed:
ubi-utils/tests/Makefile [new file with mode: 0644]
ubi-utils/tests/common.c [new file with mode: 0644]
ubi-utils/tests/common.h [new file with mode: 0644]
ubi-utils/tests/integ.c [new file with mode: 0644]
ubi-utils/tests/io_basic.c [new file with mode: 0644]
ubi-utils/tests/io_paral.c [new file with mode: 0644]
ubi-utils/tests/io_read.c [new file with mode: 0644]
ubi-utils/tests/io_update.c [new file with mode: 0644]
ubi-utils/tests/mkvol_bad.c [new file with mode: 0644]
ubi-utils/tests/mkvol_basic.c [new file with mode: 0644]
ubi-utils/tests/mkvol_paral.c [new file with mode: 0644]
ubi-utils/tests/rsvol.c [new file with mode: 0644]
ubi-utils/tests/runtests.pl [new file with mode: 0755]
ubi-utils/tests/runtests.sh [new file with mode: 0755]

diff --git a/ubi-utils/tests/Makefile b/ubi-utils/tests/Makefile
new file mode 100644 (file)
index 0000000..1b17f81
--- /dev/null
@@ -0,0 +1,40 @@
+
+INCLUDE1=../inc
+INCLUDE2=../../include
+LIB=.
+
+CC := $(CROSS)gcc
+
+ALL_FILES=libubi.a io_update
+ALL_FILES+=io_paral io_read io_basic mkvol_basic mkvol_bad mkvol_paral rsvol
+ALL_FILES+=integ
+
+CFLAGS += -Wall -I$(INCLUDE1) -I$(INCLUDE2) -L$(LIB) -ggdb
+
+all: $(ALL_FILES)
+
+libubi.a: ../src/libubi.c  ../inc/libubi.h  ../src/libubi_int.h
+       $(CC) $(CFLAGS) -c ../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 $@
+integ: integ.c
+       $(CC) $(CFLAGS) $^ -lubi -o $@
+
+clean:
+       rm -rf $(ALL_FILES) $(addsuffix .o, $(ALL_FILES))
diff --git a/ubi-utils/tests/common.c b/ubi-utils/tests/common.c
new file mode 100644 (file)
index 0000000..cb63e77
--- /dev/null
@@ -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/ubi-utils/tests/common.h b/ubi-utils/tests/common.h
new file mode 100644 (file)
index 0000000..3e8ada8
--- /dev/null
@@ -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/ubi-utils/tests/integ.c b/ubi-utils/tests/integ.c
new file mode 100644 (file)
index 0000000..4da7121
--- /dev/null
@@ -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/ubi-utils/tests/io_basic.c b/ubi-utils/tests/io_basic.c
new file mode 100644 (file)
index 0000000..2e8809a
--- /dev/null
@@ -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/ubi-utils/tests/io_paral.c b/ubi-utils/tests/io_paral.c
new file mode 100644 (file)
index 0000000..4857bf8
--- /dev/null
@@ -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  100
+
+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/ubi-utils/tests/io_read.c b/ubi-utils/tests/io_read.c
new file mode 100644 (file)
index 0000000..c5d1da7
--- /dev/null
@@ -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/ubi-utils/tests/io_update.c b/ubi-utils/tests/io_update.c
new file mode 100644 (file)
index 0000000..2e3422a
--- /dev/null
@@ -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/ubi-utils/tests/mkvol_bad.c b/ubi-utils/tests/mkvol_bad.c
new file mode 100644 (file)
index 0000000..58ac4e1
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * 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.avail_bytes;
+       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)) {
+                       failed("ubi_mkvol");
+                       err_msg("vol_id %d", i);
+                       goto remove;
+               }
+       }
+
+       req.vol_id = UBI_VOL_NUM_AUTO;
+       req.name = TESTNAME ":impossible";
+       ret = ubi_mkvol(libubi, node, &req);
+       if (check_failed(ret, ENOSPC, "ubi_mkvol", "volume %d created",
+                        req.vol_id))
+               goto remove;
+
+       req.vol_id = dev_info.max_vol_count;
+       ret = ubi_mkvol(libubi, node, &req);
+       if (check_failed(ret, EINVAL, "ubi_mkvol", "volume %d created",
+                        req.vol_id))
+               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/ubi-utils/tests/mkvol_basic.c b/ubi-utils/tests/mkvol_basic.c
new file mode 100644 (file)
index 0000000..2c6a512
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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;
+       const char *name = TESTNAME ":mkvol_multiple()";
+
+       /* Create maximum number of 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)) {
+                       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 < dev_info.max_vol_count; 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/ubi-utils/tests/mkvol_paral.c b/ubi-utils/tests/mkvol_paral.c
new file mode 100644 (file)
index 0000000..faf085c
--- /dev/null
@@ -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/ubi-utils/tests/rsvol.c b/ubi-utils/tests/rsvol.c
new file mode 100644 (file)
index 0000000..51fa069
--- /dev/null
@@ -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", 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", 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/ubi-utils/tests/runtests.pl b/ubi-utils/tests/runtests.pl
new file mode 100755 (executable)
index 0000000..8005716
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl -w
+
+sub usage;
+
+my @tests = ("mkvol_basic", "mkvol_bad", "mkvol_paral", "rsvol",
+            "io_basic", "io_read", "io_update", "io_paral");
+
+if (not defined @ARGV) {
+       usage();
+       exit;
+}
+
+foreach (@ARGV) {
+       -c or die "Error: $_ is not character device\n";
+}
+
+my $dev;
+foreach $dev (@ARGV) {
+       foreach (@tests) {
+               print "Running: $_ $dev";
+               system "./$_ $dev" and die;
+               print "\tSUCCESS\n";
+       }
+}
+
+sub usage
+{
+       print "Usage:\n";
+       print "$0 <UBI device 1> <UBI device 2> ...\n";
+}
diff --git a/ubi-utils/tests/runtests.sh b/ubi-utils/tests/runtests.sh
new file mode 100755 (executable)
index 0000000..f993bc0
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+ubidev="$1"
+ubiloadcmd="$2"
+tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update io_paral"
+
+if test -z "$ubidev" || test -z "$ubiloadcmd";
+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
+
+./integ "$ubiloadcmd" || exit 1
+
+echo SUCCESS
+
+exit 0