0

I have a certain custom Raspbian image downloaded from the internet for a project which I need to use. The file is a little less than 32 GB and I am having issues getting a card which can fit that size. I know that the image definitely does not use all the space and it is probably the OS filling up the space of the SD card it was made on. I tried looking up how to shrink the size of the image but all the methods I saw involved booting the image on a pi which I cannot do since I downloaded the image online. I even got ahold of a 32 GB card to solve the issue but then discovered that the card's true capacity is 31.9 GB, and so my image burning software says I have too little space. Does anyone know how I could shrink the size of this image to 16 GB?

Nicokofi
  • 101
  • 1
    It is possible to modify images on a Linux machine, although this requires some expertise. You could just buy a 64GB card. If someone is supplying 32GB images I wonder if it is worthwhile - it isn't hard to make a proper image. – Milliways Dec 01 '20 at 22:23
  • Balena Etcher has some options for resizing partitions - not tried it explored but maybe that's a way to look into?I guess the image was pulled from a 32Gb card after the partition was expanded and is full of blank space - whose was the image? Maybe its worth asking them for a higher one??? –  Dec 02 '20 at 20:16
  • You have to use a Linux machine where you saved the raw uncompressed image. Then execute this command and copy/paste its output to your question: sudo parted your-32gb.img print. If parted isn't available then install it. – Ingo Dec 03 '20 at 01:04
  • @Andyroo Are you sure that Balena can/will resize the ext4 filesystem in the root partition before it shrinks the partition itself? I suspect it doesn't have such a feature. If you use it just to shrink the partition and not the fs, the card probably won't work (and shouldn't be used that way even if it does, initially). – goldilocks Dec 06 '20 at 16:08
  • @goldilocks As I tried (and obviously failed) to explain I have no idea - I noticed it in passing the last time I created a card and if you are desperate it may be worth exploring. Worse case it's 15 mins wasted. –  Dec 06 '20 at 16:50

1 Answers1

-1

Assuming this is an image of an ext-formatted partition you can simply use resize2fs - as available on the pi itself or typical linux installations. So either you have a running linux somewhere, can put the image on an external USB-storage and access it via the pi, or can share it via network (including write premission) and use the pi. If this is given run

fsck name_of_image
resize2fs -M name_of_image

The first command runs a sanity check on the image, the second one resizes it to the minimum possible (i.e. skips empty parts).

Once the image is burnt to the SD, you may want to increase the size of the system to the maximum of the SD. Again resize will help in that case, however this is only possible when booted from the image or when the SD is directly connected to a pi/linux system. Alternatively windows tools for growing the partition should be available.


EDIT/UPDATE:

As stated in the comments resize2fs works on images of partitions only. So you would need to create separate images for the partitions.

In the following we'll thus

a) mount the big image you downloaded

b) extract the image of the rootfs partition & resize

Furthermore:

c) prepare a partition table for the new SD

d) copy the old boot and the new, smaller rottfs partitions & extend rootfs to the maximum

a) Mounting a multi-partition disk image

losetup will do the trick here and create /dev entries for the disk and partitions on the image:

losetup -Pf big.img

List them with just losetup (no options). In my example I took one of the official RasPi OS images and got it on loop10:

losetup
/dev/loop10  0  0  0  0 /data/downloads/2020-12-02-raspios-buster-armhf-lite.img  0  512

Now /dev will also have the two partitions ready:

ls /dev/loop10*
/dev/loop10  /dev/loop10p1  /dev/loop10p2

Use lsblk for some more info:

lsblk -o NAME,FSTYPE,LABEL,SIZE /dev/loop10
NAME       FSTYPE LABEL   SIZE
loop10                    1.7G
├─loop10p1 vfat   boot    256M
└─loop10p2 ext4   rootfs  1.5G

All as expected. Note that the downloaded file for the light image is ~440MB, yet the image shows 1.7GB. This is due to the partition table.

We'll make a quick trick by writing zeros to empty spaces of the partition image, this will make copying the image faster. Mount the image, create a dummy file, write zeros to it to fill up empty space, remove the dummy file and unmount the device. (Please note that this is only necessary if the image was not created with zeroed empty space). Be aware of dd-dangers and make sure you have no typos!

mount /dev/loop10p2 /mnt
touch /mnt/dummy
dd if=/dev/zero of=/mnt/dummy
rm /mnt/dummy
umount /mnt

b) Extracting the image of rootfs

dd is the best tool here, please again be aware of the dangers of dd and triple check each dd-command you execute. boot is no issue, the size should be around 250MB. Yet the big image must be copied once on the hard drive, say under your home:

dd if=/dev/loop10p2 of=/home/joe/local_rootfs.img bs=4M conv=sparce

A bytesize (bs) of 4M should be reasonably fast, conv=sparce will quickly skip empty parts - including the zeros we created in the last step of the previous section.

Now shrink the image to the minimum available:

fsck /home/joe/local_rootfs.img
resize2fs -M /home/joe/local_rootfs.img

It will now be the size of the data only.

c) Preparing the new SD

First of all, we will just copy the master boot record (MBR). Put your SD in and check which device it is with lsblk. Say it is /dev/sdb. AGAIN: Make sure you get the right device and be careful with dd!

We will copy the partition table of the image to the SD:

dd if=/path/to/big.img of=/dev/sdb bs=512 count=1

and delete the second partition in the table, while creating a new one. Example:

cfdisk /dev/sdb
Device     Boot  Start    End      Sectors   Size   Id Type
/dev/sdb1        2048     526335   524288    256M   83 Linux                  

>> /dev/sdb2 526336 4095999 3569664 1.7G 83 Linux

Navigate to the second partition and delete it, then create a new one with the maximum possible size (default value for new partition), write it and confirm with "yes", then quit. No need to care about which FS-type - this will come from the rootfs partition image.

d) Copy old boot and shrunk rootfs + expand it to SD size

Boot contents may just be copied, thus

dd if=/loop12p1 of=/dev/sdb1 bs=4M

As we copied the partition table, the size will fit exactly. Analogous we copy the content of the now smaller rootfs to the new SD partition:

dd if=/home/joe/local_rootfs.img of=/dev/sdb2 bs=4M

Resize the file system of the rootfs to fill up to the maximum available on the new SD:

resize2fs /dev/sdb2

(Maybe you need to run a fsck beforehand as above).

Final checks

It might have happened, that the UUIDs of your new partitions have changed. Mount the new rootfs and compare fstab with the PARTUUIDs of the partitions. Example:

mount /dev/sdb2 /mnt
cat /mnt/etc/fstab

proc /proc proc defaults 0 0 PARTUUID=067e19d7-01 /boot vfat defaults 0 2 PARTUUID=067e19d7-02 / ext4 defaults,noatime 0 1

versus results of blkid for sdb1 & sdb2 - if need be, update the fstab file - AND be sure to do it on the mount not your local /etc!!

Finally run sync and unmount just to be sure that all got written to the SD card properly before you remove it.

FelixJN
  • 109
  • 5
  • The command is resize2fs. 2. It does not shrink a partition. It shrinks a filesystem in the partition, meaning even if this worked, afterward the image file will still be exactly the same size. 3. It will not work anyway because device images for the Raspberry Pi have two fs partitions in them, and resize2fs only works on a filesystem image. The same it true WRT your fsck invocation.
  • – goldilocks Dec 06 '20 at 16:02
  • @goldilocks Ah yes, good point, indeed. Updated with proper way and corrected typo. – FelixJN Dec 08 '20 at 16:22