Write a bash script to calculate the last sector of /dev/nbd0 and automatically truncate the backing raw img file


Gemini

To shrink the underlying .img file (backed by /dev/nbd0) to match your reduced partition layout, you need to determine the exact end sector of the last partition, calculate the required total image size, and truncate the image file.

1. Identify the Exact Last Sector

Find the highest sector used by your partitions using fdisk:

sudo fdisk -l /dev/nbd0

Look at the End sector of the highest-numbered partition (e.g., /dev/nbd0p2).

2. Calculate the Required File Size

Assuming standard 512-byte sectors:

Total Sectors Required=Last Sector+1
Size in Bytes=Total Sectors Required×512

Example: If the last partition ends at sector 20971519:

  • Total sectors needed = 20971520
  • Size in bytes = 20971520 ×512 =10737418240 bytes (10 GiB)

3. Disconnect NBD and Truncate the Image File

Unmount any mounted filesystems, disconnect the NBD device, and shrink the image file:

# Unmount any active mounts
sudo umount /dev/nbd0p* 2>/dev/null

# Disconnect NBD device
sudo qemu-nbd --disconnect /dev/nbd0

# Truncate image to exact sector boundary (replace <BYTES> with calculated value)
truncate -s <BYTES> /path/to/your_image.img

Data Loss Risk

Ensure the calculated size is larger than the final sector of your last partition. Truncating to a size smaller than the partition table boundaries will corrupt the filesystem.

4. Verify Partition Integrity

Reconnect the image and inspect the partition table:

sudo qemu-nbd --connect=/dev/nbd0 /path/to/your_image.img
sudo fdisk -l /dev/nbd0

fdisk should now report the disk size matching your last partition boundary without unallocated trailing space. Once verified, disconnect NBD again:

sudo qemu-nbd --disconnect /dev/nbd0

Would you like a single script to automatically calculate and truncate the image?


You

Write a bash script to calculate the last sector of /dev/nbd0 and automatically truncate the backing raw img file.


Gemini

Here is a script that parses the highest sector used on /dev/nbd0, calculates the exact byte boundary, safely disconnects the device, and truncates the backing .img file.

#!/usr/bin/env bash
set -euo pipefail

NBD_DEV="/dev/nbd0"

# Check root privileges
if [[ $EUID -ne 0 ]]; then
    echo "Error: This script must be run with sudo or as root." >&2
    exit 1
fi

# Ensure NBD device is connected
if ! lsblk "$NBD_DEV" &>/dev/null; then
    echo "Error: $NBD_DEV is not active or connected." >&2
    exit 1
fi

# Find the backing image file
IMG_FILE=$(qemu-nbd --list-connections 2>/dev/null | awk -v dev="$NBD_DEV" '$0 ~ dev {print $NF}' || true)

# Fallback check via sysfs if qemu-nbd output isn't parsed
if [[ -z "$IMG_FILE" && -b "$NBD_DEV" ]]; then
    # Retrieve backing file path using losetup/sysfs inspection if applicable
    if [[ -f "/sys/block/${NBD_DEV#/dev/}/size" ]]; then
        read -rp "Enter the full path to the raw .img file: " IMG_FILE
    fi
fi

if [[ ! -f "$IMG_FILE" ]]; then
    echo "Error: Could not locate backing image file '$IMG_FILE'." >&2
    exit 1
fi

# Find the highest end sector across all partitions
LAST_SECTOR=$(fdisk -l "$NBD_DEV" 2>/dev/null | awk -v dev="$NBD_DEV" '$1 ~ dev {print $3}' | sort -n | tail -n 1)

if [[ -z "$LAST_SECTOR" || ! "$LAST_SECTOR" =~ ^[0-9]+$ ]]; then
    echo "Error: Could not determine last sector from partition table on $NBD_DEV." >&2
    exit 1
fi

# Get sector size (usually 512 bytes)
SECTOR_SIZE=$(blockdev --getss "$NBD_DEV")

# Calculate total required bytes (Last Sector + 1) * Sector Size
REQUIRED_SECTORS=$((LAST_SECTOR + 1))
REQUIRED_BYTES=$((REQUIRED_SECTORS * SECTOR_SIZE))

echo "--------------------------------------------------"
echo "Backing File   : $IMG_FILE"
echo "Last End Sector: $LAST_SECTOR"
echo "Sector Size    : $SECTOR_SIZE bytes"
echo "Target Size    : $REQUIRED_BYTES bytes ($((REQUIRED_BYTES / 1024 / 1024)) MiB)"
echo "--------------------------------------------------"

# Unmount active partitions on nbd0
echo "[1/3] Unmounting active mounts on $NBD_DEV..."
umount ${NBD_DEV}p* 2>/dev/null || true

# Disconnect NBD device
echo "[2/3] Disconnecting $NBD_DEV..."
qemu-nbd --disconnect "$NBD_DEV"

# Truncate image file
echo "[3/3] Truncating $IMG_FILE to $REQUIRED_BYTES bytes..."
truncate -s "$REQUIRED_BYTES" "$IMG_FILE"

echo "Done. $IMG_FILE has been successfully resized."

Usage Instructions

  1. Save the script as shrink_nbd.sh.
  2. Make it executable:
    chmod +x shrink_nbd.sh
    
  3. Run it with root privileges while /dev/nbd0 is active:
    sudo ./shrink_nbd.sh
    

Want me to add a safety prompt that asks for confirmation before truncating?