In a previous post I compared dd, dc3dd and dcfldd, but only on the features. Two questions remained open: which one is faster, and which one is more reliable when the drive has bad sectors? To answer them I made a test. In this post you will find how I made it and the results.
See also these posts:
- Forensic imaging with dd, dc3dd and dcfldd
- dcfldd command examples: hash, hashlog, split and verify
- Drive acquisition with dc3dd
The test environment
- Linux VM with 2 vCPU (Intel Xeon 2.1 GHz) and 8 GB of RAM, Ubuntu 24.04
- GNU dd (coreutils) 9.4
- dc3dd 7.3.1 (Ubuntu package)
- dcfldd 1.9.1
- GNU ddrescue 1.27, only for the reliability test
Before each run I cleared the Linux cache, so every run reads the data from the source again:
sync; echo 1 > /proc/sys/vm/drop_caches
In the speed tests the output is /dev/null. In this way the test measures the read, the tool and the hash, and not the speed of the destination disk.
Test 1: speed with a fast source
The first source is a loop device of 2 GiB with the data in RAM. This source is faster than all the tools, so the test shows the speed of the tools themselves. I ran each command three times and I took the median.
Without hash:
| Command | Time | Speed |
|---|---|---|
dd (default, bs=512) | 2.33 s | 922 MB/s |
dd bs=4K | 0.77 s | 2,791 MB/s |
dd bs=1M | 0.55 s | 3,877 MB/s |
dcfldd (default, bs=32K) | 0.66 s | 3,242 MB/s |
dcfldd bs=1M | 0.51 s | 4,194 MB/s |
dc3dd (default) | 2.81 s | 764 MB/s |
dc3dd bufsz=1M | 0.91 s | 2,361 MB/s |
With SHA-256:
| Command | Time | Speed |
|---|---|---|
sha256sum only (reference) | 2.02 s | 1,062 MB/s |
dd bs=1M + sha256sum (2 reads) | 2.68 s | 802 MB/s |
dcfldd hash=sha256 | 12.24 s | 175 MB/s |
dcfldd bs=1M hash=sha256 | 11.01 s | 195 MB/s |
dc3dd hash=sha256 | 10.02 s | 214 MB/s |
dc3dd bufsz=1M hash=sha256 | 9.07 s | 236 MB/s |
With MD5 and SHA-256:
| Command | Time | Speed |
|---|---|---|
dd bs=1M + md5sum + sha256sum (3 reads) | 6.66 s | 322 MB/s |
dcfldd hash=md5,sha256 | 18.12 s | 118 MB/s |
dc3dd hash=md5 hash=sha256 | 9.82 s | 218 MB/s |
What I see from these numbers:
- Without hash, the block size is the most important parameter. dd with the default block size of 512 bytes is slow. With
bs=1M, dd and dcfldd have about the same speed. dc3dd is faster withbufsz=1M. - With a hash, the hash is the bottleneck. On the same machine, the SHA-256 inside dcfldd and dc3dd is about 200 MB/s, while
sha256sumis about 1,000 MB/s. - With two algorithms, dc3dd keeps the same speed. dcfldd becomes slower (118 MB/s). It seems that dc3dd calculates the hashes in parallel.
Test 2: speed with a normal drive (about 100 MB/s)
A source in RAM is not the normal case. A hard disk or a USB 3 stick reads at about 100 MB/s. To simulate it, I limited the read speed of a loop device of 1 GiB to 100 MiB/s with the blkio cgroup:
mkdir /sys/fs/cgroup/blkio/slow
echo "7:2 104857600" > /sys/fs/cgroup/blkio/slow/blkio.throttle.read_bps_device
echo $$ > /sys/fs/cgroup/blkio/slow/tasks
Command explanation:
7:2is the major and minor number of the loop device (/dev/loop2)104857600is the maximum read speed in bytes per second (100 MiB/s)- the last line moves the current shell in the cgroup, so all the commands that you run from it have the limit
I ran each command two times and I took the average:
| Command | Time | Speed |
|---|---|---|
dd bs=1M (no hash) | 10.15 s | 105 MB/s |
dd bs=1M + sha256sum | 20.40 s | 52 MB/s |
dd bs=1M + md5sum + sha256sum | 30.56 s | 35 MB/s |
dcfldd hash=sha256 | 10.22 s | 105 MB/s |
dcfldd hash=md5,sha256 | 10.63 s | 101 MB/s |
dc3dd hash=sha256 | 10.24 s | 104 MB/s |
dc3dd hash=md5 hash=sha256 | 10.34 s | 103 MB/s |
Here the result is different. The drive is the bottleneck, and dcfldd and dc3dd calculate the hashes at the same speed of the drive. With dd you must read the drive again for each hash: one hash, two times the time; two hashes, three times the time. On a 1 TB drive at 100 MB/s, an acquisition with MD5 and SHA-256 takes about 3 hours with dcfldd or dc3dd. With dd + md5sum + sha256sum it takes about 8 hours.
Test 3: reliability with bad sectors
This is the most interesting part. I created a source of 256 MB with three areas that cannot be read:
- one sector of 4 KiB at about 100 MiB
- one sector of 4 KiB at about 143 MiB
- a damaged area of 64 KiB at 200 MiB
In total, 73,728 bytes cannot be read. This is the minimum data that a tool must lose.
To simulate the bad sectors, I wrote a small FUSE filesystem. It gives access to a file and returns an I/O error when a read touches a bad area. This is the read function:
static int b_read(const char *p, char *buf, size_t n, off_t o, struct fuse_file_info *fi) {
for (int i = 0; i < nbad; i++)
if (o < bad_off[i] + bad_len[i] && o + (off_t)n > bad_off[i]) return -EIO;
return pread(bfd, buf, n, o);
}
Then I attached the file to a loop device, in read-only mode:
losetup -r --show -f /mnt/badfs/disk.img
In this way the tools read a real block device (/dev/loop0) that returns real I/O errors. After each acquisition I compared the image with the original data, sector by sector.
These are the results. I ran the main commands three times, and the results were the same every time:
| Command | Image size | Data lost |
|---|---|---|
dd bs=4096 conv=noerror,sync | Correct | 200,704 bytes |
dd bs=64K conv=noerror,sync | Correct | 327,680 bytes |
dd bs=1M conv=noerror,sync | Correct | 3,145,728 bytes |
dd bs=4096 conv=noerror,sync iflag=direct | Correct | 73,728 bytes (only the bad sectors) |
dd bs=4096 (without conv=noerror) | Stops at 100 MiB | Everything after the first error |
dcfldd conv=noerror,sync (default, bs=32K) | 32 KiB too big | 118,562,816 bytes (data shifted) |
dcfldd bs=4096 conv=noerror,sync | Correct | 200,704 bytes |
dc3dd (default) | Correct | 73,728 bytes (only the bad sectors) |
ddrescue | Correct | 393,216 bytes |
ddrescue -d | Correct | 73,728 bytes (only the bad sectors) |
dc3dd
dc3dd was the best tool with the default options. It lost only the bad sectors and it wrote a clear summary at the end:
input results for device `/dev/loop0':
524288 sectors in
144 bad sectors replaced by zeros
aeefb1506d6449c3f4a3450979b87400a5739c0fbf169f0d78e308270f255a1c (sha256)
The hash in the output is the hash of the image file, so you can use it directly in your report.
dcfldd
With the default block size (32 KiB), dcfldd created a broken image. After the second bad area the image is 32 KiB bigger than the source, and all the data after that point is at the wrong offset. dcfldd did not show any warning, only the I/O errors. The only hint is in the last lines: 8193+0 records out instead of 8192.
It seems related to a short read just before the bad sector, but I did not check the source code. With bs=4096 the image was correct.
There is also a second problem. When there are read errors, the hash that dcfldd writes is not the hash of the image file. In my test with bs=4096:
- hash written by dcfldd:
803fe811… - sha256sum of the image file:
a2e459df…
The reason is the option hashconv. By default dcfldd calculates the hash before the conversions, so the zeros added by sync are not in the hash. Add hashconv=after and the hash matches the image file:
sudo dcfldd if=/dev/sdb of=/evidence/image.dd bs=4096 conv=noerror,sync hash=sha256 hashconv=after hashlog=/evidence/hash.txt
dd and ddrescue
dd never created a broken image, but with normal reads it lost more data than the bad sectors. The reason is the Linux page cache: in my test, Linux marked as unreadable an area of 128 KiB around the first bad sector. With a bigger block size dd lost more data, because sync fills the full block with zeros: with bs=1M it lost 3 MB.
With iflag=direct dd reads the drive without the page cache, and it lost only the bad sectors. ddrescue had the same behaviour: without -d it lost 393 KB, with -d (direct disc access) only the bad sectors.
Please note that a real damaged drive is different from my simulation. It can be very slow near the bad sectors, it can get worse at every read, and two runs can give different results. ddrescue is made for this: it reads the good areas first and goes back to the bad areas at the end.
So, which one should I use?
- Healthy drive (hard disk, USB): dcfldd or dc3dd. They read the drive one time and calculate the hash at the same time. dd plus sha256sum takes two times longer.
- Fast drive (SSD, NVMe): in my test the hash inside dcfldd and dc3dd stops at about 200 MB/s. If you need two hashes, dc3dd is faster.
- Drive with bad sectors: dc3dd with the default options, or ddrescue with
-d. If you use dcfldd, addbs=4096andhashconv=after. If you use dd, addbs=4096 iflag=direct conv=noerror,sync.
In all cases, at the end compare the size of the image with the size of the source, and check the hash.
If you made similar tests, especially on real damaged drives, please share your results in the comments!
Leave a Reply