Understanding the internal structure of disk images is essential for debugging conversions, verifying configurations, and troubleshooting boot issues. This tutorial demonstrates systematic approaches to analyze partition layouts and manually inspect file system contents within any disk image. While the techniques apply universally to disk images, the examples focus on Raspberry Pi images processed through mender-convert
.
The Challenge
Converting disk images with mender-convert
creates complex partition structures with multiple file systems. Quick verification commands often rely on hardcoded offsets that break when image layouts change, making systematic inspection techniques necessary for reliable analysis.
Understanding Partition Structure
Start by examining the overall partition layout using fdisk
:
# Display partition table with sector information
fdisk -l deploy/raspberrypi4-mender.img
# Show partition boundaries in human-readable format
parted deploy/raspberrypi4-mender.img unit MB print
The fdisk
output reveals crucial information:
- Start sectors: Where each partition begins on the disk
- Size: Number of sectors per partition
- Type: Partition type codes (83 for Linux, c for FAT32)
- Sector size: Usually 512 bytes, but verify in the header
For Raspberry Pi conversions, expect to see:
- Boot partition (FAT32, typically 256MB-512MB)
- Root filesystem A (ext4, variable size)
- Root filesystem B (ext4, same size as A)
- Data partition (ext4, remaining space)
Calculating Mount Offsets
Rather than guessing offsets, calculate them systematically:
#!/bin/bash
# Save this as calculate-offsets.sh and run it
DISK_IMAGE="deploy/raspberrypi4-mender.img"
# Get sector size (usually 512 bytes)
SECTOR_SIZE=$(fdisk -l "$DISK_IMAGE" | grep "Sector size" | awk '{print $7}')
# Extract start sectors for each partition (handle boot flag * in field 2)
BOOT_START=$(fdisk -l "$DISK_IMAGE" | awk '$1 ~ /1$/ {if ($2 == "*") print $3; else print $2}')
ROOTFS_A_START=$(fdisk -l "$DISK_IMAGE" | awk '$1 ~ /2$/ {print $2}')
ROOTFS_B_START=$(fdisk -l "$DISK_IMAGE" | awk '$1 ~ /3$/ {print $2}')
DATA_START=$(fdisk -l "$DISK_IMAGE" | awk '$1 ~ /4$/ {print $2}')
# Calculate byte offsets
BOOT_OFFSET=$((BOOT_START * SECTOR_SIZE))
ROOTFS_A_OFFSET=$((ROOTFS_A_START * SECTOR_SIZE))
ROOTFS_B_OFFSET=$((ROOTFS_B_START * SECTOR_SIZE))
DATA_OFFSET=$((DATA_START * SECTOR_SIZE))
echo "Boot partition offset: $BOOT_OFFSET bytes"
echo "Root A partition offset: $ROOTFS_A_OFFSET bytes"
echo "Root B partition offset: $ROOTFS_B_OFFSET bytes"
echo "Data partition offset: $DATA_OFFSET bytes"
Loop Device Approach
For more convenient access to multiple partitions, use loop devices with the -P
flag to automatically create partition devices:
# Create loop device for the entire image
# Use --show -f to automatically find free loop device
sudo losetup -P --show -f $DISK_IMAGE
# Verify partition detection (replace loop0 with actual device from previous command)
ls -la /dev/loop0p*
# Mount individual partitions
sudo mkdir -p /mnt/{boot,rootfs-a,rootfs-b,data}
sudo mount /dev/loop0p1 /mnt/boot
sudo mount /dev/loop0p2 /mnt/rootfs-a
sudo mount /dev/loop0p3 /mnt/rootfs-b
sudo mount /dev/loop0p4 /mnt/data
# Inspect contents
ls -la /mnt/boot/
ls -la /mnt/rootfs-a/etc/
df -h /mnt/*
# Cleanup when finished
sudo umount /mnt/{boot,rootfs-a,rootfs-b,data}
sudo losetup -d /dev/loop0
File System Analysis
Once partitions are mounted, analyze their contents systematically:
Boot Partition Inspection
The boot partition contains the bootloader configuration and kernel files. Raspberry Pi systems use specific configuration files that control hardware initialization and boot parameters. Understanding these files helps diagnose boot failures and verify correct hardware configuration.
# Create mount point and mount boot partition (adjust loop device number as needed)
sudo mkdir -p /mnt/boot
sudo mount /dev/loop0p1 /mnt/boot
# Check bootloader configuration
cat /mnt/boot/config.txt
cat /mnt/boot/cmdline.txt
# Verify kernel and device tree presence
find /mnt/boot -name "*.dtb" | head -5
find /mnt/boot -name "kernel*"
# Check for U-Boot files (if using U-Boot)
find /mnt/boot -name "u-boot*"
find /mnt/boot -name "boot.scr"
Root Filesystem Analysis
The root filesystem contains the complete operating system installation. After conversion, mender-convert
creates two identical root partitions for A/B updates. Examining the filesystem structure verifies that the conversion preserved the original system correctly and that required directories and services remain intact.
# Create mount point and mount root filesystem A (adjust loop device as needed)
sudo mkdir -p /mnt/rootfs-a
sudo mount /dev/loop0p2 /mnt/rootfs-a
# Verify essential system directories
ls -la /mnt/rootfs-a/
du -sh /mnt/rootfs-a/{bin,etc,lib,usr,var}
# Check system configuration
cat /mnt/rootfs-a/etc/fstab
cat /mnt/rootfs-a/etc/hostname
ls -la /mnt/rootfs-a/etc/systemd/system/
# Inspect installed packages (Debian/Ubuntu systems)
dpkg --admindir=/mnt/rootfs-a/var/lib/dpkg --get-selections | head -20
Alternative Inspection Methods
Using Offset Mounting
When loop devices are unavailable, use direct offset mounting:
# Create mount points and mount using calculated offsets (use literal values)
# For example, if BOOT_OFFSET=12582912:
sudo mkdir -p /mnt/{boot,rootfs-a}
sudo mount -o loop,offset=12582912 deploy/raspberrypi4-mender.img /mnt/boot
sudo mount -o loop,offset=549453824 deploy/raspberrypi4-mender.img /mnt/rootfs-a
# Inspect and unmount
ls -la /mnt/boot/
sudo umount /mnt/boot /mnt/rootfs-a
File System Type Detection
Before attempting to mount partitions, identify their filesystem types to select appropriate mount commands and avoid errors. The file
command analyzes filesystem signatures, while blkid
provides additional metadata like UUIDs and labels when available.
# Identify file system types without mounting
sudo file -sL /dev/loop0p1
sudo file -sL /dev/loop0p2
# Or using blkid if available
sudo blkid /dev/loop0p1
sudo blkid /dev/loop0p2
Debugging Common Issues
Boot Problems
Boot failures often stem from missing files, incorrect configuration, or corrupted boot partition contents. Raspberry Pi systems require specific firmware files and properly formatted configuration files. These commands verify the essential boot components are present and correctly configured.
# Create mount point and mount boot partition if not already mounted
sudo mkdir -p /mnt/boot
sudo mount /dev/loop0p1 /mnt/boot 2>/dev/null
cat /mnt/boot/config.txt | grep -v "^#" | grep -v "^$"
# Verify kernel command line
cat /mnt/boot/cmdline.txt
# Check for required boot files
ls -la /mnt/boot/bootcode.bin /mnt/boot/start*.elf 2>/dev/null || echo "Missing Pi boot files"
Root Filesystem Issues
Root filesystem problems prevent successful system initialization. Common issues include filesystem corruption, missing system binaries, or incorrect directory structures. These checks verify filesystem integrity and ensure critical system components exist in expected locations.
# Check file system integrity (read-only check)
sudo fsck.ext4 -n /dev/loop0p2
# Create mount point and mount rootfs if not already mounted
sudo mkdir -p /mnt/rootfs-a
sudo mount /dev/loop0p2 /mnt/rootfs-a 2>/dev/null
test -x /mnt/rootfs-a/sbin/init && echo "Init found" || echo "Init missing"
ls -la /mnt/rootfs-a/lib/systemd/systemd 2>/dev/null || echo "Systemd missing"
Space Usage Analysis
Understanding partition space utilization helps identify oversized images, locate bloated components, or diagnose insufficient space issues. Converted images should have similar content across both root partitions, making space analysis useful for detecting unexpected differences or problematic content.
# Analyze partition space usage
df -h /mnt/boot /mnt/rootfs-a /mnt/rootfs-b /mnt/data
# Find largest directories in root filesystem
sudo find /mnt/rootfs-a -maxdepth 1 -type d -exec du -sh {} \; | sort -hr | head -10
# Check for unexpected large files
sudo find /mnt/rootfs-a -size +100M -type f -exec ls -lh {} \;
Automation Script
Create a helper script for systematic image inspection:
#!/bin/bash
# image-inspect.sh - Systematic disk image inspection
IMAGE="$1"
if [ -z "$IMAGE" ]; then
echo "Usage: $0 <disk-image>"
exit 1
fi
echo "=== Partition Layout ==="
fdisk -l "$IMAGE"
echo
echo "=== File System Types ==="
LOOP_DEV=$(sudo losetup -P --show -f "$IMAGE")
for part in ${LOOP_DEV}p*; do
echo -n "$(basename $part): "
sudo blkid -o value -s TYPE "$part" 2>/dev/null || echo "unknown"
done
echo
echo "=== Mounting and Basic Inspection ==="
sudo mkdir -p /tmp/img-inspect/{boot,rootfs-a,rootfs-b,data}
# Mount partitions (adapt numbering as needed)
sudo mount ${LOOP_DEV}p1 /tmp/img-inspect/boot 2>/dev/null
sudo mount ${LOOP_DEV}p2 /tmp/img-inspect/rootfs-a 2>/dev/null
sudo mount ${LOOP_DEV}p3 /tmp/img-inspect/rootfs-b 2>/dev/null
sudo mount ${LOOP_DEV}p4 /tmp/img-inspect/data 2>/dev/null
# Show space usage
df -h /tmp/img-inspect/*
# Cleanup
sudo umount /tmp/img-inspect/* 2>/dev/null
sudo rmdir /tmp/img-inspect/* /tmp/img-inspect
sudo losetup -d "$LOOP_DEV"
Security Considerations
When inspecting images from untrusted sources:
- Use read-only mounts:
sudo mkdir -p /mnt/boot && sudo mount -o ro /dev/loop0p1 /mnt/boot
orsudo mkdir -p /mnt/boot && sudo mount -o loop,ro,offset=12582912 image.img /mnt/boot
- Avoid executing binaries from mounted filesystems
- Be cautious with symbolic links that might escape the mount point
- Consider using containerized inspection environments
Performance Notes
Image inspection operations can be I/O intensive:
- Loop device setup is faster than repeated offset calculations
- Loop devices with
-P
flag provide more reliable partition detection than manual offset calculation - For large images, consider working on faster storage (SSD vs HDD)
- Multiple concurrent mounts of the same image file are supported
This systematic approach to image inspection eliminates guesswork and provides reliable methods for understanding disk image structure, making debugging and verification more efficient and accurate.