dehydrate-fs/map.sh

71 lines
1.7 KiB
Bash
Raw Normal View History

2022-07-29 13:40:16 -04:00
#!/usr/bin/env bash
set -eo pipefail
image="$1"; [ -z "$1" ] && { echo "No image file specified." >&2; exit 1; }
inspect () {
( #Jerry-rig error handling onto debugfs
{ debugfs -R "$*" "$image" 1>&3; } 2>&1 | tail -n +2 |\
{ if IFS= read -r line; then
printf '%s\n' "$line"
cat; exit 1
fi; } >&2
) 3>&1 | cat #Never invoke pager.
}
BLOCKSIZE=`inspect stats | sed -nE 's/^Block size:\s+([0-9]+)$/\1/p'`
#Prefer same filesystem for efficiency on large files
TMPDIR="$(mktemp -d "`pwd`/.pool.XXXXXXXX")" || "$(mktemp -d)" || exit 1
trap 'rm -rf -- "$TMPDIR"' EXIT
size () {
inspect stat "$1" \
| head -n3 \
| sed -nE 's/.*Size: ([0-9]+).*/\1/p'
}
extents () {
remainder=$((`size $1`%BLOCKSIZE))
inspect dump_extents "$1" \
| tail -n +2 \
| awk '{print $5, $8, $11}' \
| sed '$s/$/ '$remainder'/'
}
THRESHOLD=${THRESHOLD:-$((1024*1024))}
listall () {
if [ $# -eq 0 ]; then
listall "`inspect ls -p /`"
wait; return
fi
perl -p -e 's!/\n$!/\x0!g' <<< "$1" \
| while IFS=/ read -r -d $'\0' _ inode itype _ _ name isize; do
case ${itype:0:3} in
'100') [ $isize -ge $THRESHOLD ] && echo $inode $isize;;
'040') [[ "${name}" != @(.|..) ]] && {
echo "Recursing into $name" >&2;
listall "`inspect ls -p "<$inode>"`" & };;
esac
done
}
mkdir -p pool
{
echo -e "#BLOCKSIZE=$BLOCKSIZE\n#INODE SHA256SUM\n#INDEX BLOCK LENGTH [REMAINDER]"
listall \
| while IFS=' ' read -r -d $'\n' inode isize; do
#TODO: Make asynchronus
tmp="$(mktemp -p "$TMPDIR")"
sha=`inspect dump "<$inode>" /dev/stdout \
| tee "$tmp" \
| sha256sum \
| cut -d' ' -f1`
[ -f pool/$sha ] \
&& rm -f "$tmp" \
|| mv -v "$tmp" pool/$sha 1>&2
echo $inode $sha
extents "<$inode>"
done
} > ${2:-/dev/stdout}