1. Introduction

Raw storage images, also called dumps, can be an invaluable way to clone, preserve, and migrate whole systems without using additional mediums. While we can do that with virtual disk images, a raw medium snapshot can directly be applied to bare metal. This has performance benefits as well.

In this tutorial, we explore raw disk image structure and how to mount and clone such images and their partitions. First, we briefly refresh our knowledge about storage medium structure and dump generation. After that, we show loop device creation for a particular storage dump and mount it. Next, we show ways to extract and mount a specific partition. Finally, we perform a partition clone operation without going to an intermediary device.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.

2. Raw Storage Image Structure

Before we dive into management, let’s get an overview of the structure that a whole hard disk drive (HDD) or solid-state disk (SSD) raw image has.

To generate a raw disk or partition copy, we can use different tools. Usually, the most prominent is dd.

Let’s generate a disk dump via dd:

$ dd if=/dev/sdx of=sdx.img bs=1k conv=sync,noerror

Since the resulting file contains a bit-for-bit copy of the original medium, we can expect an equivalent structure:

 Offset     Storage Medium    
      0 +--------------------+
      . | Master Boot Record |
      . |--------------------|
      . |     Partition      |
      . |       Table        |
      . |--------------------|
      . |    Partition 1     |
      . |    Partition 2     |
      . |        ...         |
      . |    Partition N     |
      X +--------------------+
      . |        ...         |
      . |                    |

Although they differ slightly, the MBR (Master Boot Record) and GPT (GUID Partition Table) disk structures are more or less similar for our purposes. Both start with an MBR-like structure and the relevant partition table. After that come all partitions, usually in sequence.

For convenience, we also show the Offset column, which tells the position of each structure in (usually 512B) sectors.

When it comes to MBR, the disk ends at offset X. However, GPT mediums can have multiple entries and a secondary header.

2. Create Loop Device

Let’s assume we produce the sdx1.img raw dump of a single partition. Once we have the file, we first convert it to a device:

$ losetup /dev/loop666 sdx1.img

Here, losetup creates the /dev/loop666 loop device. In short, loop devices cross-map each block from a given regular file to a supplied or automatically generated loop* block device.

Let’s see the device in question via the –list flag of losetup:

$ losetup --list
NAME         SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                             DIO LOG-SEC
[...]
/dev/loop666         0      0         0  0 /sdx1.img                               0     512

Thus, we can work with the resulting /dev/loop666 path. When ready, we can –detach.

3. Mount Raw Storage Image

After having the loop device associated with our raw single-partition file, raw mounting is fairly simple:

$ mkdir /mnt/sdx1
$ mount /dev/loop666 /mnt/sdx1

As usual, we employ mount to attach the partition to an empty path.

However, this command would not work if dealing with a copy of an entire HDD or SSD. The reason behind this is the reliance on partition types by the mount command.

4. Mount Partition From Raw Storage Image

In this case, we assume sdx.img is a raw dump of a whole disk, linked to loop device /dev/loop100.

Since devices have partitions, a partition manager like fdisk can aid in the analysis of our dump:

$ fdisk --list sdx.img
Disk sdx.img: 6 GiB, 6368709120 bytes, 12438885 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0666430f

Device     Boot   Start     End Sectors  Size Id Type
sdx.img1           2048 6289408 6291456    3G 83 Linux
sdx.img2        6289409 7653377 1363968  666M 83 Linux
sdx.img3        7653378 7886944 233566   456M 83 Linux

Notably, the Start column values provide some of the offsets in sectors from the structure we saw earlier:

  Offset     Storage Medium    
       0 +--------------------+
       . |        ...         |
       . |--------------------|
    2048 |    Partition 1     |
 6289409 |    Partition 2     |
 7653378 |    Partition 3     |
 7886945 |--------------------|
       . |    Unallocated     |
12438885 +--------------------+

Armed with this knowledge, we can extract a partition from the file.

5. Partitions From Raw Storage Image

At this point, we have the sdx.img raw dump file image of an entire drive and the offsets of each of its partitions.

Let’s attempt to get a loop device for a separate partition:

$ losetup --offset $(( 2048*512 )) --sizelimit $(( 6291456*512 )) /dev/loop101 sdx.img

In this case, we create the /dev/loop101 device for Partition 1 of the sdx.img file. In particular, we supply the $(( 6291456*512 )) partition size and $(( 2048*512 )) start offset to the –sizelimit and –offset options, respectively. We multiply both by the 512-byte sector size.

This way, losetup knows how to identify the particular partition. Consequently, we have the /dev/loop101 loop device associated with Partition 1 to work with.

For instance, we can now mount the partition the usual way:

$ mkdir /mnt/sdx1
$ mount /dev/loop101 /mnt/sdx1

However, we don’t always need losetup to extract partitions.

6. Clone Raw Storage Image Partition

By using dd, we can directly clone a specific partition from a raw dump file, as long as we know its position:

$ dd if=sdx.img of=sdx1.img bs=512 iseek=2048 count=6291456 conv=sync,noerror

In this case, we employ several options:

  • if: input is the sdx.img file with the whole disk raw image
  • of: output is the sdx1.img file with only the first partition
  • bs: work with 512-byte input and output blocks
  • iseek: start from bs block (sector) 2048
  • count: read 6291456 bs blocks (sectors)

As a result, we have the sdx1.img file to work with.

7. Summary

In this article, we talked about raw storage dumps, their structure, and ways to handle them via loop devices and direct reads.

In conclusion, although they might look daunting at first, raw images of data can be relatively easy to work with as long as we consider their structure.