#!/bin/bash

# Freetz script to modify AVM FRITZ!Box and OEM firmware images
#
# Copyright (C) 2005-2006 Daniel Eiband <eiband@online.de>
# Copyright (C) 2006-2012 by the Freetz developers (http://freetz.org)
#
# Licensed under the GPL v2, see the file COPYING in this tarball.
#
# This script is based on Christian Volkmann's fritzbox mod-0.57.
# Legacy URL: http://www.ip-phone-forum.de/showthread.php?t=65894
#
# Special thanks to Andreas Buehmann, Christian Volkmann, Enrik Berkhan,
# SpeedyBZ and all members on ip-phone-forum.de who contributed to this mod.


usage()
{
	cat << EOF
Usage: $SELF [-u|-m|-p|-a] [-z] [-c <dir>] [-n] [-f] [-i <cfg>] [-d <dir>] <orig_fw> [<tk_fw> [<aux_fw>]]
  main actions
    -u         unpack firmware image
    -m         modify previously unpacked image
    -p         pack firmware image
    -a         all: unpack, modify and pack firmware image
  special actions
    -s         sign firmware image
    -z         zip file system into archive for USB/NFS root
    -c <dir>   copy file system to target directory for NFS/USB root (implies -z)
  options
    -n         firmware-nocompile: do not install kernel and busybox
    -f         force pack even if image is too big for flash (AVM SDK)
  input/output
    -i <cfg>   input file for configuration data (default: .config)
    -d <dir>   build directory (default: <orig_firmware>.mod)
    <orig_fw>  original firmware name
    <tk_fw>    2nd firmware name (e.g. for merging in web UI)
    <aux_fw>   3rd firmware name (e.g. to borrow missing files)
EOF
}

##################################################
## Initialise and check command line parameters ##
##################################################

# This script's name ("fwmod" if not linked/renamed)
SELF="$(basename "$0")"

# No parameters -> print usage info to stdout
[ $# -eq 0 ] && usage && exit 0

# Backup command line parameters [$0..$n] to save them from getopts eating them
# away, because we need them again later for the recursive fakerooted call.
for ((i=0; i<=$#; i++)); do
	CMDLINE_ORIG[i]="${!i}"
done

# Default values for unset command line parameters
DOT_CONFIG="$(dirname "$0")/.config"
DO_UNPACK=0; DO_MOD=0; DO_PACK=0; DO_SIGN=0
FIRMWARE_NOCOMPILE=0; FORCE_PACK=0; DO_ZIP=0; COPY_FS_DIR=; DIR=

# Parse command line parameters
while getopts umpsanfzc:i:d: opt; do
	case "$opt" in
		u) DO_UNPACK=1 ;;
		m) DO_MOD=1 ;;
		p) DO_PACK=1 ;;
		s) DO_SIGN=1 ;;
		a) DO_UNPACK=1; DO_MOD=1; DO_PACK=1 ;;
		n) FIRMWARE_NOCOMPILE=1 ;;
		f) FORCE_PACK=1 ;;
		z) DO_ZIP=1 ;;
		c) COPY_FS_DIR="$OPTARG"; [ "$COPY_FS_DIR" ] && DO_ZIP=1 ;;
		i) DOT_CONFIG="$OPTARG" ;;
		d) DIR="$OPTARG" ;;
		*) usage >&2; exit 1 ;;
	esac
done
shift $((OPTIND-1))

# At least one action and one (non-empty) firmware image name must be specified
[ $(($DO_UNPACK + $DO_MOD + $DO_PACK + $DO_ZIP)) -eq 0 -o $# -lt 1 -o $# -gt 3 -o ! "$1" ] && usage >&2 && exit 1

# Check if it's a completely automated run in the "no freetz"-mode and cache the value.
# We are interested in what the user asked us for and not what we are actually doing,
# the UNPACK step might be skipped (and the value of DO_UNPACK changed) if the already
# unpacked firmware is found on the disk.
[ "$DO_UNPACK" -gt 0 -a "$DO_MOD" -eq 0 -a "$DO_PACK" -gt 0 ] && NO_FREETZ_AUTOMATED_RUN=1 || NO_FREETZ_AUTOMATED_RUN=0

# Freetz base + tools directories
BASE_DIR="$(dirname "$0")"
ABS_BASE_DIR="$(readlink -f "$BASE_DIR")"
TOOLS_DIR="${ABS_BASE_DIR}/tools"

# Include common helper functions (isFreetzType, echo*, error, modunsqfs* etc.)
source "${TOOLS_DIR}/freetz_functions" ||
	(echo "cannot find script freetz_functions" >&2; exit 1)

if [ "$DO_SIGN" -gt 0 -a $(($DO_PACK + $DO_ZIP)) -eq 0 ]; then
	error 1 "Firmware signing without packing is not supported, please use PeterPawn's sign_image tool directly if you plan to sign some already existing .image file"
fi

# Initialise firmware image names
FIRMWARE="$1"
FIRMWARE2="$2"
FIRMWARE3="$3"
FIRMWARE_EXTENSION="${FIRMWARE##*.}"
FIRMWARE_EXTENSION="${FIRMWARE_EXTENSION,,}"

# Given firmware image(s) must exist
[ -r "$FIRMWARE" ] ||
	error 1 "firmware image $(basename "$FIRMWARE") not found"
[ "$FIRMWARE2" -a ! -r "$FIRMWARE2" ] &&
	error 1 "firmware image $(basename "$FIRMWARE2") not found"
[ "$FIRMWARE3" -a ! -r "$FIRMWARE3" ] &&
	error 1 "firmware image $(basename "$FIRMWARE3") not found"

# Config file must exist
[ -r "$DOT_CONFIG" ] ||
	error 1 "not configured"

# Set default output directory, if not specified on command line
: ${DIR:=$FIRMWARE.mod}
# Create output directory if necessary
mkdir -p "$DIR"


#################################
## Initialise script variables ##
#################################

# Relative subdirectories
SOURCE_SUBDIR="source"
FIRMWARE_SUBDIR="firmware"
FILESYSTEM_SUBDIR="filesystem"
FILESYSTEM_OUTER_SUBDIR="filesystem.outer"
AVMPLUGINS_SUBDIR="plugins.image"
EXTERNAL_SUBDIR="external"
KERNEL_SUBDIR="kernel"
VARTAR_SUBDIR="var.tar"
HTML_SUBDIR="usr/www"
GRAPHICS_SUBDIR="graphics"

# (temporary) files created during firmware unpacking
RAW_KERNEL_FILE="kernel.raw"
RAW_HIDDEN_FILE="kernelsquashfs.raw"
VARTAR_FILE="var.tar"
UNPACKED_FILE="$DIR/.unpacked"

# Path to bash (shell required by fwmod)
SHELL=$BASH

# Tools used by fwmod
FINDSQUASHFS_TOOL="find-squashfs"
TICHKSUM_TOOL="tichksum"
MAKEDEVS_TOOL="makedevs"
DEBUGFS_TOOL="debugfs"
EXTERNAL_TOOL="external"
TAR_TOOL="tar"
MD5SUM_TOOL="md5sum"
BUSYBOX_TOOL="busybox"

# Absolute tools paths
EXTERNAL="${TOOLS_DIR}/${EXTERNAL_TOOL}"
DEBUGFS="${TOOLS_DIR}/${DEBUGFS_TOOL}"
FINDSQUASHFS="${TOOLS_DIR}/${FINDSQUASHFS_TOOL}"
TICHKSUM="${TOOLS_DIR}/${TICHKSUM_TOOL}"
MAKEDEVS="${TOOLS_DIR}/${MAKEDEVS_TOOL}"
MAKEDEVS_FILE="${TOOLS_DIR}/device_table.txt"
TAR="${TOOLS_DIR}/${TAR_TOOL}"
TAR_GNU="${TOOLS_DIR}/tar-gnu"
MD5SUM="${TOOLS_DIR}/${MD5SUM_TOOL}"
BUSYBOX="${TOOLS_DIR}/${BUSYBOX_TOOL}"
BLKID="${TOOLS_DIR}/blkid"

# Freetz directores relative to base dir
PACKAGES_DIR_ROOT="${BASE_DIR}/packages"
PATCHES_DIR="${BASE_DIR}/patches"
PATCHES_COND_DIR="${PATCHES_DIR}/cond"
PATCHES_DEVICES_DIR="${PATCHES_DIR}/devices"
PATCHES_SCRIPTS_DIR="${PATCHES_DIR}/scripts"
ROOT_DIR="${BASE_DIR}/root"
KERNEL_REP_DIR="${BASE_DIR}/kernel"
ADDON_DIR="${BASE_DIR}/addon"
GRAPHICS_DIR="${BASE_DIR}/${GRAPHICS_SUBDIR}"

# Freetz branding directories relative to graphics subdirectory
FAVICON_DIR="${GRAPHICS_DIR}/favicon"
TAGGING_DIR="${GRAPHICS_DIR}/tagging"

# Package files
STATIC_PACKAGES_FILE="${BASE_DIR}/.static"
STATIC_ADDON_FILE="${ADDON_DIR}/static.pkg"

# Alien variables
TK_DIR="${DIR}/.tk/original"
FIRMWARE_TK_DIR="${TK_DIR}/${FIRMWARE_SUBDIR}"
FILESYSTEM_TK_DIR="${TK_DIR}/${FILESYSTEM_SUBDIR}"
KERNEL_TK_DIR="${TK_DIR}/${KERNEL_SUBDIR}"
VARTAR_TK_DIR="${KERNEL_TK_DIR}/${VARTAR_SUBDIR}"
AUX_DIR="${DIR}/.aux/original"
FIRMWARE_AUX_DIR="${AUX_DIR}/${FIRMWARE_SUBDIR}"
FILESYSTEM_AUX_DIR="${AUX_DIR}/${FILESYSTEM_SUBDIR}"
KERNEL_AUX_DIR="${AUX_DIR}/${KERNEL_SUBDIR}"
VARTAR_AUX_DIR="${KERNEL_AUX_DIR}/${VARTAR_SUBDIR}"


##########################################################
## Include config file + initialise some more variables ##
##########################################################

# Include config file, but do not override variables which are already defined
# in the environment, e.g. via "make FREETZ_SOMETHING=y"
sed -nr 's/^([^=]+)=(.*)/: ${\1:=\2}/p' "$DOT_CONFIG" > "$DOT_CONFIG.fwmod"
source "$DOT_CONFIG.fwmod"
rm "$DOT_CONFIG.fwmod"

# Set default verbosity level, just in case none was defined in the (possibly
# user-defined and minimal) config file
: ${FREETZ_VERBOSITY_LEVEL:=0}

# Kernel/SquashFS images contained in firmware
KERNEL_IMAGE="${FREETZ_AVM_IMAGES_SUBDIR}/kernel.image"
FILESYSTEM_IMAGE="${FREETZ_AVM_IMAGES_SUBDIR}/filesystem.image"
FILESYSTEM_INNER_IMAGE="filesystem_core.squashfs"
AVMPLUGINS_FILE="${FREETZ_AVM_IMAGES_SUBDIR}/plugins.update"

# Use FREETZ_TARGET_CROSS to determine build tool paths
NM="${ABS_BASE_DIR}/toolchain/target/bin/${FREETZ_TARGET_CROSS}nm"
STRIP="${ABS_BASE_DIR}/toolchain/target/bin/${FREETZ_TARGET_CROSS}strip"

# Tools and options for (un)packing SquashFS
UNSQUASHFS_TOOL="unsquashfs4-avm-${FREETZ_AVM_SQUASHFS_ENDIANNESS}"
UNSQUASHFS="${TOOLS_DIR}/${UNSQUASHFS_TOOL}"
UNSQUASHFS_OPTIONS="-no-progress -processors 1 -exit-on-error"

MKSQUASHFS_TOOL[2]="mksquashfs2-lzma"
MKSQUASHFS_TOOL[3]="mksquashfs3-multi"
MKSQUASHFS_TOOL[4]="mksquashfs4-avm-${FREETZ_AVM_SQUASHFS_ENDIANNESS}"
MKSQUASHFS="${TOOLS_DIR}/${MKSQUASHFS_TOOL[${FREETZ_SQUASHFS_VERSION}]}"
MKSQUASHFS_OPTIONS="-all-root -info"
if [ "${FREETZ_SQUASHFS_VERSION}" -gt 2 ]; then
	MKSQUASHFS_OPTIONS+=" -no-progress -no-exports -no-sparse"
fi
if [ "${FREETZ_SQUASHFS_VERSION}" -lt 4 ]; then
	MKSQUASHFS_OPTIONS+=" -noappend"
	MKSQUASHFS_OPTIONS+=" -${FREETZ_AVM_SQUASHFS_ENDIANNESS}"
fi
if [ "${FREETZ_SQUASHFS_VERSION}" -eq 3 -a "${FREETZ_AVM_SQUASHFS_COMPRESSION}" == "lzma" ]; then
	MKSQUASHFS_OPTIONS+=" -lzma1"
fi


#######################################
## Initialise fakeroot incl. caching ##
#######################################

# Create id string based on MD5 hashes of firmware images
if [ "$FIRMWARE2" ]; then
	FIRMWARE_MD5="$("$MD5SUM" "$FIRMWARE2" | cut -d ' ' -f 1)"
fi
FIRMWARE_MD5="$("$MD5SUM" "$FIRMWARE" | cut -d ' ' -f 1)_$FIRMWARE_MD5"

FAKEROOT="$TOOLS_DIR/build/bin/fakeroot"
FAKEROOT_CACHE_DIR="$ABS_BASE_DIR/.fakeroot-cache"
FAKEROOT_CACHE="$FAKEROOT_CACHE_DIR/$FIRMWARE_MD5"
if [ ! -e "$FAKEROOT_CACHE_DIR" ]; then
	mkdir -p "$FAKEROOT_CACHE_DIR"
fi

# Are we in a fakeroot context?
if [ "$FAKEROOTKEY" ] && [ $UID -eq 0 ]; then
	# Yes -> fakeroot is active
	# Make sure fakeroot call came from ourselves so we can assume the cache settings to be correct
	if [ $((FWMOD_RECURSIVE)) -eq 0 ] || [ "$SELF" != "$(sed -nr 's/^Name:[[:blank:]]+(.*)/\1/p' /proc/$FWMOD_RECURSIVE/status)" ]; then
		error 1 "$SELF must not run in a fakeroot environment"
	fi
	# Does fakeroot cache (size>0) exist for current combination of firmwares and has this combination already been unpacked?
	if [ -s "$FAKEROOT_CACHE" ] && [ -s "$UNPACKED_FILE" ] && [ "$(cat "$UNPACKED_FILE")" == "$FIRMWARE_MD5" ]; then
		# Yes -> skip unpack
		[ $DO_UNPACK -gt 0 ] && SKIP_UNPACK="STEP 1: UNPACK (SKIPPED)\n\n"
		DO_UNPACK=0
	else
		# No -> force unpack
		[ $DO_UNPACK -eq 0 ] && FORCE_UNPACK=" (FORCED)"
		DO_UNPACK=1
	fi
else
	# No -> fakeroot is inactive
	# Does fakeroot cache (size>0) exist for current combination of firmwares and has this combination already been unpacked?
	if [ -s "$FAKEROOT_CACHE" ] && [ -s "$UNPACKED_FILE" ] && [ "$(cat "$UNPACKED_FILE")" == "$FIRMWARE_MD5" ]; then
		# Yes -> use existing cache and firmwares (no unpack necessary)
		NON_FAKEROOT_USERNAME=$(id -u -n) FWMOD_RECURSIVE=$$ "$FAKEROOT" -i "$FAKEROOT_CACHE" -s "$FAKEROOT_CACHE" '--' "${CMDLINE_ORIG[@]}"
	else
		# No -> create new fakeroot cache and unpack firmwares
		NON_FAKEROOT_USERNAME=$(id -u -n) FWMOD_RECURSIVE=$$ "$FAKEROOT" -s "$FAKEROOT_CACHE" '--' "${CMDLINE_ORIG[@]}"
	fi
	retval=$?
	[ $retval -eq 0 ] || exit $retval

	# ------------------------------------------------------------------
	# -- Copy file system to target folder (NFS root, maybe USB root) --
	# ------------------------------------------------------------------

	# This cannot be done in the pack/zip section (needs real sudo, not fakeroot)
	if [ "$COPY_FS_DIR" ]; then
		echo0 "copying root file system to directory $COPY_FS_DIR"
		rootfs_archive="$(cat "${DIR}/.rootfs_archive")" ||
			error 1 "cannot determine root file system archive name"
		[ -d "$COPY_FS_DIR" ] && (sudo rm -rf "$COPY_FS_DIR"/* ||
			error 1 "cannot clean up directory $COPY_FS_DIR")
		mkdir -p "$COPY_FS_DIR" ||
			error 1 "cannot create directory $COPY_FS_DIR"
		sudo "$TAR" -C "$COPY_FS_DIR" -xf "$rootfs_archive" ||
			error 1 "cannot unpack root file system"
		echo -e "done.\n"
		echo0 -b "FINISHED"
	fi

	exit 0
fi


############################################
## Unpack and unsquash the firmware image ##
############################################

ORG_DIR="${DIR}/original"
FIRMWARE_DIR="${ORG_DIR}/${FIRMWARE_SUBDIR}"
FILESYSTEM_DIR="${ORG_DIR}/${FILESYSTEM_SUBDIR}"
FILESYSTEM_OUTER_DIR="${ORG_DIR}/${FILESYSTEM_OUTER_SUBDIR}"
KERNEL_DIR="${ORG_DIR}/${KERNEL_SUBDIR}"
AVMPLUGINS_DIR="${KERNEL_DIR}/${AVMPLUGINS_SUBDIR}"
VARTAR_DIR="${KERNEL_DIR}/${VARTAR_SUBDIR}"
HTML_DIR="${FILESYSTEM_DIR}/${HTML_SUBDIR}"

RAW_KERNEL="${KERNEL_DIR}/${RAW_KERNEL_FILE}"
RAW_FILESYSTEM="${KERNEL_DIR}/${RAW_HIDDEN_FILE}"
KERNEL="${FIRMWARE_DIR}/${KERNEL_IMAGE}"
if [ "${FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE}" == "y" ]; then
	if [ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ]; then
		FILESYSTEM="${FILESYSTEM_OUTER_DIR}/${FILESYSTEM_INNER_IMAGE}"
		INNER_FS_PREFIX="inner-"
	else
		FILESYSTEM="${FIRMWARE_DIR}/${FILESYSTEM_IMAGE}"
	fi
else
	FILESYSTEM="${RAW_FILESYSTEM}"
fi
AVMPLUGINS="${FIRMWARE_DIR}/${AVMPLUGINS_FILE}"
VARTAR="${FILESYSTEM_DIR}/${VARTAR_FILE}"

if [ "$DO_UNPACK" -gt 0 ]; then
	[ "$FIRMWARE2" ] && UNPACK_SUBSTEP="A" && UNPACK_MAIN=" MAIN" && FIRMWARE_MAIN=" main"
	echo0 -b "STEP 1${UNPACK_SUBSTEP}: UNPACK${UNPACK_MAIN}${FORCE_UNPACK}"

	# Remove old "unpacked" marker to avoid an undefined state in case of
	# interruption before unpacking operation is finished
	rm -f "$UNPACKED_FILE"

	rm -rf "$ORG_DIR"
	mkdir "$ORG_DIR"

	echo "unpacking firmware image"
	mkdir "$FIRMWARE_DIR"

	if [ "$FIRMWARE_EXTENSION" != "exe" ]; then
		# Unpack standard *.image file in tar format
		"$TAR_GNU" -xif "$FIRMWARE" -C "$FIRMWARE_DIR" || untar_failed=1
	fi
	if [ "$FIRMWARE_EXTENSION" == "exe" ] || [ "$untar_failed" ]; then
		# Unpack Windows recover.exe file or mtdblock dump
		exe_images=($("${TOOLS_DIR}/extract-images" "$ABS_BASE_DIR" "$FIRMWARE" | sed -nr 's/^ *(.*\.image) - .*/\1/p'))
		[ "$exe_images" ] || exit 1
		mkdir -p "$FIRMWARE_DIR/${FREETZ_AVM_IMAGES_SUBDIR}"
		mkdir "$KERNEL_DIR"
		unset exe_hr_mode
		# NOTE: This loop is sensitive to the order and format of output of
		# "tools/extract-images", e.g. a hidden root image is always listed
		# before its parts.
		for (( i=0; i<${#exe_images[@]}; i++ )); do
			exe_image="${exe_images[i]}"
			case "$exe_image" in
				*/urlader.image)
					mv "$exe_image" "$FIRMWARE_DIR/${FREETZ_AVM_IMAGES_SUBDIR}"
					;;
				*/hr_kernel.image)
					exe_hr_mode=1
					mv "$exe_image" "$FIRMWARE_DIR/${FREETZ_AVM_IMAGES_SUBDIR}/kernel.image"
					touch "$FIRMWARE_DIR/${FREETZ_AVM_IMAGES_SUBDIR}/filesystem.image"
					;;
				*/kernel.image)
					[ "$exe_hr_mode" ] || cp "$exe_image" "$FIRMWARE_DIR/${FREETZ_AVM_IMAGES_SUBDIR}/kernel.image"
					mv "$exe_image" "$KERNEL_DIR/kernel.raw"
					;;
				*/filesystem.image)
					if [ "$exe_hr_mode" ]; then
						mv "$exe_image" "$KERNEL_DIR/kernelsquashfs.raw"
					else
						mv "$exe_image" "$FIRMWARE_DIR/${FREETZ_AVM_IMAGES_SUBDIR}/filesystem.image"
					fi
					;;
			esac
		done
		rm -r "$(dirname "$exe_image")"
	fi

	# Do the images exist ?
	[ ! -r "${KERNEL}" ] && error 1 "cannot find kernel.image (${KERNEL})"
	[ ! -r "${FIRMWARE_DIR}/${FILESYSTEM_IMAGE}" ] && error 1 "cannot find filesystem.image (${FIRMWARE_DIR}/${FILESYSTEM_IMAGE})"

	# Do we have the tool ?
	for tool in "$UNSQUASHFS" "$FINDSQUASHFS" "$TICHKSUM"; do
		[ ! -x $tool ] && error 1 "cannot find the tool $tool"
	done

	# Remove NMI vector from SquashFS
	if [ "$FREETZ_AVM_HAS_NMI_VECTOR" == "y" ]; then
		echo0 "removing NMI vector from SquashFS"
		[ "$FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE" == "y" ] && FILE_WITH_NMI_VECTOR_TO_PROCESS="${FIRMWARE_DIR}/${FILESYSTEM_IMAGE}" || FILE_WITH_NMI_VECTOR_TO_PROCESS="$KERNEL"
		if [ "$FREETZ_VERBOSITY_LEVEL" -lt 2 ]; then
			"$TOOLS_DIR/remove-nmi-vector" "$FILE_WITH_NMI_VECTOR_TO_PROCESS" "$FILE_WITH_NMI_VECTOR_TO_PROCESS.no-nmi" >/dev/null 2>&1
		else
			"$TOOLS_DIR/remove-nmi-vector" "$FILE_WITH_NMI_VECTOR_TO_PROCESS" "$FILE_WITH_NMI_VECTOR_TO_PROCESS.no-nmi"
		fi
		case "$?" in
			0) mv -f "$FILE_WITH_NMI_VECTOR_TO_PROCESS.no-nmi" "$FILE_WITH_NMI_VECTOR_TO_PROCESS" ;;
			1) warn2 "NMI vector not found. Assuming Freetz or Freetz-repacked image." ;;
			*) error 1 "fatal error while removing NMI vector from SquashFS." ;;
		esac
	fi

	"$TICHKSUM" -r "${KERNEL}" > /dev/null
	"$TICHKSUM" -r "${FIRMWARE_DIR}/${FILESYSTEM_IMAGE}" > /dev/null

	mkdir -p "$KERNEL_DIR"
	if [ "${FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE}" == "y" ]; then
		cp "${KERNEL}" "${RAW_KERNEL}"
		[ ! -r "$RAW_KERNEL" ] && error 1 "copying kernel image failed"

		if [ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ]; then
			echo "unpacking outer-filesystem image"
			modunpack_autodetect_fs "${FILESYSTEM_OUTER_DIR}" "${FIRMWARE_DIR}/${FILESYSTEM_IMAGE}"
			[ ! -r "$FILESYSTEM" ] && error 1 "unpacking outer-filesystem image failed"
		fi

		echo "unpacking ${INNER_FS_PREFIX}filesystem image"
		modunpack_autodetect_fs "$FILESYSTEM_DIR" "$FILESYSTEM" || error 1 "unpacking ${INNER_FS_PREFIX}filesystem image failed"
	else
		echo "splitting kernel image"
		( cd "$KERNEL_DIR" && "${TOOLS_DIR}/${FINDSQUASHFS_TOOL}" "../${FIRMWARE_SUBDIR}/${KERNEL_IMAGE}" > /dev/null 2>&1 )

		if [ ! -r "$RAW_KERNEL" -o ! -r "$FILESYSTEM" ]; then
			if [ -r "${FIRMWARE_DIR}/${FILESYSTEM_IMAGE}" ] && [ $(stat -c %s "${FIRMWARE_DIR}/${FILESYSTEM_IMAGE}") -gt 0 ]; then
				_config_hint=" - maybe you should configure a firmware with FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE/FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM"
			fi
			error 1 "kernel splitting failed${_config_hint}"
		fi

		echo "unpacking filesystem image"
		modunpack_autodetect_fs "$FILESYSTEM_DIR" "$FILESYSTEM" || error 1 "unpacking filesystem image failed"
	fi

	if [ -r "$AVMPLUGINS" ]; then
		echo0 "unpacking AVM plugins"
		mkdir -p "$AVMPLUGINS_DIR"
		"$TAR_GNU" -xif "$AVMPLUGINS" -C "$AVMPLUGINS_DIR" || exit 1

		for i in "${AVMPLUGINS_DIR}/var/"*.image; do
			AVMPLUGIN="${i##*/plugin-}"
			AVMPLUGIN="${AVMPLUGIN%\.image}"
			echo2 "$AVMPLUGIN image"
			modunpack_autodetect_fs "$AVMPLUGINS_DIR/plugin-${AVMPLUGIN}" "$i" > /dev/null
		done
		chmod -R +w "$AVMPLUGINS_DIR"
	fi

	if [ ! -r "$FILESYSTEM_DIR/var" ]; then
		error 1 "could not unpack the filesystem image"
	fi

	if [ ! -r "$VARTAR" ]; then
		error 1 "no var.tar found"
	fi

	echo "unpacking var.tar"
	mkdir "$VARTAR_DIR"
	"$TAR_GNU" -xipf "$VARTAR" -C "$VARTAR_DIR" || exit 1

	echo -e "done.\n"

	# Unpack secondary firmware image
	if [ -n "$FIRMWARE2" ]; then
		echo0 -b "FINISHED"
		rm -rf "${DIR}/.tk"
		"$0" -u -d "${DIR}/.tk" "$FIRMWARE2" |
			sed "s/STEP 1: UNPACK/STEP 1B: UNPACK SECONDARY${FORCE_UNPACK}/;s/ firmware/ secondary&/"
	fi

	# Unpack auxiliary firmware image
	if [ -n "$FIRMWARE3" ]; then
		rm -rf "${DIR}/.aux"
		"$0" -u -d "${DIR}/.aux" "$FIRMWARE3" |
			sed "s/STEP 1: UNPACK/STEP 1C: UNPACK AUXILIARY${FORCE_UNPACK}/;s/ firmware/ auxiliary&/"
	fi

	# Create "unpacked" marker, recording exactly *what* was unpacked (MD5)
	echo "$FIRMWARE_MD5" > "$UNPACKED_FILE"
fi
echo0 -n -b "$SKIP_UNPACK"


###############################################
# Lets copy and modify the unpacked firmware ##
###############################################

# FREETZ_LIBRARY_DIR is the 1st FREETZ_RPATH entry
FREETZ_LIBRARY_DIR=$(stripTrailingSlash "${FREETZ_RPATH%%:*}")

MOD_DIR="${DIR}/modified"
FIRMWARE_MOD_DIR="${MOD_DIR}/${FIRMWARE_SUBDIR}"
FILESYSTEM_MOD_DIR="${MOD_DIR}/${FILESYSTEM_SUBDIR}"
FILESYSTEM_OUTER_MOD_DIR="${MOD_DIR}/${FILESYSTEM_OUTER_SUBDIR}"
EXTERNAL_MOD_DIR="${MOD_DIR}/${EXTERNAL_SUBDIR}"
KERNEL_MOD_DIR="${MOD_DIR}/${KERNEL_SUBDIR}"
AVMPLUGINS_MOD_DIR="${KERNEL_MOD_DIR}/${AVMPLUGINS_SUBDIR}"
AVMPLUGINS_FILESYSTEM_MOD_DIR="${FILESYSTEM_MOD_DIR}/usr/share/avmplugins"
VARTAR_MOD_DIR="${KERNEL_MOD_DIR}/${VARTAR_SUBDIR}"

KERNEL_MOD="${FIRMWARE_MOD_DIR}/${KERNEL_IMAGE}"
FILESYSTEM_MOD="${FIRMWARE_MOD_DIR}/${FILESYSTEM_IMAGE}"

RAW_KERNEL_MOD="${KERNEL_MOD_DIR}/${RAW_KERNEL_FILE}"
RAW_FILESYSTEM_MOD="${KERNEL_MOD_DIR}/${RAW_HIDDEN_FILE}"

if [ "${FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE}" == "y" ]; then
	if [ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ]; then
		MAIN_FILESYSTEM_MOD="${FILESYSTEM_OUTER_MOD_DIR}/${FILESYSTEM_INNER_IMAGE}"
	else
		MAIN_FILESYSTEM_MOD="$RAW_FILESYSTEM_MOD"
	fi
else
	MAIN_FILESYSTEM_MOD="$RAW_FILESYSTEM_MOD"
fi

VARTAR_MOD="${FILESYSTEM_MOD_DIR}/${VARTAR_FILE}"

[ "${FREETZ_AVM_UCLIBC_NPTL_ENABLED}" == "y" ] && TARGET_TOOLCHAIN_ID__UCLIBC_SUFFIX="-nptl"
[ "${FREETZ_KERNEL_VERSION_3_10_MIN}" == "y" ] && TARGET_TOOLCHAIN_ID__KERNEL_SUFFIX="_kernel-${FREETZ_KERNEL_VERSION_MAJOR}"
TARGET_TOOLCHAIN_ID="${FREETZ_TARGET_ARCH_ENDIANNESS_DEPENDENT}_gcc-${FREETZ_TARGET_GCC_VERSION}_uClibc-${FREETZ_TARGET_UCLIBC_VERSION}${TARGET_TOOLCHAIN_ID__UCLIBC_SUFFIX}${TARGET_TOOLCHAIN_ID__KERNEL_SUFFIX}"
PACKAGES_DIR="${PACKAGES_DIR_ROOT}/target-${TARGET_TOOLCHAIN_ID}"
TARGET_SPECIFIC_ROOT_DIR="${PACKAGES_DIR}/root"

HTML_MOD_DIR="${FILESYSTEM_MOD_DIR}/${HTML_SUBDIR}"
# $LUA_MOD_DIR can't be defined here. eg: ${FILESYSTEM_MOD_DIR}/usr/www/all/lua
# $HTML_LANG_MOD_DIR can't be defined here. eg: ${FILESYSTEM_MOD_DIR}/usr/www/all
# $HTML_SPEC_MOD_DIR can't be defined here. eg: ${FILESYSTEM_MOD_DIR}/usr/www/all/html/de


if [ -z "$FREETZ_SQUASHFS_BLOCKSIZE" ]; then # expected to be empty only if FREETZ_SQUASHFS_BLOCKSIZE_ORIG is set
	echo -n "determining SquashFS block size of the original firmware: "
	FREETZ_SQUASHFS_BLOCKSIZE=$("$UNSQUASHFS" -s "$FILESYSTEM" 2>/dev/null | sed -nr 's/Block size ([0-9]+)/\1/p')
	[ -z "$FREETZ_SQUASHFS_BLOCKSIZE" ] && error 1 "failed" || echo -e "$((FREETZ_SQUASHFS_BLOCKSIZE/1024)) kB\n"
fi
MKSQUASHFS_OPTIONS+=" -b $FREETZ_SQUASHFS_BLOCKSIZE"
#echo "Using '$MKSQUASHFS_OPTIONS' as MKSQUASHFS_OPTIONS"



if [ "$FREETZ_SIZEINFO_COMPRESSED" == "y" ]; then
	let FILESYSTEM_BLOCKSIZE_KB=$FREETZ_SQUASHFS_BLOCKSIZE/1024
	SIZEINFO_CACHEDIR=${PACKAGES_DIR}/freetzcachedsizes
	mkdir -p ${SIZEINFO_CACHEDIR}
fi
function sizeinfo()
{
	[ "$FREETZ_VERBOSITY_LEVEL" -lt 1 ] && return
	#formated output
	if [ $# -eq 1 ]; then
		if [ "$FREETZ_SIZEINFO_COMPRESSED" != "y" ]; then
			echo1 "$1"
		else
			echo1 -n "$(printf "%-34s " "$1" | sed 's/ /\./g;s/\.\./ ./;s/\.(addon)/ (addon)/')"
		fi
		return
	fi
	[ "$FREETZ_SIZEINFO_COMPRESSED" != "y" ] && return

	#cache file
	local sizeinfo_mode=$1
	shift
	case $sizeinfo_mode in
		lib)	#libraries: multiple files
			local shash="$(ls -l $@ | sort | $MD5SUM)"
			local sfile="${SIZEINFO_CACHEDIR}/$(echo $@ | sed -rn 's!.*/(.*)\.so.*!\1!p').sizeinfo.MD5_${shash/ */}.BS_${FILESYSTEM_BLOCKSIZE_KB}"
			;;
		mod)	#modules: one .ko file
			local shash="$(ls -l $1 | $MD5SUM)"
			local sfile="${SIZEINFO_CACHEDIR}/${1##*/}.sizeinfo.MD5_${shash/ */}.BS_${FILESYSTEM_BLOCKSIZE_KB}"
			;;
		pkg)	#packages: files (without .excluded)
			local shash="$(echo $* | sort | $MD5SUM)"
			local sfile="${1}/.sizeinfo.MD5_${shash/ */}.BS_${FILESYSTEM_BLOCKSIZE_KB}"
			shift
			;;
		add)	#addons: one directory
			local shash="$(find $1 -type f -exec ls -l {} ';' | $MD5SUM)"
			local sfile="${SIZEINFO_CACHEDIR}/${1##*/}.sizeinfo.MD5_${shash/ */}.BS_${FILESYSTEM_BLOCKSIZE_KB}"
			;;
		*)
			echo "invalid"
			return
			;;
	esac

	#compressed size
	if [ ! -s "$sfile" ]; then
		if [ $# -eq 0 ]; then
			echo -n "    0.00 kB (uncompressed:    0.00 kB)" > ${sfile}
		else
			local tempf=/tmp/freetz-sizeinfo-$$.tmp
			"$MKSQUASHFS" $@ $tempf $MKSQUASHFS_OPTIONS 2>/dev/null | grep -A1 ^Filesystem \
				| sed -n 'N;s!Filesystem size \(.*\)ytes (.*\n.*(\(.*\)ytes.*$!\1 \2!p' \
				| awk '{printf "%8s%3s (uncompressed:%8s%3s)",$1,$2,$3,$4}' > ${sfile}
			rm -rf $tempf 2>/dev/null
		fi
	fi

	#uncompressed size
	if [ "$FREETZ_SIZEINFO_UNCOMPRESSED" = "y" ]; then
		cat ${sfile}
	else
		cat ${sfile} | sed 's/(uncompressed.*//'
	fi
	echo
}

# show avm image details
AVM_FW_PRODUCT="$(sed -rn 's/export CONFIG_PRODUKT_NAME=".*([0-9]{3}[0-9V].*)"/\1/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf | head -n1)" # 4-digits in /etc/init.d/rc.conf
[ -z "${AVM_FW_PRODUCT}" ] && AVM_FW_PRODUCT="$(sed -rn 's/export CONFIG_PRODUKT_NAME="(FRITZ!Box|Speedport|Sinus|Eumex) ([^"]+)"/\2/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf | head -n1 | tr ' ' '-')" # symbolic name in /etc/init.d/rc.conf
if [ -e "${FILESYSTEM_DIR}/etc/init.d/rc.init" ]; then
	# /etc/init.d/rc.init is not available in all firmwares
	[ -z "${AVM_FW_PRODUCT}" ] && AVM_FW_PRODUCT="$(sed -rn 's/^HW=[0-9]* OEM=all _PRODUKT_NAME=(FRITZ!Box|Speedport|Sinus|Eumex)#//p' ${FILESYSTEM_DIR}/etc/init.d/rc.init | head -n1 | sed 's/#//g;s/Annex.//;s/WLAN/-&/g;s/^$/FritzBox/g')" # symbolic name in /etc/init.d/rc.init
	[ -z "${AVM_FW_PRODUCT}" ] && AVM_FW_PRODUCT="$(sed -rn 's/^HW=[0-9]* OEM=all _PRODUKT_NAME=([0-9A-Z\#]{4,6}).*/\1/p' ${FILESYSTEM_DIR}/etc/init.d/rc.init)" # 4-digits in /etc/init.d/rc.init
fi
[ -z "${AVM_FW_PRODUCT}" ] && warn "Failed to determine AVM_FW_PRODUCT, even though this is a cosmetic bug only please report it to the Freetz developers and provide your .config"
AVM_FW_PRODUCT="${AVM_FW_PRODUCT%% Edition*}"
AVM_FW_PRODUCT="${AVM_FW_PRODUCT% }"
AVM_FW_LANGUAGE="$(sed -n 's/^language /_/p' ${FILESYSTEM_DIR}/etc/default.language)" #primary language
AVM_FW_LANGUAGE="${AVM_FW_LANGUAGE}$(for x in $(ls ${FILESYSTEM_DIR}/etc/htmltext_??.db 2>/dev/null| sed 's/.*_//g;s/\.db//g'); do [ "_$x" != "${AVM_FW_LANGUAGE}" ] && echo -en "-$x"; done; echo)" # other languages
AVM_FW_MAJOR="$(sed -rn 's/^export CONFIG_VERSION_MAJOR="(.*)"/\1/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf | tail -n1)" # newer firmware
[ -z "${AVM_FW_MAJOR}" ] && AVM_FW_MAJOR="$(sed -n 's/^HW=[0-9].*VERSION_MAJOR=//p' ${FILESYSTEM_DIR}/etc/init.d/rc.init)" # older firmware
AVM_FW_VERSION="$(sed -n 's/^export FIRMWARE_VERSION=.*}[.]//p' ${FILESYSTEM_DIR}/etc/version)"
AVM_FW_LABOR="$(sed -rn 's/^export CONFIG_LABOR_ID_NAME="(.*)"/-\1/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf)"
AVM_FW_REVISION="$(sed -n '/--project)$/{N;s/.*echo //p}' ${FILESYSTEM_DIR}/etc/version)"
AVM_FW_DATE="$(sed -rn 's/^export FIRMWARE_DATE="(.*)"/\1/p' ${FILESYSTEM_DIR}/etc/version)"
echo1 -l "detected${FIRMWARE_MAIN} firmware ${AVM_FW_PRODUCT/ /-}${AVM_FW_LANGUAGE} ${AVM_FW_MAJOR}.${AVM_FW_VERSION}${AVM_FW_LABOR} rev${AVM_FW_REVISION} (${AVM_FW_DATE})\n"

if [ "$DO_MOD" -gt 0 ]; then
	echo0 -b "STEP 2: MODIFY"

	# Check if firmware is unpacked
	if [ ! -r "$UNPACKED_FILE" ]; then
		error 1 "firmware image has to be unpacked before modifying"
	fi

	rm -rf "$MOD_DIR"
	rm -f "${DIR}/.modified"

	# Copy the unpacked directory
	mkdir -p "$MOD_DIR"
	[ "$FREETZ_AVM_HAS_UDEV" == y ] || exclude_dev="--exclude=./filesystem/dev"
	"$TAR" -c -C "$ORG_DIR" $exclude_dev  --exclude='*.log' . | "$TAR" -x -C "$MOD_DIR" || exit 1

	# Fix some permissions
	chmod -R u+w "$FILESYSTEM_MOD_DIR"
	find "${FILESYSTEM_MOD_DIR}" "${VARTAR_MOD_DIR}" -type d -exec chmod 755 {} '+'
	# Give all users write permissions to /var/tmp to allow it to be used as a temp-directory.
	# Set sticky bit to prevent users from deleting/renaming files they are not the owners of.
	# NB: The permissions set are the regular temp-directory permissions on Unix-like systems.
	chmod 1777 "${VARTAR_MOD_DIR}/var/tmp"

	# determine OEMs supported by the original firmware
	all_known_oems="1und1 aol arcor avm avme ewetel freenet otwo tcom versatel"
	iterate_over_oems_pattern='for[ ]+i[ ]+in[ ]+('"${all_known_oems// /|}"')[^;]*;[ ]*do'
	oems=$(sed -n -r -e '/'"${iterate_over_oems_pattern}"'/ { s/^.*for[ ]+i[ ]+in[ ]+([^;]+);[ ]+do.*$/\1/p; q }' "${FIRMWARE_MOD_DIR}/var/install")
	[ -z "$oems" ] && error 1 "failed to determine OEMs supported by the original firmware"

	echo0 -n "applying symlinks, deleting additional webinterfaces in:"
	for webdir in \
	  ${FILESYSTEM_MOD_DIR}/usr/www \
	  ${FILESYSTEM_MOD_DIR}/usr/www.nas \
	  ${FILESYSTEM_MOD_DIR}/usr/www.myfritz \
	; do
		[ ! -d ${webdir} ] && continue

		for edition in ewetel; do
			[ ! -d ${webdir}/$edition ] && continue
			[ -d ${webdir}/avm ] && continue
			[ -d ${webdir}/all ] && continue
			# force creation of /usr/www/all/
			mv ${webdir}/$edition ${webdir}/avm
		done

		# some recent LTE firmwares contain both avm and avme OEMs (identical in all firmwares seen so far)
		# use the 1st of them (according to the alphabetical order) as the main one
		main_oem=$(find "${webdir}" -maxdepth 1 -type d -name "avm*" -printf "%P\n" | sort | head -n 1)

		if [ -n "${main_oem}" ]; then
			echo0 -n " ${webdir#${FILESYSTEM_MOD_DIR}/}"
			mv ${webdir}/${main_oem} ${webdir}/all
			for i in $oems; do
				rm -rf ${webdir}/$i
				ln -s all ${webdir}/$i
			done
		fi
	done
	echo0

	echo0 "applying patches"

	# Include patching helper function (modpatch)
	source "${TOOLS_DIR}/freetz_patch" || error 1 "cannot find script freetz_patch"

	PATCHES_DEVICES_DIR_FW_SERIES_SPECIFIC="${PATCHES_DEVICES_DIR}/${FREETZ_TYPE_PREFIX_SERIES_SUBDIR}"
	PATCHES_DEVICES_DIR_BOX_SPECIFIC="${PATCHES_DEVICES_DIR_FW_SERIES_SPECIFIC}/${FREETZ_TYPE_PREFIX%_*}"

	declare -a PATCHES_DEVICES_DIR_FULL_LIST=($(
		for j in \
			${PATCHES_DEVICES_DIR_FW_SERIES_SPECIFIC} \
			${PATCHES_DEVICES_DIR_BOX_SPECIFIC} \
			$( [ -n "${FREETZ_TYPE_PREFIX_LABOR_FIRMWARE}" ] && echo "${PATCHES_DEVICES_DIR_BOX_SPECIFIC}/${FREETZ_TYPE_PREFIX_LABOR_FIRMWARE:1}" ) \
		; do
			for i in $j $j/${FREETZ_TYPE_LANGUAGE}; do
				echo "${i}"
			done
		done
	))
	echo1 "applying patches: ${FREETZ_TYPE_PREFIX_SERIES_SUBDIR}/${FREETZ_TYPE_PREFIX}${FREETZ_TYPE_PREFIX_LABOR_FIRMWARE}-${FREETZ_TYPE_LANGUAGE} (${PATCHES_DEVICES_DIR_FULL_LIST[@]#${PATCHES_DEVICES_DIR}/})"

	# Apply patches
	if [ ! -d "${PATCHES_DEVICES_DIR_BOX_SPECIFIC}" -a ! -d "${PATCHES_DEVICES_DIR_FW_SERIES_SPECIFIC}" ]; then
		error 1 "missing ${PATCHES_DEVICES_DIR_BOX_SPECIFIC}"
	fi

	# Execute firmware-series-specific and box-specific patch scripts (both common for all languages and language-specific)
	shopt -s nullglob
	for j in ${PATCHES_DEVICES_DIR_FULL_LIST[@]}; do
		for i in $j/*.sh; do
			echo2 "applying patch file $i"
			source $i
		done
	done
	shopt -u nullglob

	# Now we are in place to check html directory
	for i in all/en all ewetel; do
		if [ -d "${HTML_MOD_DIR}/$i" ]; then
			HTML_LANG_MOD_DIR="${HTML_MOD_DIR}/$i"
			break
		fi
	done

	# Now check language specific html directory (if there is any)
	for i in de en; do
		if [ -d "${HTML_LANG_MOD_DIR}/html/$i" ]; then
			HTML_SPEC_MOD_DIR="${HTML_LANG_MOD_DIR}/html/$i"
			break
		fi
	done
	echo1 "Language specific HTML directory: ${HTML_SPEC_MOD_DIR:-none}"
	if [ -z "$HTML_SPEC_MOD_DIR" ]; then
		HTML_SPEC_MOD_DIR="${HTML_LANG_MOD_DIR}/html/doesnotexist"
		FREETZ_AVM_HAS_ONLY_LUA=y
		[ -e "${FILESYSTEM_MOD_DIR}/usr/www/all/css/rd/images/fritzLogo.svg" ] && FREETZ_AVM_HAS_LUA_SCALABLE=y
	fi

	# Check for LUA directory
	LUA_MOD_DIR="${FILESYSTEM_MOD_DIR}/usr/lua"
	[ -d "$LUA_MOD_DIR" ] || LUA_MOD_DIR="${HTML_LANG_MOD_DIR}/lua"
	[ -d "$LUA_MOD_DIR" ] && echo1 "LUA directory: ${LUA_MOD_DIR}" || FREETZ_AVM_HAS_ONLY_HTML=y

	# Determine $MODULES_DIR in AVM image, eg: "${FILESYSTEM_MOD_DIR}/lib/modules/2.6.19.2
	MODULES_DIR="$(find ${FILESYSTEM_MOD_DIR}/lib/modules/2.6.* ${FILESYSTEM_MOD_DIR}/lib/modules/[34].*.* -maxdepth 0 -type d 2>/dev/null)"
	[ ! -d "$MODULES_DIR" ] && error 1 "Cannot find modules dir."
	# Set $MODULES_SUBDIR relative to root dir in AVM image, eg: "lib/modules/2.6.19.2
	MODULES_SUBDIR="${MODULES_DIR#${FILESYSTEM_MOD_DIR}/}"

	# Apply firmware-series-specific and box-specific patches (both common for all languages and language-specific)
	shopt -s nullglob
	for ((j=0; j<${#PATCHES_DEVICES_DIR_FULL_LIST[@]}; j++)); do
		for i in ${PATCHES_DEVICES_DIR_FULL_LIST[$j]}/*.patch; do
			# Check if a more specific version of the patch exists,
			# i.e. define an order on all patches with the same name
			#   FW_SERIES/XY.patch
			#   FW_SERIES/LANG/XY.patch
			#   FW_SERIES/BOXID/XY.patch
			#   FW_SERIES/BOXID/LANG/XY.patch
			# and do NOT apply patch XY.patch from dir FOO
			# if it also exists in a directory with a higher order.
			#
			# This makes it possible in situations where one box needs
			# a box-specific version of the patch to have just two versions
			# of it: one box-specific and one for all other boxes.
			i_basename=$(basename "$i")
			for ((jj=j+1; jj<${#PATCHES_DEVICES_DIR_FULL_LIST[@]}; jj++)); do
				if [ -f "${PATCHES_DEVICES_DIR_FULL_LIST[$jj]}/${i_basename}" ]; then
					continue 2
				fi
			done

			modpatch "$FILESYSTEM_MOD_DIR" "$i"
		done
	done
	shopt -u nullglob

	echo1 "creating symlinks /tmp, /mod and /home"
	rm -rf "$FILESYSTEM_MOD_DIR"/tmp
	ln -s var/{tmp,mod} "$FILESYSTEM_MOD_DIR"/
	ln -s var/mod/home "$FILESYSTEM_MOD_DIR"/

	echo1 "creating /mnt and /opt"
	mkdir -p "$FILESYSTEM_MOD_DIR"/mnt
	# 7390: /opt is symlink to /var/InternerSpeicher/opt
	[ ! -L "$FILESYSTEM_MOD_DIR"/opt ] && mkdir -p "$FILESYSTEM_MOD_DIR"/opt

	# Set version and options
	SUBVERSION_FILE="${BASE_DIR}/.version"

	if [ ! -r "$SUBVERSION_FILE" ]; then
		error 1 "cannot determine version"
	fi

	if [ -d .svn ]; then
		# be compatible: older versions of svnversion need WC_PATH
		if REVISION="$(svnversion . 2>/dev/null | tr ":" "_")"; then
			[ "${REVISION:0:6}" == "export" ] && REVISION=""
		fi
	elif [ -d .git ]; then
		REVISION="$(git log 2>/dev/null | sed -rn 's!.*git-svn-id.*\@([^ ]*).*!\1!p' | head -n1)"
	fi
	[ -n "$REVISION" ] && REVISION="-$REVISION"

	SUBVERSION="$(cat $SUBVERSION_FILE)${REVISION}"

	for i in $(set | sed -rn 's:^FREETZ_REMOVE_(.*)=y$:\1:gp' | tr '[:upper:]' '[:lower:]' | sort); do
		OPTIONS+=" -$i"
	done

	OPTIONS_ADD=" +busybox"
	for i in $(set | sed -rn 's:^FREETZ_PATCH_(.*)=y$:\1:gp' | tr '[:upper:]' '[:lower:]'); do
		OPTIONS_ADD+=" +$i"
	done

	PACKAGES_LIST_FILE="${BASE_DIR}/.packages"
	rm -f "${PACKAGES_LIST_FILE}"
	for pkg in $(static_packages) $(static_addons); do
		OPTIONS_ADD+=" +$pkg"
		echo "$pkg" >> "${PACKAGES_LIST_FILE}"
	done

	KERNEL_ID="${FREETZ_SYSTEM_TYPE}${FREETZ_SYSTEM_TYPE_CORE_SUFFIX}-${FREETZ_AVM_SOURCE_ID}"
	if [ "$FIRMWARE_NOCOMPILE" != "1" -a "$FREETZ_REPLACE_KERNEL" == "y" -a "$FWMOD_PATCH_TEST" != "y" ]; then
		KERNEL_SUBVERSION_FILE="${KERNEL_REP_DIR}/.version-${KERNEL_ID}"

		if [ ! -r "$KERNEL_SUBVERSION_FILE" ]; then
			error 1 "cannot determine kernel version"
		fi

		KERNEL_SUBVERSION="$(cat $KERNEL_SUBVERSION_FILE)"
		OPTIONS_ADD+=" +kernel ($KERNEL_SUBVERSION)"
	fi

	for i in $(echo ${OPTIONS_ADD}|sed -e's: +:\n+:g'|sort); do
		OPTIONS+=" $i"
	done

	echo1 "setting freetz-version '${SUBVERSION}'"
	echo "$SUBVERSION" > "${FILESYSTEM_MOD_DIR}/etc/.freetz-version"

	if [ "$FREETZ_MODIFY_AVM_VERSION" == "y" ]; then
		echo1 "setting AVM firmware subversion '${SUBVERSION}'"
		echo "$SUBVERSION" > "${FILESYSTEM_MOD_DIR}/etc/.subversion"
		sed -i -e "s/\export\ FIRMWARE_SUBVERSION=.*\$/export\ FIRMWARE_SUBVERSION=\"${SUBVERSION}\"/g" "${FILESYSTEM_MOD_DIR}/etc/version"

		if [ ! -e "${FILESYSTEM_MOD_DIR}/etc/init.d/rc.init" ]; then
			AVM_SUBVERSION=`grep "SUBVERSION=" "${FILESYSTEM_MOD_DIR}/etc/init.d/rc.conf" | sed -e "s/[^-0-9]//g"`
			sed -i -e "s/SUBVERSION=\"$AVM_SUBVERSION\"/SUBVERSION=\"-$SUBVERSION\"/" "${FILESYSTEM_MOD_DIR}/etc/init.d/rc.conf"
		fi

		sed -i -e "s/<subversion>/${SUBVERSION}/g" "${FILESYSTEM_MOD_DIR}/usr/bin/system_status"
		sed -i -e "s/<options>/${OPTIONS}/g" "${FILESYSTEM_MOD_DIR}/usr/bin/system_status"
	fi

	# Execute general patch scripts
	for i in "${PATCHES_SCRIPTS_DIR}/"*.sh; do
		[ -r "$i" ] || continue
		echo2 "applying patch file $i"
		source $i
	done

	# stop execution if in patch test mode
	if [ "$FWMOD_PATCH_TEST" == "y" ]; then
		echo0 -b "Patch test mode: exiting without error."
		exit
	fi

	# remove oems
	[ "$FREETZ_VERBOSITY_LEVEL" -ge 1 ] && echo -n "${L1}removing oem:"
	oem_removed=0
	oem_kept=0
	oem_list=

	ln -sf /usr/www/cgi-bin/freetz_status "${HTML_LANG_MOD_DIR}/cgi-bin/freetz_status"
	[ "$FREETZ_PACKAGE_WOL_CGI" == "y" ] && ln -sf /usr/www/cgi-bin/freetz_wol "${HTML_LANG_MOD_DIR}/cgi-bin/freetz_wol"

	for i in $oems; do
		if [ "$(eval "echo \"\$FREETZ_REMOVE_BRANDING_${i}\"")" == "y" ]; then
			[ "$FREETZ_VERBOSITY_LEVEL" -ge 1 ] && echo -n " $i"
			# delete webinterface symlinks otherwise user can choose branding in status.cgi
			rm -rf "${FILESYSTEM_MOD_DIR}/usr/www/${i}"
			rm -rf "${FILESYSTEM_MOD_DIR}/usr/www.nas/${i}"
			rm -rf "${FILESYSTEM_MOD_DIR}/usr/www.myfritz/${i}"
			find ${FILESYSTEM_MOD_DIR}/etc/default.* -name "*${i}*" | xargs rm -rf
			oem_removed=1
		else
			oem_list+=" $i"
			oem_kept=1
		fi
	done

	if [ "$FREETZ_VERBOSITY_LEVEL" -ge 1 ]; then
		[ "$oem_removed" -eq 0 ] && echo -n " none"
		echo
	fi

	if isFreetzType W501V; then
		oem_list="tcom avm"
	fi
	sed -i -r -e 's/'"${iterate_over_oems_pattern}"'/for i in '"$oem_list"' ; do/' "${FIRMWARE_MOD_DIR}/var/install"

	if [ "$oem_kept" -eq 0 ]; then
		error 1 "please choose at least one OEM (branding) in menuconfig: AVM, 1&1, Freenet etc."
	fi

	# Copy selected avmplugins to the firmware
	if [ "$FREETZ_AVMPLUGINS_INTEGRATE" == "y" ]; then
		echo0 "integrating AVM plugins"
		echo "#" > ${FILESYSTEM_MOD_DIR}/sbin/start_plugin.sh
		rm -rf ${FIRMWARE_MOD_DIR}/${AVMPLUGINS_FILE}
		mkdir "${AVMPLUGINS_FILESYSTEM_MOD_DIR}"
		for i in `find "${AVMPLUGINS_MOD_DIR}" -maxdepth 1 -type d -name var -prune -false , -type d -name "plugin-*" 2>/dev/null`; do
			AVMPLUGIN="${i##*plugin-}"
			if [ "$(eval "echo \"\$FREETZ_AVMPLUGINS_$(echo "$AVMPLUGIN" | tr [:lower:] [:upper:])\"")" == "y" ]; then
				echo1 "$AVMPLUGIN plugin"
				cp -a "${AVMPLUGINS_MOD_DIR}/plugin-${AVMPLUGIN}" "${AVMPLUGINS_FILESYSTEM_MOD_DIR}/plugin-${AVMPLUGIN}"
				ln -s "/usr/share/avmplugins/plugin-${AVMPLUGIN}" "${VARTAR_MOD_DIR}/var/plugin-${AVMPLUGIN}"
				for module in `cd "${AVMPLUGINS_FILESYSTEM_MOD_DIR}/plugin-${AVMPLUGIN}";find -type f -name "*.ko"`; do
					echo2 "moving ${module##*/} to standard modules dir"
					mv "${AVMPLUGINS_FILESYSTEM_MOD_DIR}/plugin-${AVMPLUGIN}/$module" "${FILESYSTEM_MOD_DIR}/$module"
				done
				case "$AVMPLUGIN" in
					tam)
						modsed \
						  "s/ CONFIG_TAM_ONRAM=.*$/ CONFIG_TAM_ONRAM=\"n\"/g" \
						  "${FILESYSTEM_MOD_DIR}/etc/init.d/rc.conf"
						;;
					wlan)
						modsed -r \
						  's,(if) (.+ plugin-wlan),\1 test -d "/usr/share/avmplugins/plugin-wlan" || \2,' \
						  "${FILESYSTEM_MOD_DIR}/etc/init.d/rc.wlan"
						;;
				esac
			fi
		done
	fi

	if [ "$FREETZ_INSTALL_BASE" == "y" ]; then
		echo "installing mod base"

		MOD_CRON="${VARTAR_MOD_DIR}/var/spool/cron/crontabs"
		MOD_ROOT="${VARTAR_MOD_DIR}/var/mod"

		# Remove symlink before creating directory
		rm -rf ${VARTAR_MOD_DIR}/var/spool

		mkdir -p "$MOD_CRON"
		mkdir -p "${MOD_ROOT}/pkg" "${MOD_ROOT}/home" "${MOD_ROOT}/lib" "${MOD_ROOT}/root"
		mkdir -p "${MOD_ROOT}/bin" "${MOD_ROOT}/sbin"
		mkdir -p "${MOD_ROOT}/var/cache"
		mkdir -p "${MOD_ROOT}/usr/bin" "${MOD_ROOT}/usr/sbin" "${MOD_ROOT}/usr/share" \
			"${MOD_ROOT}/usr/lib" "${MOD_ROOT}/usr/lib/cgi-bin" "${MOD_ROOT}${FREETZ_LIBRARY_DIR}"
		mkdir -p "${MOD_ROOT}/etc/conf" "${MOD_ROOT}/etc/init.d" "${MOD_ROOT}/etc/reg"
		ln -s /tmp/flash/mod/.profile "${MOD_ROOT}/root/.profile"
		# AVM compatibility symlink
		ln -s ../sys "${VARTAR_MOD_DIR}/var/sysfs"

		echo "root:x:0:0:root:/mod/root:/bin/sh" > "${VARTAR_MOD_DIR}/var/tmp/passwd"
		echo 'root:$1$$zO6d3zi9DefdWLMB.OHaO.:12332:0:99999:7:::' > "${VARTAR_MOD_DIR}/var/tmp/shadow"
		echo "root:x:0:" > "${VARTAR_MOD_DIR}/var/tmp/group"
		echo "users:x:1:" >> "${VARTAR_MOD_DIR}/var/tmp/group"
		touch "${VARTAR_MOD_DIR}/var/tmp/ethers"
		touch "${VARTAR_MOD_DIR}/var/tmp/exports"
		touch "${VARTAR_MOD_DIR}/var/tmp/gshadow"
		chmod 644 "${VARTAR_MOD_DIR}/var/tmp/passwd" "${VARTAR_MOD_DIR}/var/tmp/group"
		chmod 600 "${VARTAR_MOD_DIR}/var/tmp/shadow" "${VARTAR_MOD_DIR}/var/tmp/gshadow"
		# Fix permissions set by AVM, in particular revoke write permissions for g- and o-users.
		chmod 644 "${VARTAR_MOD_DIR}/var/tmp/hosts" "${VARTAR_MOD_DIR}/var/tmp/resolv.conf"
		mkdir -p "${VARTAR_MOD_DIR}/var/tmp/onlinechanged"
		mkdir -p "${VARTAR_MOD_DIR}/var/mod/etc/onlinechanged"
		mkdir -p "${VARTAR_MOD_DIR}/var/mod/etc/cron.d"
		ln -s ../var/tmp/ethers "${FILESYSTEM_MOD_DIR}/etc/ethers"
		ln -s ../var/tmp/exports "${FILESYSTEM_MOD_DIR}/etc/exports"
		ln -s ../var/tmp/gshadow "${FILESYSTEM_MOD_DIR}/etc/gshadow"

		rm -f ${VARTAR_MOD_DIR}/var/*/.dummy

		echo0 "installing libs"
		for i in $(set | grep ^FREETZ_LIB_.*=y | grep -v ^FREETZ_LIB_STDCXXLIB); do
			all_files=""
			found=0
			conf=${i%%=*}
			[ "$conf" != "${conf%%_WITH_*}" ] && continue # skip suboptions

			bn=${conf#FREETZ_LIB_}

			if [ "$FREETZ_KEEP_AVM_UCLIBC" == "y" ]; then
				# libutil is actually also a uClibc lib, but it's not provided by AVM, so keep installing Freetz version
				if echo "${bn}" | grep -qE '^(ld_uClibc|libcrypt|libdl|libm|libpthread|librt|libthread_db|libubacktrace|libuClibc)$'; then
					echo1 "keeping AVM version of uClibc library $bn"
					continue
				fi
			fi

			fn=${bn//_/[-+._]}
			sizeinfo "$bn"
			for dn in lib usr/lib ${FREETZ_LIBRARY_DIR#/} usr/lib/xtables; do
				files=$(shopt -s nullglob; echo ${TARGET_SPECIFIC_ROOT_DIR}/$dn/$fn[-.]*so*)
				if [ -z "$files" ]; then
					continue;
				fi
				found=1
				[ -d "${FILESYSTEM_MOD_DIR}/${dn}" ] || mkdir -p "${FILESYSTEM_MOD_DIR}/${dn}"
				cp -a $files "${FILESYSTEM_MOD_DIR}/${dn}/"

				if [ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ]; then
					wrapper_fs_required_libs="ld_uClibc libuClibc"

					# replaced busybox might require some additional libraries not available in AVM's wrapper filesystem
					[ "$FREETZ_REPLACE_BUSYBOX" == "y" -a -r "${PACKAGES_DIR}/busybox/busybox" ] && \
					  wrapper_fs_required_libs+=" $(getNeededEntries ${PACKAGES_DIR}/busybox/busybox | sed -r -e 's,[.]so([.].+)?$,,' | sort | tr $'\n' " ")"

					if echo "${bn}" | grep -qE "^($(join '|' ${wrapper_fs_required_libs}))$"; then
						cp -a $files "${FILESYSTEM_OUTER_MOD_DIR}/${dn}/"
					fi
				fi

				all_files="$all_files $files"
			done
			if [ "$found" = 0 ]; then
				warn "Library $bn selected, but no files found"
			else
				sizeinfo lib "$all_files"
			fi
		done
		if [ "$FREETZ_KEEP_AVM_UCLIBC" != "y" -a "$FREETZ_LIB_libuClibc" == "y" ]; then
			cp -a "${TARGET_SPECIFIC_ROOT_DIR}/lib/libc.so.${FREETZ_TARGET_UCLIBC_MAJOR_VERSION}" "${FILESYSTEM_MOD_DIR}/lib/"
			[ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ] && cp -a "${TARGET_SPECIFIC_ROOT_DIR}/lib/libc.so.${FREETZ_TARGET_UCLIBC_MAJOR_VERSION}" "${FILESYSTEM_OUTER_MOD_DIR}/lib/"
		fi

		echo1 "checking uClibc version"
		DETECTED_UCLIBC_VER="$(find ${FILESYSTEM_DIR}/lib/libuClibc-*.so | sed -rn 's@.*/.*-(.*)\.so@\1@p')"
		echo2 "... used by AVM ...... $DETECTED_UCLIBC_VER"
		echo2 "... used by Freetz ... ${FREETZ_TARGET_UCLIBC_VERSION}"
		if [ "${FREETZ_TARGET_UCLIBC_VERSION#$DETECTED_UCLIBC_VER}" == "$FREETZ_TARGET_UCLIBC_VERSION" \
		  -a "${FREETZ_TARGET_UCLIBC_VERSION}" != "$DETECTED_UCLIBC_VER" ]; then
			warn2 "Freetz and AVM uClibc versions do not match"
		fi
		unset DETECTED_UCLIBC_VER

		if [ "$FREETZ_SHARE_terminfo" == "y" ]; then
			echo1 "installing terminfos"
			for i in $(set | grep ^FREETZ_SHARE_terminfo_.*=y |grep -v '^FREETZ_SHARE_terminfo_showall=' ); do
				dn=usr/share/terminfo
				conf=${i%%=*}
				bn=${conf#FREETZ_SHARE_terminfo_}
				fn=$(echo "$bn" | sed 's/MINUS/-/g;s/PLUS/+/g;s/\DOT/./g')
				echo2 "$fn"
				fn="${fn:0:1}/$fn"
				file="${TARGET_SPECIFIC_ROOT_DIR}/$dn/$fn"
				if [ ! -e "$file" ]; then
					warn "Terminfo $bn selected, but no file found"
					continue;
				fi
				dest="${FILESYSTEM_MOD_DIR}/$dn/${fn:0:1}"
				[ -d "$dest" ] || mkdir -p "$dest"
				cp -a $file "$dest/"
			done
		fi
	else
		error 1 "installation of base system is mandatory"
	fi

	if [ "$FIRMWARE_NOCOMPILE" != "1" ]; then
		[ "$FREETZ_REPLACE_BUSYBOX" != "y" ] && error 1 "installation of busybox replacement is mandatory"

		echo0 "installing busybox"

		DIRS_BUSYBOX_TO_REPLACE_WITHIN="${FILESYSTEM_MOD_DIR}"
		[ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ] && DIRS_BUSYBOX_TO_REPLACE_WITHIN+=" ${FILESYSTEM_OUTER_MOD_DIR}"

		echo1 "replacing busybox"
		[ ! -r "${PACKAGES_DIR}/busybox/busybox" ] && error 1 "cannot find busybox replacement"
		for d in ${DIRS_BUSYBOX_TO_REPLACE_WITHIN}; do cp -pf "${PACKAGES_DIR}/busybox/busybox" "${d}/bin/busybox"; done

		echo1 "installing symlinks"
		[ ! -r "${PACKAGES_DIR}/busybox/busybox.links" ] && error 1 "cannot find busybox links"

		# Remove old busybox links
		# be compatible: do not use -delete (not found in older versions of find)
		find ${DIRS_BUSYBOX_TO_REPLACE_WITHIN} \( -lname "busybox" -or -lname "*/busybox" \) -print0 | xargs -0 rm

		# Install new busybox links
		for link in $(cat "${PACKAGES_DIR}/busybox/busybox.links"); do
			LINK_DIR="$(dirname "$link")"
			LINK_NAME="$(basename "$link")"

			case "$LINK_DIR" in
				/)
					BUSYBOX_PATH="bin/busybox"
					;;
				/bin)
					BUSYBOX_PATH="busybox"
					;;
				/sbin)
					BUSYBOX_PATH="../bin/busybox"
					;;
				/usr/bin|/usr/sbin)
					BUSYBOX_PATH="../../bin/busybox"
					;;
				*)
					error 1 "unknown installation directory: $link"
					;;
			esac

			for d in ${DIRS_BUSYBOX_TO_REPLACE_WITHIN}; do
				mkdir -p "${d}${LINK_DIR}"
				ln -sf "$BUSYBOX_PATH" "${d}${LINK_DIR}/$LINK_NAME" || error 1 "could not create link for $link"
			done
		done || exit 1
	fi

	echo0 "installing packages"
	for pkg in $(static_packages); do
		sizeinfo "$pkg"
		pkg_name=$(pkg_name "$pkg")

		"$TAR" -c -C "${PACKAGES_DIR}/${pkg}/root" \
			$(find "${PACKAGES_DIR}/${pkg}/" -maxdepth 1 -type f -name ".exclude*" -printf '--exclude-from=%p ') \
			. | \
		"$TAR" -x -C "$FILESYSTEM_MOD_DIR" || exit 1

		[ -d "${PACKAGES_DIR}/${pkg}/${VARTAR_SUBDIR}" ] && \
			$(
				"$TAR" -c -C "${PACKAGES_DIR}/${pkg}/${VARTAR_SUBDIR}" . | \
				"$TAR" -x -C ${VARTAR_MOD_DIR} || exit 1
			)

		[ -r "${PACKAGES_DIR}/${pkg}/.language" ] && \
			modlang "${PACKAGES_DIR}/${pkg}/.language" "${FILESYSTEM_MOD_DIR}"

		if ! grep -q "^$pkg_name$" "${VARTAR_MOD_DIR}/var/mod/etc/static.pkg" >/dev/null 2>&1; then
			echo "$pkg_name" >> "${VARTAR_MOD_DIR}/var/mod/etc/static.pkg"
		fi

		pkgfiles=$(collect_pkg_files "${pkg}" | sed -e "s,^,${PACKAGES_DIR}/${pkg}/root,")
		sizeinfo pkg "${PACKAGES_DIR}/${pkg}" $pkgfiles
	done
	for pkg in $(static_addons); do
		sizeinfo "$pkg (addon)"
		pkg_name=$(pkg_name "$pkg")

		if [ -e "${ADDON_DIR}/${pkg}/etc/init.d/rc.${pkg_name}" ]; then
			echo "NOTICE: addon '$pkg' is in old-style format (no language support)." 1>&2
			"$TAR" -c -C "${ADDON_DIR}/${pkg}" . | "$TAR" -x -C "$FILESYSTEM_MOD_DIR" || exit 1
		else
			"$TAR" -c -C "${ADDON_DIR}/${pkg}/root" \
				$(find "${ADDON_DIR}/${pkg}/" -maxdepth 1 -type f -name ".exclude*" -printf '--exclude-from=%p ') \
				. | \
			"$TAR" -x -C "$FILESYSTEM_MOD_DIR" || exit 1

			[ -d "${ADDON_DIR}/${pkg}/${VARTAR_SUBDIR}" ] && \
				$(
					"$TAR" -c -C "${ADDON_DIR}/${pkg}/${VARTAR_SUBDIR}" . | \
					"$TAR" -x -C ${VARTAR_MOD_DIR} || exit 1
				)

			[ -r "${ADDON_DIR}/${pkg}/.language" ] && \
				modlang "${ADDON_DIR}/${pkg}/.language" "${FILESYSTEM_MOD_DIR}"
		fi

		if ! grep -q "^$pkg_name$" "${VARTAR_MOD_DIR}/var/mod/etc/static.pkg" >/dev/null 2>&1; then
			echo "$pkg_name" >> "${VARTAR_MOD_DIR}/var/mod/etc/static.pkg"
		fi
		sizeinfo add "${ADDON_DIR}/${pkg}"
	done

	if [ "$NOCOMPILE" != "1" -a "$FREETZ_REPLACE_KERNEL" == "y" ]; then
		echo0 "replacing kernel"

		if [ ! -r "${KERNEL_REP_DIR}/kernel-${KERNEL_ID}.bin" ]; then
			error 1 "can't find kernel for ref ${KERNEL_ID}"
		fi

		echo1 "replacing kernel-${KERNEL_ID} (${KERNEL_SUBVERSION})"
		cp "${KERNEL_REP_DIR}/kernel-${KERNEL_ID}.bin" "$RAW_KERNEL_MOD"
	fi

	# modules directory / correctly configured kernel version
	echo1 "checking kernel version"
	echo2 "... used by AVM ...... ${MODULES_SUBDIR##*/}"
	echo2 "... used by Freetz ... ${FREETZ_KERNEL_VERSION_MODULES_SUBDIR}"
	if [ "lib/modules/${FREETZ_KERNEL_VERSION_MODULES_SUBDIR}" != "$MODULES_SUBDIR" ]; then
		if [ "$FREETZ_REPLACE_MODULE_AVAILABLE" == "y" ]; then
			error 1 "Freetz and AVM kernel versions do not match. Wrong kernel version configuration in Freetz?"
		else
			warn2 "Freetz and AVM kernel versions do not match. Wrong kernel version\nconfiguration in Freetz or no sources matching firmware version provided by AVM?"
		fi
	fi

	if [ "$FREETZ_REPLACE_KERNEL" == "y" -o -n "$(set|grep ^FREETZ_MODULE_.*=y)" ]; then
		[ "$FREETZ_STRIP_MODULES_FREETZ" == "y" ] && strip_output=" and stripping"
		echo0 "installing$strip_output modules"
		for i in \
		$( \
			cd "${KERNEL_REP_DIR}/modules-${KERNEL_ID}" && \
			find . -type d -name .svn -prune -false , -type f \( -name modules.dep -o -name "*.ko" \) \
		); do
			bn="$(basename "$i")"
			ko_install="$(eval "echo \"\$FREETZ_MODULE_$(echo "${bn%\.ko}" | tr '\-+' '_x')\"")"
			[ "$FREETZ_MODULES_ALL" = "y" -o "$ko_install" == "y" ] || continue
			sizeinfo "$bn"
			ko_file_src="${KERNEL_REP_DIR}/modules-${KERNEL_ID}/${i}"
			ko_path_dst="${MODULES_DIR}/kernel/$(dirname "$i")"
			[ -d "$ko_path_dst" ] || mkdir -p "$ko_path_dst"
			cp -a $ko_file_src "$ko_path_dst/"
			if [ "$FREETZ_STRIP_MODULES_FREETZ" == "y" ]; then
				${ABS_BASE_DIR}/toolchain/kernel/bin/${FREETZ_KERNEL_CROSS}strip -p --strip-unneeded \
					--remove-section={.comment,.pdr,.mdebug.abi32,.note.gnu.build-id} "$ko_path_dst/$bn"
			fi
			sizeinfo mod "$ko_path_dst/$bn"
		done

		echo1 "generating modules.dep"
		DEPMOD_TEMP="$(mktemp)"
		NM=$NM ${TOOLS_DIR}/depmod.pl -e -b "$MODULES_DIR" \
			-F ${KERNEL_REP_DIR}/System-${KERNEL_ID}.map \
			2>&1 | tee "$DEPMOD_TEMP"
		if grep -q "^unresolved symbol " "$DEPMOD_TEMP"; then
			warn "Unresolved symbols detected, not all AVM-features may work.\nNo current sources by AVM? Error in kernel's .config?"
		fi
		rm -rf "$DEPMOD_TEMP" 2>/dev/null
		# add plugin kernel modules for 7270v1 if plugins are not enabled
		if [ "$FREETZ_TYPE_7270_V1" == "y" -a "$FREETZ_AVMPLUGINS_INTEGRATE" != "y" ]; then
			echo "kernel/drivers/net/rfcntl/rfcntl.ko: kernel/drivers/char/audio/avm_audio.ko" >> $MODULES_DIR/modules.dep
			echo "kernel/drivers/char/audio/avm_audio.ko:" >> $MODULES_DIR/modules.dep
		fi
	fi

	for f in modules.dep.bin modules.alias.bin modules.builtin.bin modules.symbols.bin; do
		if [ -e "$MODULES_DIR/$f" ]; then
			echo1 "removing (unused) $f"
			rm -f "$MODULES_DIR/$f"
		fi
	done

	kolinecnt="$(cat $MODULES_DIR/modules.dep | wc -l)"
	kofilecnt="$(find $MODULES_DIR -type f -name '*.ko' | wc -l)"
	echo1 "kernel modules installed: $kolinecnt entries in modules.dep and $kofilecnt .ko-files found."
	if [ "$FREETZ_KERNEL_VERSION_2_6_28" == "y" ]; then
		if [ "$FREETZ_REPLACE_KERNEL" != "y" ];then
			koreplacek=", try 'replace kernel'"
			komaxcount=50
		else
			komaxcount=99
		fi
		if [ $kolinecnt -gt $komaxcount -o $kofilecnt -gt $komaxcount ]; then
			warn0 "This kernel can only load $komaxcount modules at the same time${koreplacek}."
		fi
	fi

	echo1 "removing empty kernel module directories"
	for dir in $(find ${MODULES_DIR} -type d -empty -delete -printf "%P\n"); do
		echo2 $dir
	done

	invoke_fwmod_custom all

	# Processing some options, must be done after base package is in place
	if [ "$FREETZ_INSTALL_BASE" == "y" ]; then
		echo0 "processing mod base options"

		# color scheme
		if [ "$FREETZ_STYLE" == "grey" ]; then
			echo1 "setting grey style as default"
			ln -s colorscheme-grey.css ${FILESYSTEM_MOD_DIR}/usr/share/style/colorscheme.css
		else
			echo1 "setting colored style as default"
			ln -s colorscheme-colored.css ${FILESYSTEM_MOD_DIR}/usr/share/style/colorscheme.css
		fi

		# favicon
		if [ "$FREETZ_FAVICON_STRING" != "none" ]; then
			echo1 "adding favicon(s) (${FREETZ_FAVICON_STRING})"
			cp "${FAVICON_DIR}/${FREETZ_FAVICON_STRING}/freetz.ico" "${FILESYSTEM_MOD_DIR}/usr/share/favicon.ico"
			ln -s "../share/favicon.ico" "${FILESYSTEM_MOD_DIR}/usr/mww/favicon.ico"
			if [ -e "${FAVICON_DIR}/${FREETZ_FAVICON_STRING}/box.ico" ]; then
				cp "${FAVICON_DIR}/${FREETZ_FAVICON_STRING}/box.ico" "${HTML_LANG_MOD_DIR}/html/favicon.ico"
				ln -fs "./html/favicon.ico" "${HTML_LANG_MOD_DIR}/favicon.ico"
			fi
		fi

		# security level
		echo1 "patching security level ($FREETZ_SECURITY_LEVEL)"
		sed -i -e "s/sec_level=1/sec_level=$FREETZ_SECURITY_LEVEL/g" ${FILESYSTEM_MOD_DIR}/usr/lib/libmodcgi.sh

		# external
		[ ! "$EXTERNAL_ENABLED" != "y" ] && echo1 "integrated external"
		# FREETZMOUNT
		[ ! "$FREETZ_PATCH_FREETZMOUNT" != "y" ] && echo1 "integrated FREETZMOUNT"
		# Box-Info
		[ "$FREETZ_REMOVE_BOX_INFO" != "y" ] && echo1 "integrated Box-Info"
		# FREETZ-Info
		[ "$FREETZ_REMOVE_FREETZ_INFO" != "y" ] && echo1 "integrated FREETZ-Info"

		# .config
		if [ ! "$FREETZ_REMOVE_DOT_CONFIG" == "y" ]; then
			{ grep '^[^#].*$' "$DOT_CONFIG"; grep '^#.*$' "$DOT_CONFIG.compressed"; echo "FREETZ_LIBRARY_DIR=\"${FREETZ_LIBRARY_DIR}\""; } \
			| grep -v "FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD" > "${FILESYSTEM_MOD_DIR}/etc/.config"
			echo1 "integrated .config (stripped)"
		fi

		# logo tagging
		if [ "$FREETZ_TAGGING_STRING" != "none" ]; then
			tagging() {
				[ $# -lt 3 ] && return
				local tagging_geo="$1"
				shift
				local tagging_pos="$1"
				shift
				local tagging_arg="$*"
				local tagging_pic="${TAGGING_DIR}/${FREETZ_TAGGING_STRING}.png"
				for tagging_file in $tagging_arg; do
					echo2 "${tagging_file}"
					composite -dissolve 100% -gravity $tagging_pos -geometry $tagging_geo -quality 100 $tagging_pic $tagging_file $tagging_file
					[ $? -ne 0 ] && error 1 "tagging failed while processing ${tagging_file#${FILESYSTEM_MOD_DIR}/}"
				done
			}
			echo1 "tagging avm webif (tag by ${FREETZ_TAGGING_STRING})"
			for d in "${HTML_MOD_DIR}" "${HTML_MOD_DIR}.nas"; do
				[ -d "${d}" ] || continue
				# lua since  Fritz!OS 6.2x (small logo @left, eg 7390 06.23)
				tagging "+0+6" "center" $(find "${d}" -name kopfbalken_links.png)
				# lua before Fritz!OS 6.2x (small logo @left, eg 7270 04.88)
				tagging "+0+0" "center" $(find "${d}" -name kopfbalken_links.gif)
				# newer html (big header @top, eg 7320 05.29)
				tagging "-20+0" "west" $(find "${d}" -name kopfbalken.gif)
				# older (big header @top, eg 7141 04.76)
				tagging "+0+0" "west" $(find "${d}" -name fw_header980.gif)
				# even older (big header @top, eg 7050 04.03)
				tagging "+0-5" "west" $(find "${d}" -name fw_header.gif)
			done
		fi

	fi

	if [ "$FREETZ_STRIP_LIBRARIES" == "y" ]; then
		echo0 "shrinking shared libs"
		source ${TOOLS_DIR}/freetz_mklibs
		mklibs || error 1 "mklibs returned an error. Please see mklibs.log."
	fi
	if  [ "$FREETZ_STRIP_MODULES_ALL" == "y" ]; then
		echo0 "stripping all kernel modules"
		for i in $(find ${MODULES_DIR} -name '*.ko'); do
			echo2 $i
			${ABS_BASE_DIR}/toolchain/kernel/bin/${FREETZ_KERNEL_CROSS}strip -p --strip-unneeded \
				--remove-section={.comment,.pdr,.mdebug.abi32,.note.gnu.build-id} $i
		done
	fi
	if [ "$FREETZ_STRIP_SCRIPTS" == "y" ]; then
		echo0 "stripping shell scripts"
		# This is (necessarily) slow because it checks *all* files
		for f in $(find ${FILESYSTEM_MOD_DIR} -type f); do
			# Syntax check. Use ash (not bash) because on target we also have ash.
			# This is the first step because it is faster than 'file'.
			$BUSYBOX ash -n "$f" 2>/dev/null &&
			# Sometimes the ash syntax check succeeds on binaries.
			# So we need to check we really have a text file here.
			file -b --mime-type "$f" | grep -sq '^text/' &&
			# Only strip scripts (possibly also stuff like awk or perl) with
			# shebang. This excludes dot-included files without shebang, but
			# anything else would be too unsafe. We do not want plain text
			# files stripped.
			grep -sq '#[^!]' "$f" ||
			# continue the loop with the next file if one of the conditions above is false
			continue

			# Strip ...
			STRIP_SCRIPTS_SED_SCRIPT='/^[ \t]*$/d;'               # blank lines
			# TODO: doesn't work because of multiline IFS definitions, i.e. IFS='\t\n'
			#STRIP_SCRIPTS_SED_SCRIPT+='s/[ \t]+$//;'             # trailing white spaces
			STRIP_SCRIPTS_SED_SCRIPT+='/^[ \t]*#[^!].*/d;'        # shell comments (not shebangs)
			if [ "${f: -3:3}" != ".py" ]; then
				STRIP_SCRIPTS_SED_SCRIPT+='s/^[ \t]*//g;'     # and indentation if it's not a python script
			fi
			sed -i -r "${STRIP_SCRIPTS_SED_SCRIPT}" "$f" &&
			echo2 "${f##${FILESYSTEM_MOD_DIR}}"
		done
	fi
	if [ "$FREETZ_STRIP_BINARIES" == "y" ]; then
		echo0 "stripping leftover unstripped binaries"
		for i in $(find \
				"${FILESYSTEM_MOD_DIR}" $([ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ] && echo "${FILESYSTEM_OUTER_MOD_DIR}") \
				! \( -name '*.ko' -o -path '*/usr/www/*' -o -path '*/etc/default*' -o -path '*/lib/modules*' \) \
				\( -perm /100 -o -name "*.so*" \) \
				-type f \
				-exec file {} '+' | sed -n 's|^\(.*\):.*MIPS.*not stripped.*|\1|p' \
		); do
			echo2 $i
			chmod u+r $i
			$STRIP -p --remove-section={.comment,.note,.pdr} $i
		done
	fi

	# External should run after modifying, stripping etc.
	if [ "$EXTERNAL_ENABLED" == "y" ]; then
		echo0 "processing external"
		[ ! -x "$EXTERNAL" ] && error 1 "cannot find the tool $EXTERNAL_TOOL"
		source "${EXTERNAL}"
	fi

	touch "${DIR}/.modified"
	echo -e "done.\n"
fi


#################################################
## Pack, sign, zip, copy the modified firmware ##
#################################################

DO_AVOID_MOD_IN_STEP3=0
[ "$DO_PACK" -gt 0 ] && action_names+="/PACK"
[ "$DO_SIGN" -gt 0 ] && action_names+="/SIGN"
[ "$DO_ZIP"  -gt 0 ] && action_names+="/ZIP"
[ "$COPY_FS_DIR"   ] && action_names+="/COPY"
action_names=${action_names##/}

# ------------------------------------------------------
# -- Common initial actions for pack, sign, zip, copy --
# ------------------------------------------------------

if [ "$action_names" ]; then
	echo0 -b "STEP 3: ${action_names}"

	if [ ! "$DO_MOD" -gt 0 ]; then
		echo "WARNING: Modifications (STEP 2) and this step should never" 1>&2
		echo "         ever be run with different configurations!" 1>&2
		echo "         This can result in invalid images!!!" 1>&2
	fi

	[ "$FW_IMAGES_DIR" ] && mkdir -p "$FW_IMAGES_DIR"

	# Check if firmware is unpacked
	if [ ! -r "$UNPACKED_FILE" ]; then
		error 1 "firmware image has to be unpacked before packing"
	fi

	# Check if firmware is modified by the script
	if [ ! -r "${DIR}/.modified" ]; then
		warn "firmware does not seem to be modified by the script"
		DO_AVOID_MOD_IN_STEP3=1

		# Check if this is a completely automated run in the "no freetz"-mode
		if [ "$NO_FREETZ_AUTOMATED_RUN" -gt 0 ]; then
			# Removing (possibly already existing) MOD_DIR is necessary
			# as Freetz doesn't have any checks if some of the patches
			# are already applied and might (try to) apply them over
			# and over again.
			rm -rf "$MOD_DIR"
		#else
			# In the manual "step by step"-mode it's up to the user to invoke
			# the script in a proper configuration so that the patches do not
			# get applied multiple times.
		fi

		if [ ! -d "$MOD_DIR" ]; then
			# Copy the unpacked directory
			cp -r "$ORG_DIR" "$MOD_DIR"
		fi

		invoke_fwmod_custom all_no_freetz
	fi

	# Remove left over Subversion directories
	echo1 "checking for left over Subversion directories"
	find "$MOD_DIR" \( -type d -name '.svn' \) -o -name '.gitignore' | xargs rm -rf

	# Delete all files that we are going to re-create now
	rm -f "$VARTAR_MOD"
	rm -f "$KERNEL_MOD"
	rm -f "$FILESYSTEM_MOD"
	rm -f "${DIR}/*.image"
	rm -f "${FIRMWARE_MOD_DIR}/var/signature"

	modmakedate=$(date +%Y%m%d-%H%M%S)
	modbaseimagename="${FREETZ_TYPE_PREFIX_ALIEN_HARDWARE}${FREETZ_TYPE_PREFIX%_0?_??}"
	modbaseimagename+="_${AVM_FW_VERSION}"
	if [ -n "$FREETZ_TYPE_PREFIX_LABOR_FIRMWARE" ]; then
		modbaseimagename+="-rev${AVM_FW_REVISION}${FREETZ_TYPE_PREFIX_LABOR_FIRMWARE}"
	fi
	modbaseimagename+="${SUBVERSION:+-}${SUBVERSION}" # missing in DO_MOD=0 mode
	modbaseimagename+=".${FREETZ_TYPE_LANGUAGE}"
	if [ "$FREETZ_CUSTOM_IMAGE_NAME" == "y" -a ! -z "$FREETZ_USER_DEFINED_COMMENT" ]; then
		modcustomimagestring=$(echo $FREETZ_USER_DEFINED_COMMENT | sed -e "s/ /_/g" | tr -cd 'a-zA-Z0-9_+-.<->')
	else
		modcustomimagestring=""
	fi
	if [ "$FREETZ_CUSTOM_IMAGE_NAME" == "y" -a ! -z "${modcustomimagestring}" ]; then
		if [ "$FREETZ_CUSTOM_IMAGE_NAME_PREFIX" == "y" ]; then
			modimagenameprefix="${modcustomimagestring}_"
			modimagenamesuffix=""
		else
			modimagenameprefix=""
			modimagenamesuffix="_${modcustomimagestring}"
		fi
	else
		modimagenameprefix=""
		modimagenamesuffix=""
	fi
	modstring="${modimagenameprefix}${modbaseimagename}${modimagenamesuffix}_${modmakedate}"
	modimage="${modstring}.image"

	# collect externalized files
	externalised_files=$(find "$EXTERNAL_MOD_DIR/" -type f 2>/dev/null | sed -e "/external[.]pkg/d;/[.]external/d;s,$EXTERNAL_MOD_DIR/,,")

	if [ ! "$DO_AVOID_MOD_IN_STEP3" -gt 0 ]; then
		# Create freetz-info
		echo1 "integrate freetz info file into image"
		freetz_info_file="$MOD_DIR/filesystem/etc/freetz_info.cfg"
		echo "export FREETZ_INFO_BOXTYPE='${FREETZ_TYPE_PREFIX_ALIEN_HARDWARE}${FREETZ_TYPE_PREFIX%_0?_??}${FREETZ_TYPE_PREFIX_LABOR_FIRMWARE}'" > "$freetz_info_file"
		echo "export FREETZ_INFO_FIRMWAREVERSION='${AVM_FW_VERSION}'" >> "$freetz_info_file"
		echo "export FREETZ_INFO_SUBVERSION='${SUBVERSION}'" >> "$freetz_info_file"
		echo "export FREETZ_INFO_LANG='${FREETZ_TYPE_LANGUAGE}'" >> "$freetz_info_file"
		echo "export FREETZ_INFO_MAKEDATE='${modmakedate}'" >> "$freetz_info_file"
		echo "export FREETZ_INFO_IMAGE_NAME='$modimage'" >> "$freetz_info_file"
		echo "export FREETZ_INFO_COMMENT='${FREETZ_USER_DEFINED_COMMENT}'" >> "$freetz_info_file"
		echo "export FREETZ_INFO_EXTERNAL_FILES='${externalised_files}'" >> "$freetz_info_file"
	fi

	# Pack var.tar (use old tar for compatibility)
	echo0 "packing var.tar"
	"$TAR" -C "$VARTAR_MOD_DIR" -cf "$VARTAR_MOD" .
	if [ $? -ne 0 ] || ! is_valid_tarball "$VARTAR_MOD"; then
		mv "${VARTAR_MOD}" "${VARTAR_MOD}.corrupted" 2>/dev/null
		error 1 "packing of var.tar failed"
	fi

	[ "$FREETZ_AVM_HAS_UDEV" == "y" ] || \
		$MAKEDEVS -d $MAKEDEVS_FILE $FILESYSTEM_MOD_DIR > $MOD_DIR/filesystem.log 2>&1
fi


if [ "$DO_SIGN" -gt 0 ]; then
	FREETZ_IMAGE_SIGNING_PREFIX="${HOME}/.freetz.image_signing"
	function getNumberOfImageSigningFiles() {
		local ret=0
		for i in asc key pem; do
			[ -e "${FREETZ_IMAGE_SIGNING_PREFIX}.$i" ] && ret=$((ret+1))
		done
		echo $ret
	}

	NO_IMAGE_SIGNING_FILES=$(getNumberOfImageSigningFiles)
	if [ "${NO_IMAGE_SIGNING_FILES}" -eq 0 ]; then
		echo0 "No image signing files found, generating them ..."
		if [ -z "${FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD}" ]; then
			# be verbose if no private key password provided
			"${TOOLS_DIR}/yf/signimage/generate_signing_key" "${FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD}"
		else
			"${TOOLS_DIR}/yf/signimage/generate_signing_key" "${FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD}" 2>"${MOD_DIR}/generate_signing_key.log"
		fi
		if [ $? -ne 0 ]; then
			error 1 "failed to generate image signing files, see console output or ${MOD_DIR}/generate_signing_key.log for details"
		fi
		NO_IMAGE_SIGNING_FILES=$(getNumberOfImageSigningFiles)
	else
		echo0 "Image signing files found, checking their consistency"
	fi
	if [ "${NO_IMAGE_SIGNING_FILES}" -ne 3 ]; then
		error 1 "Unexpected/wrong number of image signing files"
	fi

	echo0 "Copying ${FREETZ_IMAGE_SIGNING_PREFIX}.asc to /etc/avm_firmware_public_key9"
	cp -a "${FREETZ_IMAGE_SIGNING_PREFIX}.asc" "${FILESYSTEM_MOD_DIR}/etc/avm_firmware_public_key9"
fi

# ------------------------------------------------------------------
# -- Zip file system to tar.gz archive (USB root, maybe NFS root) --
# ------------------------------------------------------------------

if [ "$DO_ZIP" -gt 0 ]; then
	if [ "$FW_IMAGES_DIR" ]; then
		zip_name="${FW_IMAGES_DIR}/${modstring}_rootfs.tar.gz"
	else
		zip_name="${DIR}/${modstring}_rootfs.tar.gz"
	fi
	echo0 "zipping root file system to $zip_name"
	"$TAR" -C "$FILESYSTEM_MOD_DIR" -czf "$zip_name" .
	if [ $? -ne 0 ] || ! is_valid_tarball "$zip_name"; then
		mv "${zip_name}" "${zip_name}.corrupted" 2>/dev/null
		error 1 "zipping of root file system failed"
	fi
	echo "$zip_name" > "${DIR}/.rootfs_archive"
fi

# -------------------------
# -- Pack firmware image --
# -------------------------

if [ "$DO_PACK" -gt 0 ]; then
	[ ! -x "$MKSQUASHFS" ] && error 1 "cannot find $MKSQUASHFS"

	echo0 "creating ${INNER_FS_PREFIX}filesystem image (SquashFS${FREETZ_SQUASHFS_VERSION}-${FREETZ_AVM_SQUASHFS_COMPRESSION})"
	echo1 "SquashFS block size: $(($FREETZ_SQUASHFS_BLOCKSIZE/1024)) kB ($FREETZ_SQUASHFS_BLOCKSIZE bytes)"

	rm -f $ABS_BASE_DIR/${MAIN_FILESYSTEM_MOD}
	"$MKSQUASHFS" $FILESYSTEM_MOD_DIR/* $ABS_BASE_DIR/${MAIN_FILESYSTEM_MOD} $MKSQUASHFS_OPTIONS >> $MOD_DIR/filesystem.log 2>&1

	if [ "${FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE}" == "y" ]; then
		echo0 "copying kernel image"
		cp -a "$RAW_KERNEL_MOD" "$KERNEL_MOD"
	else
		[ ! -s "$RAW_FILESYSTEM_MOD" ] && error 1 "creation of filesystem failed"

		touch "$FILESYSTEM_MOD"

		echo0 "merging kernel image"
		dd if="$RAW_KERNEL_MOD" of="$KERNEL_MOD" bs=256 conv=sync 2> /dev/null
		cat "$RAW_FILESYSTEM_MOD" >> "$KERNEL_MOD"

		[ ! -s "$KERNEL_MOD" ] && error 1 "kernel merging failed"
	fi

	# Check size of kernel image (multiple of 64 kB blocks)
	if [ -n "$FREETZ_KERNEL_CUSTOM_MTD_SIZE" ]; then
		let KERNEL_LIMIT="$FREETZ_KERNEL_CUSTOM_MTD_SIZE*64*1024"
	else
		KERNEL_LIMIT="$(sed -nr 's/^kernel_size=(.*)/\1/p' ${FIRMWARE_DIR}/var/install)"
		[ -z "$KERNEL_LIMIT" ] && error 1 "Can't find kernel_size in var/install"
	fi
	let KERNEL_SIZE="$(wc -c < "$KERNEL_MOD")"
	let KERNEL_DIFF="KERNEL_SIZE-KERNEL_LIMIT"

	echo1 "kernel image size: $(byte_to_mb $KERNEL_SIZE), max $(byte_to_mb $KERNEL_LIMIT), free $(byte_to_mb $((-KERNEL_DIFF))) ($((-KERNEL_DIFF)) bytes)"
	[ "$KERNEL_SIZE" -eq 0 ] && error 1 "kernel image is empty"
	if [ "$KERNEL_SIZE" -gt "$KERNEL_LIMIT" ]; then
		if [ "${FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE}" == "y" ]; then
				error 1 -b "kernel image is $KERNEL_DIFF bytes too big. See http://freetz.org/wiki/FAQ#Filesystemimagetoobig for details."
		else
			if [ "$FORCE_PACK" -eq 0 -a "$DO_ZIP" -eq 0 ]; then
				error 1 -b "combined kernel+filesystem image is $KERNEL_DIFF bytes too big. See http://freetz.org/wiki/FAQ#Filesystemimagetoobig for details."
			fi
			if [ "$FORCE_PACK" -gt 0 -a "$DO_ZIP" -eq 0 ]; then
				echo0   -b "Use for SDK mode only. See http://wehavemorefun.de/fritzbox/index.php/SDK-Firmware"
			fi
			if [ "$FORCE_PACK" -eq 0 -a "$DO_ZIP" -gt 0 ]; then
				echo0   -b "Use for USBroot or NFSroot mode only."
			fi
		fi
	fi

# ---------------------------
# -- Pack filesystem image --
# ---------------------------
	if [ "${FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE}" == "y" ]; then

		if [ "${FREETZ_AVM_HAS_INNER_OUTER_FILESYSTEM}" == "y" ]; then
			if [ "${FREETZ_AVM_OUTER_FILESYSTEM_TYPE}" == "Ext2FS" ]; then
				echo0 "creating outer-filesystem image (Ext2FS)"
				PATH="$TOOLS_DIR:$PATH" $TOOLS_DIR/mke2img -G 2 -R 0 -d "${FILESYSTEM_OUTER_MOD_DIR}" -o "$ABS_BASE_DIR/$RAW_FILESYSTEM_MOD.ext2" >> $MOD_DIR/filesystem.log 2>&1
				[ $? -ne 0 -o ! -s "$RAW_FILESYSTEM_MOD.ext2" ] && error 1 "creation of ext2 image failed"

				{
					# create pseudo SquashFS-header: "sqsh" followed by 252 zeros
					echo -n "sqsh" > "$RAW_FILESYSTEM_MOD" && \
					dd if=/dev/zero of="$RAW_FILESYSTEM_MOD" bs=1 count=252 oflag=append conv=notrunc
				} >> $MOD_DIR/filesystem.log 2>&1 && \
				cat "$RAW_FILESYSTEM_MOD.ext2" >> "$RAW_FILESYSTEM_MOD" && \
				rm -f "$RAW_FILESYSTEM_MOD.ext2"
			elif [ "${FREETZ_AVM_OUTER_FILESYSTEM_TYPE}" == "SquashFS" ]; then
				echo0 "creating outer-filesystem image (SquashFS${FREETZ_SQUASHFS_VERSION}-${FREETZ_AVM_SQUASHFS_COMPRESSION})"
				"$MKSQUASHFS" ${FILESYSTEM_OUTER_MOD_DIR}/* $ABS_BASE_DIR/$RAW_FILESYSTEM_MOD $MKSQUASHFS_OPTIONS >> $MOD_DIR/filesystem.log 2>&1
			else
				error 1 "unknown/unsupported outer-filesystem type \"${FREETZ_AVM_OUTER_FILESYSTEM_TYPE}\""
			fi
		fi

		[ ! -s "$RAW_FILESYSTEM_MOD" ] && error 1 "creation of filesystem failed"
		let FILESYSTEM_SIZE="$(wc -c < "$RAW_FILESYSTEM_MOD")"
		[ "$FILESYSTEM_SIZE" -eq 0 ] && error 1 "filesystem image is empty"

		echo0 "copying filesystem image"
		rm -f "$FILESYSTEM_MOD" "$FILESYSTEM_MOD.ext2"
		cp -a "$RAW_FILESYSTEM_MOD" "$FILESYSTEM_MOD"

		# Check size of filesystem image (multiple of 64 kB blocks)
		if [ -n "$FREETZ_FILESYSTEM_CUSTOM_MTD_SIZE" ]; then
			let FILESYSTEM_LIMIT="$FREETZ_FILESYSTEM_CUSTOM_MTD_SIZE*64*1024"
		else
			FILESYSTEM_LIMIT="$(sed -nr 's/^filesystem_size=(.*)/\1/p' ${FIRMWARE_DIR}/var/install)"
			[ -z "$FILESYSTEM_LIMIT" ] && error 1 "Can't find filesystem_size in var/install"
		fi
		let FILESYSTEM_SIZE="$(wc -c < "$FILESYSTEM_MOD")"
		let FILESYSTEM_DIFF="FILESYSTEM_SIZE-FILESYSTEM_LIMIT"

		echo1 "filesystem image size: $(byte_to_mb $FILESYSTEM_SIZE), max $(byte_to_mb $FILESYSTEM_LIMIT), free $(byte_to_mb $((-FILESYSTEM_DIFF))) ($((-FILESYSTEM_DIFF)) bytes)"
		[ "$FILESYSTEM_SIZE" -eq 0 ] && error 1 "filesystem image is empty"
		if [ "$FILESYSTEM_SIZE" -gt "$FILESYSTEM_LIMIT" ]; then
			if [ "$FORCE_PACK" -eq 0 -a "$DO_ZIP" -eq 0 ]; then
				error 1 -b "filesystem image is $FILESYSTEM_DIFF bytes too big. See http://freetz.org/wiki/FAQ#Filesystemimagetoobig for details."
			fi
			if [ "$FORCE_PACK" -ne 0 -o "$DO_ZIP" -ne 0 ]; then
				echo0   -b "Use for USBroot or NFSroot mode only."
			fi
		fi
	fi


	if [ "$FREETZ_VERBOSITY_LEVEL" -ge 1 ]; then
		if [ "$FREETZ_AVM_HAS_TAM" = "y" -a "${FREETZ_AVM_HAS_SEPARATE_FILESYSTEM_IMAGE}" != "y" ]; then
			# Calculate aproximately free space in seconds for the answering machine
			# 5*64kB sectors needed, see http://freetz.org/ticket/1680#comment:4
			FREE_BYTE_JFFS2=$((($KERNEL_LIMIT - $KERNEL_SIZE - 327680)))
			FREE_MINUTES=$((($FREE_BYTE_JFFS2 / 2017 / 60)))
			if [ "$FREE_BYTE_JFFS2" -gt 0 ]; then
				echo "${L1}Aproximately maximal time for the answering machine: ${FREE_MINUTES} min, $((($FREE_BYTE_JFFS2 / 2017 - $FREE_MINUTES * 60))) sec ($((($FREE_BYTE_JFFS2 / 2017))) sec)"
			else
				if [ "$FREETZ_REMOVE_JFFS2" != "y" ]; then
					echo "${L1}WARNING: Not enough free flash space for answering machine!"
				else
					echo "${L1}Telephone answering machine will need a usb storage device!"
				fi
			fi
		fi
	fi

	# Create in-memory image
	img_name="${FW_IMAGES_DIR:-$DIR}/${modimage}"
	if [ "${FREETZ_FWMOD_CREATE_IN_MEMORY_IMAGE}" == "y" ]; then
		echo0 "creating in-memory image ${img_name}.in-memory"
		{
			cat "$KERNEL_MOD"     | dd bs=256 conv=sync 2>/dev/null;
			cat "$FILESYSTEM_MOD" | dd bs=256 conv=sync 2>/dev/null;
		} > "${img_name}.in-memory"
	fi

	# Write checksum
	for f in "$KERNEL_MOD" "$FILESYSTEM_MOD"; do
		[ -s "$f" ] || continue

		fbn=$(basename $f)
		echo0 "adding checksum to $fbn"
		"$TICHKSUM" -a "$f" > "${MOD_DIR}/$fbn-chksum.log" || { rm -f "${img_name}.in-memory"; error 1 "adding checksum to $fbn failed"; }
	done

	# Consistency check
	if [ -s "${FIRMWARE_DIR}/${KERNEL_IMAGE}" -a ! -s "${FIRMWARE_MOD_DIR}/${KERNEL_IMAGE}" ] || \
		[ ! -s "${FIRMWARE_DIR}/${KERNEL_IMAGE}" -a -s "${FIRMWARE_MOD_DIR}/${KERNEL_IMAGE}" ]; then
		error 1 "inconsistency comparing size of old and new kernel.image"
	fi

	if [ ! "$DO_AVOID_MOD_IN_STEP3" -gt 0 ]; then
		# Last, but not least, include .config and addon/static.pkg into firmware
		# image for further reference, e.g. user support
		[ -r "$DOT_CONFIG" ] && { cat "$DOT_CONFIG" | grep -v "FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD" > "$FIRMWARE_MOD_DIR/var/.config"; }
		[ -r "$PACKAGES_LIST_FILE" ] && cp "$PACKAGES_LIST_FILE" "$FIRMWARE_MOD_DIR/var"
	fi

	# Pack firmware image

	# use GNU tar with --format=gnu for compatibility
	TAR_FOR_IMAGE_TARBALL="$TAR_GNU"
	TAR_FOR_IMAGE_TARBALL_FORMAT_OPT="--format=gnu"

	echo0 "packing ${img_name}"
	"${TAR_FOR_IMAGE_TARBALL}" ${TAR_FOR_IMAGE_TARBALL_FORMAT_OPT} -C "$FIRMWARE_MOD_DIR" -cf "${img_name}.unsigned" ./var
	if [ $? -ne 0 ] || ! is_valid_tarball "${img_name}.unsigned"; then
		mv "${img_name}.unsigned" "${img_name}.unsigned.corrupted" 2>/dev/null
		error 1 "packing of firmware image failed"
	fi
	echo1 "unsigned image file size: $(byte_to_mb $(wc -c < "${img_name}.unsigned")) ($(wc -c < "${img_name}.unsigned") bytes)"

	if [ "$DO_SIGN" -gt 0 ]; then
		echo0 "signing ${img_name}"
		if [ -z "${FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD}" ]; then
			# be verbose if no private key password provided
			YF_SIGNIMAGE_TAR="${TAR_FOR_IMAGE_TARBALL}" \
			YF_SIGNIMAGE_9K_WORKAROUND_METHOD="${FREETZ_SIGNIMAGE_9K_WORKAROUND_METHOD}" \
			"${TOOLS_DIR}/yf/signimage/sign_image" "${img_name}.unsigned" "${FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD}"                                                > "${img_name}.signed"
		else
			YF_SIGNIMAGE_TAR="${TAR_FOR_IMAGE_TARBALL}" \
			YF_SIGNIMAGE_9K_WORKAROUND_METHOD="${FREETZ_SIGNIMAGE_9K_WORKAROUND_METHOD}" \
			"${TOOLS_DIR}/yf/signimage/sign_image" "${img_name}.unsigned" "${FREETZ_FWMOD_SIGN_PRIVATE_KEY_PASSWORD}" 2>"${MOD_DIR}/sign_image.log" > "${img_name}.signed"
		fi
		if [ $? -ne 0 ] || ! is_valid_tarball "${img_name}.signed"; then
			mv "${img_name}.signed" "${img_name}.signed.corrupted" 2>/dev/null
			error 1 "signing of firmware image failed, see console output or ${MOD_DIR}/sign_image.log for details"
		fi
		echo1 "signed image file size: $(byte_to_mb $(wc -c < "${img_name}.signed")) ($(wc -c < "${img_name}.signed") bytes)"
	fi

	[ "$DO_SIGN" -gt 0 ] && final_img_suffix=signed || final_img_suffix=unsigned
	echo0 "using ${final_img_suffix} image as the final one"
	cp -a "${img_name}.${final_img_suffix}" "${img_name}"

	# delete temporary unsigned/signed files unless we're debugging
	[ "$DEBUG" != "1" ] && rm -f "${img_name}.unsigned" "${img_name}.signed"
fi

# ----------------------------------------------
# -- Common final actions for pack, zip, copy --
# ----------------------------------------------

if [ "$action_names" ]; then
	# Pack externalised files
	if [ "$EXTERNAL_CREATEPAK" == "y" -a -n "$externalised_files" ]; then
		tplexternal="${modimagenameprefix}${modbaseimagename}${modimagenamesuffix}_${modmakedate}"
		#static packages
		modexternal="${FW_IMAGES_DIR:-$DIR}/${tplexternal}.external"
		echo "packing ${modexternal}"
		"$TAR" -C "$EXTERNAL_MOD_DIR" -cf "$modexternal" .
		if [ $? -ne 0 ] || ! is_valid_tarball "$modexternal"; then
			mv "${modexternal}" "${modexternal}.corrupted" 2>/dev/null
			error 1 "packing of external tar failed"
		fi
		echo1 "external file size: $(byte_to_mb $(wc -c < "$modexternal"))"
		#dynamic packages
		if [ "$EXTERNAL_CREATEPAK_DYNAMIC" == "y" ]; then
			shopt -s nullglob
			for pkg in ${EXTERNAL_MOD_DIR}-*; do
				pkg=${pkg#${EXTERNAL_MOD_DIR}-}
				pkgexternal="${FW_IMAGES_DIR:-$DIR}/${tplexternal}_${pkg}.external"
				echo "packing ${pkgexternal}"
				"$TAR" -C "$EXTERNAL_MOD_DIR-${pkg}" -cf "$pkgexternal" .
				if [ $? -ne 0 ] || ! is_valid_tarball "$pkgexternal"; then
					mv "${pkgexternal}" "${pkgexternal}.corrupted" 2>/dev/null
					error 1 "packing of external-$pkg tar failed"
				fi
				echo1 "package file size: $(byte_to_mb $(wc -c < "$pkgexternal"))"
			done
			shopt -u nullglob
		fi
	fi

	if [ "$FREETZ_REPLACE_OPENSSL" == "y" ] && [ "$FREETZ_LIB_libcrypto" == "y" -o "$FREETZ_LIB_libssl" == "y" ]; then
		echo "Caution: Replacing libcrypto or libssl may cause an unusable image."
		echo "See http://freetz.org/wiki/FAQ#NachdemFlashenistdasAVM-Webinterfacenichtmehrerreichbar for details."
	fi

	[ "$COPY_FS_DIR" ] || echo -e "done.\n"
fi

[ "$COPY_FS_DIR" ] || echo0 -b "FINISHED"
exit 0