3 # Copyright (c) 2010 iXsystems, Inc. All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
8 # 1. Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
14 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 # Functions related to disk operations using bsdlabel
30 # Check if we are are provided a geli password on the nextline of the config
35 get_next_cfg_line "${CFGF}" "${CURLINE}"
36 echo ${VAL} | grep -q "^encpass=" 2>/dev/null
37 if [ $? -eq 0 ] ; then
38 # Found a password, return it
39 get_value_from_string "${VAL}"
47 # On check on the disk-label line if we have any extra vars for this device
53 echo $LINE | cut -d ' ' -f 4 | grep -q '(' 2>/dev/null
54 if [ $? -ne 0 ] ; then return ; fi
56 # See if we are looking for ZFS specific options
57 echo $LINE | grep -q '^ZFS' 2>/dev/null
58 if [ $? -eq 0 ] ; then
60 ZFSVARS="`echo $LINE | cut -d ' ' -f 4-20 |cut -d '(' -f 2- | cut -d ')' -f 1 | xargs`"
62 echo $ZFSVARS | grep -qE "^(disk|file|mirror|raidz(1|2|3)?|spare|log|cache):" 2>/dev/null
63 if [ $? -eq 0 ] ; then
64 ZTYPE=`echo $ZFSVARS | cut -f1 -d:`
65 tmpVars=`echo $ZFSVARS | sed "s|$ZTYPE: ||g" | sed "s|$ZTYPE:||g"`
67 # make sure we have a '/dev' in front of the extra devices
70 echo $i | grep -q '/dev/'
71 if [ $? -ne 0 ] ; then
72 ZFSVARS="$ZFSVARS /dev/${i}"
79 # Return the ZFS options
80 if [ "${ZTYPE}" = "NONE" ] ; then
81 VAR="${ACTIVEDEV} ${ZFSVARS}"
83 VAR="${ZTYPE} ${ACTIVEDEV} ${ZFSVARS}"
89 # See if we are looking for UFS specific newfs options
90 echo $LINE | grep -q '^UFS' 2>/dev/null
91 if [ $? -eq 0 ] ; then
92 FSVARS="`echo $LINE | cut -d '(' -f 2- | cut -d ')' -f 1 | xargs`"
98 # If we got here, set VAR to empty and export
103 # Init each zfs mirror disk with a boot sector so we can failover
104 setup_zfs_mirror_parts()
108 ZTYPE="`echo ${1} | awk '{print $1}'`"
110 # Using mirroring, setup boot partitions on each disk
111 _mirrline="`echo ${1} | sed 's|mirror ||g' | sed 's|raidz1 ||g' | sed 's|raidz2 ||g' | sed 's|raidz3 ||g' | sed 's|raidz ||g'`"
112 for _zvars in $_mirrline
114 echo "Looping through _zvars: $_zvars" >>${LOGOUT}
115 echo "$_zvars" | grep -q "${2}" 2>/dev/null
116 if [ $? -eq 0 ] ; then continue ; fi
117 if [ -z "$_zvars" ] ; then continue ; fi
119 is_disk "$_zvars" >/dev/null 2>/dev/null
120 if [ $? -eq 0 ] ; then
121 echo "Setting up ZFS disk $_zvars" >>${LOGOUT}
122 init_gpt_full_disk "$_zvars" >/dev/null 2>/dev/null
123 rc_halt "gpart add -a 4k -t freebsd-zfs ${_zvars}" >/dev/null 2>/dev/null
124 rc_halt "gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ${_zvars}" >/dev/null 2>/dev/null
125 _nZFS="$_nZFS ${_zvars}p2"
127 _nZFS="$_nZFS ${_zvars}"
130 echo "$ZTYPE $2 `echo $_nZFS | tr -s ' '`"
133 # Function which creates a unique label name for the specified mount
141 if [ "$TYPE" = "ZFS" ] ; then
143 elif [ "$MOUNT" = "/" ] ; then
146 # If doing a swap partition, also rename it
147 if [ "${TYPE}" = "SWAP" ]
151 NAME="`echo $MOUNT | sed 's|/||g' | sed 's| ||g'`"
155 # Loop through and break when we find our first available label
159 glabel status | grep -q "${NAME}${NUM}" 2>/dev/null
167 if [ $NUM -gt $MAXNUM ]
169 exit_err "Cannot allocate additional glabel name for $NAME"
175 export VAL="${NAME}${NUM}"
178 # Function to determine the size we can safely use when 0 is specified
181 # Disk tag to look for
185 get_disk_mediasize_mb "$2"
190 # Check for data on this slice
191 echo $line | grep -q "^${_dTag}-part=" 2>/dev/null
192 if [ $? -ne 0 ] ; then continue ; fi
194 get_value_from_string "${line}"
197 # Get the size of this partition
198 SIZE=`echo $STRING | tr -s '\t' ' ' | cut -d ' ' -f 2`
199 if [ $SIZE -eq 0 ] ; then continue ; fi
200 _aSize=`expr $_aSize - $SIZE`
204 _aSize=`expr $_aSize - 2`
210 # Function to setup partitions using gpart
211 setup_gpart_partitions()
221 # Lets read in the config file now and setup our partitions
222 if [ "${_pType}" = "gpt" ] ; then
224 elif [ "${_pType}" = "apm" ] ; then
229 if [ "${_pType}" = "mbr" ] ; then
230 rc_halt "gpart create -s BSD ${_wSlice}"
236 # Check for data on this slice
237 echo $line | grep -q "^${_dTag}-part=" 2>/dev/null
241 # Found a slice- entry, lets get the slice info
242 get_value_from_string "${line}"
245 # We need to split up the string now, and pick out the variables
246 FS=`echo $STRING | tr -s '\t' ' ' | cut -d ' ' -f 1`
247 SIZE=`echo $STRING | tr -s '\t' ' ' | cut -d ' ' -f 2`
248 MNT=`echo $STRING | tr -s '\t' ' ' | cut -d ' ' -f 3`
250 # Check if we have a .eli extension on this FS
251 echo ${FS} | grep -q ".eli" 2>/dev/null
254 FS="`echo ${FS} | cut -d '.' -f 1`"
256 check_for_enc_pass "${line}"
257 if [ "${VAL}" != "" ] ; then
258 # We have a user supplied password, save it for later
265 # Check if the user tried to setup / as an encrypted partition
266 check_for_mount "${MNT}" "/"
267 if [ $? -eq 0 -a "${ENC}" = "ON" ]
269 export USINGENCROOT="0"
272 # Now check that these values are sane
274 UFS|UFS+S|UFS+J|UFS+SUJ|ZFS|SWAP) ;;
275 *) exit_err "ERROR: Invalid file system specified on $line" ;;
278 # Check that we have a valid size number
279 expr $SIZE + 1 >/dev/null 2>/dev/null
280 if [ $? -ne 0 ]; then
281 exit_err "ERROR: The size specified on $line is invalid"
284 # Check that the mount-point starts with /
285 echo "$MNT" | grep -qe "^/" -e "^none" 2>/dev/null
286 if [ $? -ne 0 ]; then
287 exit_err "ERROR: The mount-point specified on $line is invalid"
292 if [ $USEDAUTOSIZE -eq 1 ] ; then
293 exit_err "ERROR: You can not have two partitions with a size of 0 specified!"
296 gpt|apm) get_autosize "${_dTag}" "$_pDisk" ;;
297 *) get_autosize "${_dTag}" "$_wSlice" ;;
305 # Check if we found a valid root partition
306 check_for_mount "${MNT}" "/"
307 if [ $? -eq 0 ] ; then
309 if [ "${CURPART}" = "2" -a "$_pType" = "gpt" ] ; then
312 if [ "${CURPART}" = "3" -a "$_pType" = "apm" ] ; then
315 if [ "${CURPART}" = "1" -a "$_pType" = "mbr" ] ; then
318 if [ "${CURPART}" = "1" -a "$_pType" = "gptslice" ] ; then
323 check_for_mount "${MNT}" "/boot"
324 if [ $? -eq 0 ] ; then
325 export USINGBOOTPART="0"
326 if [ "${CURPART}" != "2" -a "${_pType}" = "gpt" ] ; then
327 exit_err "/boot partition must be first partition"
329 if [ "${CURPART}" != "3" -a "${_pType}" = "apm" ] ; then
330 exit_err "/boot partition must be first partition"
332 if [ "${CURPART}" != "1" -a "${_pType}" = "mbr" ] ; then
333 exit_err "/boot partition must be first partition"
335 if [ "${CURPART}" != "1" -a "${_pType}" = "gptslice" ] ; then
336 exit_err "/boot partition must be first partition"
339 if [ "${FS}" != "UFS" -a "${FS}" != "UFS+S" -a "${FS}" != "UFS+J" -a "${FS}" != "UFS+SUJ" ] ; then
340 exit_err "/boot partition must be formatted with UFS"
344 # Generate a unique label name for this mount
345 gen_glabel_name "${MNT}" "${FS}"
348 # Get any extra options for this fs / line
349 if [ "${_pType}" = "gpt" ] ; then
350 get_fs_line_xvars "${_pDisk}p${CURPART}" "${STRING}"
351 elif [ "${_pType}" = "apm" ] ; then
352 get_fs_line_xvars "${_pDisk}s${CURPART}" "${STRING}"
354 get_fs_line_xvars "${_wSlice}${PARTLETTER}" "${STRING}"
358 # Check if using zfs mirror
359 echo ${XTRAOPTS} | grep -q -e "mirror" -e "raidz"
360 if [ $? -eq 0 -a "$FS" = "ZFS" ] ; then
361 if [ "${_pType}" = "gpt" -o "${_pType}" = "gptslice" ] ; then
362 XTRAOPTS=$(setup_zfs_mirror_parts "$XTRAOPTS" "${_pDisk}p${CURPART}")
363 elif [ "${_pType}" = "apm" ] ; then
364 XTRAOPTS=$(setup_zfs_mirror_parts "$XTRAOPTS" "${_pDisk}s${CURPART}")
366 XTRAOPTS=$(setup_zfs_mirror_parts "$XTRAOPTS" "${_wSlice}${PARTLETTER}")
370 # Figure out the gpart type to use
372 ZFS) PARTYPE="freebsd-zfs" ;;
373 SWAP) PARTYPE="freebsd-swap" ;;
374 *) PARTYPE="freebsd-ufs" ;;
377 # Create the partition
378 if [ "${_pType}" = "gpt" ] ; then
379 if [ "$CURPART" = "2" ] ; then
380 # If this is GPT, make sure first partition is aligned to 4k
382 rc_halt "gpart add -a 4k ${SOUT} -t ${PARTYPE} ${_pDisk}"
385 rc_halt "gpart add ${SOUT} -t ${PARTYPE} ${_pDisk}"
387 elif [ "${_pType}" = "gptslice" ]; then
389 rc_halt "gpart add ${SOUT} -t ${PARTYPE} ${_wSlice}"
390 elif [ "${_pType}" = "apm" ]; then
392 rc_halt "gpart add ${SOUT} -t ${PARTYPE} ${_pDisk}"
395 rc_halt "gpart add ${SOUT} -t ${PARTYPE} -i ${CURPART} ${_wSlice}"
398 # Check if this is a root / boot partition, and stamp the right loader
399 for TESTMNT in `echo ${MNT} | sed 's|,| |g'`
401 if [ "${TESTMNT}" = "/" -a -z "${BOOTTYPE}" ] ; then
402 BOOTTYPE="${PARTYPE}"
404 if [ "${TESTMNT}" = "/boot" ] ; then
405 BOOTTYPE="${PARTYPE}"
409 # Save this data to our partition config dir
410 if [ "${_pType}" = "gpt" ] ; then
411 _dFile="`echo $_pDisk | sed 's|/|-|g'`"
412 echo "${FS}#${MNT}#${ENC}#${PLABEL}#GPT#${XTRAOPTS}" >${PARTDIR}/${_dFile}p${CURPART}
414 # Clear out any headers
416 dd if=/dev/zero of=${_pDisk}p${CURPART} count=2048 2>/dev/null
418 # If we have a enc password, save it as well
419 if [ -n "${ENCPASS}" ] ; then
420 echo "${ENCPASS}" >${PARTDIR}-enc/${_dFile}p${CURPART}-encpass
422 elif [ "${_pType}" = "apm" ] ; then
423 _dFile="`echo $_pDisk | sed 's|/|-|g'`"
424 echo "${FS}#${MNT}#${ENC}#${PLABEL}#GPT#${XTRAOPTS}" >${PARTDIR}/${_dFile}s${CURPART}
426 # Clear out any headers
428 dd if=/dev/zero of=${_pDisk}s${CURPART} count=2048 2>/dev/null
430 # If we have a enc password, save it as well
431 if [ -n "${ENCPASS}" ] ; then
432 echo "${ENCPASS}" >${PARTDIR}-enc/${_dFile}s${CURPART}-encpass
435 # MBR Partition or GPT slice
436 _dFile="`echo $_wSlice | sed 's|/|-|g'`"
437 echo "${FS}#${MNT}#${ENC}#${PLABEL}#MBR#${XTRAOPTS}#${IMAGE}" >${PARTDIR}/${_dFile}${PARTLETTER}
438 # Clear out any headers
440 dd if=/dev/zero of=${_wSlice}${PARTLETTER} count=2048 2>/dev/null
442 # If we have a enc password, save it as well
443 if [ -n "${ENCPASS}" ] ; then
444 echo "${ENCPASS}" >${PARTDIR}-enc/${_dFile}${PARTLETTER}-encpass
449 # Increment our parts counter
450 if [ "$_pType" = "gpt" -o "$_pType" = "apm" ] ; then
451 CURPART=$((CURPART+1))
452 # If this is a gpt/apm partition,
453 # we can continue and skip the MBR part letter stuff
456 CURPART=$((CURPART+1))
457 if [ "$CURPART" = "3" ] ; then CURPART="4" ; fi
461 # This partition letter is used, get the next one
462 case ${PARTLETTER} in
469 h) PARTLETTER="ERR" ;;
470 *) exit_err "ERROR: bsdlabel only supports up to letter h for partitions." ;;
473 fi # End of subsection locating a slice in config
475 echo $line | grep -q "^commitDiskLabel" 2>/dev/null
476 if [ $? -eq 0 -a "${FOUNDPARTS}" = "0" ]
479 # If this is the boot disk, stamp the right gptboot
480 if [ ! -z "${BOOTTYPE}" -a "$_pType" = "gpt" ] ; then
482 freebsd-ufs) rc_halt "gpart bootcode -p /boot/gptboot -i 1 ${_pDisk}" ;;
483 freebsd-zfs) rc_halt "gpart bootcode -p /boot/gptzfsboot -i 1 ${_pDisk}" ;;
487 # Make sure to stamp the MBR loader
488 if [ "$_pType" = "mbr" ] ; then
489 rc_halt "gpart bootcode -b /boot/boot ${_wSlice}"
492 # Found our flag to commit this label setup, check that we found at least 1 partition
493 if [ "${CURPART}" = "1" ] ; then
494 exit_err "ERROR: commitDiskLabel was called without any partition entries for it!"
502 # Reads through the config and sets up a BSDLabel for the given slice
503 populate_disk_label()
507 exit_err "ERROR: populate_disk_label() called without argument!"
510 # Set some vars from the given working slice
511 diskid="`echo $1 | cut -d ':' -f 1`"
512 disk="`echo $1 | cut -d ':' -f 1 | sed 's|-|/|g'`"
513 slicenum="`echo $1 | cut -d ':' -f 2`"
514 type="`echo $1 | cut -d ':' -f 3`"
516 # Set WRKSLICE based upon format we are using
517 if [ "$type" = "mbr" ] ; then
518 wrkslice="${diskid}s${slicenum}"
520 if [ "$type" = "apm" ] ; then
521 wrkslice="${diskid}s${slicenum}"
523 if [ "$type" = "gpt" -o "$type" = "gptslice" ] ; then
524 wrkslice="${diskid}p${slicenum}"
527 if [ ! -e "${SLICECFGDIR}/${wrkslice}" ] ; then
528 exit_err "ERROR: Missing SLICETAG data. This shouldn't happen - please let the developers know"
531 disktag="`cat ${SLICECFGDIR}/${wrkslice}`"
532 slicedev="`echo $wrkslice | sed 's|-|/|g'`"
534 # Setup the partitions with gpart
535 setup_gpart_partitions "${disktag}" "${disk}" "${slicedev}" "${slicenum}" "${type}"
539 # Function which reads in the disk slice config, and performs it
542 # We are ready to start setting up the label, lets read the config and do the actions
543 # First confirm that we have a valid WORKINGSLICES
544 if [ -z "${WORKINGSLICES}" ]; then
545 exit_err "ERROR: No slices were setup! Please report this to the maintainers"
548 # Check that the slices we have did indeed get setup and gpart worked
549 for i in $WORKINGSLICES
551 disk="`echo $i | cut -d '-' -f 1`"
552 pnum="`echo $i | cut -d '-' -f 2`"
553 type="`echo $i | cut -d '-' -f 3`"
554 if [ "$type" = "mbr" -a ! -e "${disk}s${pnum}" ] ; then
555 exit_err "ERROR: The partition ${i} doesn't exist! gpart failure!"
557 if [ "$type" = "gpt" -a ! -e "${disk}p${pnum}" ] ; then
558 exit_err "ERROR: The partition ${i} doesn't exist! gpart failure!"
560 if [ "$type" = "apm" -a ! -e "${disk}s${pnum}" ] ; then
561 exit_err "ERROR: The partition ${i} doesn't exist! gpart failure!"
563 if [ "$type" = "gptslice" -a ! -e "${disk}p${pnum}" ] ; then
564 exit_err "ERROR: The partition ${i} doesn't exist! gpart failure!"
568 # Setup some files which we'll be referring to
569 export LABELLIST="${TMPDIR}/workingLabels"
570 rm $LABELLIST >/dev/null 2>/dev/null
572 # Set our flag to determine if we've got a valid root partition in this setup
573 export FOUNDROOT="-1"
575 # Check if we are using a /boot partition
576 export USINGBOOTPART="1"
578 # Set encryption on root check
579 export USINGENCROOT="1"
581 # Make the tmp directory where we'll store FS info & mount-points
582 rm -rf ${PARTDIR} >/dev/null 2>/dev/null
583 mkdir -p ${PARTDIR} >/dev/null 2>/dev/null
584 rm -rf ${PARTDIR}-enc >/dev/null 2>/dev/null
585 mkdir -p ${PARTDIR}-enc >/dev/null 2>/dev/null
587 for i in $WORKINGSLICES
589 populate_disk_label "${i}"
592 # Check if we made a root partition
593 if [ "$FOUNDROOT" = "-1" ]
595 exit_err "ERROR: No root (/) partition specified!!"
598 # Check if we made a root partition
599 if [ "$FOUNDROOT" = "1" -a "${USINGBOOTPART}" != "0" ]
601 exit_err "ERROR: (/) partition isn't first partition on disk!"
604 if [ "${USINGENCROOT}" = "0" -a "${USINGBOOTPART}" != "0" ]
606 exit_err "ERROR: Can't encrypt (/) with no (/boot) partition!"
625 PARTLETTER=`echo "$SLICE" | sed -E 's|^.+([a-h])$|\1|'`
627 cat "${FSTAB}" | awk '{ print $2 }' | grep -qE '^/$' 2>&1
630 if [ "${PARTLETTER}" = "a" ]
643 cat "${FSTAB}" | awk '{ print $2 }' | grep -qE '^/boot$' 2>&1
646 if [ "${PARTLETTER}" = "a" ]
650 exit_err "/boot partition must be first partition"
676 PARTNUMBER=`echo "${SLICE}" | sed -E 's|^.+p([0-9]*)$|\1|'`
678 cat "${FSTAB}" | awk '{ print $2 }' | grep -qE '^/$' 2>&1
681 if [ "${PARTNUMBER}" = "2" ]
694 cat "${FSTAB}" | awk '{ print $2 }' | grep -qE '^/boot$' 2>&1
697 if [ "${PARTNUMBER}" = "2" ]
701 exit_err "/boot partition must be first partition"
729 SLICES_MBR=`ls /dev/${DISK}s[1-4]*[a-h]* 2>/dev/null`
730 SLICES_GPT=`ls /dev/${DISK}p[0-9]* 2>/dev/null`
731 SLICES_SLICE=`ls /dev/${DISK}[a-h]* 2>/dev/null`
733 if [ -n "${SLICES_MBR}" ]
735 SLICES="${SLICES_MBR}"
739 if [ -n "${SLICES_GPT}" ]
741 SLICES="${SLICES_GPT}"
745 if [ -n "${SLICES_SLICE}" ]
747 SLICES="${SLICES_SLICE}"
752 for slice in ${SLICES}
755 mount ${slice} /mnt 2>/dev/null
761 if [ "${TYPE}" = "MBR" ]
763 check_fstab_mbr "${slice}" "/mnt"
766 elif [ "${TYPE}" = "GPT" ]
768 check_fstab_gpt "${slice}" "/mnt"