#!/bin/sh 
 | 
# 
 | 
# SPDX-License-Identifier: GPL-2.0-only 
 | 
# 
 | 
  
 | 
# 1MB blocksize 
 | 
BLOCKSIZE=1048576 
 | 
  
 | 
usage() { 
 | 
    echo "Usage: $(basename $0) IMAGE DEVICE" 
 | 
} 
 | 
  
 | 
image_details() { 
 | 
    IMG=$1 
 | 
    echo "Image details" 
 | 
    echo "=============" 
 | 
    echo "    image: $(basename $IMG)" 
 | 
    # stat format is different on Mac OS and Linux 
 | 
    if [ "$(uname)" = "Darwin" ]; then 
 | 
        echo "     size: $(stat -L -f '%z bytes' $IMG)" 
 | 
        echo " modified: $(stat -L -f '%Sm' $IMG)" 
 | 
    else 
 | 
        echo "     size: $(stat -L -c '%s bytes' $IMG)" 
 | 
        echo " modified: $(stat -L -c '%y' $IMG)" 
 | 
    fi 
 | 
    echo "     type: $(file -L -b $IMG)" 
 | 
    echo "" 
 | 
} 
 | 
  
 | 
device_details() { 
 | 
    BLOCK_SIZE=512 
 | 
  
 | 
    echo "Device details" 
 | 
    echo "==============" 
 | 
  
 | 
    # Collect disk info using diskutil on Mac OS 
 | 
    if [ "$(uname)" = "Darwin" ]; then 
 | 
        diskutil info $DEVICE | egrep "(Device Node|Media Name|Total Size)" 
 | 
        return 
 | 
    fi 
 | 
  
 | 
    # Default / Linux information collection 
 | 
    ACTUAL_DEVICE=`readlink -f $DEVICE` 
 | 
    DEV=`basename $ACTUAL_DEVICE` 
 | 
    if [ "$ACTUAL_DEVICE" != "$DEVICE" ] ; then 
 | 
        echo "  device: $DEVICE -> $ACTUAL_DEVICE" 
 | 
    else 
 | 
        echo "  device: $DEVICE" 
 | 
    fi 
 | 
    if [ -f "/sys/class/block/$DEV/device/vendor" ]; then 
 | 
        echo "  vendor: $(cat /sys/class/block/$DEV/device/vendor)" 
 | 
    else 
 | 
        echo "  vendor: UNKNOWN" 
 | 
    fi 
 | 
    if [ -f "/sys/class/block/$DEV/device/model" ]; then 
 | 
        echo "   model: $(cat /sys/class/block/$DEV/device/model)" 
 | 
    else 
 | 
        echo "   model: UNKNOWN" 
 | 
    fi 
 | 
    if [ -f "/sys/class/block/$DEV/size" ]; then 
 | 
        echo "    size: $(($(cat /sys/class/block/$DEV/size) * $BLOCK_SIZE)) bytes" 
 | 
    else 
 | 
        echo "    size: UNKNOWN" 
 | 
    fi 
 | 
    echo "" 
 | 
} 
 | 
  
 | 
check_mount_device() { 
 | 
    if cat /proc/self/mounts | awk '{ print $1 }' | grep /dev/ | grep -q -E "^$1$" ; then 
 | 
        return 0 
 | 
    fi 
 | 
    return 1 
 | 
} 
 | 
  
 | 
is_mounted() { 
 | 
    if [ "$(uname)" = "Darwin" ]; then 
 | 
        if df | awk '{ print $1 }' | grep /dev/ | grep -q -E "^$1(s[0-9]+)?$" ; then 
 | 
            return 0 
 | 
        fi 
 | 
    else 
 | 
        if check_mount_device $1 ; then 
 | 
            return 0 
 | 
        fi 
 | 
        DEV=`basename $1` 
 | 
        if [ -d /sys/class/block/$DEV/ ] ; then 
 | 
            PARENT_BLKDEV=`basename $(readlink -f "/sys/class/block/$DEV/..")` 
 | 
            if [ "$PARENT_BLKDEV" != "block" ] ; then 
 | 
                if check_mount_device $PARENT_BLKDEV ; then 
 | 
                    return 0 
 | 
                fi 
 | 
            fi 
 | 
            for CHILD_BLKDEV in `find /sys/class/block/$DEV/ -mindepth  1 -maxdepth 1 -name "$DEV*" -type d` 
 | 
            do 
 | 
                if check_mount_device /dev/`basename $CHILD_BLKDEV` ; then 
 | 
                    return 0 
 | 
                fi 
 | 
            done 
 | 
        fi 
 | 
    fi 
 | 
    return 1 
 | 
} 
 | 
  
 | 
is_inuse() { 
 | 
    HOLDERS_DIR="/sys/class/block/`basename $1`/holders" 
 | 
    if [ -d $HOLDERS_DIR ] && [ `ls -A $HOLDERS_DIR` ] ; then 
 | 
        return 0 
 | 
    fi 
 | 
    return 1 
 | 
} 
 | 
  
 | 
if [ $# -ne 2 ]; then 
 | 
    usage 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
IMAGE=$1 
 | 
DEVICE=$2 
 | 
  
 | 
if [ ! -e "$IMAGE" ]; then 
 | 
    echo "ERROR: Image $IMAGE does not exist" 
 | 
    usage 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
if [ ! -e "$DEVICE" ]; then 
 | 
    echo "ERROR: Device $DEVICE does not exist" 
 | 
    usage 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
if [ "$(uname)" = "Darwin" ]; then 
 | 
    # readlink doesn't support -f on MacOS, just assume it isn't a symlink 
 | 
    ACTUAL_DEVICE=$DEVICE 
 | 
else 
 | 
    ACTUAL_DEVICE=`readlink -f $DEVICE` 
 | 
fi 
 | 
if is_mounted $ACTUAL_DEVICE ; then 
 | 
    echo "ERROR: Device $DEVICE is currently mounted - check if this is the right device, and unmount it first if so" 
 | 
    device_details 
 | 
    exit 1 
 | 
fi 
 | 
if is_inuse $ACTUAL_DEVICE ; then 
 | 
    echo "ERROR: Device $DEVICE is currently in use (possibly part of LVM) - check if this is the right device!" 
 | 
    device_details 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
if [ ! -w "$DEVICE" ]; then 
 | 
    echo "ERROR: Device $DEVICE is not writable - possibly use sudo?" 
 | 
    usage 
 | 
    exit 1 
 | 
fi 
 | 
  
 | 
image_details $IMAGE 
 | 
device_details 
 | 
  
 | 
printf "Write $IMAGE to $DEVICE [y/N]? " 
 | 
read RESPONSE 
 | 
if [ "$RESPONSE" != "y" ]; then 
 | 
    echo "Write aborted" 
 | 
    exit 0 
 | 
fi 
 | 
  
 | 
echo "Writing image..." 
 | 
if which pv >/dev/null 2>&1; then 
 | 
    pv "$IMAGE" | dd of="$DEVICE" bs="$BLOCKSIZE" 
 | 
else 
 | 
    dd if="$IMAGE" of="$DEVICE" bs="$BLOCKSIZE" 
 | 
fi 
 | 
sync 
 |