71 lines
1.7 KiB
Bash
Executable File
71 lines
1.7 KiB
Bash
Executable File
#!/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}
|