Copy SCSI Data Safely with sg_dd
You will finish with a checked sg_dd command for copying a known input file, plus a safer pattern for imaging a SCSI device. The examples use the locally installed sg3-utils package version 1.46-3ubuntu4, whose sg_dd reports version 6.25 (20210326).
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes for a file-based test. Device imaging can take much longer and needs an accurate destination, a suitable maintenance window and enough storage. You need a shell and sg3-utils. Reading a device or writing a file is normally unprivileged, but device permissions often mean that the real copy needs elevated access.
1. Confirm the installed command
Check the binary and version before copying. This is read-only and does not need sudo:
$ command -v sg_dd
/usr/bin/sg_dd
$ sg_dd --version
sg_dd: version: 6.25 20210326
$ dpkg-query -W -f='${Package} ${Version}\n' sg3-utils
sg3-utils 1.46-3ubuntu4
The command uses dd-like operands such as if=, of=, bs= and count=, written as separate arguments rather than options beginning with a dash. Run sg_dd --help if the installed version differs from this guide.
2. Choose the block size and scope
For SCSI command I/O, bs must be the device's logical block size. The default is 512 bytes, which is often right for disks but normally wrong for optical media, where 2048 bytes is usual. Unlike ordinary dd, sg_dd does not treat an arbitrary multiple as a valid SCSI block size.
Use an explicit count whenever the source is a device and you know the intended range. It counts blocks, so bs=512 count=2048 requests 1 MiB. If you omit count, sg_dd tries to derive a device length, but normal files are not probed for their size. A missing count can therefore stop the command before any copy takes place.
Checkpoint: write down the source, destination, logical block size and number of blocks before moving to the copy step. A wrong destination can destroy data; no command-line spelling can undo an overwrite.
3. Test the syntax without copying
The dry-run mode parses and prepares the command, but bypasses the actual copy. It can still open the named files to determine their lengths, so use real paths and do not treat it as a complete permission test.
$ sg_dd --dry-run if=/path/to/input.img of=/path/to/output.img bs=512 count=2048
sg_dd: dry-run, bypassing actual copy
The wording of the informational line can vary. The useful verification is a zero exit status:
$ printf 'exit status: %s\n' "$?"
exit status: 0
If the dry run reports an unknown operand, invalid block size or missing file, fix that first. Do not add conv=sync expecting GNU dd behaviour: in sg_dd, that conversion is ignored. Its supported conversions are narrower, including sparse, nocreat, noerror and notrunc.
4. Copy a bounded file range
For a normal file, this command copies 1 MiB from the beginning of the input to the beginning of the output:
$ sg_dd if=/path/to/input.img of=/path/to/output.img bs=512 count=2048
2048+0 records in
2048+0 records out
The record summary is sent to standard error, so the output file remains usable if you use standard output as a destination. Exact summary wording depends on the build. Verify the result using the byte count and, when a byte-for-byte comparison is appropriate:
$ stat -c '%n %s bytes' /path/to/input.img /path/to/output.img
/path/to/input.img 1048576 bytes
/path/to/output.img 1048576 bytes
$ cmp --bytes=1048576 /path/to/input.img /path/to/output.img
$ printf 'exit status: %s\n' "$?"
exit status: 0
If the output already exists, sg_dd overwrites it from the start but does not truncate it by default. That can leave old trailing bytes beyond the copied range. Use a new destination, remove the old file only after checking your backup, or arrange the intended final length explicitly. oflag=append appends to a regular file and cannot be combined with seek=.
5. Image a device with a guarded command
Stop before this step if you have not identified the devices. Source and destination roles are easy to reverse, and a device write is destructive. Unmount filesystems that must not change during the read, record stable device identifiers, and check the mapping of SCSI generic nodes with sg_map or /proc/scsi/scsi. Do not use this utility for tape devices: its SCSI READ and WRITE commands are intended for disks and optical media.
For a disk with 512-byte logical blocks, a bounded image command looks like this:
$ sudo sg_dd if=/dev/sgX of=/safe/path/disk.img \
bs=512 count=BLOCKS iflag=coe oflag=nocreat time=1 --progress
Replace /dev/sgX, /safe/path/disk.img and BLOCKS with values you have independently checked. iflag=coe continues through certain SCSI read errors and supplies recovered or zero data according to the utility's recovery path; it is not a guarantee that damaged data is correct. oflag=nocreat refuses to create a missing output file, which is a useful guard when the destination should already exist. Create an appropriately sized destination deliberately before using that guard.
progress is a command-line option in this installed interface. It reports progress every two minutes and prints a final completed line; repeat it for one-minute or 30-second reports. time=1 prints the transfer duration and throughput to standard error. Neither option validates the contents of the image.
6. Use direct or SCSI I/O only when you need it
A block device normally uses buffered Unix reads and writes. iflag=direct or oflag=direct requests O_DIRECT, while iflag=sgio or oflag=sgio sends SCSI commands through SG_IO. The alternative blk_sgio=1 applies SCSI I/O to block devices on supported systems, but it also ignores partition information and accesses the underlying device. That is a dangerous surprise: a command naming /dev/sda3 can read the whole underlying disk when this mode is enabled.
Do not add these flags as performance folklore. Confirm the device type and the I/O path you need. Direct I/O can fall back to indirect I/O for an sg device, and the command reports that at completion. An ATA disk is not automatically a SCSI target merely because it is represented by a block device.
7. Verify an SCSI-capable destination
--verify replaces WRITE commands with SCSI VERIFY commands. It requires of= and an sg device, or a block device opened with oflag=sgio. It is not a general-purpose comparison mode for two regular files.
$ sudo sg_dd if=/dev/sgX of=/dev/sgY bs=512 count=BLOCKS --verify
$ printf 'exit status: %s\n' "$?"
exit status: 0
A non-zero status or a miscompare means the verification did not establish equality. Without oflag=coe, the first miscompare stops the operation. For regular files, use cmp or a checksum comparison instead. Keep the source and image until verification has completed; deleting the only copy is the irreversible step.
8. Diagnose a failed or interrupted transfer
All informative, warning and error output goes to standard error. Add --verbose or repeat it, for example -vv, when you need more detail. Higher verbosity can produce a large amount of SCSI command output. An exit status of zero means the utility completed successfully, but with error-continuation settings individual SCSI command failures do not necessarily make the process exit non-zero.
For a long transfer, interrupting it prints remaining-block and record information before the normal signal action. SIGUSR1 requests the same progress information while the copy continues. If a transfer stops, preserve the partial output, record the exact command and inspect the source and destination before deciding whether to resume with skip= and seek=. Those operands count bs-sized blocks and must be calculated carefully.
Done means
- The installed version and logical block size are known.
- The command passed
--dry-runbefore a real transfer. - The source, destination, block count and direction were checked independently.
- A file copy was checked for size and, where suitable, with
cmp. - Device imaging used an explicit scope, an existing destination guard and a recovery plan.
--verifywas used only with an SCSI-capable destination, not as a substitute for file comparison.