hoowl

Physicist, Emacser, Digitales Spielkind

Shrinking an SD card image using Linux shell commands
Published on Oct 09, 2021.

I really enjoy messing with Raspberry Pi computing systems and have several running inside my home, either feeding my stereo system with music or keeping my personal data in sync. To make backups or creating clones of existing systems, I can always stick the SD card running the operating system of the Raspberry Pi into my laptop and copy the entire content onto my hard disk. On those occasions it is sometimes nice to be able to shrink the resulting image down to the amount of actual data contained within (instead of the size of the SD card). I prefer to use command-line tools as they are easier to document and control.

This post is part of the notes-to-future-self series: I will probably want to do this again, so better write down how I worked things out the first time! ;)

A great help was this article on which much of this post is based upon. I managed to simplify a couple of things, however, sticking with only command-line tools.

First, I made a copy of the SD card using dd. Now I want to shrink part of the image containing the root file system. I can list the partitions contained in the image using fdisk:

fdisk -l /home/hanno/rasppi_20210429_SHRUNK.img
Disk /home/hanno/rasppi_20210429_SHRUNK.img: 29,81 GiB, 31992053760 bytes, 62484480 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: 0xf16f32c5

Device                                    Boot Start      End  Sectors  Size Id Type
/home/hanno/rasppi_20210429_SHRUNK.img1       8192    98045    89854 43,9M  c W95 FAT32 (LBA)
/home/hanno/rasppi_20210429_SHRUNK.img2      98304 62484479 62386176 29,8G 83 Linux

Using the --partscan option of losetup, we can access these partitions directly via a loopback device and thus making them individually visible to the operating system:

losetup --show -f --partscan /home/hanno/rasppi_20210429_SHRUNK.img
/dev/loop22
ls /dev/loop22*
/dev/loop22  /dev/loop22p1  /dev/loop22p2

On these partitions reside the filesystems that contain the actual data. Find out what the minimum size of the image would be (i.e. the actual data contained within):

sudo resize2fs -P /dev/loop22p2
resize2fs 1.45.5 (07-Jan-2020)
Estimated minimum size of the filesystem: 615955

This is in file-system block size which are 4 kB large. Add a little and use this number to shrink the filesystem down to this size:

sudo e2fsck -p -f /dev/loop22p2 && sudo resize2fs /dev/loop22p2 750000
rootfs: 61061/1884960 files (0.1% non-contiguous), 544080/7798272 blocks
resize2fs 1.45.5 (07-Jan-2020)
Resizing the filesystem on /dev/loop22p2 to 750000 (4k) blocks.
The filesystem on /dev/loop22p2 is now 750000 (4k) blocks long.

Now we can be sure that all the data is contained within those limits. Time to reduce the partition boundaries to these values.

We can use fdisk on the main loopback device to delete the 2nd partition and recreate it with the same start and the new end (i.e. 750000*4 kB):

echo +$((750000*4))K
+3000000K

The plus in the beginning is important as it indicates a relative number and not an absolute number.

Run fdisk:

fdisk /dev/loop22

Press d, select the 2nd partition, confirm, press n and follow the prompts and enter the values above.

Then, remove the loopback devices again:

sudo losetup -d /dev/loop22*
losetup: /dev/loop22p1: failed to use device: No such device
losetup: /dev/loop22: detach failed: No such device or address
losetup: /dev/loop22p2: failed to use device: No such device
losetup: /dev/loop22: detach failed: No such device or address

Looks like they were removed already. Let’s look at the new partition table:

fdisk -l /home/hanno/rasppi_20210429_SHRUNK.img
Disk /home/hanno/rasppi_20210429_SHRUNK.img: 29,81 GiB, 31992053760 bytes, 62484480 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: 0xf16f32c5

Device                                    Boot Start     End Sectors  Size Id Type
/home/hanno/rasppi_20210429_SHRUNK.img1       8192   98045   89854 43,9M  c W95 FAT32 (LBA)
/home/hanno/rasppi_20210429_SHRUNK.img2      98304 6098943 6000640  2,9G 83 Linux

It worked so far! The total image is still 30 GB large but the data partition is only about 3 GB. Now we can remove everything from the image until the end sector of the 2nd partition as provided in the fdisk output above:

END=6098943
truncate -s $(((END+1)*512)) /home/hanno/rasppi_20210429_SHRUNK.img
ls -lh /home/hanno/rasppi_20210429_SHRUNK.img
-rw-r--r-- 1 hanno hanno 3,0G jul  8 23:33 /home/hanno/rasppi_20210429_SHRUNK.img

And that is it, the file is a mere 3 GB in size now! :)

Of course, from within the Raspian OS, you can easily stretch the system again to fill any size SD card from within the configuration tool raspi-config.

Tags: raspberrypi, linux