#!/bin/sh #- # Copyright (c) 2010 iXsystems, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD: src/usr.sbin/pc-sysinstall/backend/functions-disk.sh,v 1.8 2010/11/10 05:32:36 imp Exp $ # Functions related to disk operations using gpart # See if device is a full disk or partition/slice is_disk() { for _dsk in `sysctl -n kern.disks` do if [ "$_dsk" = "${1}" ] ; then return 0 ; fi done return 1 } # Get a MBR partitions sysid get_partition_sysid_mbr() { INPART="0" DISK="$1" PARTNUM=`echo ${2} | sed "s|${DISK}s||g"` fdisk ${DISK} >${TMPDIR}/disk-${DISK} 2>/dev/null while read i do echo "$i" | grep "The data for partition" >/dev/null 2>/dev/null if [ "$?" = "0" ] ; then INPART="0" PART="`echo ${i} | cut -d ' ' -f 5`" if [ "$PART" = "$PARTNUM" ] ; then INPART="1" fi fi # In the partition section if [ "$INPART" = "1" ] ; then echo "$i" | grep "^sysid" >/dev/null 2>/dev/null if [ "$?" = "0" ] ; then SYSID="`echo ${i} | tr -s '\t' ' ' | cut -d ' ' -f 2`" break fi fi done < ${TMPDIR}/disk-${DISK} rm ${TMPDIR}/disk-${DISK} VAL="${SYSID}" export VAL }; # Get the partitions MBR label get_partition_label_mbr() { INPART="0" DISK="$1" PARTNUM=`echo ${2} | sed "s|${DISK}s||g"` fdisk ${DISK} >${TMPDIR}/disk-${DISK} 2>/dev/null while read i do echo "$i" | grep "The data for partition" >/dev/null 2>/dev/null if [ "$?" = "0" ] ; then INPART="0" PART="`echo ${i} | cut -d ' ' -f 5`" if [ "$PART" = "$PARTNUM" ] ; then INPART="1" fi fi # In the partition section if [ "$INPART" = "1" ] ; then echo "$i" | grep "^sysid" >/dev/null 2>/dev/null if [ "$?" = "0" ] ; then LABEL="`echo ${i} | tr -s '\t' ' ' | cut -d ',' -f 2-10`" break fi fi done < ${TMPDIR}/disk-${DISK} rm ${TMPDIR}/disk-${DISK} VAL="${LABEL}" export VAL }; # Get a GPT partitions label get_partition_label_gpt() { DISK="${1}" PARTNUM=`echo ${2} | sed "s|${DISK}p||g"` gpart show ${DISK} >${TMPDIR}/disk-${DISK} while read i do SLICE="`echo ${i} | grep -v ${DISK} | grep -v ' free ' |tr -s '\t' ' ' | cut -d ' ' -f 3`" if [ "${SLICE}" = "${PARTNUM}" ] ; then LABEL="`echo ${i} | grep -v ${DISK} | grep -v ' free ' |tr -s '\t' ' ' | cut -d ' ' -f 4`" break fi done <${TMPDIR}/disk-${DISK} rm ${TMPDIR}/disk-${DISK} VAL="${LABEL}" export VAL }; # Get a partitions startblock get_partition_startblock() { DISK="${1}" PARTNUM=`echo ${2} | sed "s|${DISK}p||g" | sed "s|${DISK}s||g"` gpart show ${DISK} >${TMPDIR}/disk-${DISK} while read i do SLICE="`echo ${i} | grep -v ${DISK} | grep -v ' free ' |tr -s '\t' ' ' | cut -d ' ' -f 3`" if [ "$SLICE" = "${PARTNUM}" ] ; then SB="`echo ${i} | grep -v ${DISK} | grep -v ' free ' |tr -s '\t' ' ' | cut -d ' ' -f 1`" break fi done <${TMPDIR}/disk-${DISK} rm ${TMPDIR}/disk-${DISK} VAL="${SB}" export VAL }; # Get a partitions blocksize get_partition_blocksize() { DISK="${1}" PARTNUM=`echo ${2} | sed "s|${DISK}p||g" | sed "s|${DISK}s||g"` gpart show ${DISK} >${TMPDIR}/disk-${DISK} while read i do SLICE="`echo ${i} | grep -v ${DISK} | grep -v ' free ' |tr -s '\t' ' ' | cut -d ' ' -f 3`" if [ "$SLICE" = "${PARTNUM}" ] ; then BS="`echo ${i} | grep -v ${DISK} | grep -v ' free ' |tr -s '\t' ' ' | cut -d ' ' -f 2`" break fi done <${TMPDIR}/disk-${DISK} rm ${TMPDIR}/disk-${DISK} VAL="${BS}" export VAL }; # Function which returns the partitions on a target disk get_disk_partitions() { gpart show ${1} >/dev/null 2>/dev/null if [ "$?" != "0" ] ; then VAL="" ; export VAL return fi gpart show ${1} | grep "MBR" >/dev/null 2>/dev/null if [ "$?" = "0" ] ; then type="MBR" else type="GPT" fi SLICES="`gpart show ${1} | grep -v ${1} | grep -v ' free ' |tr -s '\t' ' ' | cut -d ' ' -f 4 | sed '/^$/d'`" for i in ${SLICES} do case $type in MBR) name="${1}s${i}" ;; GPT) name="${1}p${i}";; *) name="${1}s${i}";; esac if [ -z "${RSLICES}" ] then RSLICES="${name}" else RSLICES="${RSLICES} ${name}" fi done VAL="${RSLICES}" ; export VAL }; # Function which returns a target disks cylinders get_disk_cyl() { cyl=`diskinfo -v ${1} | grep "# Cylinders" | tr -s ' ' | cut -f 2` VAL="${cyl}" ; export VAL }; # Function which returns a target disks sectors get_disk_sectors() { sec=`diskinfo -v ${1} | grep "# Sectors" | tr -s ' ' | cut -f 2` VAL="${sec}" ; export VAL }; # Function which returns a target disks heads get_disk_heads() { head=`diskinfo -v ${1} | grep "# Heads" | tr -s ' ' | cut -f 2` VAL="${head}" ; export VAL }; # Function which returns a target disks mediasize in sectors get_disk_mediasize() { mediasize=`diskinfo -v ${1} | grep "# mediasize in sectors" | tr -s ' ' | cut -f 2` VAL="${mediasize}" ; export VAL }; # Function which exports all zpools, making them safe to overwrite potentially export_all_zpools() { # Export any zpools for i in `zpool list -H -o name` do zpool export -f ${i} done }; # Function to delete all gparts before starting an install delete_all_gpart() { echo_log "Deleting all gparts" DISK="$1" # Check for any swaps to stop for i in `gpart show ${DISK} 2>/dev/null | grep 'freebsd-swap' | tr -s ' ' | cut -d ' ' -f 4` do swapoff /dev/${DISK}s${i}b >/dev/null 2>/dev/null swapoff /dev/${DISK}p${i} >/dev/null 2>/dev/null done # Delete the gparts now for i in `gpart show ${DISK} 2>/dev/null | tr -s ' ' | cut -d ' ' -f 4` do if [ "${i}" != "${DISK}" -a "${i}" != "-" ] ; then rc_nohalt "gpart delete -i ${i} ${DISK}" fi done rc_nohalt "dd if=/dev/zero of=/dev/${DISK} count=3000" }; # Function to export all zpools before starting an install stop_all_zfs() { # Export all zpools again, so that we can overwrite these partitions potentially for i in `zpool list -H -o name` do zpool export -f ${i} done }; # Function which stops all gmirrors before doing any disk manipulation stop_all_gmirror() { DISK="${1}" GPROV="`gmirror list | grep ". Name: mirror/" | cut -d '/' -f 2`" for gprov in $GPROV do gmirror list | grep "Name: ${DISK}" >/dev/null 2>/dev/null if [ "$?" = "0" ] then echo_log "Stopping mirror $gprov $DISK" rc_nohalt "gmirror remove $gprov $DISK" rc_nohalt "dd if=/dev/zero of=/dev/${DISK} count=4096" fi done }; # Make sure we don't have any geli providers active on this disk stop_all_geli() { _geld="${1}" cd /dev for i in `ls ${_geld}*` do echo $i | grep '.eli' >/dev/null 2>/dev/null if [ "$?" = "0" ] then echo_log "Detaching GELI on ${i}" rc_halt "geli detach ${i}" fi done }; # Function which reads in the disk slice config, and performs it setup_disk_slice() { # Cleanup any slice / mirror dirs rm -rf ${SLICECFGDIR} >/dev/null 2>/dev/null mkdir ${SLICECFGDIR} rm -rf ${MIRRORCFGDIR} >/dev/null 2>/dev/null mkdir ${MIRRORCFGDIR} # Start with disk0 disknum="0" # Make sure all zpools are exported export_all_zpools # We are ready to start setting up the disks, lets read the config and do the actions while read line do echo $line | grep "^disk${disknum}=" >/dev/null 2>/dev/null if [ "$?" = "0" ] then # Found a disk= entry, lets get the disk we are working on get_value_from_string "${line}" strip_white_space "$VAL" DISK="$VAL" # Before we go further, lets confirm this disk really exists if [ ! -e "/dev/${DISK}" ] then exit_err "ERROR: The disk ${DISK} does not exist!" fi # Make sure we stop any gmirrors on this disk stop_all_gmirror ${DISK} # Make sure we stop any geli stuff on this disk stop_all_geli ${DISK} # Make sure we don't have any zpools loaded stop_all_zfs fi # Lets look if this device will be mirrored on another disk echo $line | grep "^mirror=" >/dev/null 2>/dev/null if [ "$?" = "0" ] then # Found a disk= entry, lets get the disk we are working on get_value_from_string "${line}" strip_white_space "$VAL" MIRRORDISK="$VAL" # Before we go further, lets confirm this disk really exists if [ ! -e "/dev/${MIRRORDISK}" ] then exit_err "ERROR: The mirror disk ${MIRRORDISK} does not exist!" fi fi # Lets see if we have been given a mirror balance choice echo $line | grep "^mirrorbal=" >/dev/null 2>/dev/null if [ "$?" = "0" ] then # Found a disk= entry, lets get the disk we are working on get_value_from_string "${line}" strip_white_space "$VAL" MIRRORBAL="$VAL" fi echo $line | grep "^partition=" >/dev/null 2>/dev/null if [ "$?" = "0" ] then # Found a partition= entry, lets read / set it get_value_from_string "${line}" strip_white_space "$VAL" PTYPE=`echo $VAL|tr A-Z a-z` # We are using free space, figure out the slice number if [ "${PTYPE}" = "free" ] then # Lets figure out what number this slice will be LASTSLICE="`gpart show ${DISK} \ | grep -v ${DISK} \ | grep -v ' free' \ | tr -s '\t' ' ' \ | cut -d ' ' -f 4 \ | sed '/^$/d' \ | tail -n 1`" if [ -z "${LASTSLICE}" ] then LASTSLICE="1" else LASTSLICE="`expr $LASTSLICE + 1`" fi if [ $LASTSLICE -gt 4 ] then exit_err "ERROR: BSD only supports primary partitions, and there are none availble on $DISK" fi fi fi # Check if we have an image file defined echo $line | grep "^image=" >/dev/null 2>/dev/null if [ "$?" = "0" ] ; then # Found an image= entry, lets read / set it get_value_from_string "${line}" strip_white_space "$VAL" IMAGE="$VAL" if [ ! -f "$IMAGE" ] ; then exit_err "$IMAGE file does not exist" fi fi # Check if we have a partscheme specified echo $line | grep "^partscheme=" >/dev/null 2>/dev/null if [ "$?" = "0" ] ; then # Found a partscheme= entry, lets read / set it get_value_from_string "${line}" strip_white_space "$VAL" PSCHEME="$VAL" if [ "$PSCHEME" != "GPT" -a "$PSCHEME" != "MBR" ] ; then exit_err "Unknown partition scheme: $PSCHEME" fi fi echo $line | grep "^bootManager=" >/dev/null 2>/dev/null if [ "$?" = "0" ] then # Found a bootManager= entry, lets read /set it get_value_from_string "${line}" strip_white_space "$VAL" BMANAGER="$VAL" fi echo $line | grep "^commitDiskPart" >/dev/null 2>/dev/null if [ "$?" = "0" ] then # Found our flag to commit this disk setup / lets do sanity check and do it if [ ! -z "${DISK}" -a ! -z "${PTYPE}" ] then case ${PTYPE} in all) if [ "$PSCHEME" = "MBR" -o -z "$PSCHEME" ] ; then PSCHEME="MBR" tmpSLICE="${DISK}s1" else tmpSLICE="${DISK}p1" fi run_gpart_full "${DISK}" "${BMANAGER}" "${PSCHEME}" ;; s1|s2|s3|s4) tmpSLICE="${DISK}${PTYPE}" # Get the number of the slice we are working on s="`echo ${PTYPE} | awk '{print substr($0,length,1)}'`" run_gpart_slice "${DISK}" "${BMANAGER}" "${s}" ;; free) tmpSLICE="${DISK}s${LASTSLICE}" run_gpart_free "${DISK}" "${LASTSLICE}" "${BMANAGER}" ;; image) if [ -z "${IMAGE}" ] then exit_err "ERROR: partition type image specified with no image!" fi ;; *) exit_err "ERROR: Unknown PTYPE: $PTYPE" ;; esac if [ -n "${IMAGE}" ] then local DEST if [ -n "${tmpSLICE}" ] then DEST="${tmpSLICE}" else DEST="${DISK}" fi write_image "${IMAGE}" "${DEST}" check_disk_layout "${DEST}" fi # Now save which disk this is, so we can parse it later during slice partition setup if [ -z "${IMAGE}" ] then echo "disk${disknum}" >${SLICECFGDIR}/$tmpSLICE fi # Save any mirror config if [ ! -z "$MIRRORDISK" ] then # Default to round-robin if the user didn't specify if [ -z "$MIRRORBAL" ] then MIRRORBAL="round-robin" fi echo "$MIRRORDISK:$MIRRORBAL" >${MIRRORCFGDIR}/$DISK fi # Increment our disk counter to look for next disk and unset unset BMANAGER PTYPE DISK MIRRORDISK MIRRORBAL PSCHEME IMAGE disknum="`expr $disknum + 1`" else exit_err "ERROR: commitDiskPart was called without procceding disk= and partition= entries!!!" fi fi done <${CFGF} }; # Stop all gjournals on disk / slice stop_gjournal() { _gdsk="$1" # Check if we need to shutdown any journals on this drive ls /dev/${_gdsk}*.journal >/dev/null 2>/dev/null if [ "$?" = "0" ] then cd /dev for i in `ls ${_gdsk}*.journal` do rawjournal="`echo ${i} | cut -d '.' -f 1`" gjournal stop -f ${rawjournal} >>${LOGOUT} 2>>${LOGOUT} gjournal clear ${rawjournal} >>${LOGOUT} 2>>${LOGOUT} done fi } ; # Function which runs gpart and creates a single large GPT partition scheme init_gpt_full_disk() { _intDISK=$1 # Set our sysctl so we can overwrite any geom using drives sysctl kern.geom.debugflags=16 >>${LOGOUT} 2>>${LOGOUT} # Stop any journaling stop_gjournal "${_intDISK}" # Remove any existing partitions delete_all_gpart "${_intDISK}" #Erase any existing bootloader echo_log "Cleaning up ${_intDISK}" rc_halt "dd if=/dev/zero of=/dev/${_intDISK} count=2048" sleep 2 echo_log "Running gpart on ${_intDISK}" rc_halt "gpart create -s GPT ${_intDISK}" rc_halt "gpart add -b 34 -s 128 -t freebsd-boot ${_intDISK}" echo_log "Stamping boot sector on ${_intDISK}" rc_halt "gpart bootcode -b /boot/pmbr ${_intDISK}" } # Function which runs gpart and creates a single large MBR partition scheme init_mbr_full_disk() { _intDISK=$1 _intBOOT=$2 startblock="63" # Set our sysctl so we can overwrite any geom using drives sysctl kern.geom.debugflags=16 >>${LOGOUT} 2>>${LOGOUT} # Stop any journaling stop_gjournal "${_intDISK}" # Remove any existing partitions delete_all_gpart "${_intDISK}" #Erase any existing bootloader echo_log "Cleaning up ${_intDISK}" rc_halt "dd if=/dev/zero of=/dev/${_intDISK} count=2048" sleep 2 echo_log "Running gpart on ${_intDISK}" rc_halt "gpart create -s mbr ${_intDISK}" # Lets figure out disk size in blocks # Get the cyl of this disk get_disk_cyl "${_intDISK}" cyl="${VAL}" # Get the heads of this disk get_disk_heads "${_intDISK}" head="${VAL}" # Get the tracks/sectors of this disk get_disk_sectors "${_intDISK}" sec="${VAL}" # Multiply them all together to get our total blocks totalblocks="`expr ${cyl} \* ${head}`" totalblocks="`expr ${totalblocks} \* ${sec}`" if [ -z "${totalblocks}" ] then totalblocks=`gpart show "${_intDISK}"|tail -2|head -1|awk '{ print $2 }'` fi # Now set the ending block to the total disk block size sizeblock="`expr ${totalblocks} - ${startblock}`" # Install new partition setup echo_log "Running gpart add on ${_intDISK}" rc_halt "gpart add -b ${startblock} -s ${sizeblock} -t freebsd -i 1 ${_intDISK}" sleep 2 echo_log "Cleaning up ${_intDISK}s1" rc_halt "dd if=/dev/zero of=/dev/${_intDISK}s1 count=1024" if [ "$_intBOOT" = "bsd" ] ; then echo_log "Stamping boot0 on ${_intDISK}" rc_halt "gpart bootcode -b /boot/boot0 ${_intDISK}" else echo_log "Stamping boot1 on ${_intDISK}" rc_halt "gpart bootcode -b /boot/boot1 ${_intDISK}" fi } # Function which runs gpart and creates a single large slice run_gpart_full() { DISK=$1 BOOT=$2 SCHEME=$3 if [ "$SCHEME" = "MBR" ] ; then init_mbr_full_disk "$DISK" "$BOOT" slice="${DISK}-1-mbr" else init_gpt_full_disk "$DISK" slice="${DISK}-1-gpt" fi # Lets save our slice, so we know what to look for in the config file later on if [ -z "$WORKINGSLICES" ] then WORKINGSLICES="${slice}" export WORKINGSLICES else WORKINGSLICES="${WORKINGSLICES} ${slice}" export WORKINGSLICES fi }; # Function which runs gpart on a specified s1-4 slice run_gpart_slice() { DISK=$1 if [ ! -z "$2" ] then BMANAGER="$2" fi # Set the slice we will use later slice="${1}s${3}" # Set our sysctl so we can overwrite any geom using drives sysctl kern.geom.debugflags=16 >>${LOGOUT} 2>>${LOGOUT} # Get the number of the slice we are working on slicenum="$3" # Stop any journaling stop_gjournal "${slice}" # Make sure we have disabled swap on this drive if [ -e "${slice}b" ] then swapoff ${slice}b >/dev/null 2>/dev/null swapoff ${slice}b.eli >/dev/null 2>/dev/null fi # Modify partition type echo_log "Running gpart modify on ${DISK}" rc_halt "gpart modify -t freebsd -i ${slicenum} ${DISK}" sleep 2 # Clean up old partition echo_log "Cleaning up $slice" rc_halt "dd if=/dev/zero of=/dev/${DISK}s${slicenum} count=1024" sleep 1 if [ "${BMANAGER}" = "bsd" ] then echo_log "Stamping boot sector on ${DISK}" rc_halt "gpart bootcode -b /boot/boot0 ${DISK}" fi # Set the slice to the format we'll be using for gpart later slice="${1}-${3}-mbr" # Lets save our slice, so we know what to look for in the config file later on if [ -z "$WORKINGSLICES" ] then WORKINGSLICES="${slice}" export WORKINGSLICES else WORKINGSLICES="${WORKINGSLICES} ${slice}" export WORKINGSLICES fi }; # Function which runs gpart and creates a new slice from free disk space run_gpart_free() { DISK=$1 SLICENUM=$2 if [ ! -z "$3" ] then BMANAGER="$3" fi # Set our sysctl so we can overwrite any geom using drives sysctl kern.geom.debugflags=16 >>${LOGOUT} 2>>${LOGOUT} slice="${DISK}s${SLICENUM}" slicenum="${SLICENUM}" # Working on the first slice, make sure we have MBR setup gpart show ${DISK} >/dev/null 2>/dev/null if [ "$?" != "0" -a "$SLICENUM" = "1" ] ; then echo_log "Initializing disk, no existing MBR setup" rc_halt "gpart create -s mbr ${DISK}" fi # Lets get the starting block first if [ "${slicenum}" = "1" ] then startblock="63" else # Lets figure out where the prior slice ends checkslice="`expr ${slicenum} - 1`" # Get starting block of this slice sblk=`gpart show ${DISK} | grep -v ${DISK} | tr -s '\t' ' ' | sed '/^$/d' | grep " ${checkslice} " | cut -d ' ' -f 2` blksize=`gpart show ${DISK} | grep -v ${DISK} | tr -s '\t' ' ' | sed '/^$/d' | grep " ${checkslice} " | cut -d ' ' -f 3` startblock="`expr ${sblk} + ${blksize}`" fi # No slice after the new slice, lets figure out the free space remaining and use it # Get the cyl of this disk get_disk_cyl "${DISK}" cyl="${VAL}" # Get the heads of this disk get_disk_heads "${DISK}" head="${VAL}" # Get the tracks/sectors of this disk get_disk_sectors "${DISK}" sec="${VAL}" # Multiply them all together to get our total blocks totalblocks="`expr ${cyl} \* ${head}`" totalblocks="`expr ${totalblocks} \* ${sec}`" # Now set the ending block to the total disk block size sizeblock="`expr ${totalblocks} - ${startblock}`" # Install new partition setup echo_log "Running gpart on ${DISK}" rc_halt "gpart add -b ${startblock} -s ${sizeblock} -t freebsd -i ${slicenum} ${DISK}" sleep 2 echo_log "Cleaning up $slice" rc_halt "dd if=/dev/zero of=/dev/${slice} count=1024" sleep 1 if [ "${BMANAGER}" = "bsd" ] then echo_log "Stamping boot sector on ${DISK}" rc_halt "gpart bootcode -b /boot/boot0 ${DISK}" fi slice="${DISK}-${SLICENUM}-mbr" # Lets save our slice, so we know what to look for in the config file later on if [ -z "$WORKINGSLICES" ] then WORKINGSLICES="${slice}" export WORKINGSLICES else WORKINGSLICES="${WORKINGSLICES} ${slice}" export WORKINGSLICES fi };