# e2fsprogs part 2 -- e2fsck checks a volume, debugfs reads and writes its # contents, and e2fsck rebuilds the primary superblock after it is destroyed. . "$BST_LIB" require e2fsck e2fsprogs-fsck/present T=${TMPDIR:-/tmp}/bst-e2fsck.$$ mkdir -p "$T" || exit 0 trap 'rm -rf "$T"' EXIT IMG=$T/fs.img sb() { dumpe2fs -h "$IMG" 2>/dev/null | sed -n "s/^$1: *//p"; } msg() { _i=$1; _n=$2; shift 2; _o=$("$@" 2>&1) case "$_o" in *"$_n"*) bst_ok "$_i" ;; *) bst_notok "$_i" "want:'$_n' got:'$(_bst_1line "$_o")'" ;; esac; } # 12288 blocks of 1024 is the smallest geometry that still puts a backup # superblock on disk (blocks_per_group is 8192, so group 1 starts at 8192 and # carries its copy at 8193) -- the recovery case at the end needs one to exist. dd if=/dev/zero of="$IMG" bs=1024 count=12288 2>/dev/null check e2fsprogs-fsck/mke2fs mke2fs -q -F -t ext4 -b 1024 "$IMG" check_rc e2fsprogs-fsck/clean 0 e2fsck -f -n "$IMG" msg e2fsprogs-fsck/pass5 "Pass 5" e2fsck -f -n "$IMG" # ------------------------------------------------------ debugfs reads + writes # This is the ext2fs library doing structural work rather than reading a header: # allocating an inode and a block, linking a directory entry, then finding it # again on a second open. printf 'brintos-e2fsprogs-payload\n' > "$T/payload.txt" msg e2fsprogs-fsck/debugfs-lost-found "lost+found" debugfs -R "ls /" "$IMG" check e2fsprogs-fsck/debugfs-write debugfs -w -R "write $T/payload.txt payload" "$IMG" msg e2fsprogs-fsck/debugfs-ls "payload" debugfs -R "ls /" "$IMG" msg e2fsprogs-fsck/debugfs-cat "brintos-e2fsprogs-payload" debugfs -R "cat payload" "$IMG" msg e2fsprogs-fsck/debugfs-stats "Filesystem magic number: 0xEF53" debugfs -R "stats -h" "$IMG" # a write debugfs made must leave the filesystem consistent, not merely readable check_rc e2fsprogs-fsck/clean-after-write 0 e2fsck -f -n "$IMG" # ------------------------------------------- destroy the superblock and rebuild BS=$(sb 'Block size') BACKUP=$(dumpe2fs "$IMG" 2>/dev/null | sed -n 's/.*Backup superblock at \([0-9][0-9]*\).*/\1/p' | head -1) case "$BACKUP" in ''|*[!0-9]*) bst_notok e2fsprogs-fsck/backup-sb "dumpe2fs reported no backup superblock: '$BACKUP'" ;; *) bst_ok e2fsprogs-fsck/backup-sb ;; esac dd if=/dev/zero of="$IMG" bs=1024 seek=1 count=1 conv=notrunc 2>/dev/null # e2fsck signals through its exit status, and the failure that matters here is the # silent one -- calling a filesystem with no superblock clean. Assert non-zero # rather than a particular code, which varies with what it manages to salvage. e2fsck -f -n "$IMG" >/dev/null 2>&1; rc=$? if [ "$rc" -ne 0 ]; then bst_ok e2fsprogs-fsck/detects-wiped-sb else bst_notok e2fsprogs-fsck/detects-wiped-sb "e2fsck called a filesystem with a zeroed primary superblock clean"; fi msg e2fsprogs-fsck/wiped-sb-message "Superblock invalid" e2fsck -f -n "$IMG" e2fsck -f -y -b "$BACKUP" -B "$BS" "$IMG" >/dev/null 2>&1 check_rc e2fsprogs-fsck/repaired 0 e2fsck -f -n "$IMG" # the repair has to have restored the CONTENT, not just a mountable header msg e2fsprogs-fsck/repaired-payload "brintos-e2fsprogs-payload" debugfs -R "cat payload" "$IMG"