diff --git a/mount-firmware b/mount-firmware new file mode 100755 index 0000000..1f4d356 --- /dev/null +++ b/mount-firmware @@ -0,0 +1,79 @@ +#!/bin/sh + +# check if firmware is already mounted +if egrep -qs "^overlayfs:/firmware " /proc/mounts; then + echo "firmware is already mounted">&2 + exit 1 +fi + +# find firmware mtd partition +firmware=$(grep '"firmware"' /proc/mtd | cut -d: -f1) +firmware_size=$(cat /sys/class/mtd/${firmware}/size) +firmware_blocksize=$(cat /sys/class/mtd/${firmware}/erasesize) + +# setup filesystem markers (rootfs: squashfs, rootfs_data: jffs2) +rootfs_marker="6873" +rootfsdata_marker="(deadc0de|1985)" + +# search for the rootfs filesystem offset within the firmware mtd partition +rootfs_offset=0 +while [ ${rootfs_offset} -lt ${firmware_size} ]; do + # dump four bytes at offset (as hexadecimal string) and check if matches the marker pattern + if hexdump -e '"%x"' -n 4 -s ${rootfs_offset} /dev/${firmware} | egrep -qs "^${rootfs_marker}"; then + # marker found, break loop + break + fi + # continue searching, move to the next mtd block + rootfs_offset=$((rootfs_offset + firmware_blocksize)) +done + +# search for the rootfs_data filesystem offset within the firmware mtd partition +rootfsdata_offset=$((rootfs_offset + firmware_blocksize)) +while [ ${rootfsdata_offset} -lt ${firmware_size} ]; do + # dump four bytes at offset (as hexadecimal string) and check if matches the marker pattern + if hexdump -e '"%x"' -n 4 -s ${rootfsdata_offset} /dev/${firmware} | egrep -qs "^${rootfsdata_marker}"; then + # marker found, break loop + break + fi + # continue searching, move to the next mtd block + rootfsdata_offset=$((rootfsdata_offset + firmware_blocksize)) +done + +# calculate filesystem sizes +rootfs_size=$((rootfsdata_offset - rootfs_offset)) +rootfsdata_size=$((firmware_size - rootfsdata_offset)) + +# create loop device for each filesystem (virtual "partition" delimited by offset and size) +rootfs_loop=$(losetup --offset ${rootfs_offset} --sizelimit ${rootfs_size} -f --show /dev/mtdblock${firmware:3}) +rootfsdata_loop=$(losetup --offset ${rootfsdata_offset} --sizelimit ${rootfsdata_size} -f --show /dev/mtdblock${firmware:3}) + +# create virtual mtd for the loop devices +modprobe -q block2mtd +echo "${rootfs_loop},${firmware_blocksize}" > /sys/module/block2mtd/parameters/block2mtd +echo "${rootfsdata_loop},${firmware_blocksize}" > /sys/module/block2mtd/parameters/block2mtd + +# search for the newly created mtd names +rootfs=$(grep '"'${rootfs_loop}'"' /proc/mtd | cut -d: -f1) +rootfsdata=$(grep '"'${rootfsdata_loop}'"' /proc/mtd | cut -d: -f1) + +# create mountpoint if needed +[ ! -d /firmware ] && mkdir /firmware + +# mount filesystems (rootfs and rootfs_data) +mount -t squashfs /dev/mtdblock${rootfs:3} /firmware +mount -t jffs2 -o noatime /dev/mtdblock${rootfsdata:3} /firmware/overlay + +# initialize rootfs_data +[ ! -d /firmware/overlay/upper ] && mkdir /firmware/overlay/upper +[ ! -d /firmware/overlay/work ] && mkdir /firmware/overlay/work +[ ! -h /firmware/overlay/.fs_state ] && ln -s 2 /firmware/overlay/.fs_state + +# create rootfs_data overlay at /mnt +mount -t overlay -o noatime,lowerdir=/firmware,upperdir=/firmware/overlay/upper,workdir=/firmware/overlay/work overlayfs:/firmware /mnt + +# combine all mountpoints on /mnt +mount --move /firmware/overlay /mnt/overlay +mount --move /firmware /mnt/rom + +# finally, move the result to /firmware +mount --move /mnt /firmware diff --git a/mount_firmware b/mount_firmware deleted file mode 100755 index 582b1f3..0000000 --- a/mount_firmware +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/sh - -mtdpart="firmware" - -workdir="/var/run/mount_firmware" -statefile="${workdir}/state" -dumpfile="${workdir}/${mtdpart}.bin" - -workromdir="${workdir}/rom" -workoverlaydir="${workdir}/overlay" - -overlayfsdir="/${mtdpart}" -romdir="${overlayfsdir}/rom" -overlaydir="${overlayfsdir}/overlay" - -squashfs_marker="^6873" -jffs2_marker="^(1985|deadc0de)" - -function fs_search() { - local device=$1 - local offset=$2 - local size=$3 - local blocksize=$4 - local pattern=$5 - - while [[ ${offset} -lt ${size} ]]; do - local data=$(hexdump -e '"%x"' -n 4 -s ${offset} ${device} 2>/dev/null | egrep "${pattern}") - [[ ! -z "${data}" ]] && echo "${offset}" && break - offset=$((offset+blocksize)) - done -} - -function main() { - if [[ -e "${statefile}" ]]; then - echo "Firmware already mounted at ${overlayfsdir}" - exit 1 - fi - - mkdir -p ${workdir} - - echo "Searching for ${mtdpart}..." - local mtd=$(awk '$4 == "\"'${mtdpart}'\"" { print }' /proc/mtd | cut -d: -f1) - local size=$(cat /sys/class/mtd/${mtd}/size 2>/dev/null) - local blocksize=$(cat /sys/class/mtd/${mtd}/erasesize 2>/dev/null) - if [[ -z "${mtd}" || -z "${size}" || -z "${blocksize}" ]]; then - echo "Could not find ${mtdpart} partition" - exit 1 - fi - - echo "Dumping contents of ${mtdpart} partition..." - dd if=/dev/${mtd}ro of=${dumpfile} bs=${blocksize} 2>/dev/null - if [[ "$?" == "1" ]]; then - echo "Could not dump contents of ${mtdpart} partition" - exit 1 - fi - - echo "Searching for squashfs filesystem..." - local squashfs=$(fs_search ${dumpfile} 0 ${size} ${blocksize} ${squashfs_marker}) - if [[ -z "${squashfs}" ]]; then - echo "Could not find squashfs filesystem" - exit 1 - fi - - echo "Searching for jffs2 filesystem..." - local jffs2=$(fs_search ${dumpfile} ${squashfs} ${size} ${blocksize} ${jffs2_marker}) - if [[ -z "${jffs2}" ]]; then - echo "Could not find jffs2 filesystem" - exit 1 - fi - - echo "Creating rootfs loop device..." - local rootfs=$(losetup --offset ${squashfs} --sizelimit $((jffs2-squashfs)) -f --show ${dumpfile} 2>/dev/null) - if [[ -z "${rootfs}" ]]; then - echo "Could not create rootfs loop device" - exit 1 - fi - - echo "Creating rootfs_data loop device..." - local rootfs_data=$(losetup --offset ${jffs2} --sizelimit $((size-jffs2)) -f --show ${dumpfile} 2>/dev/null) - if [[ -z "${rootfs_data}" ]]; then - echo "Could not create rootfs_data loop device" - losetup -d ${rootfs} 2>/dev/null - exit 1 - fi - - modprobe -q block2mtd - - echo "Creating auxiliary mtdblock device partitions..." - echo "${rootfs},${blocksize}" > /sys/module/block2mtd/parameters/block2mtd - echo "${rootfs_data},${blocksize}" > /sys/module/block2mtd/parameters/block2mtd - local rom=$(awk '$4 == "\"'${rootfs}'\"" { print }' /proc/mtd | cut -d: -f1 | sed 's/^mtd/mtdblock/') - local overlay=$(awk '$4 == "\"'${rootfs_data}'\"" { print }' /proc/mtd | cut -d: -f1 | sed 's/^mtd/mtdblock/') - - echo "Mounting rootfs..." - mkdir -p ${workromdir} - mount -t squashfs /dev/${rom} ${workromdir} - - echo "Mounting rootfs_data..." - mkdir -p ${workoverlaydir} - mount -t jffs2 -o noatime /dev/${overlay} ${workoverlaydir} - mkdir -p ${workoverlaydir}/upper - mkdir -p ${workoverlaydir}/work - ln -sf 2 ${workoverlaydir}/.fs_state - - echo "Creating overlay filesystem..." - mkdir -p ${overlayfsdir} - mount -t overlay -o noatime,lowerdir=${workromdir},upperdir=${workoverlaydir}/upper,workdir=${workoverlaydir}/work overlayfs:${overlayfsdir} ${overlayfsdir} - mount -o bind ${workromdir} ${romdir} - mount -o bind ${workoverlaydir} ${overlaydir} - - echo "Saving mount state..." - echo "rootfs=${rootfs}" > ${statefile} - echo "rootfs_data=${rootfs_data}" >> ${statefile} - - echo "${mtdpart} was successfully mounted at ${overlayfsdir}" -} - -main \ No newline at end of file diff --git a/mtd-rw b/mtd-rw new file mode 100755 index 0000000..3aa2bd2 --- /dev/null +++ b/mtd-rw @@ -0,0 +1,25 @@ +#!/bin/sh + +case "$1" in + lock) # lock all mtd partitions + "$0" unlocked && rmmod mtd-rw + ;; + + unlock) # unlock all mtd partitions + "$0" locked && insmod "$(find /lib/modules/ -name mtd-rw.ko)" i_want_a_brick=1 + ;; + + locked) # check if mtd partitions are locked + ! "$0" unlocked + ;; + + unlocked) # check if mtd partitions are unlocked + lsmod | egrep -qs "^mtd_rw " + ;; + + *) # usage help message + echo "Usage: $(basename $0) " + ;; +esac + +exit $? diff --git a/recovery-config b/recovery-config new file mode 100755 index 0000000..c295500 --- /dev/null +++ b/recovery-config @@ -0,0 +1,37 @@ +#!/bin/sh + +# check if script arguments are valid +if [ "$1" != "save" -a "$1" != "reset" ]; then + # display usage message in case of insufficient or invalid arguments + echo "Usage: $(basename $0) " + exit 1 +fi + +# check mtd state and unlock if needed +if mtd-rw locked; then + mtd-rw unlock + relock=1 +fi + +# check whether to save or reset the current configuration +if [ "$1" == "save" ]; then + # create archive of the current configuration + tar czf /tmp/config.tar.gz -C /volatile/upper $(ls /volatile/upper/) +else + # create an empty configuration archive + dd if=/dev/zero bs=10240 count=1 2>/dev/null | gzip > /tmp/config.tar.gz +fi + +# store the configuration archive to mtd +snapshot_tool config_write &>/dev/null +if [ "$?" != "0" ]; then + echo "operation failed" +fi + +# clean up configuration archive +rm -f /tmp/config.tar.gz + +# lock mtd if it was unlocked by this script +if [ "${relock}" == "1" ]; then + mtd-rw lock +fi diff --git a/reset-firmware b/reset-firmware new file mode 100755 index 0000000..aa7857b --- /dev/null +++ b/reset-firmware @@ -0,0 +1,42 @@ +#!/bin/sh + +# display warn message, wait for user confirmation before continuing +echo -n "erase all settings and remove any installed packages? [N/y] ">&2 && read +[ "${REPLY}" != "y" ] && exit 1 + +# check firmware mount state to then delete data accordingly +if egrep -qs "^overlayfs:/firmware " /proc/mounts; then + # inform user about firmware mount state and action taken to reset rootfs_data + echo "firmware is mounted, only erasing files">&2 + + # erase all files (including "dot" and "dot-dot" files) + rm -rf /firmware/overlay/upper/* + rm -rf /firmware/overlay/upper/.[^.]* + rm -rf /firmware/overlay/upper/..?* +else + # inform user about firmware mount state and action taken to reset rootfs_data + echo "firmware not mounted, marking it to be erased on next mount">&2 + + # find firmware mtd partition + firmware=$(grep '"firmware"' /proc/mtd | cut -d: -f1) + firmware_size=$(cat /sys/class/mtd/${firmware}/size) + firmware_blocksize=$(cat /sys/class/mtd/${firmware}/erasesize) + + # initialize rootfs_data search variables + rootfsdata_marker="(deadc0de|1985)" + rootfsdata_offset=0 + + # search for rootfs_data inside firmware mtd partition + while [ ${rootfsdata_offset} -lt ${firmware_size} ]; do + # dump four bytes at offset (as hexadecimal string) and check if matches the marker pattern + if hexdump -e '"%x"' -n 4 -s ${rootfsdata_offset} /dev/${firmware} | egrep -qs "^${rootfsdata_marker}"; then + # marker found, break loop + break + fi + # continue searching, move to the next mtd block + rootfsdata_offset=$((rootfsdata_offset + firmware_blocksize)) + done + + # write jffs2 marker (0xdeadc0de) to force initialization of rootfs_data on the next mount + echo -ne "\xde\xad\xc0\xde" | dd of=/dev/mtdblock${firmware:3} bs=${firmware_blocksize} seek=$((rootfsdata_offset / firmware_blocksize)) count=1 conv=sync &>/dev/null +fi diff --git a/umount-firmware b/umount-firmware new file mode 100755 index 0000000..e167bbc --- /dev/null +++ b/umount-firmware @@ -0,0 +1,27 @@ +#!/bin/sh + +# check if firmware is mounted +if ! egrep -qs "^overlayfs:/firmware " /proc/mounts; then + echo "firmware is not mounted">&2 + exit 1 +fi + +# search /proc/mounts for firmware's virtual mtd devices +rootfs=$(grep " /firmware/rom " /proc/mounts | cut -d" " -f1 | cut -d/ -f3 | sed 's/block//') +rootfsdata=$(grep " /firmware/overlay " /proc/mounts | cut -d" " -f1 | cut -d/ -f3 | sed 's/block//') + +# map the virtual mtd devices to the corresponding loop devices +rootfs_loop=$(egrep "^${rootfs}:" /proc/mtd | cut -d\" -f2) +rootfsdata_loop=$(egrep "^${rootfsdata}:" /proc/mtd | cut -d\" -f2) + +# umount all mountpoints +umount /firmware/overlay +umount /firmware/rom +umount /firmware + +# remove virtual mtd devices (only possible by unloading the block2mtd kernel module) +rmmod block2mtd + +# remove the corresponding loop devices +losetup -d ${rootfs_loop} +losetup -d ${rootfsdata_loop} diff --git a/umount_firmware b/umount_firmware deleted file mode 100755 index 5745817..0000000 --- a/umount_firmware +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/sh - -mtdpart="firmware" - -workdir="/var/run/mount_firmware" -statefile="${workdir}/state" -dumpfile="${workdir}/${mtdpart}.bin" - -workromdir="${workdir}/rom" -workoverlaydir="${workdir}/overlay" - -overlayfsdir="/${mtdpart}" -romdir="${overlayfsdir}/rom" -overlaydir="${overlayfsdir}/overlay" - -function main() { - if [[ ! -e "${statefile}" ]]; then - echo "Firmware not mounted" - exit 1 - fi - - echo "Loading mount state..." - source ${statefile} - - echo "Unmounting overlay filesystem..." - umount ${romdir} && umount ${overlaydir} && umount ${overlayfsdir} - if [[ "$?" == "1" ]]; then - echo "Could not unmount overlay filesystem" - exit 1 - fi - - echo "Unmounting rootfs and rootfs_data..." - umount ${workromdir} && umount ${workoverlaydir} - if [[ "$?" == "1" ]]; then - echo "Could not unmount rootfs and rootfs_data" - exit 1 - fi - - echo "Flushing buffers..." - sync - - echo "Removing auxiliary mtdblock device partitions..." - rmmod block2mtd - if [[ "$?" == "1" ]]; then - echo "Could not remove auxiliary mtdblock device partitions" - exit 1 - fi - - echo "Removing rootfs and rootfs_data loop devices..." - losetup -d ${rootfs} ${rootfs_data} - if [[ "$?" == "1" ]]; then - echo "Could not remove rootfs and rootfs_data loop devices" - exit 1 - fi - - while true; do - read -p "Do you want to write changes to flash? [yn] " yn - case $yn in - [Yy]*) - echo "Writing changes to flash..." - mtd -q -q write ${dumpfile} ${mtdpart} - break - ;; - [Nn]*) - break - ;; - esac - done - - echo "Cleaning up any leftover files..." - rmdir ${overlayfsdir} - rm -rf ${workdir} - - echo "${mtdpart} was successfully unmounted from ${overlayfsdir}" -} - -main \ No newline at end of file