I have a raw img file. I resized the partition but now it doesn't fill the disk. How do I shrink the disk. (/dev/nbd0)


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

Those warnings point to two specific technical details when working with a resized raw image on NBD:

  1. qemu-nbd Warning: Probing raw images defaults to read-only restrictions on block 0 for safety. Adding -f raw explicitly removes this warning and enables write access.
  2. GParted GPT Warning: When you shrink a partition on a GPT disk, the backup GPT header still sits at the very end of the original disk size. Because the backing file is truncated or smaller, GParted cannot find the backup header at its expected sector location.

Update the shrink script to include the mandatory 33-sector secondary GPT header padding when calculating required bytes.


Gemini

Here is the updated script. It automatically accounts for the 33-sector backup GPT header requirement, formats qemu-nbd calls explicitly with -f raw to eliminate write restriction warnings, and safely parses the backing .img file input.

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

NBD_DEV="/dev/nbd0"
GPT_BACKUP_SECTORS=33

# 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

# Attempt automatic detection of the backing file
IMG_FILE=""
if command -v qemu-nbd &>/dev/null; then
    IMG_FILE=$(qemu-nbd --list-connections 2>/dev/null | awk -v dev="$NBD_DEV" '$0 ~ dev {print $NF}' || true)
fi

# Fallback prompt if auto-detection doesn't return a valid file
if [[ -z "$IMG_FILE" || ! -f "$IMG_FILE" ]]; then
    read -rp "Enter full path to backing .img file: " RAW_INPUT
    # Extract only the file path if extra text or device names were pasted
    IMG_FILE=$(echo "$RAW_INPUT" | awk '{print $NF}')
fi

if [[ ! -f "$IMG_FILE" ]]; then
    echo "Error: Backing image file '$IMG_FILE' does not exist." >&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 sectors including GPT backup header padding
# Last partition end sector + 1 (0-indexed offset) + 33 sectors for secondary GPT table
REQUIRED_SECTORS=$((LAST_SECTOR + 1 + GPT_BACKUP_SECTORS))
REQUIRED_BYTES=$((REQUIRED_SECTORS * SECTOR_SIZE))

echo "=================================================="
echo " NBD Device          : $NBD_DEV"
echo " Backing File        : $IMG_FILE"
echo " Last Partition End  : Sector $LAST_SECTOR"
echo " GPT Backup Padding  : $GPT_BACKUP_SECTORS sectors"
echo " Total Target Sectors: $REQUIRED_SECTORS sectors"
echo " Target Size         : $REQUIRED_BYTES bytes ($((REQUIRED_BYTES / 1024 / 1024)) MiB)"
echo "=================================================="

# Interactive confirmation prompt
read -rp "Relocate secondary GPT header and truncate $IMG_FILE? (y/N): " CONFIRM
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
    echo "Operation canceled by user."
    exit 0
fi

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

# Relocate secondary GPT header to fit newly calculated boundary
echo "[2/4] Relocating secondary GPT header..."
sgdisk -e "$NBD_DEV" 2>/dev/null || true

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

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

echo "--------------------------------------------------"
echo "Success: $IMG_FILE has been safely truncated with secondary GPT structures intact."

Key Enhancements

  1. Calculates GPT Overhead: Automatically adds 33 sectors (16.5 KiB on 512-byte disks) to LAST_SECTOR + 1 so the secondary partition table is not truncated or overlapped.
  2. Automates sgdisk -e: Executes sgdisk -e "$NBD_DEV" prior to disconnecting to ensure the backup header is cleanly placed right before the new truncation line.
  3. Pasted-Path Sanitization: Uses awk '{print $NF}' on the interactive input string so pasting both device and path (e.g., /dev/nbd0 /media/.../file.img) automatically resolves to the correct path.

Want to inspect the partition table using qemu-nbd with explicit raw formatting?