sg_xcopy copies a chosen range of logical blocks between two SCSI block devices, offloading the transfer to the devices themselves. Allow 20 to 30 minutes for a first run, longer if you must identify the correct LUNs. The copy itself can be much faster than reading and writing through the host, but that depends on the devices and their copy manager.
This guide describes sg_xcopy from sg3-utils 1.46-3ubuntu4, installed on this machine. Its local manpage is labelled sg3_utils 1.45 and documents the LID1 XCOPY interface. Check the command on your own host before relying on version-specific output.
Warning: An incorrect of= can overwrite a disk. Treat every device path as untrusted until you have checked it. Do not test these examples against a mounted filesystem, a production LUN or a device containing data that has no verified backup.
These are ordinary, read-only checks that need no elevated privileges:
$ command -v sg_xcopy
/usr/bin/sg_xcopy
$ sg_xcopy --version
sg_xcopy: 0.71 20200510
$ dpkg-query -W -f='${Package} ${Version}\n' sg3-utils
sg3-utils 1.46-3ubuntu4
The syntax resembles dd, but it is not a general file copier. The installed utility implements XCOPY for block-to-block transfers, so if= and of= must identify SCSI block devices. The manpage says that regular files are not supported as the source and destination for the implemented transfer.
Checkpoint: If sg_xcopy --version or the package query reports something different, read that host's sg_xcopy(8) before using the examples.
Use your storage inventory, multipath configuration and change record to establish the exact source and target paths. You may need elevated privileges for inventory commands or for opening a device, but do not add sudo automatically: it does not make an ambiguous path safe.
Set shell variables only after checking both paths. The values below are placeholders, not devices to paste unchanged:
$ SRC=/dev/disk/by-id/REPLACE_WITH_SOURCE_ID
$ DST=/dev/disk/by-id/REPLACE_WITH_DESTINATION_ID
$ printf 'source: '; readlink -f -- "$SRC"
$ printf 'destination: '; readlink -f -- "$DST"
source: /dev/REPLACE_SOURCE
destination: /dev/REPLACE_DESTINATION
Stop if the resolved paths are the same, if either link is missing, or if the destination contains data you have not approved for replacement. The command opens an existing output without truncating it, then overwrites from the start unless you use oflag=append or seek=. That still means destructive writes to a device.
bs= must be the logical block size of the physical device when either endpoint is accessed through SCSI commands. It is not the flexible multiple accepted by ordinary dd. The source and destination should normally use the same logical block size, and ibs= and obs=, if supplied, must equal bs=.count= for a controlled copy. It counts blocks, so a 512-byte block size and 2048 blocks transfer 1 MiB. Confirm the value from the device documentation or an approved inventory command before running it:$ BS=512
$ COUNT=2048
$ printf 'planned bytes: %s\n' "$((BS * COUNT))"
planned bytes: 1048576
If you omit count=, sg_xcopy tries to derive a device-sized count from SCSI READ CAPACITY. That is convenient for a whole-device operation but a poor default for a first test. It cannot derive a count for every file-like input, and a missing count can stop the copy before it starts.
For a range inside a logical unit, skip= selects the source start block and seek= selects the destination start block, measured in bs=-sized blocks. The tool does not account for partitions: a path such as /dev/sdc2 is treated as the whole logical unit starting at LBA 0. Calculate offsets yourself from a trusted partition layout, and do not assume a partition path protects the rest of the disk.
Run this only during the approved maintenance window, with both endpoints unmounted or otherwise quiesced as required by your storage design. The command below copies 2048 blocks from block zero on the source to block zero on the destination:
# sg_xcopy if="$SRC" of="$DST" bs="$BS" count="$COUNT" list_id=2 dc=1 time=1
sg_xcopy: if=/dev/REPLACE_SOURCE skip=0 of=/dev/REPLACE_DESTINATION seek=0 count=2048
sg_xcopy: 2048 blocks, 1 command
# because access to a raw device commonly needs root or a carefully delegated device permission. Exact progress and timing output varies. Informational, warning and error messages go to standard error, leaving standard output available for the of=- form, although this guide does not use that form.dc=1 makes the count refer to the target descriptor. With equal block sizes and a straightforward device-to-device copy, that is an explicit choice rather than something to leave unclear.list_id=2 gives the operation an identifier for the follow-up status query. Do not reuse an identifier when your copy manager requires uniqueness.app=1 or oflag=append for a disk copy. They are intended for appending to a regular output file, and append semantics on a device are ignored or may fail. If you need a non-zero destination offset, use a reviewed seek= value instead.A successful sg_xcopy exit status is useful, but query the copy manager as well when the device supports copy results. The manpage supplies sg_copy_results for this purpose:
$ sg_copy_results --status --list_id=2 "$DST"
Receive copy results (copy status):
Held data discarded: Yes
Copy manager status: Operation completed without errors
Segments processed: 1
Transfer count units: 0
Transfer count: 0
Exact spacing and fields vary by device. The useful checkpoint is a completed status with no device error, not a particular transfer-count value. If the XCOPY command fails, save the complete stderr output and the copy-results response before retrying. A retry can repeat or overwrite part of the requested range.
If source and destination logical block sizes differ, residual data must be handled deliberately. Without suitable cat=1 or oflag=pad/iflag=pad settings, the command can fail with an inexact-segment error. Do not add those flags by trial and error: confirm the segment and padding requirements in the device documentation first.
By default, the XCOPY command is sent to of=, the destination. --on_src sends it to if= instead; iflag=xflag has the same effect. If the command line does not make the choice unambiguous, the environment variables XCOPY_TO_SRC and XCOPY_TO_DST can select one endpoint, but only one should exist. Avoid relying on ambient environment variables in automation. Use --on_dst or --on_src in the command itself when the endpoint matters.
The fco=1 option requests fast-copy-only behaviour. If the device cannot perform the transfer by a faster technique, it should return a copy-aborted condition instead of silently falling back to ordinary reads and writes. Use it only when that failure behaviour is wanted.