7

I have a disk image from a bootable flashdrive. It is GPT for EFI booting, so there is a small Vfat32 partition and three other Linux partitions, followed by about 3G of free space.

I need to disk image copy to slightly smaller flashdrives, so I need to shrink from 15G to 14G more or less. I'll be using dd on linux to make the copies.

gparted is excellent for modifying and resizing the partitions, but it can't shrink the freespace at the end. Theoretically it should be possible to truncate the file, but when I try it blows up the (?disk table? a guess) so there are no partitions and the file is ruined. I am pretty sure the techniques below would have worked if I were using spinning disks and/or MBR disks, but I haven't tested.

Tried:

dd bs=1M count=14000 iflag=fullblock if=/dev/sda  of=myfile.raw

didn't shrink

rsync --sparse filename filename 

that didn't blow up the disk tables but shrank swap and kept the unallocated area.

qemu-img convert -f raw -O qcow2 -S 4k filename filename ( planned to convert back to raw)

blew up tables

truncate size=140000M <filename>

also blew up tables

cp --sparse=always filename filename

didn't work

1 Answer 1

10

There should be a primary (main) GUID Partition Table at the beginning and a secondary (backup) one at the end of the raw file created by dd. Truncating the file destroys the secondary one. There is a way to fix it.

Let's start with your untruncated file with free space at the end. Run gdisk -l myfile.raw. Notice the logical sector size (512B probably). Find the maximal end sector (the one for the last partition probably, but partition entries may not be in order, so look carefully for the maximal one). Sectors are numbered from 0, therefore you need (sector size)*(maximal end sector + 1) bytes to store all the partitions.

Additionally you need at least 33 full sectors of free space at the end to store new secondary GPT. See this picture from Wikipedia.

Altogether you need (sector size)*(maximal end sector + 34) bytes of your file. Truncate the file to this or bigger size:

truncate -s <new_size> myfile.raw

Next invoke

gdisk myfile.raw

You will get (among other things):

Warning! Disk size is smaller than the main header indicates!
Caution: invalid backup GPT header, but valid main header; regenerating backup header from main header.

Type w, hit Enter to write correct partition tables. You will see a caution because secondary GPT is about to be moved. You have enough free space at the end of the file, so there is nothing to worry about. Confirm when asked.

Quit with q, Enter. Run gdisk again – there should be no warnings. GPT is fixed.


In case you need to use gparted with your image, I have some hints.

The command sudo gparted myfile.raw expects files myfile.raw1, myfile.raw2 etc. to exist and correspond with partitions inside myfile.raw. If it was special file like /dev/sdb then udev would take care of /dev/sdb1, /dev/sdb2… It is not the case with regular file. Many gparted features will fail if there are no myfile.rawN files.

To create such files use kpartx (or partx+losetup tandem):

sudo kpartx -av myfile.raw

Observe its output (which loopXpY devices were created) and create symlinks to all partitions. The first one may be:

ln -s /dev/mapper/loop0p1 myfile.raw1

Now gparted should run and operate on those partitions. There is a pitfall though: when partition changes (e.g. it is moved/resized) the mapping created by kpartx is not updated. Normally gparted would call partprobe or something to update /dev/sd*; this won't work in our case. You should destroy the mappings and recreate them. While moving/resizing partitions run one gparted task, close program, fix the mappings, run gparted with second task and so on.

To destroy the mappings invoke sudo kpartx -dv myfile.raw. Delete orphaned symlinks at the very end.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .