From: Thayne Harbaugh This patch makes several tweaks so that an initramfs image can be completely created by an unprivileged user. It should maintain compatibility with previous initramfs early userspace cpio/image creation and it updates documentation. There are a few very important tweaks: CONFIG_INITRAMFS_SOURCE is now either a single cpio archive that is directly used or a list of directories and files for building a cpio archive for the initramfs image. Making the cpio archive listable in CONFIG_INITRAMFS_SOURCE makes the cpio step more official and automated so that it doesn't have to be copied by hand to usr/initramfs_data.cpio (I think this was broken anyway and would be overwritten). The alternative list of directories *and* files means that files can be install in a "root" directory and device-special files can be listed in a file list. CONFIG_ROOT_UID and CONFIG_ROOT_GID are now available for doing simple user/group ID translation. That means that user ID 500, group ID 500 can create all the files in the "root" directory, but that they can all be owned by user ID 0, group ID 0 in the cpio image. Various documentation updates to pull it all together. Removal of old cruft that was unused/misleading. Signed-off-by: Andrew Morton --- 25-akpm/Documentation/early-userspace/README | 69 +++++++++++--- 25-akpm/drivers/block/Kconfig | 42 +++++++- 25-akpm/scripts/gen_initramfs_list.sh | 129 +++++++++++++++++++++------ 25-akpm/usr/Makefile | 50 +++++++--- 4 files changed, 229 insertions(+), 61 deletions(-) diff -puN Documentation/early-userspace/README~initramfs-unprivileged-image-creation Documentation/early-userspace/README --- 25/Documentation/early-userspace/README~initramfs-unprivileged-image-creation Thu Dec 23 14:12:52 2004 +++ 25-akpm/Documentation/early-userspace/README Thu Dec 23 14:12:52 2004 @@ -1,7 +1,7 @@ Early userspace support ======================= -Last update: 2004-11-12 +Last update: 2004-12-20 tlh "Early userspace" is a set of libraries and programs that provide @@ -21,19 +21,62 @@ It consists of several major infrastruct The cpio file format used by initramfs is the "newc" (aka "cpio -c") format, and is documented in the file "buffer-format.txt". There are -three ways to add an early userspace filesystem: - -1) Put your gzip'ed cpio in usr/initramfs_data.cpio.gz. - -2) Set CONFIG_INITRAMFS_SOURCE to the filename of a gen_init_cpio -input file. This provides the most flexibility and allows creation of -archives with files not owned by the build user. This means that an -unprivileged user can create an early userspace with files owned by -root. - -3) Set CONFIG_INITRAMFS_SOURCE to point to a directory containing the -files for your filesystem. +two ways to add an early userspace image: specify an existing cpio +archive to be used as the image or have the kernel build process build +the image from specifications. + +CPIO ARCHIVE method + +You can create a cpio archive that contains the early userspace image. +Youre cpio archive should be specified in CONFIG_INITRAMFS_SOURCE and it +will be used directly. Only a single cpio file may be specified in +CONFIG_INITRAMFS_SOURCE and directory and file names are not allowed in +combination with a cpio archive. + +IMAGE BUILDING method + +The kernel build process can also build an early userspace image from +source parts rather than supplying a cpio archive. This method provides +a way to create images with root-owned files even though the image was +built by an unprivileged user. + +The image is specified as one or more sources in +CONFIG_INITRAMFS_SOURCE. Sources can be either directories or files - +cpio archives are *not* allowed when building from sources. + +A source directory will have it and all of it's contents packaged. The +specified directory name will be mapped to '/'. When packaging a +directory, limited user and group ID translation can be performed. +INITRAMFS_ROOT_UID can be set to a user ID that needs to be mapped to +user root (0). INITRAMFS_ROOT_GID can be set to a group ID that needs +to be mapped to group root (0). + +A source file must be directives in the format required by the +usr/gen_init_cpio utility (run 'usr/gen_init_cpio --help' to get the +file format). The directives in the file will be passed directly to +usr/gen_init_cpio. + +When a combination of directories and files are specified then the +initramfs image will be an aggregate of all of them. In this way a user +can create a 'root-image' directory and install all files into it. +Because device-special files cannot be created by a unprivileged user, +special files can be listed in a 'root-files' file. Both 'root-image' +and 'root-files' can be listed in CONFIG_INITRAMFS_SOURCE and a complete +early userspace image can be built by an unprivileged user. + +As a technical note, when directories and files are specified, the +entire CONFIG_INITRAMFS_SOURCE is passed to +scripts/gen_initramfs_list.sh. This means that CONFIG_INITRAMFS_SOURCE +can really be interpreted as any legal argument to +gen_initramfs_list.sh. If a directory is specified as an argument then +the contents are scanned, uid/gid translation is performed, and +usr/gen_init_cpio file directives are output. If a directory is +specified as an arugemnt to scripts/gen_initramfs_list.sh then the +contents of the file are simply copied to the output. All of the output +directives from directory scanning and file contents copying are +processed by usr/gen_init_cpio. +See also 'scripts/gen_initramfs_list.sh -h'. Where's this all leading? ========================= diff -puN drivers/block/Kconfig~initramfs-unprivileged-image-creation drivers/block/Kconfig --- 25/drivers/block/Kconfig~initramfs-unprivileged-image-creation Thu Dec 23 14:12:52 2004 +++ 25-akpm/drivers/block/Kconfig Thu Dec 23 14:12:52 2004 @@ -359,16 +359,48 @@ config BLK_DEV_INITRD for details. config INITRAMFS_SOURCE - string "Source directory of cpio_list" + string "Initramfs source file(s)" default "" help - This can be set to either a directory containing files, etc to be - included in the initramfs archive, or a file containing entries - according to the format described by the "usr/gen_init_cpio" - program in the kernel tree. + This can be either a single cpio archive with a .cpio suffix or a + space-separated list of directories and files for building the + initramfs image. A cpio archive should contain a filesystem archive + to be used as an initramfs image. Directories should contain a + filesystem layout to be included in the initramfs image. Files + should contain entries according to the format described by the + "usr/gen_init_cpio" program in the kernel tree. + + When multiple directories and files are specified then the + initramfs image will be the aggregate of all of them. + + See # Released under the terms of the GNU GPL # -# Generate a newline separated list of entries from the file/directory pointed -# out by the environment variable: CONFIG_INITRAMFS_SOURCE +# Generate a newline separated list of entries from the file/directory +# supplied as an argument. # -# If CONFIG_INITRAMFS_SOURCE is non-existing then generate a small dummy file. +# If a file/directory is not supplied then generate a small dummy file. # -# The output is suitable for gen_init_cpio as found in usr/Makefile. +# The output is suitable for gen_init_cpio built from usr/gen_init_cpio.c. # -simple_initramfs() { +default_initramfs() { cat <<-EOF - # This is a very simple initramfs + # This is a very simple, default initramfs dir /dev 0755 0 0 nod /dev/console 0600 0 0 c 5 1 @@ -63,6 +63,9 @@ parse() { 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 @@ -101,30 +104,100 @@ parse() { return 0 } -if [ -z "$1" ]; then - simple_initramfs -elif [ -f "$1" ]; then - print_mtime "$1" - cat "$1" -elif [ -d "$1" ]; then - 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 - print_mtime "$1" +usage() { + printf "Usage:\n" + printf "$0 [ [-u ] [-g ] [-d | ] ] . . .\n" + printf "\n" + printf -- "-u User ID to map to user ID 0 (root).\n" + printf " is only meaningful if \n" + printf " is a directory.\n" + printf -- "-g Group ID to map to group ID 0 (root).\n" + printf " is only meaningful if \n" + printf " is a directory.\n" + printf " File list or directory for cpio archive.\n" + printf " If is not provided then a\n" + printf " a default list will be output.\n" + printf -- "-d Output the default cpio list. If no \n" + printf " is given then the default cpio list will be output.\n" + printf "\n" + printf "All options may be repeated and are interpreted sequentially\n" + printf "and immediately. -u and -g states are preserved across\n" + printf " options so an explicit \"-u 0 -g 0\" is required\n" + printf "to reset the root/group mapping.\n" +} + +build_list() { + printf "\n#####################\n# $cpio_source\n" + + if [ -f "$cpio_source" ]; then + print_mtime "$cpio_source" + cat "$cpio_source" + elif [ -d "$cpio_source" ]; then + srcdir=$(echo "$cpio_source" | 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 + print_mtime "$cpio_source" - echo "${dirlist}" | \ - while read x; do - parse ${x} - done + echo "${dirlist}" | \ + while read x; do + parse ${x} + done + else + # Failsafe in case directory is empty + default_initramfs + fi else - # Failsafe in case directory is empty - simple_initramfs + echo " $0: Cannot open '$cpio_source'" >&2 + exit 1 fi -else - echo " $0: Cannot open '$1' (CONFIG_INITRAMFS_SOURCE)" >&2 - exit 1 -fi +} + + +root_uid=0 +root_gid=0 + +while [ $# -gt 0 ]; do + arg="$1" + shift + case "$arg" in + "-u") + root_uid="$1" + shift + ;; + "-g") + root_gid="$1" + shift + ;; + "-d") + default_list="$arg" + default_initramfs + ;; + "-h") + usage + exit 0 + ;; + *) + case "$arg" in + "-"*) + printf "ERROR: unknown option \"$arg\"\n" >&2 + printf "If the filename validly begins with '-', 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 + ;; + *) + cpio_source="$arg" + build_list + ;; + esac + ;; + esac +done + +# spit out the default cpio list if a source hasn't been specified +[ -z "$cpio_source" -a -z "$default_list" ] && default_initramfs exit 0 diff -puN usr/Makefile~initramfs-unprivileged-image-creation usr/Makefile --- 25/usr/Makefile~initramfs-unprivileged-image-creation Thu Dec 23 14:12:52 2004 +++ 25-akpm/usr/Makefile Thu Dec 23 14:12:52 2004 @@ -5,40 +5,60 @@ hostprogs-y := gen_init_cpio clean-files := initramfs_data.cpio.gz initramfs_list -# If you want a different list of files in the initramfs_data.cpio -# then you can either overwrite the cpio_list in this directory -# or set INITRAMFS_LIST to another filename. -INITRAMFS_LIST := $(obj)/initramfs_list - # 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 -# initramfs-y are the programs which will be copied into the CPIO -# archive. Currently, the filenames are hardcoded in gen_init_cpio, -# but we need the information for the build as well, so it's duplicated -# here. - -# Commented out for now -# initramfs-y := $(obj)/root/hello +ifdef CONFIG_INITRAMFS_ROOT_UID +gen_initramfs_args += -u $(CONFIG_INITRAMFS_ROOT_UID) +endif + +ifdef CONFIG_INITRAMFS_ROOT_GID +gen_initramfs_args += -g $(CONFIG_INITRAMFS_ROOT_GID) +endif + +# The $(shell echo $(CONFIG_INITRAMFS_SOURCE)) is to remove the +# gratuitous begin and end quotes from the Kconfig string type. +# Internal, escaped quotes in the Kconfig string will loose the +# escape and become active quotes. +quotefixed_initramfs_source := $(shell echo $(CONFIG_INITRAMFS_SOURCE)) filechk_initramfs_list = $(CONFIG_SHELL) \ - $(srctree)/scripts/gen_initramfs_list.sh $(CONFIG_INITRAMFS_SOURCE) - + $(srctree)/scripts/gen_initramfs_list.sh $(gen_initramfs_args) $(quotefixed_initramfs_source) + $(obj)/initramfs_list: FORCE $(call filechk,initramfs_list) quiet_cmd_cpio = CPIO $@ cmd_cpio = ./$< $(obj)/initramfs_list > $@ + +# Check if the INITRAMFS_SOURCE is a cpio archive +ifneq (,$(findstring .cpio,$(quotefixed_initramfs_source))) + +# INITRAMFS_SOURCE has a cpio archive - verify that it's a single file +ifneq (1,$(words $(quotefixed_initramfs_source))) +$(error Only a single file may be specified in CONFIG_INITRAMFS_SOURCE (="$(quotefixed_initramfs_source)") when a cpio archive is directly specified.) +endif +# Now use the cpio archive directly +initramfs_data_cpio = $(quotefixed_initramfs_source) +targets += $(quotefixed_initramfs_source) + +else + +# INITRAMFS_SOURCE is not a cpio archive - create one $(obj)/initramfs_data.cpio: $(obj)/gen_init_cpio \ $(initramfs-y) $(obj)/initramfs_list FORCE $(call if_changed,cpio) targets += initramfs_data.cpio +initramfs_data_cpio = $(obj)/initramfs_data.cpio + +endif + -$(obj)/initramfs_data.cpio.gz: $(obj)/initramfs_data.cpio FORCE +$(obj)/initramfs_data.cpio.gz: $(initramfs_data_cpio) FORCE $(call if_changed,gzip) targets += initramfs_data.cpio.gz _