#!/bin/sh
set -eu

DEV="/dev/mmcblk1p1"
DISK="/dev/mmcblk1"
PN="1"                          # partition number
NEWROOT="/tmp/newroot"
TMPFS_SIZE="250M"               # adjust if needed
SAFETY_PERCENT="2"              # extra % over minimum FS size
SAFETY_MIB="64"                 # and at least this many MiB extra

need() { command -v "$1" >/dev/null 2>&1 || { echo "Missing command: $1"; exit 1; }; }
need busybox
need e2fsck
need resize2fs
need tune2fs
need parted
need awk
need sed

[ "$(id -u)" -eq 0 ] || { echo "Run as root."; exit 1; }
[ -b "$DEV" ] || { echo "Block device $DEV not found"; exit 1; }
[ -b "$DISK" ] || { echo "Disk $DISK not found"; exit 1; }

echo "[1/9] Create tmpfs root and make / a private, distinct mount"
mkdir -p "$NEWROOT/oldroot"
mount -t tmpfs -o "size=${TMPFS_SIZE}" tmpfs "$NEWROOT"
mount --make-rprivate /
mount --bind / /

echo "[2/9] Minimal FS layout in tmpfs"
mkdir -p "$NEWROOT"/{bin,sbin,usr/bin,usr/sbin,dev,proc,sys,run,lib,lib64}

echo "[3/9] Copy tools into tmpfs"
cp -v /bin/busybox "$NEWROOT/bin/" >/dev/null
for a in sh mount umount pivot_root chroot ls cat awk sed grep cp mv ln mkdir rmdir echo true false; do
  ln -sf busybox "$NEWROOT/bin/$a"
done

# Tools needed for ext4 + partitioning
for b in /sbin/e2fsck /sbin/resize2fs /sbin/tune2fs /sbin/parted; do
  [ -x "$b" ] && cp -v "$b" "$NEWROOT/sbin/" >/dev/null
done

echo "[4/9] Copy shared libraries required by those tools"
copy_libs() {
  BIN="$1"
  # ldd output: lib -> /path (0xaddr)
  ldd "$BIN" | awk '{if (match($3,"/")) print $3; else if (match($1,"/")) print $1}' | while read -r L; do
    [ -f "$L" ] || continue
    DEST="$NEWROOT$L"
    mkdir -p "$(dirname "$DEST")"
    cp -v "$L" "$DEST" >/dev/null
  done
}
for BIN in /bin/busybox "$NEWROOT/sbin/e2fsck" "$NEWROOT/sbin/resize2fs" "$NEWROOT/sbin/tune2fs" "$NEWROOT/sbin/parted"; do
  [ -x "${BIN%/*}/${BIN##*/}" ] || continue
  # If BIN is inside NEWROOT, map back to host path for ldd
  HOSTBIN="${BIN#$NEWROOT}"
  [ -z "$HOSTBIN" ] && HOSTBIN="$BIN"
  [ -x "$HOSTBIN" ] && copy_libs "$HOSTBIN" || true
done

# Make sure dynamic loader path exists (some distros need it)
[ -e /lib64 ] && mkdir -p "$NEWROOT/lib64" && cp -a /lib64/* "$NEWROOT/lib64/" 2>/dev/null || true

echo "[5/9] Move kernel pseudo filesystems into tmpfs"
mount --move /dev  "$NEWROOT/dev"
mount --move /proc "$NEWROOT/proc"
mount --move /sys  "$NEWROOT/sys"
mount --move /run  "$NEWROOT/run"

echo "[6/9] pivot_root into tmpfs"
cd "$NEWROOT"
pivot_root . oldroot
# We are now running with / = tmpfs, and old root mounted at /oldroot
exec /bin/sh -c '
set -eu

echo "[7/9] Unmount old root cleanly"
# In case anything still bind-mounted from oldroot, try recursive then lazy
umount -R /oldroot 2>/dev/null || umount -l /oldroot

echo "[8/9] Shrink filesystem on '"$DEV"' to minimum"
# Always fsck before resizing
/sbin/e2fsck -f -y '"$DEV"'
# Minimize FS to its minimum possible size
/sbin/resize2fs -M '"$DEV"'
# Query minimum size in filesystem blocks
MINBLOCKS=$(/sbin/resize2fs -P '"$DEV"' 2>&1 | awk '"'"'/minimum size/{print $NF}'"'"')
BLKSZ=$(/sbin/tune2fs -l '"$DEV"' | awk -F": " '"'"'/Block size:/{print $2}'"'"')
[ -n "$MINBLOCKS" ] && [ -n "$BLKSZ" ] || { echo "Failed to read FS minimum size"; exit 1; }

# Compute target bytes with safety margin
# bytes = minblocks*blksz * (1 + SAFETY_PERCENT/100) + SAFETY_MIB*MiB
SAFETY_PCT='"$SAFETY_PERCENT"'
SAFETY_MIB='"$SAFETY_MIB"'
REQ_BYTES=$(awk -v b="$MINBLOCKS" -v s="$BLKSZ" -v p="$SAFETY_PCT" -v m="$SAFETY_MIB" \
  "BEGIN{printf \"%.0f\", (b*s)*(1+p/100.0) + (m*1024*1024)}")

# Figure out sector size and start sector of partition
SECSZ=$(cat /sys/block/'"$(basename "$DISK")"'/queue/logical_block_size)
# Use parted machine-readable output
PARTINFO=$( /sbin/parted -m '"$DISK"' unit s print | awk -F: -v pn='"$PN"' '"'"'$1==pn{print $2}'"'"' )
# PARTINFO looks like: 2048s
PART_START=${PARTINFO%s}
[ -n "$SECSZ" ] && [ -n "$PART_START" ] || { echo "Failed to read sector size or partition start"; exit 1; }

# Round up to whole sectors
REQ_SECTORS=$(( (REQ_BYTES + SECSZ - 1) / SECSZ ))
NEW_END=$(( PART_START + REQ_SECTORS - 1 ))

echo "  FS min blocks: $MINBLOCKS"
echo "  FS block size: $BLKSZ"
echo "  Safety: +'"$SAFETY_PERCENT"'% and +'\"$SAFETY_MIB\"' MiB"
echo "  Required bytes (with safety): $REQ_BYTES"
echo "  Sector size: $SECSZ"
echo "  Start sector: $PART_START"
echo "  New end sector: $NEW_END"

echo "[8b/9] Resize partition '"$DISK$pN"' with parted"
# parted wants the end position with unit s. Use -- to avoid locale issues.
/sbin/parted -s '"$DISK"' unit s resizepart '"$PN"' ${NEW_END}s

# Kernel may still think the old size; we will just reboot forcefully
echo "[8c/9] Final fsck and grow-to-fit (tiny pad)"
/sbin/e2fsck -f -y '"$DEV"'
/sbin/resize2fs '"$DEV"' || true

echo "[9/9] Sync and reboot"
sync
reboot -f
'
