There's a good post over at brashear.me which creates a btrfs partition stored as a file. The partition supports compression and can be used by ddrescue transparently. The blog post is reproduced below, should it ever become unavailable.
As disk sizes explode, I've found myself having to mirror disks which
I don't have enough storage for. My tool of choice is ddrescue.
However, it doesn't support compression because it needs to be able to
seek through the output as it rescues data. A solution I've found is
to create a sparse file, format it btrfs, and mount it with the
compression option. This allows ddrescue to operate normally,
while giving me fast + decent compression.
Create a sparse file which is larger than your source disk:
dd if=/dev/zero bs=1 count=0 seek=4T of=image-repository.img
Mount the file as a loop device:
sudo losetup /dev/loop0 image-repository.img
Partition the loop device:
sudo gdisk /dev/loop0
Create new GPT by pressing o
followed by y
Create new partition by pressing n
and pressing enter a few times to
accept defaults.
Write the table, by pressing w
then y
Reread partitions from the loop device:
sudo partprobe /dev/loop0
Format the loop0p1 partition:
sudo mkfs.btrfs /dev/loop0p1
Mount the filesystem with compression enabled. Valid compression options are
zlib, lzo, zstd
:
mkdir /mnt/img-repo
sudo mount -o compress=zstd /dev/loop0p1 /mnt/img-repo
Set the c attr on the mount directory:
sudo chattr +c /mnt/img-repo
Now, when you create a ddrescue
image inside of the /mnt/img-repo/
folder, it will be transparently compressed!
At a later date, you can access your ddrescue'd image using:
losetup --find --show image-repository.img
partprobe /dev/loopX //where X is the output of the previous command
mkdir /mnt/img-repo
mount -o compress=zstd /dev/loopXp1 /mnt/img-repo
chattr +c /mnt/img-repo
cd /mnt/img-repo
And you can inspect the contents of the ddrescue'd image using:
losetup --read-only --find --show /mnt/img-repo/disk.img
mkdir /mnt/recovered_files/
mount /dev/loopXp1 /mnt/recovered_files/ //where X is the output of the previous command
ddrescue [...] | gzip -9 > /path/to/destination/file
?ddrescue
requires a destination with seek support, due to its mode of operation (cf. JourneymanGeek's answer). Therefore, it cannot output to pipes.