#!/bin/sh # Make a filesystem image # # Copyright (C) 1997 Free Software Foundation, Inc. # Written by Miles Bader # This file is part of the GNU Hurd. # # The GNU Hurd is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2, or (at # your option) any later version. # # The GNU Hurd is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. # USAGE="\ Usage: $0 [OPTION...] IMAGE-FILE SRC..." TRY="Try "\`"$0 --help' for more information" MAX_SIZE=1440 # size of floppy MIN_SIZE=500 # avoid lossage for compressed filesystems OWNER="`id -un`.`id -gn`" unset IMAGE SRCS COMPRESS SCRIPTS QUIET GEN_DEPS declare -a SRCS SCRIPTS NUM_SRCS=0 NUM_SCRIPTS=0 while :; do case "$1" in --compress) COMPRESS=yes; shift 1;; --fstype=*) FSTYPE="`echo "$1" | sed 's/^--fstype=//'`"; shift 1;; --fstype) FSTYPE="$2"; shift 2;; --mkfs=*) MKFS="`echo "$1" | sed 's/^--mkfs=//'`"; shift 1;; --mkfs) MKFS="$2"; shift 2;; --fstrans=*) FSTRANS="`echo "$1" | sed 's/^--fstrans=//'`"; shift 1;; --fstrans) FSTRANS="$2"; shift 2;; --owner=*) OWNER="`echo "$1" | sed 's/^--owner=//'`"; shift 1;; --owner) OWNER="$2"; shift 2;; --max-size=*) MAX_SIZE="`echo "$1" | sed 's/^--max-size=//'`"; shift 1;; --max-size) MAX_SIZE="$2"; shift 2;; --quiet|-q|-s) QUIET=yes; shift 1;; --copy=*|--copy-rules=*) SCRIPTS[NUM_SCRIPTS]="`echo "$1" | sed 's/^--[-a-z]*=//'`"; let NUM_SCRIPTS+=1; shift 1;; --copy|--copy-rules) SCRIPTS[NUM_SCRIPTS]="$2"; let NUM_SCRIPTS+=1; shift 2;; --dependencies=*) GEN_DEPS="`echo "$1" | sed 's/^--[-a-z]*=//'`"; shift 1;; --dependencies) GEN_DEPS="$2"; shift 2;; --version) echo "STANDARD_HURD_VERSION_mkfsimage_"; exit 0;; --help) echo "$USAGE" echo "Make a file-system image IMAGE-FILE from the files in SRC..." echo '' echo "\ --copy-rules=FILE Copy files in a manner described by FILE --compress Compress the final image --owner=USER[.GROUP] Make files owned by USER & GROUP (default "\`"$OWNER') --max-size=KBYTES Maximum size of final image (default $MAX_SIZE) --dependencies=DEPS Generate a make dependency rule into DEPS and exit --fstype=TYPE Type of filesystem (TYPE may be "\`"ext2' or "\`"ufs') --mkfs=PROGRAM Program to make an empty filesystem image --fstrans=PROGRAM File system translator program --help Display this help and exit --version Output version information and exit If multiple SRCs are specified, then each occurance of --files pertains only to the corresponding SRC. Each FILE named in a --copy-rules option contains lines of the form: [gzip] [rename TARGET] COPY-OP NAME and says to copy NAME from the source tree to the destination, using the method specified by COPY-OP. A preceeding "\`"rename TARGET"\'" says to give NAME a different name in the target tree, and a preceeding "\`"gzip"\'" says to compress the result (appending .gz to the name). COPY-OP may be one of the following: copy -- A plain copy, preserving symlinks objcopy -- Copy using objcopy to strip any unneeded symbols copytrans -- Copy a translator touch -- Create an empty file in the destination, ignoring the source mkdir -- Create an empty directory in the destination, ignoring the source makedev -- Create the given device in the destination, ignoring the source settrans -- Set a translator with the given arguments If both --mkfs and --fstrans are specified, no filesystem type need be given. If --fstype is not specified, an attempt is made to guess it based on the extension of IMAGE-FILE." exit 0;; -*) echo 1>&2 "$0: $1: Unknown option" echo 1>&2 "$TRY" exit 64;; '') break;; *) case "${IMAGE+set}" in set) SRCS[NUM_SRCS]="$1"; let 'NUM_SRCS += 1';; *) IMAGE="$1";; esac shift esac done case "${IMAGE+set}${SRCS[*]+set}" in setset) ;; *) echo 1>&2 "$USAGE" echo 1>&2 "$TRY" exit 64; esac # Choose format if [ "${MKFS+set}" != set -o "${MKFS+set}" != set ]; then if [ "${FSTYPE+set}" != set ]; then case "$IMAGE" in *.ext2|*.ext2.gz) FSTYPE=ext2;; *.ufs|*.ext2.gz) FSTYPE=ufs;; *) echo 1>&2 "$0: $IMAGE: Unknown filesystem type"; exit 1;; esac fi case "$FSTYPE" in ext2) MKFS="/sbin/mkfs.ext2 -ohurd"; MKFS_Q="-q"; FSTRANS=/hurd/ext2fs;; ufs) MKFS="/sbin/mkfs.ufs --tracks=1 --sectors=80"; FSTRANS=/hurd/ufs;; *) echo 1>&2 "$0: $IMAGE: Unknown filesystem type"; exit 1;; esac fi case "$QUIET" in yes) MKFS_Q="${MKFS_Q} >/dev/null"; ECHO=:;; *) MKFS_Q=''; ECHO=echo;; esac export ECHO MKFS_Q case "$IMAGE" in *.gz) COMPRESS=yes;; esac IMAGE_TMP="${IMAGE}.new" IMAGE_GZIP_TMP="${IMAGE}.new.gz" MNT="/tmp/,mkfsimage-$$.mnt" ERROUT="/tmp/,mkfsimage-$$.errout" STAGE="/tmp/,mkfsimage-$$.stage" TRANS_LIST="/tmp/,mkfsimage-$$.trans" # Extra blocks that will be used by translators TRANS_BLOCKS=0 if [ "$GEN_DEPS" ]; then GEN_DEPS_TMP="$GEN_DEPS.new" echo "$GEN_DEPS: ${SCRIPTS[*]}" >> "$GEN_DEPS_TMP" echo "$IMAGE: \\" >> "$GEN_DEPS_TMP" fi trap "settrans 2>/dev/null -a $MNT; rm -rf $MNT $IMAGE_TMP $IMAGE_GZIP_TMP $ERROUT $STAGE $TRANS_LIST $GEN_DEPS_TMP" 0 1 2 3 15 if [ ${#SRCS[@]} = 1 -a ${#SCRIPTS[@]} = 0 ]; then # No staging directory TREE="$1" else # Multiple source trees, or selective copying -- copy using a staging directory. # We record any translators in a file ($TRANS_LIST) for later, since we copy # the staging dir to the final dir using tar, and it can't handle translators. mkdir $STAGE || exit $? SRC_NUM=0 while [ $SRC_NUM -lt ${#SRCS[@]} ]; do SRC="${SRCS[SRC_NUM]}" SCRIPT="${SCRIPTS[SRC_NUM]}" let SRC_NUM+=1 if [ ! -d "$SRC" ]; then echo 1>&2 "$0: $SRC: No such directory" exit 24 fi case "$SRC" in /) PFX="/";; *) PFX="$SRC/";; esac if [ ! "$GEN_DEPS" ]; then eval $ECHO "'# Copying files from $SRC into staging directory $STAGE...'" fi if [ x"${SCRIPT}" != x ]; then eval $ECHO "'# Using copy script $SCRIPT'" ( if [ x"$SCRIPT" != x- ]; then if [ ! -r "$SCRIPT" ]; then echo 1>&2 "$0: $SCRIPT: No such file" exit 25 fi exec <"$SCRIPT" fi test "$GEN_DEPS" && echo " $SCRIPT \\" >> "$GEN_DEPS_TMP" while read -a args; do case $args in gzip) gzip=yes; unset args[0]; args=(${args[@]});; *) gzip=no;; esac case $args in rename) dst="${args[1]}"; unset args[0] args[1]; args=(${args[@]});; *) unset dst;; esac op="${args[0]}" src="${args[1]}" if echo "$src" | grep -q "[?*[]"; then # For wildcards, use the most recent file matching that pattern src="`(cd $SRC; ls -t1 $src | head -1)`" fi case ${dst+set} in set) # Destination patterns match files in the source tree (What # else could we use? This may be useful for files that exist # in the source, but which we want to use a different version # of for this filesystem). if echo "$dst" | grep -q "[?*[]"; then dst="`(cd $SRC; ls -t1 $dst | head -1)`" fi;; *) dst="$src";; esac # Pop op & src off of args set -- "${args[@]}"; shift 2; unset args; args=("$@") if [ "$GEN_DEPS" ]; then case $op in copy|objcopy) echo " ${PFX}$src \\" >> "$GEN_DEPS_TMP";; esac continue fi case $op in copy) eval $ECHO "'cp -d ${PFX}$src $STAGE/$dst'" cp -d ${PFX}$src $STAGE/$dst ;; objcopy) eval $ECHO "'objcopy --strip-unneeded ${PFX}$src $STAGE/$dst'" objcopy --strip-unneeded "${PFX}$src" "$STAGE/$dst" ;; symlink) if echo "$args" | grep -q "[?*[]"; then # symlink expansion is in the source tree, in the same # directory as the file itself. args="`(cd $PFX`dirname $dst`; ls -t1 $args | head -1)`" fi eval $ECHO "'ln -s $args $STAGE/$dst'" ln -s $args $STAGE/$dst ;; mkdir) eval $ECHO "'mkdir $STAGE/$dst'" mkdir "$STAGE/$dst" ;; touch) eval $ECHO "'touch $STAGE/$dst'" touch "$STAGE/$dst" ;; makedev|settrans|copytrans) # delay translators until later, as tar can't copy them. case $op in settrans|copytrans) # We create the node on which translators will be put so # that the owner gets set correctly; this isn't necessary for # device because MAKEDEV does all the work needed, and doing so # would cause problems with device names that are really # categories. touch "$STAGE/$dst";; esac # Accunt for space used by the translator block TRANS_BLOCKS=$(($TRANS_BLOCKS + 1)) # Record the desired operation for a later pass echo "$op $dst $src ${args[*]}" >> $TRANS_LIST ;; ''|'#') ;; *) echo 1>&2 "$0: $op: Unknown operation" ;; esac case $gzip in yes) eval $ECHO "'gzip -v9 $STAGE/$dst'" gzip -v9 "$STAGE/$dst";; esac done ) || exit $? else eval $ECHO "'# Copying all files using tar'" (cd $SRC; tar cf - .) | (cd $STAGE; tar -x --same-owner -p -f -) fi done TREE="$STAGE" fi if [ "$GEN_DEPS" ]; then echo "" >> "$GEN_DEPS_TMP" && mv "$GEN_DEPS_TMP" "$GEN_DEPS" exit 0 fi eval $ECHO "'# Changing file owners to $OWNER'" chown -R "$OWNER" $TREE # Size of source tree, plus 5% for overhead TREE_SIZE=$((($TRANS_BLOCKS + `du -s "$TREE" | sed 's/^\([0-9]*\).*/\1/'`) * 105 / 100)) if [ "${COMPRESS-no}" = yes ]; then # Add 10% to the filesystem size to leave some breathing room. # Since unused filesystem space compresses very well, this shouldn't add # much to the final size. SIZE=$(($TREE_SIZE * 110 / 100)) test $SIZE -lt $MIN_SIZE && SIZE=$MIN_SIZE else SIZE=$MAX_SIZE if [ $TREE_SIZE -gt $SIZE ]; then echo 1>&2 "$0: $TREE: Too big (${TREE_SIZE}k) to fit in ${MAX_SIZE}k" exit 10 fi fi eval $ECHO "'# Zeroing disk image...'" rm -f $IMAGE_TMP if ! dd if=/dev/zero of=$IMAGE_TMP bs=${SIZE}k count=1 2>$ERROUT; then sed -n "s@^dd:@$0@p" < $ERROUT 1>&2 exit 11 fi eval $ECHO "'# Making filesystem...'" eval "$MKFS $MKFS_Q '$IMAGE_TMP'" || exit 12 settrans -ac $MNT $FSTRANS $IMAGE_TMP || exit 13 eval $ECHO "'# Copying $TREE into filesystem...'" (cd $TREE; tar cf - .) | (cd $MNT; tar -x --same-owner -p -f -) if [ -r "$TRANS_LIST" ]; then # create any delayed translators eval $ECHO "'# Creating translators...'" cat "$TRANS_LIST" | while read -a args; do op="${args[0]}" dst="${args[1]}" src="${args[2]}" set -- "${args[@]}"; shift 3; unset args; args=("$@") case $op in copytrans) tr="`showtrans "${PFX}$src"`" eval $ECHO "'settrans $MNT/$dst $tr'" settrans "$MNT/$dst" $tr ;; settrans) eval $ECHO "'settrans $MNT/$dst ${args[*]}'" settrans "$MNT/$dst" "${args[@]}" ;; makedev) dd="/`dirname $dst`" eval $ECHO "'/sbin/MAKEDEV --devdir=$dd $MNT/$dst'" /sbin/MAKEDEV --devdir=$dd "$MNT/$dst" ;; esac done fi settrans -a $MNT case "$COMPRESS" in yes) case "$QUIET" in yes) gzip -9 $IMAGE_TMP ;; *) eval $ECHO "'# Compressing disk image...'" gzip -v9 $IMAGE_TMP 2>&1 | sed "s@$IMAGE_TMP\.gz@$IMAGE@g" ;; esac mv $IMAGE_GZIP_TMP $IMAGE ;; *) mv $IMAGE_TMP $IMAGE ;; esac exit 0