| Current File : //bin/usbcopy |
#!/usr/bin/bash
#
# Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
#
# Make sure we get the right versions of the commands
PATH=/usr/sbin:/usr/bin
# Ensure that commands whose output we're parsing don't localize numeric
# formats on us
export LC_ALL=C
# Platform (either i386 or sparc)
typeset isa=$(uname -p)
function cleanup {
{
# unmounting, and uninstalling the lofi'ed devices and
# cleanup temporary files.
for devs in $(mount -p | grep $usbdev | awk '{print $1}'); do
umount -f $devs
done
lofiadm "${img}" && \
lofiadm -d "${img}"
if [[ -d "${usbmntpt}" ]]; then
rmdir "${usbmntpt}"
fi
rm -f $fdi
} > /dev/null 2>&1
}
function error_handler {
print -u2 "\nError:\n"
print -u2 -r -- "${progname}: $*"
cleanup
exit 1
}
function setup_grub2 {
# There are pre-built MBR stage 1 and UEFI system images on the USB image.
# These need to be used instead of trying to construct it ourselves in order
# to avoid grub2 version mismatches.
mbrimg="$usbmntpt/boot/mbr.img"
uefiimg="$usbmntpt/boot/uefi.img"
# cat the (MBR) usb.img and (ESP) uefi.img to the USB device.
cat $mbrimg $uefiimg > $dev || \
error_handler "Failed to write ${mbrimg} and ${uefiimg} to ${dev}"
# Read the geometry of the physical device
while read pcyl ncyl acyl bcyl nhead nsect secsiz; do
devblksize=$secsiz
devblocks=$(($ncyl * $nhead * $nsect))
done < <(fdisk -G $dev | grep -v "*")
# Size the embedded MBR image
mbrnbytes=$(ls -lL $mbrimg | awk '{print $5}')
mbrnblocks=$(($mbrnbytes / $devblksize))
# Size the uefi.img
uefinbytes=$(ls -lL $uefiimg | awk '{print $5}')
uefinblocks=$(($uefinbytes / $devblksize))
# fdisk table should end up looking like this.
# The first line/partition for the ESP is already embedded in the mbr image
# that we just cat'd to the disk. So we need to add the solaris partition.
# id active bhead bsect bcyl ehead esect ecyl rsect numsect
# 239 0 0 0 0 0 0 0 mbrnblocks(a) uefinblocks(b)
# 191 0 0 0 0 0 0 0 (a)+(b):(c) diskblocks-(c)
solstart=$(($mbrnblocks + $uefinblocks))
solnblocks=$(($devblocks - $solstart))
addsolpart="191:128:0:0:0:0:0:0:$solstart:$solnblocks"
fdisk -A $addsolpart $dev
# fdisk is stupid and overwrites the MBR boot code region with mboot so we
# have to rewrite the first 440 bytes of the MBR image to restore what it
# so helpfully overwrote.
dd if=$mbrimg bs=1 count=440 oseek=0 of=$bdev 2>/dev/null|| \
error_handler "Failed to restore MBR stage1 image from ${usbimg}"
}
function setup_legacy_grub {
# Install grub stages to usb. The USB image is already lofi mounted so no need
# to mount the USB device.
echo Installing grub to USB device $s0cdev
installgrub -mf $usbmntpt/boot/grub/stage1 $usbmntpt/boot/grub/stage2 $s0cdev > /dev/null
}
#
# Detect image type and validate it can be deployed on current platform.
# Legacy (non dd-able) x86 image can be deployed on x86 machine only,
# deployment of dd-able images is platform independent (e.g. Sparc dd-able
# image can be deployed on x86 machine).
#
# If image type is unknown or it can't be deployed on current platform,
# inform user and abort.
#
# Return 0 for dd-able image, 1 for legacy (non dd-able) x86 image.
#
function is_image_ddable {
typeset img="$1"
#
# First probe for dd-able image. The check is platform independent.
# x86 dd-able image begins with MBR ending with magic word '55 aa'.
# Sparc dd-able image begins with Sparc label identified by magic
# word 'da be'.
#
mbrmagic=$(od -j 510 -N 2 -t x1 "$img" | head -1 | cut -d ' ' -f 2,3)
lblmagic=$(od -j 508 -N 2 -t x1 "$img" | head -1 | cut -d ' ' -f 2,3)
if [[ "$mbrmagic" == "55 aa" ]] ; then
print -u1 "Image type: dd-able x86"
return 0
fi
if [[ "$lblmagic" == "da be" ]] ; then
print -u1 "Image type: dd-able Sparc"
return 0
fi
#
# Legacy (non dd-able) x86 image is image of ufs root filesystem
# and can be deployed on x86 only.
#
imgtype=$(fstyp "$img" 2>/dev/null)
if [[ "$imgtype" == "ufs" ]] && [[ "$isa" == "i386" ]] ; then
print -u1 "Image type: legacy x86"
return 1
fi
print -u2 "Error: Image type not recognized."
exit 1
}
if [ $(id -u) != "0" ] ; then
echo "Must be root to run."
exit 1
fi
if [ $# != 1 ]; then
echo "Usage: $0 <USB image path>"
exit 1
fi
img=$1
if [ ! -r $img ] && [ ! -c $img ]; then
echo "Error: $img does not exist."
exit 1
fi
# Make sure supplied image is compatible with running platform.
is_image_ddable "$img" && isddable=true || isddable=false
# Image size (in MB)
ibytes=$(ls -lL $img | awk '{print $5}')
isz=$((ibytes >> 20))
#nawk script to output the details of plugged in USB drives
i=0
while read p l s m d; do
phys[$i]=$p
log[$i]=$l
size[$i]=$s
mult[$i]=$m
desc[$i]=$d
((i++))
done < <(rmformat 2>/dev/null | nawk 'BEGIN {
FS = ":";
bustype = "USB";
devtype = "Removable";
}
/Logical Node/ {
lnode = 1;
node = $2;
}
/Physical Node/ {
physdev = $2
}
/Connected Device/ {
devname = $2
}
/Device Type/ {
type = $2
}
/Bus/ {
bus = $2;
}
/Size/ && lnode && bus ~ bustype && type ~ devtype {
size = $2;
printf("%s\t%s\t%s\t%s\n", physdev, node, size, devname);
lnode = 0;
}')
echo Found the following USB devices:
j=0
while [ $j -lt $i ]; do
echo "$j: ${log[$j]} ${size[$j]} ${mult[$j]} ${desc[$j]}"
((j++))
done
while read -p "Enter the number of your choice: " choice; do
if [ -z "${choice}" ]; then
continue
fi
if [ $choice -lt 0 ] || [ $choice -ge $i ]; then
echo "Invalid choice"
continue
fi
break
done
dev=${log[$choice]}
bdev=${dev/rdsk/dsk}
#
# From character device name representing the whole disk (ends with 'p0' on i386
# and with 's2' on sparc), determine:
# - slice 0 character device name
# - character device name representing target which dd(1m) will populate
# with USB image:
# - x86 : s0 (for legacy image) or p0 (for dd-able image)
# - Sparc: s2
#
if [[ "$isa" == "i386" ]]; then
s0cdev=${dev/p0/s0}
if $isddable ; then
ddcdev="$dev"
else
ddcdev="$s0cdev"
fi
p1cdev=${dev/p0/p1}
p1bdev=${p1cdev/rdsk/dsk}
p2cdev=${dev/p0/p2}
p2bdev=${p2cdev/rdsk/dsk}
else
s0cdev=${dev/s2/s0}
ddcdev="$dev"
fi
s0bdev=${s0cdev/rdsk/dsk}
mountdev=${s0bdev/s0}
if [ ! -b $s0bdev ] || [ ! -c $s0cdev ]; then
echo "Missing device nodes for $dev"
exit 1
fi
if [ ! -n "$dev" ]; then
echo INFORMATION: No USB selected/found.. Please plug in and try again
exit 1
fi
sz=${size[$choice]}
multiplier=${mult[$choice]}
case "$multiplier" in
GB) sz=$(( ${sz%%.*} * 1000 + ${sz##*.} * 100 ));;
MB) sz=${sz%%.*};;
*) echo "Unknown capacity indicator: '$multiplier'"; exit 1;;
esac
while true;
do
echo ""
echo WARNING: All data on your USB storage will be lost.
echo Are you sure you want to install to
echo -n ${desc[$choice]}, $sz MB at $dev ?
read -p " (y/n) " yn
case $yn in
[yY]* )
if [ ! -w $dev ]; then
echo "Device $dev is not writable"
echo "Installation aborted"
exit 1
fi
if [ $sz -lt $isz ]; then
echo "Image ($isz MB) is larger than the device ($sz MB)"
echo "Installation aborted"
exit 1
fi
break ;;
[nN]* )
echo "Installation aborted"
exit 0 ;;
* )
echo Invalid choice.. Exiting
exit 0 ;;
esac
done
# Ensure we have things unmounted
for devs in $(mount -p | grep $mountdev | awk '{print $1}');
do
umount -f $devs > /dev/null 2>&1
done
# Drop in config file to ensure HAL ignores the disk, otherwise it will attempt
# to mount it after the partition table is rewritten
fdi=/etc/hal/fdi/preprobe/10osvendor/99-usbcopy.fdi
trap cleanup SIGTERM EXIT
cat >$fdi << EOF
<?xml version="1.0" encoding="UTF-8"?>
<deviceinfo version="0.2">
<device>
<match key="block.device" string="$bdev">
<merge key="info.ignore" type="bool">true</merge>
</match>
</device>
</deviceinfo>
EOF
#
# If deploying on Sparc, make sure that USB stick is labeled with SMI label,
# since it was observed that EFI label (e.g. created by MacOS) prevents
# dd from being able to access the whole device due to the fact that
# slice 2 is regular slice (not the 'backup' one).
# This is not a problem on x86 where USB stick is being accessed via p0
# device node, since p0 always covers the whole available space on the device.
#
if [[ "$isa" == "sparc" ]]; then
disk_name=${s0cdev#/dev/rdsk/}
disk_name=${disk_name%s0}
format -e -L vtoc -d "$disk_name" > /dev/null
(( $? != 0 )) && { echo Failed to label USB stick. ; exit 1; }
fi
# Legacy x86 image needs more work.
if ! $isddable ; then
# lofi mount the USB image file into a temp dir
usbmntpt="$(mktemp -d)"
usbdev="$(lofiadm -a "${img}")" || \
error_handler "Failed to lofiadm ${img}"
mount -F ufs -o ro $usbdev $usbmntpt || \
error_handler "Failed to mount ${usbdev} on ${usbmntpt}"
haslegacygrub=false
if [ -f $usbmntpt/boot/grub/grub.cfg ] ; then
# GRUB2
setup_grub2
zerodev=${p2bdev}
elif [ -f $usbmntpt/boot/grub/menu.lst ] ; then
# Legacy GRUB
# Install fdisk table with Solaris using entire disk, default VTOC
haslegacygrub=true
fdisk -B $dev
zerodev=${p1bdev}
else
error_handler "Failed to find boot loader configuration"
fi
# Remove the PBR from the Solaris partition to ensure no stray data confuses
# some BIOSes
dd if=/dev/zero of=${zerodev} count=1 > /dev/null 2>&1
(( $? != 0 )) && { echo Failed to zero Solaris PBR on USB device. ; exit 1; }
# Now create root partition. We want to find number of cylinders
# in backup partition from label created by fdisk and then generate
# root partition using whole disk minus cylinder 1.
acyls=$(prtvtoc $dev | awk '/accessible/{print $2}')
cyls=$((acyls - 1))
format -e $dev >/dev/null <<EOF
partition
0
root
wm
1
${cyls}c
label
0
y
EOF
fi
# Copy image to USB.
echo "Copying and verifying image to USB device"
SECONDS=0
# For now, copy in 16k chunks, 4MB at a time
# NOTE: To avoid problems, cmb should be >= 1 and bs should evenly
# divide into csz.
bs=$((16 << 10))
cmb=4
csz=$((cmb << 20))
bcnt=$((csz / bs))
icnt=$((isz / cmb))
maxt=3
i=0
retries=0
while ((i <= icnt)); do
tries=0
pcnt="($((i * 100 / icnt))%)"
s=$((i * bcnt))
echo -ne "R $i / $icnt $pcnt \\r"
imgsum=$(dd bs=$bs count=$bcnt conv=sync \
iseek=$s if=$img 2>/dev/null | digest -a md5)
(($? != 0)) && { echo Read from image failed. ; exit 1; }
while ((tries++ < maxt)); do
echo -ne "W $i / $icnt $pcnt \\r"
recs=$(dd bs=$bs count=$bcnt conv=sync iseek=$s oseek=$s \
if=$img of=$ddcdev 2>&1 | awk -F+ '/records out/{print $1}')
(($? != 0)) && { echo Write to device failed. ; exit 1; }
echo -ne "V $i / $icnt $pcnt \\r"
if [[ "$isa" == "i386" ]]; then
devsum=$(dd bs=$bs count=$recs iseek=$s if=$ddcdev 2>/dev/null | digest -a md5)
fi
if [[ "$isa" == "sparc" ]]; then
devsum=$(dd bs=$bs count=$recs conv=sync iseek=$s if=$ddcdev 2>/dev/null | digest -a md5)
fi
(($? != 0)) && { echo Read from device failed. ; exit 1; }
[[ "$imgsum" == "$devsum" ]] && break;
done
((tries > 1)) && ((retries++))
if ((tries > maxt)); then
echo "Verification failed after $maxt attempts on block $i"
exit 1
fi
((i++))
done
speed="$((isz / SECONDS)).$((isz * 10 / SECONDS % 10))MB/s"
echo "Finished $isz MB in $SECONDS seconds ($speed)"
echo "$retries block(s) re-written due to verification failure"
# If it is legacy x86 image with legacy GRUB, install bootloader now.
if ! $isddable && $haslegacygrub; then
setup_legacy_grub
fi
# Cleanup things.
cleanup
echo "Completed copy to USB"
exit 0