1 #!/bin/sh 2 3 dir="$1" 4 dev="$2" 5 6 if [ "$1" = "--help" ] || [ ! -d "${dir}" ]; then 7 echo "Usage: $0 dir [mke2fs args] dev" 8 exit 1 9 fi 10 11 shift 12 13 # Goal: Put all the files at the beginning (which mke2fs does) and minimize 14 # the number of free inodes given the minimum number of blocks required. 15 # Hence all this math to get the inode ratio just right. 16 17 bytes="$(du -ks "${dir}" | awk '{print $1}')" 18 bytes="$((bytes * 1024))" 19 inodes="$(find "${dir}" -print0 | xargs -0 stat -c '%i' | sort -g | uniq | wc -l)" 20 block_sz=4096 21 inode_sz=256 22 sb_overhead=4096 23 blocks_per_group="$((block_sz * 8))" 24 bytes_per_group="$((blocks_per_group * block_sz))" 25 inode_bytes="$((inodes * inode_sz))" 26 27 # Estimate overhead with the minimum number of groups... 28 nr_groups="$(( (bytes + inode_bytes + bytes_per_group - 1) / bytes_per_group))" 29 inode_bytes_per_group="$((inode_bytes / nr_groups))" 30 inode_blocks_per_group="$(( (inode_bytes_per_group + (block_sz - 1)) / block_sz ))" 31 per_grp_overhead="$(( ((3 + inode_blocks_per_group) * block_sz) + 64 ))" 32 overhead="$(( sb_overhead + (per_grp_overhead * nr_groups) ))" 33 used_bytes="$((bytes + overhead))" 34 35 # Then do it again with the real number of groups. 36 nr_groups="$(( (used_bytes + (bytes_per_group - 1)) / bytes_per_group))" 37 tot_blocks="$((nr_groups * blocks_per_group))" 38 tot_bytes="$((tot_blocks * block_sz))" 39 40 ratio="$((bytes / inodes))" 41 mkfs_blocks="$((tot_blocks * 4 / 3))" 42 43 mke2fs -i "${ratio}" -T ext4 -d "${dir}" -O ^resize_inode,sparse_super2,metadata_csum,64bit,^has_journal -E packed_meta_blocks=1,num_backup_sb=0 -b "${block_sz}" -I "${inodesz}" -F "${dev}" "${mkfs_blocks}" || exit 44 45 e2fsck -fyD "${dev}" 46 47 blocks="$(dumpe2fs -h "${dev}" 2>&1 | grep 'Block count:' | awk '{print $3}')" 48 while resize2fs -f -M "${dev}"; do 49 new_blocks="$(dumpe2fs -h "${dev}" 2>&1 | grep 'Block count:' | awk '{print $3}')" 50 if [ "${new_blocks}" -eq "${blocks}" ]; then 51 break; 52 fi 53 blocks="${new_blocks}" 54 done 55 56 if [ ! -b "${dev}" ]; then 57 truncate -s "$((blocks * block_sz))" "${dev}" || (e2image -ar "${dev}" "${dev}.min"; mv "${dev}.min" "${dev}") 58 fi 59 60 e2fsck -fy "${dev}" 61 62 dir_blocks="$((bytes / block_sz))" 63 overhead="$((blocks - dir_blocks))" 64 echo "Minimized image overhead: $((100 * overhead / dir_blocks))%" 65 66 exit 0 67