1. Introduction
Different filesystems can exist in files. In fact, we can also set up a whole storage medium emulated within a single file. This doesn’t necessarily require virtual disk images. There are also raw system images of an entire hard disk drive (HDD) or solid-state disk (SSD). When dealing with such potential file-systems, we often need to know ways to work with and reconfigure them after they are created.
In this tutorial, we talk about partitioning a raw IMG file, i.e., raw binary storage medium dumps. First, we go over the image dump format and ways to work with it. After that, we see a way to mount a specific partition from the raw IMG file. Finally, we go through a step-by-step process of repartitioning and potentially reducing the size of a binary storage dump.
Notably, all commands that deal with devices require root or sudo privileges. Further, it’s advisable to make backups of important data before tampering with it.
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. IMG Raw Dump File Format
IMG or image files usually have the .img extension and contain one or multiple filesystems in their raw binary format. In other words, we can expect a bit-by-bit copy of the data from a given partition or whole storage medium.
2.1. Generate IMG File
One of the most basic ways to get a raw data copy of an entire storage disk is the dd utility.
Let’s use dd to dump a secondary storage drive to a file:
$ dd if=/dev/sdb of=sdb.img bs=1k conv=sync,noerror
In this case, we perform a forensic dump from the [i]nput [f]ile /dev/sdb (our physical disk device) to the [o]nput [f]ile sdb.img:
- bs=1k: small granular block size of 1K
- sync: each block is padded with zeroes to ensure proper size in case of corrupt data
- noerror: prevents dd from giving up on the operation after an error
At this point, we have the full sdb disk within the sdb.img IMG file. It contains all partitions and data since it’s a bit-by-bit copy of the medium.
2.2. View Raw IMG File
Naturally, we might want to be able to open and view a raw disk image file for inspection.
Since IMG files are binary, we can use tools such as xxd or hexedit:
$ xxd -seek 666 -len 10 sdb.img
0000029a: 0000 0000 0000 0000 0000 ..........
Here, we use xxd to read a part of the data on our disk from the file copy by [-seek]ing to offset 666 and extracting a sequence of [-len]gth 10.
Regardless of our toolset choice, we should ensure that each utility can handle large files.
Since working with raw data directly can be challenging, let’s explore some more structured ways of dealing with raw storage IMG files.
2.3. Mount IMG File
Although IMG files can store whole disks, if a given IMG file only holds one filesystem, i.e., partition, we might be able to mount it directly.
To mount files, we use so-called loop devices. In short, a loop device maps blocks from a regular file to a loop* block device. After doing so, we can access the target loop* file as usual for such devices.
So, let’s assume we first dumped /dev/sdb1 to sdb1.img:
$ dd if=/dev/sdb1 of=sdb1.img bs=1k conv=sync,noerror
Now, we use losetup to mount this new image file:
$ losetup /dev/loop666 sdb1.img
Notably, instead of choosing it, we can omit the loop device path, and the system will choose or create a free one. In this case, we should have the /dev/loop666 device to work with.
Next, since the device contains only a single filesystem, we can create a mount point and mount it directly:
$ mkdir /mnt/sdb1
$ mount /dev/loop666 /mnt/sdb1
Thus, we have the /mnt/sdb1 mount of our sdb1.img file.
To remove the loop666 or any other loop device, we use –detach after unmounting it:
$ umount /mnt/sdb1
$ losetup --detach /dev/loop666
At this point, we know how to mount a single-partition image file. Let’s see what we can do with a whole disk dump.
3. Mount Partition From Disk Within Dump File
Since using dd to clone entire storage mediums to files is common, we might want to mount a single partition from a whole disk dump image. Let’s understand how we can do that.
First, *we use fdisk to check the partition table [-l]ist*:
$ fdisk --list sdb.img
Disk sdb.img: 5 GiB, 5368709120 bytes, 10485760 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: 0x6660010f
Device Boot Start End Sectors Size Id Type
sdb.img1 2048 3907583 3905536 1.9G 83 Linux
sdb.img2 3907584 6004735 2097152 1G 83 Linux
sdb.img3 6004736 7004159 999424 488M 83 Linux
Since fdisk works on data, it isn’t a problem to apply it directly to raw files. Let’s say we want to mount partition 2, i.e., sdb.img2. To do so, we note down its Start sector, 3907584. After that, we calculate the partition start position by multiplying the Start sector count by the sector size, 512 bytes:
$ START_PARTITION=$(( 3907584*512 ))
Now, using this position as an –offset, we issue a losetup command:
$ losetup --offset $START_PARTITION /dev/loop667 sdb.img
Finally, we can mount the new /dev/loop667 loop device:
$ mkdir /dev/sdb2
$ mount /dev/loop667 /mnt/sdb2
Naturally, we can skip the mount and work on the filesystem or filesystems in other ways.
4. Partition Filesystems Within Dump File
We already saw how to use fdisk to extract partition information from a given disk dump image file. Now, let’s understand how we can modify the partition table in place within the file.
Although we can mount sdb.img as a loop device, as expected, we can also just issue an fdisk command for our whole dump:
$ fdisk sdb.img
[...]
Command (m for help):
After that, at the interactive Command prompt of fdisk, we issue a [p]artition list command:
Command (m for help): p
[...]
Device Boot Start End Sectors Size Id Type
sdb.img1 2048 3907583 3905536 1.9G 83 Linux
sdb.img2 3907584 6004735 2097152 1G 83 Linux
sdb.img3 6004736 7004159 999424 488M 83 Linux
Naturally, we see the partition list we already know from before. Now, let’s [d]elete partition 3 and check the result:
Command (m for help): d
Partition number (1-3, default 3): 3
Partition 3 has been deleted.
Command (m for help): p
[...]
Device Boot Start End Sectors Size Id Type
sdb.img1 2048 3907583 3905536 1.9G 83 Linux
sdb.img2 3907584 6004735 2097152 1G 83 Linux
At this point, the partition table is altered. Naturally, we can perform any operation that we can on a physical medium.
If we want to make the changes permanent, we can [w]rite them back to the original source, our sdb.img file:
Command (m for help): w
The partition table has been altered.
Syncing disks.
In case we do use loop devices for this procedure, issuing a prophylactic partprobe before the losetup –detach is also a good idea to ensure the kernel has registered the new partition table.
Now, we can check new data from fdisk:
$ fdisk --list sdb.img
Disk sdb.img: 5 GiB, 5368709120 bytes, 10485760 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: 0x6660010f
Device Boot Start End Sectors Size Id Type
sdb.img1 2048 3907583 3905536 1.9G 83 Linux
sdb.img2 3907584 6004735 2097152 1G 83 Linux
Critically, we can see that the entire file size is still 5GiB or 5368709120 bytes despite the total of the leftover partitions being around 2.9G after the deletion.
Since we removed data from the end, we can attempt to resize the file by calculating the space it needs. To do so, we use the End sector 6004735 of the last partition sdb.img2 as a guide for our calculation:
$ RESIZE=$(( (6004735+1)*512 ))
Armed with this new –size, we can truncate the file:
$ truncate --size $RESIZE sdb.img
Finally, it’s usually a good idea to check the UUID of the partitions and verify they haven’t changed. If they have, we might need to change relevant /etc/fstab and other entries that use UUID.
5. Summary
In this article, we explored raw image dump files and how to work with them, creating, viewing, mounting, and resizing as desired.
In conclusion, IMG files are a fairly convenient way to transfer and modify storage mediums without the physical presence of the device.