hpa notified me in private mail that we have an issue that when building
initramfs - if the content changes (say kinit) then the initramfs will
not be rebuild.
The solution I have come up with is to let make check all the files
included in the initramfs and rebuild it if any files are newer than the
initramfs and also rebuild if arguments changes (root_uid).
In same process the usr/Kbuild file was cluttered with functionality
that really belonged in a shell script.
So in the end all functionality was moved to the shell script
gen_initramfs_list.sh.
The script got a new name and a new home during the process.
It is now located in usr/ along with gen_init_cpio
Outstanding issues:
- needs to find a nice way to get rid of temp files
- carefull review when I'm awake again
Posting it here to get some inital comments on the approach and maybe
some usefull comments on my less-skilled shell script programming.
The files are just pasted in below for comments. Gimme a day more to
finish it up.
Sam
Restructured usr/Kbuild:
#
# kbuild file for usr/ - including initramfs image and klibc
#
# klibc definitions - to be moved to top-level Makefile later
export KLIBCSRC := $(srctree)/$(src)/klibc
export KLIBCINC := $(srctree)/$(src)/include
export KLIBCOBJ := $(objtree)/$(obj)/klibc
CONFIG_KLIBC := 1
# Klibc binaries
ifdef CONFIG_KLIBC
klibc := -f $(srctree)/scripts/Kbuild.klibc obj
.PHONY: klibcdirs
$(obj)/initramfs_list: klibcdirs
klibcdirs: FORCE
$(Q)$(MAKE) $(klibc)=$(src)/klibc
$(Q)$(MAKE) $(klibc)=$(src)/kinit
$(Q)$(MAKE) $(klibc)=$(src)/utils
$(Q)$(MAKE) $(klibc)=$(src)/dash
$(Q)$(MAKE) $(klibc)=$(src)/gzip
endif
subdir- := klibc kinit utils dash gzip
# Generate builtin.o based on initramfs_data.o
obj-y := initramfs_data.o
# initramfs_data.o contains the initramfs_data.cpio.gz image.
# The image is included using .incbin, a dependency which is not
# tracked automatically.
$(obj)/initramfs_data.o: $(obj)/initramfs_data.cpio.gz FORCE
#####
# Generate the initramfs cpio archive
# Following steps are executed:
# Create list of files to be included in archive
# If any of the files are newer than target generate new cpio archive
# Create cpio archive from filelist
hostprogs-y := gen_init_cpio
# Files included in cpio archive (cpio archive is updated if
# any included files are newer than the cpio archive)
initramfs := $(CONFIG_SHELL) $(srctree)/$(src)/gen_initramfs
ramfs-input = $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \
$(CONFIG_INITRAMFS_SOURCE),-d)
ramfs-filelist = $(shell $(initramfs) -l $(ramfs-input))
quiet_cmd_initfs = GEN $@
cmd_initfs = \
$(initramfs) -o $@ \
$(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \
$(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \
$(ramfs-input)
targets := initramfs_data.cpio.gz
$(obj)/initramfs_data.cpio.gz: $(ramfs-filelist) $(obj)/gen_init_cpio FORCE
$(call if_changed,initfs)
# Cleaning time
clean-files := initramfs_list initramfs_data.cpio.gz
The restructured usr/gen_initramfs shell script:
#!/bin/bash
# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
# Released under the terms of the GNU GPL
#
# Generate a newline separated list of entries from the file/directory
# supplied as an argument.
#
# If a file/directory is not supplied then generate a small dummy file.
#
# The output is suitable for gen_init_cpio built from usr/gen_init_cpio.c.
#
list_default_initramfs() {
echo usr/kinit/kinit
}
default_initramfs() {
cat <<-EOF >> ${cpio_list}
# This is a very simple, default initramfs
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
dir /root 0700 0 0
# file /kinit usr/kinit/kinit 0755 0 0
# slink /init kinit 0755 0 0
file /init usr/kinit/kinit 0755 0 0
EOF
}
filetype() {
local argv1="$1"
# symlink test must come before file test
if [ -L "${argv1}" ]; then
echo "slink"
elif [ -f "${argv1}" ]; then
echo "file"
elif [ -d "${argv1}" ]; then
echo "dir"
elif [ -b "${argv1}" -o -c "${argv1}" ]; then
echo "nod"
elif [ -p "${argv1}" ]; then
echo "pipe"
elif [ -S "${argv1}" ]; then
echo "sock"
else
echo "invalid"
fi
return 0
}
list_print_mtime() {
:
}
print_mtime() {
local argv1="$1"
local my_mtime="0"
if [ -e "${argv1}" ]; then
my_mtime=$(find "${argv1}" -printf "%T@\n" | sort -r |
head -n 1)
fi
echo "# Last modified: ${my_mtime}" >> ${cpio_list}
echo "" >> ${cpio_list}
}
list_parse() {
echo "$1"
}
parse() {
local location="$1"
local name="${location/${srcdir}//}"
# change '//' into '/'
name="${name//\/\///}"
local mode="$2"
local uid="$3"
local gid="$4"
local ftype=$(filetype "${location}")
# remap uid/gid to 0 if necessary
[ "$uid" -eq "$root_uid" ] && uid=0
[ "$gid" -eq "$root_gid" ] && gid=0
local str="${mode} ${uid} ${gid}"
[ "${ftype}" == "invalid" ] && return 0
[ "${location}" == "${srcdir}" ] && return 0
case "${ftype}" in
"file")
str="${ftype} ${name} ${location} ${str}"
;;
"nod")
local dev_type local maj=$(LC_ALL=C ls -l "${location}" | \
gawk '{sub(/,/, "", $5); print $5}')
local min=$(LC_ALL=C ls -l "${location}" | \
gawk '{print $6}')
if [ -b "${location}" ]; then
dev_type="b"
else
dev_type="c"
fi
str="${ftype} ${name} ${str} ${dev_type} ${maj} ${min}"
;;
"slink")
local target=$(LC_ALL=C ls -l "${location}" | \
gawk '{print $11}')
str="${ftype} ${name} ${target} ${str}"
;;
*)
str="${ftype} ${name} ${str}"
;;
esac
echo "${str}" >> ${cpio_list}
return 0
}
usage() {
cat << EOF
Usage:
$0 [-o <file>] [-u <uid>] [-g <gid>] [-d |
<cpio_source>] ] ...
$0 -l [-u <uid>] [-g <gid>] [-d | <cpio_source>] ] ...
-o <file> Create gzipped initramfs file using gen_init_cpio
-l List all files that will be added to initramfs
-u <uid> User ID to map to user ID 0 (root).
<uid> is only meaningful if <cpio_source>
is a directory.
-g <gid> Group ID to map to group ID 0 (root).
<gid> is only meaningful if <cpio_source>
is a directory.
<cpio_source> File list or directory for cpio archive.
If <cpio_source> is a .cpio file it will be used
as input to initramfs.
-d Output the default cpio list.
All options may be repeated and are interpreted sequentially
and immediately. -u and -g states are preserved across
<cpio_source> options so an explicit "-u 0 -g 0" is required
to reset the root/group mapping.
EOF
}
unknown_option() {
printf "ERROR: unknown option \"$arg\"\n" >&2
printf "If the filename validly begins with '-', " >&2
printf "then it must be prefixed\n" >&2
printf "by './' so that it won't be interpreted as an
option." >&2
printf "\n" >&2
usage >&2
exit 1
}
list_header() {
:
}
header() {
printf "\n#####################\n# $1\n" >> ${cpio_list}
}
dir_filelist() {
${file_list}header "$1"
srcdir=$(echo "$1" | sed -e 's://*:/:g')
dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n"
2>/dev/null)
# If $dirlist is only one line, then the directory is empty
if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
${file_list}print_mtime "$1"
echo "${dirlist}" | \
while read x; do
${file_list}parse ${x}
done
fi
}
# if only one file is specified and it is .cpio file then use it direct as fs
# if a directory is specified then add all files in given direcotry to fs
# if a regular file is specified assume it is in gen_initramfs format
input_file() {
source="$1"
if [ -f "$1" ]; then
is_cpio="$(echo "$1" | sed 's/^.\*\.cpio/cpio/')"
if [ $2 -eq 1 -a ${is_cpio} == "cpio" ]; then
cpio_file=$1
[ ! -z ${list_file} ] && echo "$1"
return 0
fi
if [ -z ${file_list} ]; then
header "$1"
print_mtime "$1" >> ${cpio_list}
cat "$1" >> ${cpio_list}
else
grep ^file "$1" | cut -d ' ' -f 3
fi
elif [ -d "$1" ]; then
dir_filelist "$1"
else
echo " ${prog}: Cannot open '$1'" >&2
exit 1
fi
}
prog=$0
root_uid=0
root_gid=0
file_listcpio_filecpio_list="/dev/stdout"
output_file=""
input_seen
arg="$1"
case "$arg" in
"-l")
file_list="list_"
shift
;;
"-o")
shift
output_file="$1"
cpio_list="$(mktemp /tmp/cpiolist.XXXXXX)"
shift
;;
esac
while [ $# -gt 0 ]; do
arg="$1"
shift
case "$arg" in
"-u")
root_uid="$1"
shift
;;
"-g")
root_gid="$1"
shift
;;
"-d")
default_list="$arg"
${file_list}default_initramfs
;;
"-h")
usage
exit 0
;;
*)
case "$arg" in
"-"*)
unknown_option
;;
*)
input_file "$arg" "$#"
input_seen=y
;;
esac
;;
esac
done
[ -z ${input_seen} ] && ${file_list}default_initramfs
[ ! -z ${file_list} ] && exit 0
# If output_file is set we will generate cpio archive and gzip it
if [ ! -z ${output_file} ]; then
if [ -z ${cpio_file} ]; then
cpio_file="$(mktemp /tmp/cpiofile.XXXXXX)"
$(dirname ${prog})/gen_init_cpio ${cpio_list} > ${cpio_file}
fi
cat ${cpio_file} | gzip -f -9 - > ${output_file}
fi
exit 0