I regularly backup my Raspi using a script that rsyncs rootfs and /boot onto a image created with losetup. This is the current branch I am working on:
https://github.com/ephestione/RaspiBackup/blob/autosize-root-plus-free-space/RaspiBackup.sh
The improvement over the master branch is that, instead of determining the image size by using the rootfs partition size (still usually better than using the whole device size), I want to directly create a shrinked image, where the rootfs partition size is pre-calculated to be just enough to fit the extant data, plus a forfait 100MB free space (I can expand filesystem after restore if needed). In the first version I used:
df / --output=used | tail -n1
to determine the target partition size, then adding 250MB for boot and 100MB of free space, but it was incorrect, as I wasn't taking into account the reserved blocks. The resulting image, even by adding 100MB of space to the determined size, resulted as 100% in use, and I suspect was still smaller than the real data.
So, now, I use the difference between:
df / --output=size | tail -n1
and
df / --output=avail | tail -n1
like so:
ROOTPU=`expr "$(grep '/ ' /etc/fstab)" : 'PARTUUID=\(.[a-z0-9\-]*\)'`
ROOT=/dev/disk/by-partuuid/$ROOTPU
BOOTSIZE=250
ROOTFREE=100
SIZE=$(blockdev --getsz $ROOT)
# calc kilobytes size of data on rootfs
AVAILROOT=$(df / --output=avail | tail -n1)
SIZEROOT=$(df / --output=size | tail -n1)
((USEDROOT=SIZEROOT-AVAILROOT))
# bytes size of backup rootfs including free space after data
((USEDROOT=USEDROOT*1024 + ROOTFREE*1024*1024))
BLOCKSIZE=$(blockdev --getss $ROOT)
# blocks count of rootfs round up division result:
((SIZE=(USEDROOT+BLOCKSIZE-1)/BLOCKSIZE + BOOTSIZE*1024*1024/BLOCKSIZE))
#[...]
dd if=/dev/zero of="${IMAGE}" bs=${BLOCKSIZE} count=0 seek=${SIZE}
which is still WAY better than just imaging an expanded filesystem on a 32GB SD card, but it will yield an image which is 1GB bigger than it needs to be (1.1GB of resulting free space using df -h
on the restored system).
EDIT: I did some further research, taking as an example my own filesystem.
Right now I have a 4.3GB partition as reported by df
.
So let's say I want to determine the total size of the root partition on Raspbian:
sudo blockdev --getsz /dev/mmcblk0p2
multiplied by sudo blockdev --getsz /dev/mmcblk0p2
returns (512*9142272):
4 680 843 264 bytes
default df
with no other parameters (1k blocks of 1024bytes each, 4433716*1024) reports:
4 540 125 184 bytes
sudo tune2fs -l /dev/mmcblk0p2
with block count * block size (1142784*4096):
4 680 843 264 bytes
Two out of three methods, tune2fs and blockdev, report the same total size of the partition, while df reports a smaller size. The difference though, which is 134MB, doesn't seem to really account for the "missing" space I found... I'm at a loss.
EDIT 2: I just tested by using the values reported from tune2fs -l $ROOT
, namely doing ((Block count)-(Free blocks))*(Block size), and then added 100MB.
This yielded an image 200somethingMB bigger than the df
method, still (And I already knew it even before trying) on restore, df
reported 0 available blocks... at this point I know that a "0 free space" image with 250MB boot should be about 3.7GB, but with this latest method, even adding 100MB to the plate, I get a 3.4GB image size...
EDIT 3: now this is getting funny. So I started doing calculations by using df / --output=used
as base, and increasing it by 5%, and adding 200MB (I actually went further than that, and used tune2fs
to determine current percent of reserved blocks... which ended up being 5% anyway). I started getting images with just around 60MB free space instead of 200. WTH? I tried then just using "used space"*1.05 as image size, and I just now booted a such restored backup, this is the output of df /
:
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/root 3081144 3030756 0 100% /
and these are the relevant info from tune2fs -l /dev/mmcblk0p2
:
Block count: 799232
Reserved block count: 39961
Free blocks: 9524
According to tune2fs I should have 9524*4096/1024/1024
megabytes free, which amounts to 37MB, surely no 0 anyway. What gives? Who's the scumbag here? df
, tune2fs
or the filesystem?
Question: is there a way to predictably determine the image size needed to have a resulting backup image which is more or less ${ROOTFREE}
MBs larger than the extant data on rootfs plus 250MB boot partition?
df
reports in bytes is always an even multiple of the filesystem block size -- not, as per df output in blocks, a default 1K. So, eg., if you have a directory with 10 1 byte files on a 4096 byte/block fs, df will report the size as 44K (not 11K), 1 block for the directory and 1 block for each (non-zero size) file.df
reported 1K blocks while insteadtune2fs
reported 4K blocksdf
andblockdev
before you shrink the file system.(used+100MB)*105/100
, isn't that correct?