#!/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-2018 by the Freetz developers (http://freetz.org)
# Copyright (C) 2019 by Freetz developers (https://freetz-ng.github.io/ and https://freetz.github.io)
#
# 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] [-s] [-v <pub> [-w <pub> [-x <pub>]]] [-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
    -v <pub>   validate firmware image with the pub key (not for recovery.exe)
    -w <pub>   validate 2nd firmware image with the pub key (not for recovery.exe)
    -x <pub>   validate 3rd firmware image with the pub key (not for recovery.exe)
    -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; DO_VALIDATE=0; DO_VALIDATE2=0; DO_VALIDATE3=0
FIRMWARE_NOCOMPILE=0; FORCE_PACK=0; DO_ZIP=0; COPY_FS_DIR=; DIR=

# Parse command line parameters
while getopts umpsanfzc:i:d:v:w:x: opt; do
	case "$opt" in
		u) DO_UNPACK=1 ;;
		m) DO_MOD=1 ;;
		p) DO_PACK=1 ;;
		v) DO_VALIDATE=1;  FIRMWARE_PUB="$OPTARG"  ;;
		w) DO_VALIDATE2=1; FIRMWARE2_PUB="$OPTARG" ;;
		x) DO_VALIDATE3=1; FIRMWARE3_PUB="$OPTARG" ;;
		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 + $DO_VALIDATE)) -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"

# Prepend PATH with own directory (containing links)
PATH_DIR="${TOOLS_DIR}/path"
PATH="${PATH_DIR}:${PATH}"

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

[ ! -x "${TOOLS_DIR}/busybox" ] && \
	error 1 "Can not find host tools, please run 'make tools' first."

[ "$DO_SIGN" -gt 0 -a $(($DO_PACK + $DO_ZIP)) -eq 0 ] && \
	error 1 "Firmware signing without packing is not supported. Please use PeterPawn's sign_image tool to sign an already existing .image file."

[ $(($DO_SIGN + $DO_VALIDATE + $DO_VALIDATE2 + $DO_VALIDATE3 )) -gt 0 -a ! -x "$(which openssl 2>/dev/null)" ] && \
	error 1 "OpenSSL is required to verify and create image signature. Please install it or disable the option(s) in menuconfig."

# 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 not yet exists (not in validate only mode)
[ $(($DO_UNPACK + $DO_MOD + $DO_PACK + $DO_ZIP )) -ne 0 ] && 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"
SIGNATURE_SUBDIR=".signature"
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}"
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_FILES="${ADDON_DIR}/*.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


#################################
## Validate firmware signature ##
#################################

# validate_signature
# $1 ("1") when signature validating is enabled
# $2 (img) the .image file to validate
# $3 (key) public key, format: "EXPONENTxMODULUS". If no EXPONENT is given, default 010001 will be used (as in all images at the moment)
validate_signature() {
	echo -n "checking signature: "
	[ "$1" != "1" ] && echo -e "\033[1;33mdisabled\033[0m" &&  return
	[ -z "$3" ] && echo -e "\033[1;34munknown\033[0m" &&  error 1 "no public key available to validate signature"
	# var
	local TMP="$(mktemp -dt freetz-sig-XXX)"
	local MOD="${3#*x}"
	local EXP="${3%x*}"
	[ "$EXP" == "$MOD" ] && EXP='010001'
	# img
	cat "$2" > "$TMP/img"
	# fix
	local FIX="$(mktemp -p $TMP)"
	tar-gnu  -f "$TMP/img"  --owner=0 --group=0 --mode=0755 --format=oldgnu  -C "$TMP"  --append  ${FIX##*/}
	# sig
	tar-gnu  -f "$TMP/img"  --owner=0 --group=0 --mode=0755 --format=oldgnu  -C "$TMP"  --extract ./var/signature --transform='s!.*!sig!'
	tar-gnu  -f "$TMP/img"  --owner=0 --group=0 --mode=0755 --format=oldgnu             --delete  ./var/signature ${FIX##*/}
	# key
	{
		echo -n 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQ==' | base64 -d
		echo -n "$MOD" | while read -n2 x; do echo -ne "\x$x"; done
		echo -ne "\x2"
		echo -ne "\x$((${#EXP}/2))"
		echo -n "$EXP" | while read -n2 x; do echo -ne "\x$x"; done
	} > "$TMP/key"
	# chk
	VAL="$(openssl md5 -verify "$TMP/key" -keyform der -signature "$TMP/sig" "$TMP/img" 2>&1)"
	RET="$?"
	# clr
	rm -rf "$TMP"
	# act
	[ "$RET" != "0" ] && echo -e "\033[0;31mfailed\033[0m\nOpenSSL: $VAL" && error 1 "image could not be verified with public key"
	echo -e "\033[0;32mvalid\033[0m"
}
# validate_signature only
[ $DO_VALIDATE -eq 1 -a $(($DO_UNPACK + $DO_MOD + $DO_PACK + $DO_ZIP)) -eq 0 ] && validate_signature "$DO_VALIDATE" "$FIRMWARE" "$FIRMWARE_PUB" && exit 0


#######################################
## 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"
	fi

	echo0 -b "FINISHED"

	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}"

# avm image details
function image_details() {
	local AVM_FW_PRODUCT
	local AVM_FW_LANGUAGE
	local AVM_FW_MAJOR
	#AVM_FW_VERSION
	local AVM_FW_LABOR
	#AVM_FW_REVISION
	local AVM_FW_DATE
	local AVM_FW_CYCLE
	local AVM_FM_REGION
	#SOURCE_IMAGE_DETAILS
	#
	AVM_FW_PRODUCT="$(sed -rn 's/Annex./Annex/;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}" ] && AVM_FW_PRODUCT="$(sed -rn 's/export CONFIG_PRODUKT_NAME=.*(Repeater|Powerline) (.*)"/\2/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf)" # Repeater like 'DVB-C'
	[ -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)" # older firmware
	[ -z "$AVM_FW_VERSION" ] && AVM_FW_VERSION="$(sed -rn 's/^export CONFIG_VERSION=\"?([^\"]*)\"?/\1/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf)" # newer firmware
	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)" # older firmware
	[ -z "$AVM_FW_REVISION" ] && AVM_FW_REVISION="$(sed -rn 's/^export CONFIG_SUBVERSION=\"?-?([^\"]*)\"?/\1/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf)" # newer firmware
	[ -z "$AVM_FW_REVISION" ] && AVM_FW_REVISION="$(sed -rn 's/^export CONFIG_BUILDNUMBER=\"([^\"]*)\"/\1/p' ${FILESYSTEM_DIR}/etc/init.d/rc.conf)" # latest firmware
	AVM_FW_DATE="$(sed -rn 's/^export FIRMWARE_DATE="(.*)"/\1/p' ${FILESYSTEM_DIR}/etc/version)" # older firmware
	[ -z "$AVM_FW_DATE" ] && AVM_FW_DATE="$(date -d"$(stat -c '%y' ${FILESYSTEM_DIR}/etc/version)" "+%d.%m.%Y %H:%M:%S")" # newer firmware
	AVM_FW_CYCLE="$(sed -rn 's/^Releasecycle=//p' "${FIRMWARE_DIR}/var/content" 2>/dev/null)"
	[ -n "$AVM_FW_CYCLE" ] && AVM_FW_CYCLE=" [$AVM_FW_CYCLE]"
	[ -d ${FILESYSTEM_DIR}/etc/default.*/avm/ ]                                              && AVM_FM_REGION='GER'
	[ -d ${FILESYSTEM_DIR}/etc/default.*/avme/ ]                                             && AVM_FM_REGION='INT'
	[ -d ${FILESYSTEM_DIR}/etc/default.*/avme/ -a -d ${FILESYSTEM_DIR}/etc/default.*/avm/ ]  && AVM_FM_REGION='ALL'
	[ "$FREETZ_TYPE_LANG_IT" == "y" ]                                                        && AVM_FM_REGION='ITA'
	[ "$FREETZ_TYPE_LANG_A_CH" == "y" ]                                                      && AVM_FM_REGION='ACH'
	#
	SOURCE_IMAGE_DETAILS="${AVM_FW_PRODUCT/ /-}${AVM_FW_LANGUAGE} ${AVM_FW_MAJOR}.${AVM_FW_VERSION}${AVM_FW_LABOR} rev${AVM_FW_REVISION} {${AVM_FM_REGION}}${AVM_FW_CYCLE} (${AVM_FW_DATE})"
}

echo
STEP="1"
if [ "$DO_UNPACK" -gt 0 ]; then
	[ "$FIRMWARE2" ] && UNPACK_SUBSTEP="A" && UNPACK_MAIN=" 1ST"
	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"

	REAL_FIRMWARE="$(readlink ${FIRMWARE} 2>/dev/null || echo ${FIRMWARE##*/})"
	echo2 -l "source firmware file: ${FIRMWARE/${FIRMWARE##*\/}/$REAL_FIRMWARE}"

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

	# validate_signature
	if [ "$FIRMWARE_EXTENSION" == "cvc" ]; then
		[ $DO_VALIDATE -ne 1 ] && validate_signature "$DO_VALIDATE"
	elif [ "$FIRMWARE_EXTENSION" != "exe" ]; then
		validate_signature "$DO_VALIDATE" "$FIRMWARE" "$FIRMWARE_PUB"
	else
		validate_signature "0"
	fi

	echo "unpacking firmware image"
	mkdir "$FIRMWARE_DIR"

	if [ "$FIRMWARE_EXTENSION" == "cvc" ]; then
		# Check and skip garbage (cvc&p7b)
		image_size="0x$(printf "%08X\n" $(stat -c %s "$FIRMWARE"))"
		image_grbg="$($TOOLS_DIR/sfk hexfind "$FIRMWARE" _./var/_ | sed -n 's/.*hit at offset //p' |head -n1)"
		[ -n "$image_grbg" ] && echo "Skipping $(($image_grbg)) Bytes garbage"
		# validate_signature
		if [ $DO_VALIDATE -eq 1 ]; then
			TMP_SIG="$(mktemp -t freetz-sig-XXX)"
			tail -c $(( $image_size - ${image_grbg:-0x0} )) "$FIRMWARE" > "$TMP_SIG"
			validate_signature "$DO_VALIDATE" "$TMP_SIG" "$FIRMWARE_PUB"
			rm "$TMP_SIG"
		fi
		# Unpack standard *.image file in tar format
		tail -c $(( $image_size - ${image_grbg:-0x0} )) "$FIRMWARE" | \
		  tar-gnu -x -C "$FIRMWARE_DIR" || untar_failed=1
	elif [ "$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
	if [ -e "$FIRMWARE_DIR/var/firmware-update.uimg" ]; then
		"${TOOLS_DIR}/uimg" -u "$FIRMWARE_DIR/var/firmware-update.uimg" >/dev/null
		chmod +w $FIRMWARE_DIR/var/*.bin
		mkdir -p "$FIRMWARE_DIR/var/remote/var/tmp/x86"
		ln $FIRMWARE_DIR/var/*_ATOM_ROOTFS.bin "$FIRMWARE_DIR/var/remote/var/tmp/x86/filesystem.image"
		ln $FIRMWARE_DIR/var/*_ATOM_KERNEL.bin "$FIRMWARE_DIR/var/remote/var/tmp/x86/kernel.image"
	elif [ -e "$FIRMWARE_DIR/var/tmp/fit-image" ]; then
		"${TOOLS_DIR}/fitimg" -x $FIRMWARE_DIR/var/tmp/fit-image -d $FIRMWARE_DIR/var/tmp/ -n -f -q
		rm -f $FIRMWARE_DIR/var/tmp/*2.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

	image_details
	echo2 -lc "detected firmware version: ${SOURCE_IMAGE_DETAILS}"

	echo -e "done.\n"

	# Unpack secondary firmware image
	if [ -n "$FIRMWARE2" ]; then
		rm -rf "${DIR}/.tk"
		# validate_signature
		[ $DO_VALIDATE2 -eq 1 ] && UNPACK_VALIDATE2="-v $FIRMWARE2_PUB"
		( set -o pipefail; "$0" -u $UNPACK_VALIDATE2 -d "${DIR}/.tk" "$FIRMWARE2" |
			sed "s/STEP 1: UNPACK/STEP 1B: UNPACK 2ND${FORCE_UNPACK}/" )
		[ $? -ne 0 ] && exit 1
	fi

	# Unpack auxiliary firmware image
	if [ -n "$FIRMWARE3" ]; then
		rm -rf "${DIR}/.aux"
		# validate_signature
		[ $DO_VALIDATE3 -eq 1 ] && UNPACK_VALIDATE3="-v $FIRMWARE3_PUB"
		( set -o pipefail; "$0" -u $UNPACK_VALIDATE3 -d "${DIR}/.aux" "$FIRMWARE3" |
			sed "s/STEP 1: UNPACK/STEP 1C: UNPACK 3RD${FORCE_UNPACK}/" )
		[ $? -ne 0 ] && exit 1
	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
# $MENU_DATA_LUA will be set below to ${FILESYSTEM_MOD_DIR}/usr/lua/menu_data.lua or ${HTML_LANG_MOD_DIR}/menus/menu_data.lua or filenotavailable
# $SYSTEMD_CORE_MOD_DIR will be set below to ${FILESYSTEM_MOD_DIR}/etc/boot.d/core if it exists


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:0:31}" | 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
			;;
		dir)	#addons/plugins: 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
}

image_details

STEP="2"
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 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" ] && for oem in "${FILESYSTEM_MOD_DIR}/etc/default.Fritz_Box_*/*"; do oems="$oems ${oem##*/}"; done
	[ -z "$oems" ] && error 1 "failed to determine OEMs supported by the original firmware"

	echo0 -n "applying symlinks, deleting additional webinterfaces"
	echo1 -ln " 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
			echo1 -ln " ${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

	# Find menu_data.lua
	MENU_DATA_LUA="filenotavailable"
	for dir in "${FILESYSTEM_MOD_DIR}/usr/lua" "${HTML_LANG_MOD_DIR}/menus"; do
		[ -f "$dir/menu_data.lua" ] && MENU_DATA_LUA="$dir/menu_data.lua" && break
	done

	# Check systemd direcotry
	[ -d "${FILESYSTEM_MOD_DIR}/etc/boot.d/core" ] && SYSTEMD_CORE_MOD_DIR="${FILESYSTEM_MOD_DIR}/etc/boot.d/core" || SYSTEMD_CORE_MOD_DIR=""

	# 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
		[ "$FREETZ_AVM_VERSION_07_0X_MIN" == "y" -o -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, /home and /ftp"
	rm -rf "$FILESYSTEM_MOD_DIR"/tmp
	ln -s var/{tmp,mod} "$FILESYSTEM_MOD_DIR"/
	ln -s var/mod/home "$FILESYSTEM_MOD_DIR"/
	ln -s var/media/ftp "$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

	# Determine Freetz version
	FREETZ_VERSION="$(${TOOLS_DIR}/freetz-revision fwmod)"

	echo1 "setting freetz-version '${FREETZ_VERSION}'"
	echo "${FREETZ_VERSION}" > "${FILESYSTEM_MOD_DIR}/etc/.freetz-version"
	ln -f "${FILESYSTEM_MOD_DIR}/etc/.freetz-version" "${FILESYSTEM_MOD_DIR}/etc/.freetz-ng-version"

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

	# Execute general patch scripts
	for i in "${PATCHES_SCRIPTS_DIR}/"*.sh; do
		[ -r "$i" ] || continue
		echo2 -l "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
	echo1 -n "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"

	for i in $oems; do
		if [ "$(eval "echo \"\$FREETZ_REMOVE_BRANDING_${i}\"")" == "y" ]; then
			echo1 -ln " $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

	[ "$oem_removed" -eq 0 ] && echo1 -ln " none"
	echo1 -l

	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
				sizeinfo "$AVMPLUGIN"
				sizeinfo dir "$i"
				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 to standard modules dir: ${module##*/}"
					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_ROOT="${VARTAR_MOD_DIR}/var/mod"

		# Remove symlink before creating directory
		rm -rf ${VARTAR_MOD_DIR}/var/spool
		mkdir -p "${VARTAR_MOD_DIR}/var/spool/cron/crontabs" #TODO: may be removed later

		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}/var/spool/cron/crontabs"
		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"

		grep -q '^root:' "${VARTAR_MOD_DIR}/var/tmp/passwd" 2>/dev/null && \
		  sed 's,^root:.*,root:x:0:0:root:/mod/root:/bin/sh,' -i "${VARTAR_MOD_DIR}/var/tmp/passwd" || \
		  echo "root:x:0:0:root:/mod/root:/bin/sh" >> "${VARTAR_MOD_DIR}/var/tmp/passwd"
		grep -q '^root:' "${VARTAR_MOD_DIR}/var/tmp/shadow" 2>/dev/null && \
		  sed 's,^root:.*,root:$1$$zO6d3zi9DefdWLMB.OHaO.:12332:0:99999:7:::,' -i "${VARTAR_MOD_DIR}/var/tmp/shadow" || \
		  echo 'root:$1$$zO6d3zi9DefdWLMB.OHaO.:12332:0:99999:7:::' >> "${VARTAR_MOD_DIR}/var/tmp/shadow"
		grep -q '^root:' "${VARTAR_MOD_DIR}/var/tmp/group" 2>/dev/null && \
		  sed 's,^root:.*,root:x:0:,' -i "${VARTAR_MOD_DIR}/var/tmp/group" || \
		  echo "root:x:0:" >> "${VARTAR_MOD_DIR}/var/tmp/group"
		grep -q '^users:' "${VARTAR_MOD_DIR}/var/tmp/group" 2>/dev/null && \
		  sed 's,^users:.*,root:x:0:,' -i "${VARTAR_MOD_DIR}/var/tmp/group" || \
		  echo "users:x:80:" >> "${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"
			[ "$FREETZ_SEPARATE_AVM_UCLIBC" == "y" ] \
			  && LIBPATHS="${FREETZ_LIBRARY_DIR#/} lib usr/lib usr/lib/xtables" \
			  || LIBPATHS="lib usr/lib ${FREETZ_LIBRARY_DIR#/} usr/lib/xtables"
			for dn in $LIBPATHS; 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" -a "$FREETZ_REPLACE_OUTER_UCLIBC_AND_BUSYBOX" == "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
			[ "$FREETZ_SEPARATE_AVM_UCLIBC" == "y" ] && \
			  cp -a ${TARGET_SPECIFIC_ROOT_DIR}${FREETZ_LIBRARY_DIR}/libc.so.? "${FILESYSTEM_MOD_DIR}${FREETZ_LIBRARY_DIR}/" || \
			  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" -a "$FREETZ_REPLACE_OUTER_UCLIBC_AND_BUSYBOX" == "y" ] && \
			  cp -a "${TARGET_SPECIFIC_ROOT_DIR}/lib/libc.so.${FREETZ_TARGET_UCLIBC_MAJOR_VERSION}" "${FILESYSTEM_OUTER_MOD_DIR}/lib/"
		fi

		echo1 "checking OpenSSL version"
		DETECTED_OPENSSL_VER_AVM="$(find ${FILESYSTEM_DIR}/lib/libcrypto.so.* -type f  -exec strings {} \; 2>/dev/null | sed -nr 's/^@?OpenSSL ([0-9]\.[-0-9a-z\.]*)[ \t]*/\1\t/p')"
		echo2 "... used by AVM ...... ${DETECTED_OPENSSL_VER_AVM:-none}"
		if [ "$FREETZ_LIB_libcrypto" == "y" ]; then
			DETECTED_OPENSSL_VER_MOD="$(find ${FILESYSTEM_MOD_DIR}/usr/lib/freetz/libcrypto.so.* -type f  -exec strings {} \; | sed -nr 's/^@?OpenSSL ([0-9]\.[-0-9a-z\.]*)[ \t]*/\1\t/p')"
			echo2 "... used by Freetz ... ${DETECTED_OPENSSL_VER_MOD:-none}"
		#	[ "$DETECTED_OPENSSL_VER_MOD" != "$DETECTED_OPENSSL_VER_AVM" ] & warn2 "Freetz and AVM OpenSSL versions do not match"
		fi
		unset DETECTED_OPENSSL_VER_AVM
		unset DETECTED_OPENSSL_VER_MOD

		echo1 "checking libc version"
		DETECTED_UCLIBC_VER="$(readlink ${FILESYSTEM_DIR}/lib/libc.so.? | sed 's/\.so$//;s/^libc/g&/;s/^lib//;s/-/ /' | head -n1)"
		[ -z "$DETECTED_UCLIBC_VER" ] && [ -n "$(strings ${FILESYSTEM_DIR}/lib/libc.so | grep '^musl libc')" ] && \
		  DETECTED_UCLIBC_VER="musl $(strings ${FILESYSTEM_DIR}/lib/libc.so | grep '^[0-1]\.[0-9]\.[0-9][0-9]*$')"
		echo2 "... used by AVM ...... $DETECTED_UCLIBC_VER"
		echo2 "... used by Freetz ... uClibc ${FREETZ_TARGET_UCLIBC_VERSION}"
		if [ "${FREETZ_TARGET_UCLIBC_VERSION#${DETECTED_UCLIBC_VER#uClibc }}" == "$FREETZ_TARGET_UCLIBC_VERSION" -a "${FREETZ_SEPARATE_AVM_UCLIBC}" != "y" ]; 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" -a "$FREETZ_REPLACE_OUTER_UCLIBC_AND_BUSYBOX" == "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}"
				if [ -f "${d}${LINK_DIR}/$LINK_NAME" ]; then
					if [ "$FREETZ_BUSYBOX__KEEP_BINARIES" != "y" ]; then
						echo2 "overwriting file ${d}${LINK_DIR}/$LINK_NAME"
					else
						echo2 "renaming applet ${d}${LINK_DIR}/$LINK_NAME"
						LINK_NAME="${LINK_NAME}-busybox"
					fi
				fi
				ln -sf "$BUSYBOX_PATH" "${d}${LINK_DIR}/$LINK_NAME" || error 1 "could not create link for $link"
			done
		done || exit 1

		echo1 "checking BusyBox version"
		echo2 "... used by AVM ...... $(strings ${FILESYSTEM_DIR}/bin/busybox | sed -n 's/^BusyBox v//p')"
		echo2 "... used by Freetz ... $(strings ${FILESYSTEM_MOD_DIR}/bin/busybox | sed -n 's/^BusyBox v//p')"
	fi

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

		"$TAR"  $(find "${PACKAGES_DIR}/${pkg}/" -maxdepth 1 -type f -name ".exclude*" -printf '--exclude-from=%p ') \
			-c -C "${PACKAGES_DIR}/${pkg}/root" \
			. | \
		"$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

	[ -n "$(static_addons)" ] && echo0 "installing addons"
	for pkg in $(static_addons); do
		sizeinfo "$pkg"
		[ ! -d "${ADDON_DIR}/${pkg}" ] && error 1 "can not find addon directory"
		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
		elif [ "$(echo ${pkg^^}-corrupt | sed -r 's/([^.-_-]*).*/\LfLawed-\1-Addon/' | md5sum | sed 's/ .*//')" != "80d00cf16e1a48ec11801feed60a2be7" ]; then
			"$TAR"  $(find "${ADDON_DIR}/${pkg}/" -maxdepth 1 -type f -name ".exclude*" -printf '--exclude-from=%p ') \
				-c -C "${ADDON_DIR}/${pkg}/root" \
				. | \
			"$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 dir "${ADDON_DIR}/${pkg}"
	done

	if [ "$FREETZ_AVM_HAS_USB_HOST" == "y" ]; then
		echo1 "checking blkid tools"
		blkids=0
		[ "$FREETZ_AVM_HAS_E2FSPROGS" == "y" -a "$FREETZ_REMOVE_AVM_E2FSPROGS" != "y" ]                               && blkids=$((blkids+1)) && echo2 "... binary by avm"
		[ "$FREETZ_PACKAGE_FSTYP" == "y" ]                                                                            && blkids=$((blkids+1)) && echo2 "... package fstype"
		[ "$FREETZ_PACKAGE_E2FSPROGS" == "y" -a "$FREETZ_PACKAGE_E2FSPROGS_BLKID" == "y" ]                            && blkids=$((blkids+1)) && echo2 "... package e2fsprogs"
		[ "$FREETZ_PACKAGE_UTIL_LINUX" == "y" ]                                                                       && blkids=$((blkids+1)) && echo2 "... package util-linux"
		[ "$(eval echo "\$FREETZ_BUSYBOX___V$(echo ${FREETZ_BUSYBOX__VERSION_STRING/.} | head -c3)_BLKID")" == "y" ]  && blkids=$((blkids+1)) && echo2 "... busybox applet"
		[ "$blkids" -gt 1 ] && echo1 " there are $blkids blkid tools installed"
		[ "$blkids" -eq 0 ] && warn1 "no blkid tool installed, automatic mounting may not work"
	fi

	KERNEL_ID="${FREETZ_SYSTEM_TYPE}${FREETZ_SYSTEM_TYPE_CORE_SUFFIX}-${FREETZ_AVM_SOURCE_ID}"
	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}"
		cp "${KERNEL_REP_DIR}/kernel-${KERNEL_ID}.bin" "$RAW_KERNEL_MOD"
	fi

	# modules directory / correctly configured kernel version
	echo0 "processing kernel"
	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)" -o -n "$FREETZ_MODULES_OWN" ]; 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 f \( -name modules.dep -o -name "*.ko" \) \
		); do
			bn="$(basename "$i")"
			ko_symbol="$(echo "${bn%\.ko}" | tr '\-+' '_x')"
			ko_install="$(eval "echo \"\$FREETZ_MODULE_$ko_symbol\"")"
			echo " $FREETZ_MODULES_OWN " | grep -q " $ko_symbol " && ko_install="y"
			[ "$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"
			found_kos="$found_kos $bn"
		done
		for ko in $(set | sed -rn 's/^FREETZ_MODULE_(.*)=y$/\1/p') $FREETZ_MODULES_OWN; do
			echo "$found_kos " | tr '\-+' '_x' | grep -q " $ko.ko " && continue
			warn "File of kernel module not found: $ko.ko"
			kos_missing=y
		done
		[ "$FIRMWARE_NOCOMPILE" != "1" -a -n "$kos_missing" ] && error 1 "Enable missing with 'make kernel-menuconfig' as (M)odule (and push a pull request). Use '/' to search in menuconfig."

		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

	echo0 "processing modules"
	for f in modules.dep.bin modules.alias.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_DOT_CONFIG_DONOTADD" == "y" ]; then
			echo1 "integrating .config is disabled"
		else
			if [ "$FREETZ_DOT_CONFIG_STRIPPED" == "y" ]; then
				{ grep '^[^#].*$' "$DOT_CONFIG"; grep '^#.*$' "$DOT_CONFIG.compressed"; echo "FREETZ_LIBRARY_DIR=\"${FREETZ_LIBRARY_DIR}\""; } \
				  | grep -v "FREETZ_FWMOD_SIGN_PASSWORD" | grep -v "^FREETZ_BUSYBOX_[^_]" > "${FILESYSTEM_MOD_DIR}/etc/.config"
				echo1 "integrated .config (stripped)"
			else
				{ grep -v "FREETZ_FWMOD_SIGN_PASSWORD" "$DOT_CONFIG"; echo "FREETZ_LIBRARY_DIR=\"${FREETZ_LIBRARY_DIR}\""; } > "${FILESYSTEM_MOD_DIR}/etc/.config"
				echo1 "integrated .config (complete)"
			fi
		fi

		# logo tagging
		if [ "$FREETZ_TAGGING_STRING" != "none" ]; then
			tagging() {
				[ $# -lt 3 ] && return
				local tagging_geo="$1"
				local tagging_pos="$2"
				local tagging_arg="$3"
				for d in ${HTML_MOD_DIR}*; do
					[ ! -d "$d" ] && continue
					local tagging_ren=""
					for tagging_file in $(find "$d" -name "$tagging_arg"); do
						echo2 "${tagging_file}"
						if [ "${tagging_file##*/}" == "logo_fritzDiamond.svg" ]; then
							sed 's/>/&<svg>/' -i $tagging_file
							echo "<svg>$(tail -n+2 ${TAGGING_DIR}/${FREETZ_TAGGING_STRING}.svg)</svg>" >> $tagging_file
							continue
						fi
						[ "${tagging_file}" != "${tagging_file%svg}" ] && tagging_temp="${tagging_file%svg}png" || tagging_temp=""
						if [ -n "$tagging_temp" ]; then
							if ! inkscape "$tagging_file" --export-type=png --export-filename="$tagging_temp" -w 98 -h 77 -y 0 &>/dev/null; then
							if ! inkscape --without-gui "$tagging_file" --export-png="$tagging_temp" -w 98 -h 77 -y 0 &>/dev/null; then
							if ! convert -compress NONE -background NONE -resize 98x77 "$tagging_file" "$tagging_temp"; then
								error 1 "tagging failed on converting, is inkscape installed?"
							fi
							fi
							fi
						fi
						composite -dissolve 100% -gravity $tagging_pos -geometry $tagging_geo -quality 100 \
						  "${TAGGING_DIR}/${FREETZ_TAGGING_STRING}.png" ${tagging_temp:-$tagging_file} ${tagging_temp:-$tagging_file}
						[ $? -ne 0 ] && error 1 "tagging failed on compositing, is imagemagick and libpng installed?"
						[ -n "$tagging_temp" ] && tagging_ren="$tagging_ren ${tagging_file##*/}" && rm -f "${tagging_file}"
					done
					[ -z "$tagging_temp" ] && continue
					for i in $(echo "$tagging_ren" | sed 's, ,\n,g' | sort -u); do
						for j in $(grep -l "$i" "$d" -r); do
							sed "s,${i},${i%svg}png,g" -i "$j"
						done
					done
				done
			}
			echo1 "tagging avm webif (tag by ${FREETZ_TAGGING_STRING})"
			tagging      "" ""       logo_fritzDiamond.svg    # svg since  Fritz!OS 7.1x (eg 7490 07.11)
			tagging  "+0+6" "center" fritzLogo.svg            # svg since  Fritz!OS 6.5x (eg 7320 06.50)
			tagging  "+0+6" "center" kopfbalken_links.png     # lua since  Fritz!OS 6.2x (small logo @left, eg 7390 06.23)
			tagging  "+0+0" "center" kopfbalken_links.gif     # lua before Fritz!OS 6.2x (small logo @left, eg 7270 04.88)
			tagging "-20+0" "west"   kopfbalken.gif           # newer html (big header @top, eg 7320 05.29)
			tagging  "+0+0" "west"   fw_header980.gif         # older (big header @top, eg 7141 04.76)
			tagging  "+0-5" "west"   fw_header.gif            # even older (big header @top, eg 7050 04.03)
		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" -a "$FREETZ_REPLACE_OUTER_UCLIBC_AND_BUSYBOX" == "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 --
# ------------------------------------------------------

STEP="3"
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 version-constrol-system files
	echo1 "checking for left over version-control-system files"
	find "$MOD_DIR" \( -type d -name '.svn' \) -o \( -type d -name '.git' \) -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"

	# Assemble output file name
	get_modstring() {
		local modstring=
		local prefix=
		local brandings=
		local languages=
		#PREFIX
		if [ "$FREETZ_IMAGE_NAME_PREFIX" == "y" ]; then
			prefix="$(echo $FREETZ_USER_DEFINED_COMMENT | sed -e "s/ /_/g" | tr -cd 'a-zA-Z0-9_+-.<->')"
			[ -n "${prefix}" ] && modstring="${prefix%%_}_"
		fi
		#DEVICE
		if [ "$FREETZ_IMAGE_NAME_DEVICE" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" ]; then
			[ -n "$FREETZ_TYPE_PREFIX_ALIEN_HARDWARE" ] && modstring+="${FREETZ_TYPE_PREFIX_ALIEN_HARDWARE/_/}" || modstring+="${FREETZ_TYPE_PREFIX/_/}"
		fi
		#ALIEN
		if [ "$FREETZ_IMAGE_NAME_ALIEN" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" -a -n "$FREETZ_TYPE_PREFIX_ALIEN_HARDWARE" ]; then
			modstring+="-Alien${FREETZ_TYPE_PREFIX/_/}"
		fi
		#ANNEX
		if [ "$FREETZ_IMAGE_NAME_ANNEX" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" -a "$FREETZ_AVM_HAS_ANNEX_A_AND_B" == "y" ]; then
			modstring+="-Annex"
			[ "$FREETZ_TYPE_ANNEX_A" ==  "y" ] && modstring+="A"
			[ "$FREETZ_TYPE_ANNEX_B" ==  "y" ] && modstring+="B"
		fi
		#FRITZOS
		if [ "$FREETZ_IMAGE_NAME_FRITZOS" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" ]; then
			modstring+="_${AVM_FW_VERSION}"
		fi
		#REGION
		if [ "$FREETZ_IMAGE_NAME_REGION" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" ]; then
			modstring+=".$(echo ${FREETZ_TYPE_LANGUAGE} | sed 's/^de$/ger/;s/^en$/int/;s/^xx$/all/;s/^a-ch$/ach/;s/^it$/ita/')"
		fi
		#CHECKPOINT
		if [ "$FREETZ_IMAGE_NAME_CHECKPOINT" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" -a -n "$FREETZ_TYPE_PREFIX_LABOR_FIRMWARE" ]; then
			modstring+="-rev${AVM_FW_REVISION}"
		fi
		#RELEASECYCLE
		if [ "$FREETZ_IMAGE_NAME_RELEASECYCLE" == "y" ]; then
			RELEASECYCLE="$(sed -rn 's/^Releasecycle=//p' "${FIRMWARE_DIR}/var/content" 2>/dev/null)"
			[ -n "$RELEASECYCLE" ] && modstring+="_${RELEASECYCLE/ /-}"
		fi
		#BUILDTYPE
		if [ "$FREETZ_IMAGE_NAME_BUILDTYPE" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" -a -n "$FREETZ_TYPE_PREFIX_LABOR_FIRMWARE" ]; then
			modstring+="${FREETZ_TYPE_PREFIX_LABOR_FIRMWARE:-_release}"
		fi
		#BRANDINGS
		if [ "$FREETZ_IMAGE_NAME_BRANDINGS" == "y" ]; then
			for x in $(sed -rn 's/^FREETZ_AVM_HAS_BRANDING_(.*)=y$/\1/p' "$DOT_CONFIG" | sort); do
				[ "$(eval "echo \"\$FREETZ_REMOVE_BRANDING_$x\"")" != "y" ] && brandings+="-$x"
			done
			modstring+="_${brandings#-}"
		fi
		#LANGUAGES
		if [ "$FREETZ_IMAGE_NAME_LANGUAGES" == "y" ]; then
			for x in $((sed -n 's/^language //p' $FILESYSTEM_DIR/etc/*.language && \
			  ls $FILESYSTEM_DIR/etc/htmltext_??.db 2>/dev/null | sed -rn 's/.*_(..)\.db/\1/p') | sort -u); do
				[ "$(eval "echo \"\$FREETZ_REMOVE_LANGUAGE_$x\"")" != "y" ] && languages+="-$x"
			done
			modstring+="_${languages#-}"
		fi
		#FREETZ
		if [ "$FREETZ_IMAGE_NAME_FREETZ" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" ]; then
			[ -n "$FREETZ_VERSION" ] && modstring+="_${FREETZ_VERSION}" # missing in DO_MOD=0 mode
		fi
		#TIMESTAMP
		if [ "$FREETZ_IMAGE_NAME_TIMESTAMP" == "y" ] \
		  || [ -z "$FREETZ_IMAGE_NAME_CUSTOM" ]; then
			modstring+="_${modmakedate}"
		fi
		#done
		echo -n "$modstring"
	}
	modmakedate="$(date +%Y%m%d-%H%M%S)"
	modstring="$(get_modstring)"

	# 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_VERSION='${FREETZ_VERSION}'" >> "$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='${modstring}.image'" >> "$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

# -------------------------------------------
# -- Check and maybe create signature keys --
# -------------------------------------------

if [ "$DO_SIGN" -gt 0 ]; then
	echo1 "checking signature key files"

	if [ ${#FREETZ_FWMOD_SIGN_PASSWORD} -lt 6 ]; then
		while true; do
			read -sp "Enter password for private signature key file (hidden input): " FREETZ_FWMOD_SIGN_PASSWORD
			echo
			if [ -s "${SIGNATURE_SUBDIR}/prv" -a ${#FREETZ_FWMOD_SIGN_PASSWORD} -ge 6 ]; then
				openssl rsa -in "${SIGNATURE_SUBDIR}/prv" -passin "pass:$FREETZ_FWMOD_SIGN_PASSWORD" -noout &>/dev/null && break
				echo "Password is wrong, try again!"
			else
				[ ${#FREETZ_FWMOD_SIGN_PASSWORD} -ge 6 ] && break
				echo "To short password, try again!"
			fi
		done
	fi

	#dir
	[ ! -e "${SIGNATURE_SUBDIR}" -a ! -L "${SIGNATURE_SUBDIR}" ] && \
		ln -s ~/.freetz-signature "${SIGNATURE_SUBDIR}"
	[ ! -d "${SIGNATURE_SUBDIR}" ] && \
		mkdir -p "$(readlink "${SIGNATURE_SUBDIR}")"
	[ ! -d "${SIGNATURE_SUBDIR}" ] && \
		error 1 "can not create ./${SIGNATURE_SUBDIR} directory"

	#prv
	if [ ! -s "${SIGNATURE_SUBDIR}/prv" ]; then
		echo2 "creating private signature key file"
		rm -f "${SIGNATURE_SUBDIR}/prv" "${SIGNATURE_SUBDIR}/pub"
		openssl genrsa -aes128 -passout "pass:$FREETZ_FWMOD_SIGN_PASSWORD" -out "${SIGNATURE_SUBDIR}/prv" 1024 >/dev/null 2>&1 || \
			error 1 "could not create private signature key file"
	else
		openssl rsa -in "${SIGNATURE_SUBDIR}/prv" -passin "pass:$FREETZ_FWMOD_SIGN_PASSWORD" -noout >/dev/null 2>&1 || \
			error 1 "wrong password for private signature key file"
	fi
	#pub
	if [ ! -s "${SIGNATURE_SUBDIR}/pub" ]; then
		echo2 "creating public signature key file"
		EXP="$(openssl rsa -in "${SIGNATURE_SUBDIR}/prv" -passin "pass:$FREETZ_FWMOD_SIGN_PASSWORD" -noout -text    | sed -rn 's/^publicExponent:.*\(0x(.*)\).*/\1/p')"
		MOD="$(openssl rsa -in "${SIGNATURE_SUBDIR}/prv" -passin "pass:$FREETZ_FWMOD_SIGN_PASSWORD" -noout -modulus | sed -rn 's/^Modulus=(.*)/\1/p')"
		[ $(( ${#EXP} % 2 )) != 0 ] && EXP="0$EXP"
		[ $(( ${#MOD} % 2 )) != 0 ] && MOD="0$MOD"
		[ "${MOD:0:2}" != "00" ] && MOD="00$MOD"
		echo -e "$MOD\n$EXP" > "${SIGNATURE_SUBDIR}/pub"
		[ "$(stat -c%s "${SIGNATURE_SUBDIR}/pub" 2>/dev/null)" != "266" ] && \
			error 1 "could not create public signature key file"
	fi

	#cpy
	echo2 "adding public signature key file"
	cp -p "${SIGNATURE_SUBDIR}/pub" "${FILESYSTEM_MOD_DIR}/etc/avm_firmware_public_key8"
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"

	if [ "$FREETZ_AVM_PROP_INNER_FILESYSTEM_TYPE_CPIO" == "y" ]; then
		echo0 "creating ${INNER_FS_PREFIX}filesystem image (CPIO-gz)"

		find $FILESYSTEM_MOD_DIR/ -mindepth 1 -printf '%P\n' | cpio -o --quiet -H newc -D $FILESYSTEM_MOD_DIR/ | gzip -9 > $ABS_BASE_DIR/${MAIN_FILESYSTEM_MOD}
	else
		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
	fi

	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
	[ -e "$FIRMWARE_MOD_DIR/var/firmware-update.uimg" ] && KERNEL_SIZE="$(gzip "$KERNEL_MOD" -c | wc -c)" || KERNEL_SIZE="$(wc -c < "$KERNEL_MOD")"

	[ "$KERNEL_SIZE" -eq 0 ] && error 1 "kernel image is empty"
	if [ -z "$KERNEL_LIMIT" ]; then
		echo1 "kernel image size: $(byte_to_mb $KERNEL_SIZE)"
	else
		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)"
		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 https://freetz-ng.github.io/freetz-ng/wiki/00_FAQ/FAQ.de.html#filesystem-image-too-big 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 https://freetz-ng.github.io/freetz-ng/wiki/00_FAQ/FAQ.de.html#filesystem-image-too-big for details."
				fi
				if [ "$FORCE_PACK" -gt 0 -a "$DO_ZIP" -eq 0 ]; then
					echo0   -b "Use for SDK mode only. See https://web.archive.org/20200701000000/wehavemorefun.de/fritzbox/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
	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")"

		[ "$FILESYSTEM_SIZE" -eq 0 ] && error 1 "filesystem image is empty"
		if [ -z "$FILESYSTEM_LIMIT" ]; then
			echo1 "filesystem image size: $(byte_to_mb $FILESYSTEM_SIZE)"
		else
			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)"
			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 https://freetz-ng.github.io/freetz-ng/wiki/00_FAQ/FAQ.de.html#filesystem-image-too-big 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
	fi


	if [ "$FREETZ_VERBOSITY_LEVEL" -ge 1 -a -n "$KERNEL_LIMIT" ]; 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 https://trac.boxmatrix.info/freetz-ng/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

	# Write checksum
	if [ ! -e "$FIRMWARE_MOD_DIR/var/firmware-update.uimg" -a ! -e "$FIRMWARE_MOD_DIR/var/tmp/fit-image" ]; then
		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" || error 1 "adding checksum to $fbn failed"
		done
	fi

	# 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 .packages into firmware
		# image for further reference, e.g. user support
		[ -r "$DOT_CONFIG" ] && { cat "$DOT_CONFIG" | grep -v "FREETZ_FWMOD_SIGN_PASSWORD" > "$FIRMWARE_MOD_DIR/var/.config"; }
		[ -r "$PACKAGES_LIST_FILE" ] && cp "$PACKAGES_LIST_FILE" "$FIRMWARE_MOD_DIR/var"
	fi

	# Set img_name for pack, sign & link
	img_name="${FW_IMAGES_DIR:-$DIR}/${modstring}.image"

	# Pack firmware image
	echo0 "packing ${img_name}"
	if [ -e "$FIRMWARE_MOD_DIR/var/firmware-update.uimg" ]; then
		mv "$FIRMWARE_MOD_DIR/var/remote/var/tmp/x86/filesystem.image" $FIRMWARE_MOD_DIR/var/*_ATOM_ROOTFS.bin
		mv "$FIRMWARE_MOD_DIR/var/remote/var/tmp/x86/kernel.image" $FIRMWARE_MOD_DIR/var/*_ATOM_KERNEL.bin
		rm "$FIRMWARE_MOD_DIR/var/remote" "$FIRMWARE_MOD_DIR/var/firmware-update.uimg" -r
		"${TOOLS_DIR}/uimg" -p "$FIRMWARE_MOD_DIR/var/firmware-update.uimg" >/dev/null
		rm $FIRMWARE_MOD_DIR/var/firmware-update_*.bin
	elif [ -e "$FIRMWARE_MOD_DIR/var/tmp/fit-image" ]; then
		"${TOOLS_DIR}/fitimg" -r $FIRMWARE_MOD_DIR/var/tmp/fit-image -o $FIRMWARE_MOD_DIR/var/tmp/fit-image.tmp -d $FIRMWARE_MOD_DIR/var/tmp/ -f -q
		mv $FIRMWARE_MOD_DIR/var/tmp/fit-image.tmp $FIRMWARE_MOD_DIR/var/tmp/fit-image
		rm -f $FIRMWARE_MOD_DIR/var/tmp/kernel.image $FIRMWARE_MOD_DIR/var/tmp/filesystem.image
	fi
	tar-gnu  -f "${img_name}"  --owner=0 --group=0 --mode=0755 --format=oldgnu  -C "$FIRMWARE_MOD_DIR"  --create ./var
	if [ $? -ne 0 ] || ! is_valid_tarball "${img_name}"; then
		mv "${img_name}" "${img_name}.packed.corrupted" 2>/dev/null
		error 1 "packing of firmware image failed"
	fi
	echo1 "packed image file size: $(byte_to_mb $(wc -c < "${img_name}")) ($(wc -c < "${img_name}") bytes)"

	# Sign firmware image
	if [ "$DO_SIGN" -eq 1 ]; then
		echo1 -l "signing packed .image file"
		openssl md5 -sign "${SIGNATURE_SUBDIR}/prv" -passin "pass:$FREETZ_FWMOD_SIGN_PASSWORD" -out "${FIRMWARE_MOD_DIR}/var/signature" "${img_name}" || \
			error 1 "could not create signature file for firmware image"
		tar-gnu  -f "${img_name}"  --owner=0 --group=0 --mode=0755 --format=oldgnu  -C "$FIRMWARE_MOD_DIR"  --append ./var/signature
		if [ $? -ne 0 ] || ! is_valid_tarball "${img_name}"; then
			mv "${img_name}" "${img_name}.signed.corrupted" 2>/dev/null
			error 1 "adding signature to firmware image failed"
		fi
		echo1 "signed image file size: $(byte_to_mb $(wc -c < "${img_name}")) ($(wc -c < "${img_name}") bytes)"
	fi

	echo1 -l "source firmware: ${SOURCE_IMAGE_DETAILS} ${SOURCE_IMAGE_SIZE}"
	echo1 "source image file size: $(byte_to_mb $(wc -c < "${FIRMWARE}")) ($(wc -c < "${FIRMWARE}") bytes)"

	# create the link images/latest.image to the just created .image
	img_link="${img_name%/*}/latest.image"
	rm -f "$img_link"
	ln -s "${img_name#*/}" "$img_link"
fi

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

if [ "$action_names" ]; then
	# Pack externalised files
	if [ "$EXTERNAL_CREATEPAK" == "y" -a -n "$externalised_files" ]; then
		#static packages
		modexternal="${FW_IMAGES_DIR:-$DIR}/${modstring}.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}/${modstring}_${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 https://freetz-ng.github.io/freetz-ng/wiki/00_FAQ/FAQ.de.html#nach-dem-flashen-ist-das-avm-webinterface-nicht-mehr-erreichbar for details."
	fi

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

exit 0