diff mbox

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

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

Commit Message

Reda Sallahi Aug. 26, 2016, 9:16 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                 | 172 +++++++++++++++++++++++++++++++++++++++------
 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, 595 insertions(+), 28 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 Sept. 13, 2016, 3:48 p.m. UTC | #1
On Fri, Aug 26, 2016 at 11:16:38AM +0200, Reda Sallahi wrote:
> 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                 | 172 +++++++++++++++++++++++++++++++++++++++------
>  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, 595 insertions(+), 28 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

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
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 7d313fd..7b2c525 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,73 @@  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_flag(const char *arg, struct DdIo *io,
+                       const struct DdSymbols *flags, const char *err_str)
+{
+    int ret = 0;
+    const char *tok;
+    char *str, *tmp;
+
+    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)) {
+                io->flags |= flags[j].value;
+                break;
+            }
+        }
+        if (flags[j].name == NULL) {
+            error_report("%s: '%s'", err_str, tok);
+            ret = 1;
+        }
+    }
+
+    g_free(str);
+
+    return ret;
+}
+
+static int img_dd_iflag(const char *arg,
+                        struct DdIo *in, struct DdIo *out,
+                        struct DdInfo *dd)
+{
+    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}
+    };
+
+    return img_dd_flag(arg, in, flags, "invalid input flag");
+}
+
+static int img_dd_oflag(const char *arg,
+                        struct DdIo *in, struct DdIo *out,
+                        struct DdInfo *dd)
+{
+    const struct DdSymbols flags[] = {
+        { "direct", C_DIRECT },
+        { "dsync", C_DSYNC },
+        { "sync", C_IOFLAG_SYNC },
+        { "seek_bytes", C_SEEK_BYTES },
+        { NULL, 0 }
+    };
+
+    return img_dd_flag(arg, out, flags, "invalid output flag");
+}
+
 static int img_dd(int argc, char **argv)
 {
     int ret = 0;
@@ -3947,6 +4031,9 @@  static int img_dd(int argc, char **argv)
     const char *out_filename;
     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 +4043,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 +4062,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 +4129,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 +4147,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;
     }
 
     out_filename = out.filename;
@@ -4083,6 +4200,15 @@  static int img_dd(int argc, char **argv)
         out_filename = qemu_opt_get(qopts, "filename");
     }
 
+    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;
+    }
+
     ret = access(out_filename, F_OK); /* Check if file exists */
 
     if (ret == -1) {
@@ -4127,8 +4253,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;
@@ -4136,8 +4264,8 @@  static int img_dd(int argc, char **argv)
     } else {
         int64_t blk2sz = 0;
 
-        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;
@@ -4159,29 +4287,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..994b124
--- /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