diff mbox

[2/7] qemu-img: add iflag and oflag options to dd

Message ID 20160822075517.5859-3-fullmanet@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Reda Sallahi Aug. 22, 2016, 7:55 a.m. UTC
This adds the iflag and oflag options which defines the list of flags used
for reading and writing respectively. The list is comma-separated.

The iflag option supports direct, dsync, sync, count_bytes and skip_bytes
and oflag supports direct, dsync, sync and seek_bytes. They are similar to
their counterparts on GNU dd(1).

Two tests were added to test iflag and oflag.

Signed-off-by: Reda Sallahi <fullmanet@gmail.com>
---
 qemu-img-cmds.hx           |   4 +-
 qemu-img.c                 | 185 ++++++++++++++++++++++++++++++++++++++++-----
 qemu-img.texi              |  32 +++++++-
 tests/qemu-iotests/163     | 103 +++++++++++++++++++++++++
 tests/qemu-iotests/163.out | 135 +++++++++++++++++++++++++++++++++
 tests/qemu-iotests/164     | 100 ++++++++++++++++++++++++
 tests/qemu-iotests/164.out |  75 ++++++++++++++++++
 tests/qemu-iotests/group   |   2 +
 8 files changed, 609 insertions(+), 27 deletions(-)
 create mode 100755 tests/qemu-iotests/163
 create mode 100644 tests/qemu-iotests/163.out
 create mode 100755 tests/qemu-iotests/164
 create mode 100644 tests/qemu-iotests/164.out

Comments

Stefan Hajnoczi Aug. 22, 2016, 1:26 p.m. UTC | #1
On Mon, Aug 22, 2016 at 09:55:12AM +0200, Reda Sallahi wrote:
> +static int img_dd_oflag(const char *arg,
> +                        struct DdIo *in, struct DdIo *out,
> +                        struct DdInfo *dd)
> +{
> +    const char *tok;
> +    char *str, *tmp;
> +    int ret = 0;
> +    const struct DdSymbols flags[] = {
> +        { "direct", C_DIRECT },
> +        { "dsync", C_DSYNC },
> +        { "sync", C_IOFLAG_SYNC },
> +        { "seek_bytes", C_SEEK_BYTES },
> +        { NULL, 0 }
> +    };
> +
> +    tmp = str = g_strdup(arg);
> +
> +    while (tmp != NULL && !ret) {
> +        tok = qemu_strsep(&tmp, ",");
> +        int j;
> +        for (j = 0; flags[j].name != NULL; j++) {
> +            if (!strcmp(tok, flags[j].name)) {
> +                out->flags |= flags[j].value;
> +                break;
> +            }
> +        }
> +        if (flags[j].name == NULL) {
> +            error_report("invalid output flag: '%s'", tok);
> +            ret = 1;
> +        }
> +    }
> +
> +    g_free(str);
> +    return ret;
> +}

img_dd_iflag()/img_dd_oflag() are duplicated code.  I suggest a single
helper function that parses a DdSymbols array into a DdIo->flags field.
Then you can pass in either the iflag or the oflag DdSymbols arrays.

static int img_dd_oflag(const char *arg,
                        struct DdIo *in, struct DdIo *out,
                        struct DdInfo *dd)
{
    return img_dd_xflag(arg, (DdSymbols[]){
    	{ "direct", C_DIRECT },
	{ "dsync", C_DSYNC },
	...
    }, in);
}

static int img_dd_oflag(const char *arg,
                        struct DdIo *in, struct DdIo *out,
                        struct DdInfo *dd)
{
    return img_dd_xflag(arg, (DdSymbols[]){
    	{ "direct", C_DIRECT },
	{ "dsync", C_DSYNC },
	...
    }, out);
}
diff mbox

Patch

diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index e79a577..25eaf71 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -46,9 +46,9 @@  STEXI
 ETEXI
 
 DEF("dd", img_dd,
-    "dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] [seek=blocks] [conv=notrunc] if=input of=output")
+    "dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] [seek=blocks] [conv=notrunc] [iflag=flags] [oflag=flags] if=input of=output")
 STEXI
-    @item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [seek=@var{blocks}] [conv=notrunc] if=@var{input} of=@var{output}
+    @item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [seek=@var{blocks}] [conv=notrunc] [iflag=@var{flags}] [oflag=@var{flags}] if=@var{input} of=@var{output}
 ETEXI
 
 DEF("info", img_info,
diff --git a/qemu-img.c b/qemu-img.c
index a1c0381..710c5a2 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -176,7 +176,16 @@  static void QEMU_NORETURN help(void)
            "  'of=FILE' write to FILE\n"
            "  'skip=N' skip N bs-sized blocks at the start of input\n"
            "  'seek=N' seek N bs-sized blocks at the start of output\n"
-           "  'conv=notrunc' do not truncate the output file\n";
+           "  'conv=notrunc' do not truncate the output file\n"
+           "  'iflags=FLAGS' read using the comma-separated flags list\n"
+           "  'oflags=FLAGS' read using the comma-separated flags list\n\n"
+           "List of FLAGS for dd:\n"
+           "  'direct'      use direct I/O for data\n"
+           "  'dsync'       use synchronized I/O for data\n"
+           "  'sync'        use synchronized I/O for data\n"
+           "  'count_bytes' use 'count=N' as a byte count (iflag only)\n"
+           "  'skip_bytes'  use 'skip=N' as a byte count (iflag only)\n"
+           "  'seek_bytes'  use 'seek=N' as a byte count (oflag only)\n";
 
     printf("%s\nSupported formats:", help_msg);
     bdrv_iterate_format(format_print, NULL);
@@ -3811,6 +3820,8 @@  out:
 #define C_SKIP    020
 #define C_SEEK    040
 #define C_CONV    0100
+#define C_IFLAG   0200
+#define C_OFLAG   0400
 
 struct DdInfo {
     unsigned int flags;
@@ -3823,6 +3834,7 @@  struct DdIo {
     char *filename;
     uint8_t *buf;
     int64_t offset;
+    unsigned int flags;
 };
 
 struct DdOpts {
@@ -3831,6 +3843,11 @@  struct DdOpts {
     unsigned int flag;
 };
 
+struct DdSymbols {
+    const char *name;
+    unsigned int value;
+};
+
 static int img_dd_bs(const char *arg,
                      struct DdIo *in, struct DdIo *out,
                      struct DdInfo *dd)
@@ -3930,6 +3947,86 @@  static int img_dd_conv(const char *arg,
     }
 }
 
+#define C_DIRECT      01
+#define C_IOFLAG_SYNC 02
+#define C_DSYNC       04
+#define C_COUNT_BYTES 010
+#define C_SKIP_BYTES  020
+#define C_SEEK_BYTES  040
+
+static int img_dd_iflag(const char *arg,
+                        struct DdIo *in, struct DdIo *out,
+                        struct DdInfo *dd)
+{
+    const char *tok;
+    char *str, *tmp;
+    int ret = 0;
+    const struct DdSymbols flags[] = {
+        { "direct", C_DIRECT },
+        { "dsync", C_DSYNC },
+        { "sync", C_IOFLAG_SYNC },
+        { "count_bytes", C_COUNT_BYTES },
+        { "skip_bytes", C_SKIP_BYTES },
+        { NULL, 0}
+    };
+
+    tmp = str = g_strdup(arg);
+
+    while (tmp != NULL && !ret) {
+        tok = qemu_strsep(&tmp, ",");
+        int j;
+        for (j = 0; flags[j].name != NULL; j++) {
+            if (!strcmp(tok, flags[j].name)) {
+                in->flags |= flags[j].value;
+                break;
+            }
+        }
+        if (flags[j].name == NULL) {
+            error_report("invalid input flag: '%s'", tok);
+            ret = 1;
+        }
+    }
+
+    g_free(str);
+    return ret;
+}
+
+static int img_dd_oflag(const char *arg,
+                        struct DdIo *in, struct DdIo *out,
+                        struct DdInfo *dd)
+{
+    const char *tok;
+    char *str, *tmp;
+    int ret = 0;
+    const struct DdSymbols flags[] = {
+        { "direct", C_DIRECT },
+        { "dsync", C_DSYNC },
+        { "sync", C_IOFLAG_SYNC },
+        { "seek_bytes", C_SEEK_BYTES },
+        { NULL, 0 }
+    };
+
+    tmp = str = g_strdup(arg);
+
+    while (tmp != NULL && !ret) {
+        tok = qemu_strsep(&tmp, ",");
+        int j;
+        for (j = 0; flags[j].name != NULL; j++) {
+            if (!strcmp(tok, flags[j].name)) {
+                out->flags |= flags[j].value;
+                break;
+            }
+        }
+        if (flags[j].name == NULL) {
+            error_report("invalid output flag: '%s'", tok);
+            ret = 1;
+        }
+    }
+
+    g_free(str);
+    return ret;
+}
+
 static int img_dd(int argc, char **argv)
 {
     int ret = 0;
@@ -3947,6 +4044,9 @@  static int img_dd(int argc, char **argv)
     const char *fmt = NULL;
     int64_t size = 0, out_size = 0;
     int64_t block_count = 0, out_pos, in_pos;
+    bool writethrough = false;
+    int flags = 0;
+    int ibsz = 0, obsz = 0;
     struct DdInfo dd = {
         .flags = 0,
         .count = 0,
@@ -3956,13 +4056,15 @@  static int img_dd(int argc, char **argv)
         .bsz = 512, /* Block size is by default 512 bytes */
         .filename = NULL,
         .buf = NULL,
-        .offset = 0
+        .offset = 0,
+        .flags = 0
     };
     struct DdIo out = {
         .bsz = 512,
         .filename = NULL,
         .buf = NULL,
-        .offset = 0
+        .offset = 0,
+        .flags = 0
     };
 
     const struct DdOpts options[] = {
@@ -3973,6 +4075,8 @@  static int img_dd(int argc, char **argv)
         { "skip", img_dd_skip, C_SKIP },
         { "seek", img_dd_seek, C_SEEK },
         { "conv", img_dd_conv, C_CONV },
+        { "iflag", img_dd_iflag, C_IFLAG },
+        { "oflag", img_dd_oflag, C_OFLAG },
         { NULL, NULL, 0 }
     };
     const struct option long_options[] = {
@@ -4038,8 +4142,13 @@  static int img_dd(int argc, char **argv)
         arg = NULL;
     }
 
+    obsz = out.bsz;
+    if (out.flags & C_SEEK_BYTES) {
+        obsz = 1;
+    }
+
     /* Overflow check for seek */
-    if (out.offset > INT64_MAX / out.bsz) {
+    if (out.offset > INT64_MAX / obsz) {
         error_report("seek with the block size specified is too large "
                      "for data type used");
         ret = -1;
@@ -4051,29 +4160,50 @@  static int img_dd(int argc, char **argv)
         ret = -1;
         goto out;
     }
-    blk1 = img_open(image_opts, in.filename, fmt, 0, false, false);
+    /* These flags make sense only for output but we're adding them anyway
+       to have something close to GNU dd(1) */
+    if (in.flags & C_DSYNC || in.flags & C_IOFLAG_SYNC) {
+        writethrough = true;
+    }
+    if (in.flags & C_DIRECT) {
+        flags |= BDRV_O_NOCACHE;
+    }
+
+    blk1 = img_open(image_opts, in.filename, fmt, flags, writethrough, false);
 
     if (!blk1) {
         ret = -1;
         goto out;
     }
+    writethrough = false; /* Reset to the default value */
 
     size = blk_getlength(blk1);
+
     if (size < 0) {
         error_report("Failed to get size for '%s'", in.filename);
         ret = -1;
         goto out;
     }
 
-    if (dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz &&
-        dd.count * in.bsz < size) {
-        size = dd.count * in.bsz;
+    ibsz = in.bsz;
+
+    if (in.flags & C_COUNT_BYTES) {
+        ibsz = 1;
+    }
+    if (dd.flags & C_COUNT && dd.count <= INT64_MAX / ibsz &&
+        dd.count * ibsz < size) {
+        size = dd.count * ibsz;
+    }
+
+    ibsz = in.bsz; /* Reset ibsz for the skip option */
+    if (in.flags & C_SKIP_BYTES) {
+        ibsz = 1;
     }
     /* Overflow means the specified offset is beyond input image's size */
-    if (in.offset > INT64_MAX / in.bsz || size < in.offset * in.bsz) {
-        out_size = out.offset * out.bsz;
+    if (in.offset > INT64_MAX / ibsz || size < in.offset * ibsz) {
+        out_size = out.offset * obsz;
     } else {
-        out_size = size - in.offset * in.bsz + out.offset * out.bsz;
+        out_size = size - in.offset * ibsz + out.offset * obsz;
     }
 
     if (image_opts) {
@@ -4089,8 +4219,17 @@  static int img_dd(int argc, char **argv)
         qdict_put(qoptions, "driver", qstring_from_str(out_fmt));
     }
 
+    flags = BDRV_O_RDWR;
+
+    if (out.flags & C_DSYNC || out.flags & C_IOFLAG_SYNC) {
+        writethrough = true;
+    }
+    if (out.flags & C_DIRECT) {
+        flags |= BDRV_O_NOCACHE;
+    }
+
     blk2 = blk_new_open(image_opts ? NULL : out.filename,
-                        NULL, qoptions, BDRV_O_RDWR, NULL);
+                        NULL, qoptions, flags, NULL);
 
     if (!blk2) {
         drv = bdrv_find_format(out_fmt);
@@ -4133,8 +4272,10 @@  static int img_dd(int argc, char **argv)
             ret = -1;
             goto out;
         }
-        blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR,
-                        false, false);
+
+        blk2 = img_open(image_opts, out.filename, out_fmt, flags,
+                        writethrough, false);
+
         if (!blk2) {
             ret = -1;
             goto out;
@@ -4142,6 +4283,8 @@  static int img_dd(int argc, char **argv)
     } else {
         int64_t blk2sz = 0;
 
+        blk_set_enable_write_cache(blk2, !writethrough);
+
         if (!(dd.conv & C_NOTRUNC)) {
             /* We make conv=notrunc mandatory for the moment to avoid
                accidental destruction of the output image. Needs to be
@@ -4158,29 +4301,29 @@  static int img_dd(int argc, char **argv)
             goto out;
         }
 
-        if (in.offset > INT64_MAX / in.bsz || size < in.offset * in.bsz) {
-            if (blk2sz < out.offset * out.bsz) {
-                blk_truncate(blk2, out.offset * out.bsz);
+        if (in.offset > INT64_MAX / ibsz || size < in.offset * ibsz) {
+            if (blk2sz < out.offset * obsz) {
+                blk_truncate(blk2, out.offset * obsz);
             }
         } else if (blk2sz < out_size) {
             blk_truncate(blk2, out_size);
         }
     }
 
-    if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
-                              size < in.offset * in.bsz)) {
+    if (dd.flags & C_SKIP && (in.offset > INT64_MAX / ibsz ||
+                              size < in.offset * ibsz)) {
         /* We give a warning if the skip option is bigger than the input
          * size and create an empty output disk image (i.e. like dd(1)).
          */
         error_report("%s: cannot skip to specified offset", in.filename);
         in_pos = size;
     } else {
-        in_pos = in.offset * in.bsz;
+        in_pos = in.offset * ibsz;
     }
 
     in.buf = g_new(uint8_t, in.bsz);
 
-    for (out_pos = out.offset * out.bsz; in_pos < size; block_count++) {
+    for (out_pos = out.offset * obsz; in_pos < size; block_count++) {
         int in_ret, out_ret;
 
         if (in_pos + in.bsz > size) {
diff --git a/qemu-img.texi b/qemu-img.texi
index 95f603b..c663cf4 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -146,18 +146,42 @@  Parameters to dd subcommand:
 @item bs=@var{block_size}
 defines the block size
 @item count=@var{blocks}
-sets the number of input blocks to copy
+sets the number of input blocks to copy. In case 'iflags=count_bytes' is
+specified, 'blocks' is interpreted as a byte count instead of a block count.
 @item if=@var{input}
 sets the input file
 @item of=@var{output}
 sets the output file. dd truncates the output file to zero if 'conv=notrunc'
 is not specified.
 @item skip=@var{blocks}
-sets the number of input blocks to skip
+sets the number of input blocks to skip. In case 'iflags=skip_bytes' is
+specified, 'blocks' is interpreted as a byte count instead of a block count.
 @item seek=@var{blocks}
-sets the number of output blocks to skip
+sets the number of output blocks to skip. In case 'oflags=seek_bytes' is
+specified, 'blocks' is interpreted as a byte count instead of a block count.
 @item conv=notrunc
 makes dd not truncate output file to zero
+@item iflag=@var{flags}
+defines the flags used to read the input file. The flag list is seprated using
+commas.
+@item oflag=@var{flags}
+defines the flags used to write the output file. The flag list is seprated
+using commas.
+
+The flag list:
+@item direct
+direct I/O for data.
+@item dsync
+synchronised I/O for data.
+@item sync
+synchronised I/O for data.
+@item count_bytes
+interpret 'count=blocks' as a byte count. Only iflag.
+@item skip_bytes
+interpret 'skip=blocks' as a byte count. Only iflag.
+@item seek_bytes
+interpret 'seek=blocks' as a byte count. Only oflag.
+
 @end table
 
 Command description:
@@ -331,7 +355,7 @@  skipped. This is useful for formats such as @code{rbd} if the target
 volume has already been created with site specific options that cannot
 be supplied through qemu-img.
 
-@item dd [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [seek=@var{blocks}] [conv=notrunc] if=@var{input} of=@var{output}
+@item dd [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [seek=@var{blocks}] [conv=notrunc] [iflag=@var{flags}] [oflag=@var{flags}] if=@var{input} of=@var{output}
 
 Dd copies from @var{input} file to @var{output} file converting it from
 @var{fmt} format to @var{output_fmt} format.
diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163
new file mode 100755
index 0000000..e236ea4
--- /dev/null
+++ b/tests/qemu-iotests/163
@@ -0,0 +1,103 @@ 
+#! /bin/bash
+#
+# qemu-img dd test for the iflag option
+#
+# Copyright (C) 2016 Reda Sallahi
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+
+owner=fullmanet@gmail.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+
+status=1
+
+_cleanup()
+{
+    _cleanup_test_img
+    rm -f "$TEST_IMG.out" "$TEST_IMG.out.dd"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+. ./common.rc
+. ./common.filter
+. ./common.pattern
+
+_supported_fmt raw
+_supported_proto file
+_supported_os Linux
+
+TEST_SKIP_BLOCKS="1 9 37 90K 12M"
+
+for skip in $TEST_SKIP_BLOCKS; do
+    echo
+    echo "== Creating image =="
+
+    size=1M
+    _make_test_img $size
+    _check_test_img
+
+    $QEMU_IO -c "write -P 0xa 142k 512k" "$TEST_IMG" | _filter_qemu_io
+
+    echo
+    echo "== Converting the image with dd with skip=$skip iflag=skip_bytes =="
+
+    $QEMU_IMG dd if="$TEST_IMG" of="$TEST_IMG.out" skip="$skip" \
+        iflag=skip_bytes conv=notrunc -O "$IMGFMT" 2> /dev/null
+    TEST_IMG="$TEST_IMG.out" _check_test_img
+    dd if="$TEST_IMG" of="$TEST_IMG.out.dd" skip="$skip" iflag=skip_bytes \
+        conv=notrunc status=none
+
+    echo
+    echo "== Compare the images with qemu-img compare =="
+
+    $QEMU_IMG compare "$TEST_IMG.out.dd" "$TEST_IMG.out"
+done
+
+TEST_COUNT_BLOCKS="2 19 75 24K 12M 143G"
+
+for count in $TEST_COUNT_BLOCKS; do
+    echo
+    echo "== Creating image =="
+
+    size=1M
+    _make_test_img $size
+    _check_test_img
+
+    $QEMU_IO -c "write -P 0xa 342k 512k" "$TEST_IMG" | _filter_qemu_io
+
+    echo
+    echo "== Converting the image with dd with count=$count" \
+	 "iflag=count_bytes =="
+
+    $QEMU_IMG dd if="$TEST_IMG" of="$TEST_IMG.out" count="$skip" \
+        iflag=count_bytes conv=notrunc -O "$IMGFMT" 2> /dev/null
+    TEST_IMG="$TEST_IMG.out" _check_test_img
+    dd if="$TEST_IMG" of="$TEST_IMG.out.dd" count="$skip" iflag=count_bytes \
+        conv=notrunc status=none
+
+    echo
+    echo "== Compare the images with qemu-img compare =="
+
+    $QEMU_IMG compare "$TEST_IMG.out.dd" "$TEST_IMG.out"
+done
+
+echo
+echo "*** done"
+rm -f "$seq.full"
+status=0
diff --git a/tests/qemu-iotests/163.out b/tests/qemu-iotests/163.out
new file mode 100644
index 0000000..e1034cd
--- /dev/null
+++ b/tests/qemu-iotests/163.out
@@ -0,0 +1,135 @@ 
+QA output created by 163
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 145408
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with skip=1 iflag=skip_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 145408
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with skip=9 iflag=skip_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 145408
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with skip=37 iflag=skip_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 145408
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with skip=90K iflag=skip_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 145408
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with skip=12M iflag=skip_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 350208
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with count=2 iflag=count_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 350208
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with count=19 iflag=count_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 350208
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with count=75 iflag=count_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 350208
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with count=24K iflag=count_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 350208
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with count=12M iflag=count_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 350208
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with count=143G iflag=count_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+*** done
diff --git a/tests/qemu-iotests/164 b/tests/qemu-iotests/164
new file mode 100755
index 0000000..4cc69c2
--- /dev/null
+++ b/tests/qemu-iotests/164
@@ -0,0 +1,100 @@ 
+#! /bin/bash
+#
+# qemu-img dd test for the oflag option
+#
+# Copyright (C) 2016 Reda Sallahi
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+
+owner=fullmanet@gmail.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+status=1
+
+_cleanup()
+{
+    _cleanup_test_img
+    rm -f "$TEST_IMG.out" "$TEST_IMG.out.dd"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+. ./common.rc
+. ./common.filter
+. ./common.pattern
+
+_supported_fmt raw
+_supported_proto file
+_supported_os Linux
+
+TEST_SEEK_BLOCKS="1 3 15 10K 20M"
+
+for seek in $TEST_SEEK_BLOCKS; do
+    echo
+    echo "== Creating image =="
+
+    size=1M
+    _make_test_img $size
+    _check_test_img
+
+    $QEMU_IO -c "write -P 0xa 235k 512k" "$TEST_IMG" | _filter_qemu_io
+
+    echo
+    echo "== Converting the image with dd with seek=$seek oflag=seek_bytes =="
+
+    $QEMU_IMG dd if="$TEST_IMG" of="$TEST_IMG.out" seek="$seek" \
+        oflag=seek_bytes conv=notrunc -O "$IMGFMT" 2> /dev/null
+    TEST_IMG="$TEST_IMG.out" _check_test_img
+    dd if="$TEST_IMG" of="$TEST_IMG.out.dd" seek="$seek" \
+        oflag=seek_bytes status=none
+
+    echo
+    echo "== Compare the images with qemu-img compare =="
+
+    $QEMU_IMG compare "$TEST_IMG.out.dd" "$TEST_IMG.out"
+done
+
+echo
+echo "== Creating image =="
+
+size=1M
+_make_test_img $size
+_check_test_img
+seek=13K
+
+$QEMU_IO -c "write -P 0xa 235k 512k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "== Converting the image with dd with seek=$seek" \
+     "oflag=seek_bytes,direct =="
+
+$QEMU_IMG dd if="$TEST_IMG" of="$TEST_IMG.out" seek="$seek" \
+    oflag=seek_bytes conv=notrunc -O "$IMGFMT" 2> /dev/null
+TEST_IMG="$TEST_IMG.out" _check_test_img
+dd if="$TEST_IMG" of="$TEST_IMG.out.dd" seek="$seek" \
+    oflag=seek_bytes,direct conv=notrunc status=none
+
+echo
+echo "== Compare the images with qemu-img compare =="
+
+$QEMU_IMG compare "$TEST_IMG.out.dd" "$TEST_IMG.out"
+
+
+echo
+echo "*** done"
+rm -f "$seq.full"
+status=0
diff --git a/tests/qemu-iotests/164.out b/tests/qemu-iotests/164.out
new file mode 100644
index 0000000..3951a58
--- /dev/null
+++ b/tests/qemu-iotests/164.out
@@ -0,0 +1,75 @@ 
+QA output created by 164
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 240640
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with seek=1 oflag=seek_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 240640
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with seek=3 oflag=seek_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 240640
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with seek=15 oflag=seek_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 240640
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with seek=10K oflag=seek_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 240640
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with seek=20M oflag=seek_bytes ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+== Creating image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
+No errors were found on the image.
+wrote 524288/524288 bytes at offset 240640
+512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Converting the image with dd with seek=13K oflag=seek_bytes,direct ==
+No errors were found on the image.
+
+== Compare the images with qemu-img compare ==
+Images are identical.
+
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 28833ee..5ba1372 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -162,3 +162,5 @@ 
 160 rw auto quick
 161 rw auto quick
 162 auto quick
+163 rw auto quick
+164 rw auto quick