Recently I purchased a Samsung PRO+ "128GB" micro SD card for use with a RetroPie setup. I put 128GB in quotes here as after dropping it on a box and inspecting the true size, I find that Samsung pulled the old bait and switch on this card and it's true size is 119GB. Unfortunately for me, I had a multi partition 128GB .img file already to go.
Power of *nix to the rescue!
- Temporarily mount partition within imaged as a loop and remove enough files for desired target size
- Mount as a loop device and shrink the partition -- leaving unallocated space at the end of the image file
- Discover the new ending byte offset of the partition and truncate the file at that point
- Viola! A smaller (and still bootable) image file!
First, let's discover some information about the image file itself:
$ parted image.img (parted) unit Unit? [compact]? B (parted) print Model: (file) Disk /home/bryan/Downloads/image.img: 128094044160B Sector size (logical/physical): 512B/512B Partition Table: msdos Disk Flags: Number Start End Size Type File system Flags 1 4194304B 63963135B 59768832B primary fat16 boot, lba 2 63963136B 127865454591B 127801491456B primary ext4 (parted) quit
Note the start offset of 63963136. We'll use that in the next step:
$ mkdir mnt $ sudo mount -o loop,offset=63963136 ./mnt/
OK, we have a mounted image file! Browse and remove any unwanted/unnecessary files. The goal here is to free enough space for our target device...
With that out of the way, unmount the file:
$ sudo umount ./mnt/
Now, we want to mount the entire image file as a loopback device. This will allow us to muck around with it in GParted (or whatever you fancy):
$ sudo modprobe loop $ sudo losetup -f /dev/loop0 $ sudo losetup /dev/loop0 image.img $ sudo gparted /dev/loop0
From here you can use GParted to resize the main ext4 (or whatever) partition. Note that you may very likely need to still leave a small amount of space at the end of the allocated area (ie: don't fully shrink) else the resize may fail. Once you're happy here, make sure to apply and exit GParted.
Now, unmount the loopback device:
$ sudo losetup -d /dev/loop0
Let's also find out the new ending position of the partition:
$ sudo fdisk -l image.img Disk image.img: 119.3 GiB, 128094044160 bytes, 250183680 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: 0x81c0ff4b Device Boot Start End Sectors Size Id Type image.img1 * 8192 124927 116736 57M e W95 FAT16 (LBA) image.img2 124928 222177279 222052352 105.9G 83 Linux
Good deal! We see here the ending position of the partition we just resized is 222177279. Our byte offset is (end + 1) * 512. In this case (222177279 + 1) * 512 = 113754767360. Let's now truncate the image file down to size:
$ truncate --size=113754767360 image.img
...and that's it!