Commit d39a206bc35d46a3b2eb98cd4f34e340d5e56a50

Authored by Sam Ravnborg
1 parent d9df92e22a

kbuild: rebuild initramfs if content of initramfs changes

initramfs.cpio.gz being build in usr/ and included in the
kernel was not rebuild when the included files changed.

To fix this the following was done:
- let gen_initramfs.sh generate a list of files and directories included
  in the initramfs
- gen_initramfs generate the gzipped cpio archive so we could simplify
  the kbuild file (Makefile)
- utilising the kbuild infrastructure so when uid/gid root mapping changes
  the initramfs will be rebuild

With this change we have a much more robust initramfs generation.

Signed-off-by: Sam Ravnborg <sam@ravnborg.org>

Showing 2 changed files with 189 additions and 127 deletions Side-by-side Diff

scripts/gen_initramfs_list.sh
1 1 #!/bin/bash
2 2 # Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
  3 +# Copyright (c) 2006 Sam Ravnborg <sam@ravnborg.org>
  4 +#
3 5 # Released under the terms of the GNU GPL
4 6 #
5   -# Generate a newline separated list of entries from the file/directory
6   -# supplied as an argument.
7   -#
8   -# If a file/directory is not supplied then generate a small dummy file.
9   -#
10   -# The output is suitable for gen_init_cpio built from usr/gen_init_cpio.c.
11   -#
  7 +# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
  8 +# the cpio archive, and gzip to pack it.
  9 +# The script may also be used to generate the inputfile used for gen_init_cpio
  10 +# This script assumes that gen_init_cpio is located in usr/ directory
12 11  
  12 +# error out on errors
  13 +set -e
  14 +
  15 +usage() {
  16 +cat << EOF
  17 +Usage:
  18 +$0 [-o <file>] [-u <uid>] [-g <gid>] {-d | <cpio_source>} ...
  19 + -o <file> Create gzipped initramfs file named <file> using
  20 + gen_init_cpio and gzip
  21 + -u <uid> User ID to map to user ID 0 (root).
  22 + <uid> is only meaningful if <cpio_source>
  23 + is a directory.
  24 + -g <gid> Group ID to map to group ID 0 (root).
  25 + <gid> is only meaningful if <cpio_source>
  26 + is a directory.
  27 + <cpio_source> File list or directory for cpio archive.
  28 + If <cpio_source> is a .cpio file it will be used
  29 + as direct input to initramfs.
  30 + -d Output the default cpio list.
  31 +
  32 +All options except -o and -l may be repeated and are interpreted
  33 +sequentially and immediately. -u and -g states are preserved across
  34 +<cpio_source> options so an explicit "-u 0 -g 0" is required
  35 +to reset the root/group mapping.
  36 +EOF
  37 +}
  38 +
  39 +list_default_initramfs() {
  40 + # echo usr/kinit/kinit
  41 + :
  42 +}
  43 +
13 44 default_initramfs() {
14   - cat <<-EOF
  45 + cat <<-EOF >> ${output}
15 46 # This is a very simple, default initramfs
16 47  
17 48 dir /dev 0755 0 0
18 49 nod /dev/console 0600 0 0 c 5 1
19 50 dir /root 0700 0 0
  51 + # file /kinit usr/kinit/kinit 0755 0 0
  52 + # slink /init kinit 0755 0 0
20 53 EOF
21 54 }
22 55  
23 56  
24 57  
25 58  
26 59  
... ... @@ -42,18 +75,28 @@
42 75 return 0
43 76 }
44 77  
  78 +list_print_mtime() {
  79 + :
  80 +}
  81 +
45 82 print_mtime() {
46   - local argv1="$1"
47 83 local my_mtime="0"
48 84  
49   - if [ -e "${argv1}" ]; then
50   - my_mtime=$(find "${argv1}" -printf "%T@\n" | sort -r | head -n 1)
  85 + if [ -e "$1" ]; then
  86 + my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
51 87 fi
52   -
53   - echo "# Last modified: ${my_mtime}"
54   - echo
  88 +
  89 + echo "# Last modified: ${my_mtime}" >> ${output}
  90 + echo "" >> ${output}
55 91 }
56 92  
  93 +list_parse() {
  94 + echo "$1 \\"
  95 +}
  96 +
  97 +# for each file print a line in following format
  98 +# <filetype> <name> <path to file> <octal mode> <uid> <gid>
  99 +# for links, devices etc the format differs. See gen_init_cpio for details
57 100 parse() {
58 101 local location="$1"
59 102 local name="${location/${srcdir}//}"
60 103  
61 104  
62 105  
63 106  
64 107  
65 108  
66 109  
67 110  
68 111  
69 112  
70 113  
71 114  
72 115  
73 116  
... ... @@ -99,80 +142,112 @@
99 142 ;;
100 143 esac
101 144  
102   - echo "${str}"
  145 + echo "${str}" >> ${output}
103 146  
104 147 return 0
105 148 }
106 149  
107   -usage() {
108   - printf "Usage:\n"
109   - printf "$0 [ [-u <root_uid>] [-g <root_gid>] [-d | <cpio_source>] ] . . .\n"
110   - printf "\n"
111   - printf -- "-u <root_uid> User ID to map to user ID 0 (root).\n"
112   - printf " <root_uid> is only meaningful if <cpio_source>\n"
113   - printf " is a directory.\n"
114   - printf -- "-g <root_gid> Group ID to map to group ID 0 (root).\n"
115   - printf " <root_gid> is only meaningful if <cpio_source>\n"
116   - printf " is a directory.\n"
117   - printf "<cpio_source> File list or directory for cpio archive.\n"
118   - printf " If <cpio_source> is not provided then a\n"
119   - printf " a default list will be output.\n"
120   - printf -- "-d Output the default cpio list. If no <cpio_source>\n"
121   - printf " is given then the default cpio list will be output.\n"
122   - printf "\n"
123   - printf "All options may be repeated and are interpreted sequentially\n"
124   - printf "and immediately. -u and -g states are preserved across\n"
125   - printf "<cpio_source> options so an explicit \"-u 0 -g 0\" is required\n"
126   - printf "to reset the root/group mapping.\n"
  150 +unknown_option() {
  151 + printf "ERROR: unknown option \"$arg\"\n" >&2
  152 + printf "If the filename validly begins with '-', " >&2
  153 + printf "then it must be prefixed\n" >&2
  154 + printf "by './' so that it won't be interpreted as an option." >&2
  155 + printf "\n" >&2
  156 + usage >&2
  157 + exit 1
127 158 }
128 159  
129   -build_list() {
130   - printf "\n#####################\n# $cpio_source\n"
  160 +list_header() {
  161 + echo "deps_initramfs := \\"
  162 +}
131 163  
132   - if [ -f "$cpio_source" ]; then
133   - print_mtime "$cpio_source"
134   - cat "$cpio_source"
135   - elif [ -d "$cpio_source" ]; then
136   - srcdir=$(echo "$cpio_source" | sed -e 's://*:/:g')
137   - dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" 2>/dev/null)
  164 +header() {
  165 + printf "\n#####################\n# $1\n" >> ${output}
  166 +}
138 167  
139   - # If $dirlist is only one line, then the directory is empty
140   - if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
141   - print_mtime "$cpio_source"
142   -
143   - echo "${dirlist}" | \
144   - while read x; do
145   - parse ${x}
146   - done
  168 +# process one directory (incl sub-directories)
  169 +dir_filelist() {
  170 + ${dep_list}header "$1"
  171 +
  172 + srcdir=$(echo "$1" | sed -e 's://*:/:g')
  173 + dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" 2>/dev/null)
  174 +
  175 + # If $dirlist is only one line, then the directory is empty
  176 + if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
  177 + ${dep_list}print_mtime "$1"
  178 +
  179 + echo "${dirlist}" | \
  180 + while read x; do
  181 + ${dep_list}parse ${x}
  182 + done
  183 + fi
  184 +}
  185 +
  186 +# if only one file is specified and it is .cpio file then use it direct as fs
  187 +# if a directory is specified then add all files in given direcotry to fs
  188 +# if a regular file is specified assume it is in gen_initramfs format
  189 +input_file() {
  190 + source="$1"
  191 + if [ -f "$1" ]; then
  192 + ${dep_list}header "$1"
  193 + is_cpio="$(echo "$1" | sed 's/^.*\.cpio/cpio/')"
  194 + if [ $2 -eq 0 -a ${is_cpio} == "cpio" ]; then
  195 + cpio_file=$1
  196 + [ ! -z ${dep_list} ] && echo "$1"
  197 + return 0
  198 + fi
  199 + if [ -z ${dep_list} ]; then
  200 + print_mtime "$1" >> ${output}
  201 + cat "$1" >> ${output}
147 202 else
148   - # Failsafe in case directory is empty
149   - default_initramfs
  203 + grep ^file "$1" | cut -d ' ' -f 3
150 204 fi
  205 + elif [ -d "$1" ]; then
  206 + dir_filelist "$1"
151 207 else
152   - echo " $0: Cannot open '$cpio_source'" >&2
  208 + echo " ${prog}: Cannot open '$1'" >&2
153 209 exit 1
154 210 fi
155 211 }
156 212  
157   -
  213 +prog=$0
158 214 root_uid=0
159 215 root_gid=0
  216 +dep_list=
  217 +cpio_file=
  218 +cpio_list=
  219 +output="/dev/stdout"
  220 +output_file=""
160 221  
  222 +arg="$1"
  223 +case "$arg" in
  224 + "-l") # files included in initramfs - used by kbuild
  225 + dep_list="list_"
  226 + shift
  227 + ;;
  228 + "-o") # generate gzipped cpio image named $1
  229 + shift
  230 + output_file="$1"
  231 + cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
  232 + output=${cpio_list}
  233 + shift
  234 + ;;
  235 +esac
161 236 while [ $# -gt 0 ]; do
162 237 arg="$1"
163 238 shift
164 239 case "$arg" in
165   - "-u")
  240 + "-u") # map $1 to uid=0 (root)
166 241 root_uid="$1"
167 242 shift
168 243 ;;
169   - "-g")
  244 + "-g") # map $1 to gid=0 (root)
170 245 root_gid="$1"
171 246 shift
172 247 ;;
173   - "-d")
  248 + "-d") # display default initramfs list
174 249 default_list="$arg"
175   - default_initramfs
  250 + ${dep_list}default_initramfs
176 251 ;;
177 252 "-h")
178 253 usage
179 254  
180 255  
... ... @@ -181,24 +256,28 @@
181 256 *)
182 257 case "$arg" in
183 258 "-"*)
184   - printf "ERROR: unknown option \"$arg\"\n" >&2
185   - printf "If the filename validly begins with '-', then it must be prefixed\n" >&2
186   - printf "by './' so that it won't be interpreted as an option." >&2
187   - printf "\n" >&2
188   - usage >&2
189   - exit 1
  259 + unknown_option
190 260 ;;
191   - *)
192   - cpio_source="$arg"
193   - build_list
  261 + *) # input file/dir - process it
  262 + input_file "$arg" "$#"
194 263 ;;
195 264 esac
196 265 ;;
197 266 esac
198 267 done
199 268  
200   -# spit out the default cpio list if a source hasn't been specified
201   -[ -z "$cpio_source" -a -z "$default_list" ] && default_initramfs
202   -
  269 +# If output_file is set we will generate cpio archive and gzip it
  270 +# we are carefull to delete tmp files
  271 +if [ ! -z ${output_file} ]; then
  272 + if [ -z ${cpio_file} ]; then
  273 + cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
  274 + usr/gen_init_cpio ${cpio_list} > ${cpio_tfile}
  275 + else
  276 + cpio_tfile=${cpio_file}
  277 + fi
  278 + rm ${cpio_list}
  279 + cat ${cpio_tfile} | gzip -f -9 - > ${output_file}
  280 + [ -z ${cpio_file} ] && rm ${cpio_tfile}
  281 +fi
203 282 exit 0
  1 +#
  2 +# kbuild file for usr/ - including initramfs image
  3 +#
1 4  
  5 +klibcdirs:;
  6 +
  7 +# Generate builtin.o based on initramfs_data.o
2 8 obj-y := initramfs_data.o
3 9  
4   -hostprogs-y := gen_init_cpio
5   -
6   -clean-files := initramfs_data.cpio.gz initramfs_list
7   -
8 10 # initramfs_data.o contains the initramfs_data.cpio.gz image.
9 11 # The image is included using .incbin, a dependency which is not
10 12 # tracked automatically.
11 13 $(obj)/initramfs_data.o: $(obj)/initramfs_data.cpio.gz FORCE
12 14  
13   -ifdef CONFIG_INITRAMFS_ROOT_UID
14   -gen_initramfs_args += -u $(CONFIG_INITRAMFS_ROOT_UID)
15   -endif
  15 +#####
  16 +# Generate the initramfs cpio archive
16 17  
17   -ifdef CONFIG_INITRAMFS_ROOT_GID
18   -gen_initramfs_args += -g $(CONFIG_INITRAMFS_ROOT_GID)
19   -endif
  18 +hostprogs-y := gen_init_cpio
  19 +initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh
  20 +ramfs-input := $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \
  21 + $(CONFIG_INITRAMFS_SOURCE),-d)
  22 +ramfs-args := \
  23 + $(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \
  24 + $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \
  25 + $(ramfs-input)
20 26  
21   -# The $(shell echo $(CONFIG_INITRAMFS_SOURCE)) is to remove the
22   -# gratuitous begin and end quotes from the Kconfig string type.
23   -# Internal, escaped quotes in the Kconfig string will loose the
24   -# escape and become active quotes.
25   -quotefixed_initramfs_source := $(shell echo $(CONFIG_INITRAMFS_SOURCE))
26   -
27   -filechk_initramfs_list = $(CONFIG_SHELL) \
28   - $(srctree)/scripts/gen_initramfs_list.sh $(gen_initramfs_args) $(quotefixed_initramfs_source)
29   -
30   -$(obj)/initramfs_list: $(obj)/Makefile FORCE
31   - $(call filechk,initramfs_list)
32   -
33   -quiet_cmd_cpio = CPIO $@
34   - cmd_cpio = ./$< $(obj)/initramfs_list > $@
35   -
36   -
37   -# Check if the INITRAMFS_SOURCE is a cpio archive
38   -ifneq (,$(findstring .cpio,$(quotefixed_initramfs_source)))
39   -
40   -# INITRAMFS_SOURCE has a cpio archive - verify that it's a single file
41   -ifneq (1,$(words $(quotefixed_initramfs_source)))
42   -$(error Only a single file may be specified in CONFIG_INITRAMFS_SOURCE (="$(quotefixed_initramfs_source)") when a cpio archive is directly specified.)
  27 +# .initramfs_data.cpio.gz.d is used to identify all files included
  28 +# in initramfs and to detect if any files are added/removed.
  29 +# Removed files are identified by directory timestamp being updated
  30 +# The dependency list is generated by gen_initramfs.sh -l
  31 +ifneq ($(wildcard $(obj)/.initramfs_data.cpio.gz.d),)
  32 + include $(obj)/.initramfs_data.cpio.gz.d
43 33 endif
44   -# Now use the cpio archive directly
45   -initramfs_data_cpio = $(quotefixed_initramfs_source)
46   -targets += $(quotefixed_initramfs_source)
47 34  
48   -else
  35 +quiet_cmd_initfs = GEN $@
  36 + cmd_initfs = $(initramfs) -o $@ $(ramfs-args) $(ramfs-input)
49 37  
50   -# INITRAMFS_SOURCE is not a cpio archive - create one
51   -$(obj)/initramfs_data.cpio: $(obj)/gen_init_cpio \
52   - $(initramfs-y) $(obj)/initramfs_list FORCE
53   - $(call if_changed,cpio)
54   -
55   -targets += initramfs_data.cpio
56   -initramfs_data_cpio = $(obj)/initramfs_data.cpio
57   -
58   -endif
59   -
60   -
61   -$(obj)/initramfs_data.cpio.gz: $(initramfs_data_cpio) FORCE
62   - $(call if_changed,gzip)
63   -
64   -targets += initramfs_data.cpio.gz
  38 +targets := initramfs_data.cpio.gz
  39 +$(deps_initramfs): klibcdirs
  40 +# We rebuild initramfs_data.cpio.gz if:
  41 +# 1) Any included file is newer then initramfs_data.cpio.gz
  42 +# 2) There are changes in which files are included (added or deleted)
  43 +# 3) If gen_init_cpio are newer than initramfs_data.cpio.gz
  44 +# 4) arguments to gen_initramfs.sh changes
  45 +$(obj)/initramfs_data.cpio.gz: $(obj)/gen_init_cpio $(deps_initramfs) klibcdirs
  46 + $(Q)$(initramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio.gz.d
  47 + $(call if_changed,initfs)