mkinitrd(8): determine the initrd size from the contents size
authorAaron LI <aly@aaronly.me>
Fri, 30 Mar 2018 06:32:59 +0000 (14:32 +0800)
committerAaron LI <aly@aaronly.me>
Fri, 30 Mar 2018 07:37:16 +0000 (15:37 +0800)
* Calculate the contents size to determine the required initrd size.

  The used initrd size is determined from the total contents size, rounded up
  to align with 1 MiB, added additional 1 MiB.

  The calculated initrd size is 12 MiB at the moment, so we have 3 MiB
  more usable memory now (compared to the fixed 15 MiB before).

* Add options "-s" to specify the required initrd size as before.  But a check
  against the above determined required initrd size is performance to avoid the
  out-of-space error, resulting in a broken initrd image.

* Add option "-S" to constrain the allowed maximum initrd size, because a too
  big initrd image will also lead to boot failure, due to the boot loader
  limitation.

* Some minor cleanups and improvements, better error handle and messages.

* Update the mkinitrd.conf and man page accordingly.

Reviewed-by: zrj
etc/defaults/mkinitrd.conf
sbin/mkinitrd/mkinitrd.8
sbin/mkinitrd/mkinitrd.sh

index aa66f16..ae3ac1e 100644 (file)
@@ -2,6 +2,6 @@
 #
 BOOT_DIR="/boot"
 CONTENT_DIRS="/usr/share/initrd"
-INITRD_DIRS="bin boot dev etc mnt proc root sbin tmp var var/empty var/db new_root"
-INITRD_SIZE="15m"
+INITRD_DIRS="bin dev etc sbin tmp var new_root"
+INITRD_SIZE_MAX="15"  # [MiB]
 TMP_DIR="/tmp"
index 028220a..eb28a48 100644 (file)
@@ -1,5 +1,5 @@
 .\"
-.\" Copyright (c) 2010 The DragonFly Project.  All rights reserved.
+.\" Copyright (c) 2010,2018 The DragonFly Project.  All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
 .\" modification, are permitted provided that the following conditions
@@ -28,7 +28,7 @@
 .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 3, 2015
+.Dd March 30, 2018
 .Dt MKINITRD 8
 .Os
 .Sh NAME
 .Op Fl b Ar bootdir
 .Op Fl c Ar contentsdir
 .Op Fl t Ar tmpdir
+.Op Fl s Ar size
+.Op Fl S Ar max_size
 .Sh DESCRIPTION
 The
 .Nm
 script builds a ramdisk (md) image based on the UFS filesystem containing
 only the most basic tools, such as a minimal
 .Xr init 8 ,
-.Xr sh 1
-as well as many
+.Xr sh 1 ,
+various
 .Xr mount 8
-utilities.
+utilities,
+.Xr lvm 8 ,
+.Xr cryptsetup 8 ,
+.Xr tcplay 8 ,
+as well as some basic networking tools.
+And the ramdisk is required to help mount the encrypted root partition.
 .Pp
-It will also copy the contents of
+The contents of
 .Pa /usr/share/initrd
-onto the ramdisk, maintaining the same hierarchy.
+will be copied onto the ramdisk, maintaining the same hierarchy.
 This directory can be changed with the
 .Fl c
 option.
 .Pp
+By default
+.Nm
+will calculate the contents size and create a ramdisk just big enough.
+The
+.Fl s
+option accepting an integer (number of MiB) can specify the required
+size of the ramdisk.
+The special value of
+.Pa 0
+can be given to ignore the
+.Pa INITRD_SIZE
+setting from the configuration file as described below.
+The
+.Fl S
+option also accepts an integer (number of MiB) and constrains the maximum
+allowed size of the ramdisk.
+Specifying a value of
+.Pa 0
+will ignore the
+.Pa INITRD_SIZE_MAX
+setting from the configuration file.
+.Pp
 On completion, the final image will be copied to
 .Pa /boot/kernel/initrd.img.gz ,
 ready to be used as an early userland.
@@ -82,7 +111,7 @@ following variables (defaults are in
 .Pa /etc/defaults/mkinitrd.conf ) :
 .Bd -literal -offset indent
 BUILD_DIR
-INITRD_SIZE
+INITRD_SIZE_MAX
 INITRD_DIRS
 CONTENT_DIRS
 .Ed
@@ -144,6 +173,9 @@ vfs.root.realroot="local:ufs:/dev/vg00/lv0[:OPTIONS]"
 .Bd -literal -offset indent
 vfs.root.realroot="crypt:ufs:/dev/ad0s0a:secvolume[:OPTIONS]"
 .Ed
+.Bd -literal -offset indent
+vfs.root.realroot="crypt:hammer2:serno/XXXXXX.s1d:root[:OPTIONS]"
+.Ed
 .Sh SEE ALSO
 .Xr md 4 ,
 .Xr loader.conf 5
index 38bc723..714573f 100644 (file)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (c) 2010
+# Copyright (c) 2010,2018
 #      The DragonFly Project.  All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -40,26 +40,59 @@ fi
 
 VN_DEV=""
 
+# Calculate the total size of the given directory, taking care of the
+# hard links.
+calc_size()
+{
+       find "$1" -ls | \
+           awk '{ print $7,$1 }' | \
+           sort -n -k 2 | \
+           uniq -f 1 | \
+           awk '{ sum+=$1 } END { print sum }'  # [byte]
+}
+
+calc_initrd_size()
+{
+       echo "Contents directories:" >&2
+       isize=0
+       for dir in ${CONTENT_DIRS}; do
+               csize=$(calc_size ${dir})
+               echo "* ${dir}: ${csize} bytes" >&2
+               isize=$((${isize} + ${csize}))
+       done
+       # Round initrd size up by MiB
+       isize_mb=$(echo "${isize}" | awk '
+           function ceil(x) {
+               y = int(x);
+               return (x>y ? y+1 : y);
+           }
+           {
+               mb = $1/1024/1024;
+               print ceil(mb);
+           }')
+       # Add additional 1 MiB
+       echo $((${isize_mb} + 1))
+}
+
 create_vn()
 {
-       if [ ! -d  $BUILD_DIR ]; then
+       if [ ! -d "$BUILD_DIR" ]; then
                mkdir -p $BUILD_DIR
                echo "Created build directory $BUILD_DIR"
        fi
-       vnconfig -c -S ${INITRD_SIZE} -Z -T vn ${TMP_DIR}/initrd.img \
-           > ${TMP_DIR}/vndev.mkinitrd
-       if [ $? -ne 0 ]; then
-           echo "Failed to configure vn device"
-           exit 1
-       fi
+       vnconfig -c -S ${INITRD_SIZE}m -Z -T vn ${TMP_DIR}/initrd.img \
+           > ${TMP_DIR}/vndev.mkinitrd || {
+               echo "Failed to configure vn device"
+               exit 1
+       }
 
        VN_DEV=`cat ${TMP_DIR}/vndev.mkinitrd | cut -f 2 -d ' '`
-       [ -f ${TMP_DIR}/vndev.mkinitrd ] && rm ${TMP_DIR}/vndev.mkinitrd
+       rm ${TMP_DIR}/vndev.mkinitrd
 
        echo "Configured $VN_DEV"
        newfs -i 131072 -m 0 /dev/${VN_DEV}s0
        echo "Formatted initrd image with UFS"
-       mount /dev/${VN_DEV}s0 $BUILD_DIR
+       mount -t ufs /dev/${VN_DEV}s0 $BUILD_DIR
        echo "Mounted initrd image on ${BUILD_DIR}"
 }
 
@@ -83,24 +116,27 @@ make_hier()
 copy_content()
 {
        for dir in ${CONTENT_DIRS}; do
-               cpdup ${dir}/ ${BUILD_DIR}/
+               cpdup -o -u ${dir}/ ${BUILD_DIR}/ || {
+                       echo "Failed to copy ${dir} to ${BUILD_DIR}"
+                       exit 1
+               }
        done
 }
 
 print_info()
 {
        lt ${BUILD_DIR}
-       df -h | head -n 1
-       df -h | grep $VN_DEV
+       df -h ${BUILD_DIR}
 }
 
 usage()
 {
-       echo "usage: $0 [-b bootdir] [-c contentsdir] [-t tmpdir]"
+       echo "usage: ${0##*/} [-b bootdir] [-c contentsdir] [-t tmpdir]" \
+            "[-s size] [-S max_size]"
        exit 2
 }
 
-args=`getopt b:c:t: $*`
+args=`getopt b:c:s:S:t: $*`
 test $? -ne 0 && usage
 
 set -- $args
@@ -108,20 +144,47 @@ for i; do
        case "$i" in
        -b)     BOOT_DIR="$2"; shift; shift;;
        -c)     CONTENT_DIR="$2"; shift; shift;;
+       -s)     INITRD_SIZE="$2"; shift; shift;;
+       -S)     INITRD_SIZE_MAX="$2"; shift; shift;;
        -t)     TMP_DIR="$2"; shift; shift;;
        --)     shift; break;
        esac
 done
-test ! -d ${BOOT_DIR} && usage
+test ! -d ${BOOT_DIR}    && usage
 test ! -d ${CONTENT_DIR} && usage
-test ! -d ${TMP_DIR} && usage
-test ! -z $1 && usage
+test ! -d ${TMP_DIR}     && usage
+test ! -z "$1"           && usage
+
 BUILD_DIR="${TMP_DIR}/initrd"
+INITRD_SIZE=${INITRD_SIZE%[mM]}  # [MiB]
+INITRD_SIZE_MAX=${INITRD_SIZE_MAX%[mM]}  # [MiB]
+
+CSIZE=$(calc_initrd_size)
+echo "Required initrd image size: ${CSIZE} MiB"
+if [ -n "${INITRD_SIZE}" -a "${INITRD_SIZE}" != "0" ]; then
+       if [ ${CSIZE} -gt ${INITRD_SIZE} ]; then
+               echo "Specified initrd size (${INITRD_SIZE} MiB) too small"
+               exit 1
+       fi
+else
+       INITRD_SIZE=${CSIZE}
+fi
+echo "Initrd size: ${INITRD_SIZE} MiB"
+
+if [ -n "${INITRD_SIZE_MAX}" -a "${INITRD_SIZE_MAX}" != "0" ] && \
+   [ ${INITRD_SIZE} -gt ${INITRD_SIZE_MAX} ]; then
+       echo "Exceeded the specified maximum size (${INITRD_SIZE_MAX} MiB)"
+       exit 1
+fi
 
 create_vn
 copy_content
 make_hier
 print_info
 destroy_vn
-/usr/bin/gzip -9 ${TMP_DIR}/initrd.img
+echo -n "Compressing ${TMP_DIR}/initrd.img ..."
+gzip -9 ${TMP_DIR}/initrd.img
+echo " OK"
+echo -n "Copying ${TMP_DIR}/initrd.img.gz to ${BOOT_DIR}/kernel/initrd.img.gz ..."
 mv ${TMP_DIR}/initrd.img.gz ${BOOT_DIR}/kernel/initrd.img.gz
+echo " OK"