#! /bin/sh

# Unpack a 7020 distribution (as distributed by Dream) and create a
# regular tar file on standard out.  Runnable in the rather special
# (read "retarded" or even "brain dead") environment that busybox provides.

# Typical usage:
#	nfi_unpack 7020image.nfi | gzip > 7020image.tar.gz

# The boot partition will be stored (within the tar file) in the "boot"
# subdir, while the second stage loader will be stored as "secondstage.bin.gz"

# !!! Please note that ca. 33MB of free space is needed on the tmp dir.
# !!! This is because an image of the 32MB 7020 flash will be created.
# !!! This 33MB comes in addition to the input and output files.

# Requires that the "blkmtd.ko" kernel module (compiled for the correct
# kernel version!) is avaiable in the /lib/modules/`uname -r`/extra directory.

# Requires that the "nfi_extract" utility is available in the path.

# Usage and exmples
if test $# -ne 1; then
	echo "Usage: nfi_unpack 7020image.nfi" 1>&2
	echo "    Extracts the 7020 image as a tar file to standard out" 1>&2
	echo "Examples:" 1>&2
	echo "    nfi_unpack 7020image.nfi | gzip > 7020image.tar.gz" 1>&2
	echo "    mkdir dest_dir; nfi_unpack 7020image.nfi | tar -C dest_dir -xf -" 1>&2
	exit 1
fi

# Globals
nfi_file=$1				# Input file
pid=$$					# My pid (more readable)
tmp_dir='./'				# Where to put tmps (we need ca. 33M!)
verbose='yes'				# Set to '' if you want silent operation
loop_device='/dev/loop/7'		# The loop device to use
#mod_dir=/lib/modules/`uname -r`/extra	# Where to find the module
mod_dir=./
blkmod=$mod_dir/blkmtd.ko		# The needed module
mtd_device=''				# The name of the mtd block device

# Temporary files and directories that might be created
tmp_ff=${tmp_dir}lots_of_ffs_$pid	# 1M FF file
flash_img=${tmp_dir}flash_image_$pid	# 30MB flash image
mnt_pt=${tmp_dir}mount_pt_$pid		# Where the jffs2 fs will be mounted
tmp_boot=${tmp_dir}boot_tar_$pid	# Compressed tar file of boot partition

# Try to create the mount point, makes sure we have write permission
# in the temporary directory
mkdir $mnt_pt || {
	echo "Can't continue" 1>&2
	exit 1
}

# Echo to stderr if verbose
vecho()
{
	if test -n $verbose; then
		echo "$@" 1>&2
	fi
}

# Cleanup and exit
cleanup()
{
	local msg="$1"
	local exit_val="$2"

	if test -n "$msg"; then
		echo "$msg" 1>&2
	fi
	umount_jffs2 $flash_image >/dev/null 2>&1
	vecho "Cleaning up files"
	for file in $tmp_ff $flash_img $tmp_boot; do
		rm -f $file;
	done
	vecho "Cleaning up directories"
	for dir in $mnt_pt; do
		rm -rf $dir
	done
	exit $exit_val
}

# Catch trapped signals
catch_sig()
{
	cleanup "" 1
}

# Assert that a file has been created with the right size
assert_file_size()
{
	local file=$1
	local assert_size=$2		# In MB
	local foo1 foo2 foo3 foo4 foo5
	assert_size=$(expr $assert_size \* 1024 \* 1024)
	local actual_size=$(ls -la $file | (read foo1 foo2 foo3 foo4 actual_size foo5; echo $actual_size))
	echo "Actual $actual_size" 1>&2
	if test $actual_size -ne $assert_size; then
		cleanup "Expected $file to be $assert_size MB large" 1
	fi
}

# Create a temporary file that is exactly 1M long, and only contains
# 0xFF characters.
create_1M_ff()
{
	cp 1mbff.gz $1.gz
        gzip -d $1.gz
	assert_file_size $1 1
}

# Loading the blkmtd module automagically creates a new
# device in the /dev/mtdblock directory.  Figure out the
# name of this device.  It would normally be number 4 for
# a 7020, and number 7 for a 7000.  Just search for the
# first unused number.
find_mtd_device()
{
	local i
	for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
		if test ! -e /dev/mtdblock/$i; then
			mtd_device=/dev/mtdblock/$i
			break
		fi
	done
}

# Mounts a jffs2 filesystem on a flash image (regular file)
# using a loop back device and the mtdblk kernel module.
mount_jffs2()
{
	local img=$1

	vecho "Mounting $img as jffs2"
	./losetup $loop_device $img || cleanup "Can't set up loop" 1
	insmod $blkmod device=$loop_device erasesz=16 >/dev/null || cleanup "Can't insert $blkmod" 1
	mount -t jffs2 $mtd_device $mnt_pt >/dev/null  || cleanup "Can't mount jffs2" 1
}

# Umounts a jffs2 filesystem on a flash image as created
# by mount_jffs2() and deletes the temporary flash image.
# Be careful: called by cleanup also
umount_jffs2()
{
	local img=$1

	umount $mnt_pt
	rmmod $blkmod
	./losetup -d $loop_device
	rm -f $img
}

# Cleanup if the user gets impatient
trap catch_sig HUP INT KILL TERM

# Find mtd block device that will be created
find_mtd_device
vecho "Will use mtd device $mtd_device"

# Create 1M of 0xFF, we'll use it to create empty flash images
vecho "Creating 1M file containing 0xFF"
create_1M_ff $tmp_ff

# Two MB for the root file system
vecho "Creating 2M file for boot file system"
cat $tmp_ff $tmp_ff > $flash_img
assert_file_size $flash_img 2
# Extract the boot part on top of this
vecho "Extracting boot file system"
./nfi_extract 1 $nfi_file $flash_img || cleanup "Can't extract boot"
# Mount it
vecho "Mounting boot file system"
mount_jffs2 $flash_img
# Make a tar file of the contents
vecho "Copying boot file system to temporary file"
tar -C $mnt_pt -cf - . | gzip > $tmp_boot
# And get rid of the jffs2
umount_jffs2 $flash_img

# 30MB for the root file system
vecho "Creating 30M file for root file system"
for i in 1 2 3 4 5 6; do
	cat $tmp_ff $tmp_ff $tmp_ff $tmp_ff $tmp_ff >> $flash_img
done
# Extract the root part on top of this
vecho "Extracting root file system"
./nfi_extract 2 $nfi_file $flash_img || cleanup "Can't extract root"
# Mount it
vecho "Mounting root file system"
mount_jffs2 $flash_img
# Copy boot
vecho "Copy boot to root file system"
gunzip < $tmp_boot | tar -C $mnt_pt/boot -xf -
# Extract loader
vecho "Extracting second stage loader"
./nfi_extract 0 $nfi_file $mnt_pt/secondstage.bin.gz || "Can't extract secondstage"
# Make tar file on standard output
vecho "Creating tar file on stdout"
tar -C $mnt_pt -cf - .
# And clean up
umount_jffs2 $flash_img
cleanup "Finished successfully" 0
