Skip to content

Instantly share code, notes, and snippets.

@adrolter
Last active May 25, 2017 04:05
Show Gist options
  • Save adrolter/5072827b71e27661263a5cfba651b859 to your computer and use it in GitHub Desktop.
Save adrolter/5072827b71e27661263a5cfba651b859 to your computer and use it in GitHub Desktop.
Mount the partitions of a block device image to a temporary location
#!/usr/bin/sh
# mount-image.sh – Mount the partitions of a block device image ($1) to a temporary location
# Copyright (c) 2017 Adrian Günter
#
# @Authors Adrian Günter <adrian|gntr/me>
# @URL https://gist.github.com/adrianguenter/5072827b71e27661263a5cfba651b859
# @License http://opensource.org/licenses/MIT
# @Shells Bash >=4.4, Dash >=0.5
# @Usage mount-image.sh <image-path>
# @Example
# mountpoint="$(sudo mount-image.sh ~/sda-backup.img)"
# ls "$mountpoint" "$mountpoint"/p1
# sudo "$mountpoint"/remove
#
# $GLOBAL_VAR_NAME / $CONSTANT_NAME
# $local_var_name
# $CommandName
# FunctionName
#
# Put Bash in POSIX mode (ignored for other shells)
POSIXLY_CORRECT=y
# Enable nounset and noclobber
set -uf
# Set a conservative umask
umask 0027
# No path searching
# shellcheck disable=SC2123
PATH=
# Commands
readonly Cat='/usr/bin/cat'
readonly Chmod='/usr/bin/chmod'
readonly Chown='/usr/bin/chown'
readonly Date='/usr/bin/date'
readonly Grep='/usr/bin/grep'
readonly Id='/usr/bin/id'
readonly Ln='/usr/bin/ln'
readonly Logname='/usr/bin/logname'
readonly Losetup='/usr/bin/losetup'
readonly Mkdir='/usr/bin/mkdir'
readonly Mktemp='/usr/bin/mktemp'
readonly Mktempd='/usr/bin/mktemp --directory'
readonly Mount='/usr/bin/mount'
readonly Nano='/usr/bin/nano'
readonly Perl='/usr/bin/perl'
readonly Printf='/usr/bin/printf'
readonly Readlink='/usr/bin/readlink'
# printf to STDERR
Printferr(){ >&2 $Printf "${@}";}
if ! $Perl -e'$ARGV[0]=~/ built-?in$/||exit 1' "$(command -V local)" ; then
Printferr "Shell does not support the 'local' builtin\n"
exit 2
fi
# Global parameters w/ default values (can be set from parent environment)
readonly EDITOR="${EDITOR:-${Nano}}"
readonly TMPDIR="${TMPDIR:-/tmp}"
# Set INTERACTIVE if fd 0 is associated with a terminal device
[ -t 0 ] && readonly INTERACTIVE=INTERACTIVE
[ ${EUID+X} ] || readonly EUID="$($Id -u)"
readonly LOGNAME="$($Logname)"
readonly MOUNT_BASE_PATH="${TMPDIR}/image-mounts"
readonly MOUNT_USER_PATH="${MOUNT_BASE_PATH}/${LOGNAME}"
__main() {
local file_path file_name file_link_path file_mtime_iso8601 \
mountpoint partcount remscript_path
file_path="$($Readlink -f "${1}")"
if ! [ -f "${file_path}" ]; then
Printferr "Image file path '%s' does not exist\n" "${file_path}"
exit 1
fi
file_name="${file_path##*/}"
file_mtime_iso8601="$($Date --iso-8601=ns -ur "${file_path}")"
$Mkdir -p "${MOUNT_USER_PATH}"
$Chmod 0700 "${MOUNT_USER_PATH}"
$Chown "${LOGNAME}" "${MOUNT_USER_PATH}"
$Chmod 0755 "${MOUNT_BASE_PATH}"
# shellcheck disable=SC2016
mountpoint="$($Mktempd --tmpdir="${MOUNT_USER_PATH}" \
"$($Perl -e'print$ARGV[0]=~s/\s+/-/rg' "${file_name}")--${file_mtime_iso8601}--XXXX")"
$Chown "${LOGNAME}" "${mountpoint}"
loopdev="$($Losetup --show -LPrf "${file_path}")"
# shellcheck disable=SC2181
if [ $? -ne 0 ] || [ -z "${loopdev}" ]; then
Printferr 'losetup failed for image %s\n' "${file_path}"
exit 1
fi
partcount=$($Grep -c " ${loopdev##*/}p[0-9]\+\$" /proc/partitions)
local i=1; while [ "${i}" -le "${partcount}" ]; do
$Mkdir "${mountpoint}/p${i}"
$Mount -o ro "${loopdev}p${i}" "${mountpoint}/p${i}"
i=$((i+1)); done
file_link_path="${mountpoint}/image-file.link"
$Ln -s "${file_path}" "${file_link_path}"
remscript_path="${mountpoint}/remove"
$Cat << EOF > "${remscript_path}"
#!/usr/bin/sh
set -e
/usr/bin/umount '${mountpoint}'/p*
/usr/bin/rmdir '${mountpoint}'/p*
/usr/bin/rm -f '${mountpoint}'/*
/usr/bin/rmdir '${mountpoint}'
EOF
$Chmod 0755 "${remscript_path}"
$Printf '%s\n' "${mountpoint}"
}
if ! [ ${1+X} ]; then
Printferr 'Missing image file path (Argument #1)\n'
exit 1
fi
__main "${@}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment