new file mode 100644
@@ -0,0 +1,392 @@
+/*
+ * QEMU block layer library test
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Wenchao Xia <xiawenc@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ * Limitation:
+ * 1 filename do not support relative path, to save trouble in creating
+ * backing files.
+ */
+
+#include <glib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+#include "libqblock.h"
+#include "libqtest.h"
+
+#define LIBQB_TEST_ENV_DIR "LIBQBLOCK_TEST_DIR"
+#define LIBQB_TEST_DEFAULT_DIR "/tmp"
+#define LIBQB_TEST_DEFAULT_FILENAME "libqblock_qcow2_test_img"
+
+typedef struct LibqbTestSettings {
+ const char *image_filename;
+ uint64_t image_size;
+ unsigned int num_backings;
+ unsigned int io_buf_size;
+ uint64_t io_offset;
+ int print_flag;
+} LibqbTestSettings;
+
+LibqbTestSettings libqb_test_settings;
+
+static void print_loc(const QBlockLocationInfo *loc)
+{
+ if (loc == NULL) {
+ printf("loc is NULL.");
+ return;
+ }
+ switch (loc->prot_type) {
+ case QB_PROTO_NONE:
+ printf("protocol type [none].");
+ break;
+ case QB_PROTO_FILE:
+ printf("protocol type [file], filename [%s].",
+ loc->o_file.filename);
+ break;
+ default:
+ printf("protocol type not supported.");
+ break;
+ }
+}
+
+static void print_info_image_static(QBlockStaticInfo *info)
+{
+ const uint64_t *virt_size = qb_get_virt_size(info);
+ const QBlockLocationInfo *backing_loc = qb_get_backing_loc(info);
+ g_assert(virt_size != NULL);
+
+ printf("=======image location:\n");
+ print_loc(&info->loc);
+ printf("\nvirtual_size %" PRId64 ", format type %d [%s]",
+ *(virt_size),
+ info->fmt.fmt_type, qb_fmttype2str(info->fmt.fmt_type));
+ printf("\nbacking image location:\n");
+ print_loc(backing_loc);
+ printf("\n");
+}
+
+static char *generate_backing_filename(const char *filename, int index)
+{
+ char *backing_filename = NULL;
+
+ backing_filename = g_strdup_printf("%s_backing_%d", filename, index);
+ return backing_filename;
+}
+
+/* get filename in a full path */
+static const char *get_filename(const char *path)
+{
+ const char *filename;
+ filename = strrchr(path, '/');
+ if (filename == NULL) {
+ filename = path;
+ } else {
+ filename++;
+ }
+ return filename;
+}
+
+/* create a chain of files, num_backings must >= 0. */
+static void files_create_qcow2(const char *filename,
+ int num_backings,
+ uint64_t virt_size)
+{
+ QBlockContext *context = NULL;
+ QBlockImage *qbi = NULL;
+ QBlockLocationInfo *loc_info = NULL;
+ QBlockFormatInfo *fmt_info = NULL;
+ int ret;
+ int index;
+ int flag;
+ char *backing_filename = NULL, *new_filename = NULL;
+ const char *relative_filename = NULL;
+
+ ret = qb_context_new(&context);
+ g_assert(ret == 0);
+
+ ret = qb_image_new(context, &qbi);
+ g_assert(ret == 0);
+
+ ret = qb_loc_info_new(context, &loc_info);
+ g_assert(ret == 0);
+
+ ret = qb_fmt_info_new(context, &fmt_info);
+ g_assert(ret == 0);
+
+ loc_info->prot_type = QB_PROTO_FILE;
+ fmt_info->fmt_type = QB_FMT_QCOW2;
+ fmt_info->virt_size = virt_size;
+ flag = 0;
+
+ index = 0;
+ while (index < num_backings) {
+ new_filename = generate_backing_filename(filename, index);
+ loc_info->o_file.filename = new_filename;
+ if (backing_filename != NULL) {
+ fmt_info->o_qcow2.backing_loc.prot_type = QB_PROTO_FILE;
+ relative_filename = get_filename(backing_filename);
+ fmt_info->o_qcow2.backing_loc.o_file.filename =
+ relative_filename;
+ }
+ ret = qb_create(context, qbi, loc_info, fmt_info, flag);
+ g_assert(ret == 0);
+ free(backing_filename);
+ backing_filename = new_filename;
+ new_filename = NULL;
+ index++;
+ }
+
+ loc_info->o_file.filename = filename;
+ if (backing_filename != NULL) {
+ fmt_info->o_qcow2.backing_loc.prot_type = QB_PROTO_FILE;
+ relative_filename = get_filename(backing_filename);
+ fmt_info->o_qcow2.backing_loc.o_file.filename =
+ relative_filename;
+ }
+ ret = qb_create(context, qbi, loc_info, fmt_info, flag);
+ g_assert(ret == 0);
+ free(backing_filename);
+
+ qb_fmt_info_delete(context, &fmt_info);
+ qb_loc_info_delete(context, &loc_info);
+ qb_image_unref(context, &qbi);
+ qb_context_delete(&context);
+ return;
+}
+
+static void files_delete(const char *filename,
+ int num_backings,
+ uint64_t virt_size)
+{
+ char *new_filename = NULL;
+ int index = 0;
+ while (index < num_backings) {
+ new_filename = generate_backing_filename(filename, index);
+ unlink(new_filename);
+ free(new_filename);
+ index++;
+ }
+ unlink(filename);
+}
+
+static void testcase_info_image_static(void)
+{
+ const char *filename = libqb_test_settings.image_filename;
+ uint64_t image_size = libqb_test_settings.image_size;
+ int num_backings = libqb_test_settings.num_backings;
+ QBlockContext *context = NULL;
+ QBlockImage *qbi = NULL;
+ QBlockLocationInfo *loc_info = NULL;
+ int ret, flag;
+ QBlockStaticInfo *info_st = NULL;
+ int i;
+ char *backing_filename = NULL;
+ const uint64_t *virt_size = NULL;
+ const QBlockLocationInfo *backing_loc = NULL;
+
+ ret = qb_context_new(&context);
+ g_assert(ret == 0);
+
+ ret = qb_image_new(context, &qbi);
+ g_assert(ret == 0);
+
+ ret = qb_loc_info_new(context, &loc_info);
+ g_assert(ret == 0);
+
+ loc_info->prot_type = QB_PROTO_FILE;
+ loc_info->o_file.filename = filename;
+ flag = LIBQBLOCK_O_NO_BACKING;
+ ret = qb_open(context, qbi, loc_info, NULL, flag);
+ g_assert(ret == 0);
+
+ i = num_backings - 1;
+ while (1) {
+ ret = qb_info_image_static_get(context, qbi, &info_st);
+ g_assert(ret == 0);
+ if (libqb_test_settings.print_flag > 0) {
+ print_info_image_static(info_st);
+ }
+ qb_close(context, qbi);
+
+ /* checking the information */
+ g_assert(info_st->loc.prot_type == loc_info->prot_type);
+ ret = strcmp(info_st->loc.o_file.filename,
+ loc_info->o_file.filename);
+
+ g_assert(ret == 0);
+ g_assert(info_st->fmt.fmt_type == QB_FMT_QCOW2);
+ virt_size = qb_get_virt_size(info_st);
+ g_assert(virt_size != NULL);
+ g_assert(*virt_size == image_size);
+ backing_loc = qb_get_backing_loc(info_st);
+ /* qcow2 have always backing file property */
+ g_assert(backing_loc != NULL);
+ if (i >= 0) {
+ /* it should have backing file */
+ g_assert(backing_loc->prot_type == QB_PROTO_FILE);
+ backing_filename = generate_backing_filename(filename, i);
+ ret = strcmp(backing_filename, backing_loc->o_file.filename);
+ g_assert(ret == 0);
+ g_free(backing_filename);
+ } else {
+ g_assert(backing_loc->prot_type == QB_FMT_NONE);
+ }
+ i--;
+ /* see if there is backing file need to check. */
+ if (backing_loc->prot_type == QB_FMT_NONE) {
+ g_assert(i < 0);
+ break;
+ }
+ qb_loc_info_delete(context, &loc_info);
+ loc_info = qb_loc_info_dup(backing_loc);
+ ret = qb_open(context, qbi, loc_info, NULL, flag);
+ g_assert(ret == 0);
+ qb_info_image_static_delete(context, &info_st);
+ }
+ qb_info_image_static_delete(context, &info_st);
+
+ qb_loc_info_delete(context, &loc_info);
+ qb_image_unref(context, &qbi);
+ qb_context_delete(&context);
+ return;
+}
+
+/* assuming the image is new created */
+static void testcase_sync_io(void)
+{
+ const char *filename = libqb_test_settings.image_filename;
+ uint64_t io_buf_size = libqb_test_settings.io_buf_size;
+ uint64_t io_offset = libqb_test_settings.io_offset;;
+ unsigned char *buf_r, *buf_w;
+ uint64_t i;
+ unsigned int rand_value;
+
+ buf_r = g_malloc0(io_buf_size);
+ buf_w = g_malloc0(io_buf_size);
+ for (i = 0; i < io_buf_size; i++) {
+ rand_value = g_test_rand_int_range(0, 255);
+ buf_w[i] = (unsigned char)rand_value;
+ }
+
+ QBlockContext *context = NULL;
+ QBlockImage *qbi = NULL;
+ QBlockLocationInfo *loc_info = NULL;
+ int ret, flag;
+
+ ret = qb_context_new(&context);
+ g_assert(ret == 0);
+
+ ret = qb_image_new(context, &qbi);
+ g_assert(ret == 0);
+
+ ret = qb_loc_info_new(context, &loc_info);
+ g_assert(ret == 0);
+
+
+ loc_info->prot_type = QB_PROTO_FILE;
+ loc_info->o_file.filename = filename;
+ flag = LIBQBLOCK_O_RDWR;
+ ret = qb_open(context, qbi, loc_info, NULL, flag);
+ g_assert(ret == 0);
+
+ int status;
+ int64_t len;
+ ret = qb_check_allocation(context, qbi, io_offset, io_buf_size,
+ &status, &len);
+ g_assert(ret == 0);
+ g_assert(status == 0);
+ g_assert(len == io_buf_size);
+
+ ret = qb_write(context, qbi, buf_w, io_buf_size, io_offset);
+ g_assert(ret == io_buf_size);
+
+ ret = qb_read(context, qbi, buf_r, io_buf_size, io_offset);
+ g_assert(ret == io_buf_size);
+
+ int cmp = memcmp(buf_r, buf_w, io_buf_size);
+ g_assert(cmp == 0);
+
+ ret = qb_check_allocation(context, qbi, io_offset, io_buf_size,
+ &status, &len);
+ g_assert(ret == 0);
+ g_assert(status == 1);
+ g_assert(len == io_buf_size);
+
+ qb_close(context, qbi);
+
+ qb_loc_info_delete(context, &loc_info);
+ qb_image_unref(context, &qbi);
+ qb_context_delete(&context);
+ g_free(buf_r);
+ g_free(buf_w);
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ const char *root_dir;
+ const char *name = LIBQB_TEST_DEFAULT_FILENAME;
+ int ret;
+ int dir_created = 0;
+ char *filename;
+
+ root_dir = getenv(LIBQB_TEST_ENV_DIR);
+ if (root_dir == NULL) {
+ root_dir = LIBQB_TEST_DEFAULT_DIR;
+ }
+
+ ret = access(root_dir, F_OK);
+ if (ret != 0) {
+ /* try mkdir */
+ ret = mkdir(root_dir, 0755);
+ g_assert(ret == 0);
+ dir_created = 1;
+ }
+
+ filename = g_strdup_printf("%s/%s", root_dir, name);
+
+ g_test_init(&argc, &argv, NULL);
+ g_test_rand_int();
+
+ libqb_test_settings.image_filename = filename;
+ libqb_test_settings.image_size =
+ 1024 * 1024 * g_test_rand_int_range(1, 1024);
+ libqb_test_settings.num_backings = g_test_rand_int_range(0, 10);
+ libqb_test_settings.io_buf_size = g_test_rand_int_range(1, 1024 * 1024);
+ libqb_test_settings.io_offset = g_test_rand_int_range(0,
+ libqb_test_settings.image_size - libqb_test_settings.io_buf_size);
+
+ files_create_qcow2(libqb_test_settings.image_filename,
+ libqb_test_settings.num_backings,
+ libqb_test_settings.image_size);
+
+ g_test_add_func("/libqblock/sync_info_image_static",
+ testcase_info_image_static);
+ g_test_add_func("/libqblock/sync_io", testcase_sync_io);
+
+ ret = g_test_run();
+ files_delete(libqb_test_settings.image_filename,
+ libqb_test_settings.num_backings,
+ libqb_test_settings.image_size);
+
+ g_free(filename);
+ if (dir_created) {
+ ret = rmdir(root_dir);
+ g_assert(ret == 0);
+ }
+ return ret;
+}
In this example, first it will create some qcow2 images, then try get information including backing file relationship, then it will do sync IO on the image. Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com> --- tests/check-libqblock-qcow2.c | 392 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 392 insertions(+), 0 deletions(-) create mode 100644 tests/check-libqblock-qcow2.c