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:
Example: If the last partition ends at sector 20971519:
- Total sectors needed =
20971520 - Size in bytes =
(10 GiB)2 0 9 7 1 5 2 0 × 5 1 2 = 1 0 7 3 7 4 1 8 2 4 0 b y t e s
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:
qemu-nbdWarning: Probing raw images defaults to read-only restrictions on block 0 for safety. Adding-f rawexplicitly removes this warning and enables write access.- 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
- Calculates GPT Overhead: Automatically adds
33sectors ( on 512-byte disks) to1 6 . 5 K i B LAST_SECTOR + 1so the secondary partition table is not truncated or overlapped. - Automates
sgdisk -e: Executessgdisk -e "$NBD_DEV"prior to disconnecting to ensure the backup header is cleanly placed right before the new truncation line. - 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?