I had imaged a full 8GB SD card, but I only wanted to distribute a smaller image. These are a few notes:
Image the SD card
dd if=/dev/sdb of=./backup.img
Now I wanted to use gparted to resize the partition, you can run sudo gparted ./backup.img
and it will show the partitions, but it will think that the partitions are located at /dev/./backup.img1 and /dev/./backup.img2 which is not correct. The trick here is to use the loopback interface.
losetup /dev/loop0 backup.img
this will make the backup.img file show up as a block device. You can then run gparted on /dev/loop0 and once again see the partition table. There still is an issue though, it will look for the partitions to be located at /dev/loop0p1 and /dev/loop0p2, these block devices do not exist. It turns out that losetup has a flag that I have not noticed anywhere before -P this causes the kernel to read the partition table of the device and create the additional block device. This lets us avoid having to manually look at the partition table and compute the offsets for additional loopback devices.
losetup -P /dev/loop0 backup.img
$ sudo ls /dev/loop0*
/dev/loop0 /dev/loop0p1 /dev/loop0p2
Great! this now looks like a real device. gparted will work fine to resize the partitions and filesystems. I trimmed a 7.9GB partition back to 3GB and then saved the changes. This did not change how large backup.img is, it is still 8GB, but all the important data is now in the first 3GB or so of the disk (p1 in the case was a 50MB boot partition).
Lets do a quick check on the filesystems to make sure they are still looking good (just showing p2 here, p1 looks similar just a fat partition)
$ sudo fsck /dev/loop0p2
fsck from util-linux 2.24.2
e2fsck 1.42.8 (20-Jun-2013)
/dev/loop2p2: clean, 83917/193152 files, 618580/768000 blocks
Alright, now lets clear up the end of the disk that is not being used. I used fdisk to take a look at the partition table:
$ sudo fdisk /dev/loop2
Welcome to fdisk (util-linux 2.24.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): p
Disk /dev/loop0: 7.4 GiB, 7948206080 bytes, 15523840 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: 0x000981cb
Device Boot Start End Blocks Id System
/dev/loop0p1 8192 122879 57344 c W95 FAT32 (LBA)
/dev/loop0p2 122880 6266879 3072000 83 Linux
Command (m for help): q
A couple things to note here. The sector size is 512 bytes. The end block of the last partition is 6266879. To compute the blocks we add one to the block number (they are indexed from 0) and then multiply this by the sector size 512. Using the truncate command we can trim this extra data off the file.
sudo truncate --size=$(((6266879+1)*512)) backup.img
It is now 5GB smaller
ls -lh ./
-rw-r--r--. 1 root root 3.0G Apr 2 13:53 backup.img
-rw-r--r--. 1 root root 8.0G Apr 2 13:53 backup.org.img
I recommend running the filesystem checks again, the first time I did this I used 510 instead of 512 for the sector size (not enough coffee) and everything broke and had to start all over with a fresh 8GB image.
I also highly recommend bz2 compressing the image file, you will likely save a significant amount of space.
-rw-r--r--. 1 root root 3.0G Apr 2 13:53 backup.img
-rw-r--r--. 1 root root 929M Apr 2 14:03 backup.img.tar.bz2