Skip to content

Instantly share code, notes, and snippets.

@ink-splatters
Forked from joevt/DiskUtil.sh
Created April 21, 2023 18:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ink-splatters/0ef28ba7092ff4e059c231bd3665c472 to your computer and use it in GitHub Desktop.
Save ink-splatters/0ef28ba7092ff4e059c231bd3665c472 to your computer and use it in GitHub Desktop.
macOS disk labels, mounting partitions
#!/bin/bash
# by joevt May 23, 2022
directblesscmd="/Volumes/Work/Programming/XcodeProjects/bless/bless-204.40.27 joevt/DerivedData/bless/Build/Products/Debug/bless"
usedirectbless=0
if [[ -d /System/Library/PrivateFrameworks/APFS.framework/Versions/A ]]; then
if [[ ! -f "$directblesscmd" ]]; then
echo "# Download and build bless from https://github.com/joevt/bless , then update the path of directbless defined in DiskUtil.sh"
else
usedirectbless=1
fi
fi
if ((usedirectbless)); then
directbless=$directblesscmd
alias directbless='"$directbless"'
else
directbless=bless
alias directbless=bless
fi
# dump disk_label file
dump_label () { local contents; contents=$(xxd -p -c99999 "$1"); echo "${contents:10}" | perl -pe "s/(..)/\1 /g;s/00/../g;s/ //g;s/(.{$((0x${contents:2:4}*2))})/\1\n/g" ; }
# convert 4 byte number to little-endian
le () { local n; n=$(printf "%08x" $(($1))); printf "%s" "${n:6:2}${n:4:2}${n:2:2}${n:0:2}"; }
#convert disk_lbel to tiff file
convert_label () {
local thelabel="$1"
local thedest="$2"
if [[ ! -f $thelabel ]]; then
printf '# Disk label "%s" does not exist.\n' "$thelabel"
return 1
fi
if [[ -z $thedest ]]; then
thedest="$thelabel.tiff"
fi
local contents=""
contents=$(xxd -p "$thelabel")
local width=$((0x${contents:2:4}))
local height=$((0x${contents:6:4}))
echo "
4949 2a00 1800 0000 d002 0000 0a00 0000 d002 0000 0a00 0000 0d00 fe00 0400 0100
0000 0000 0000 0001 0400 0100 0000 $(le width) 0101 0400 0100 0000 $(le height) 0201
0300 0100 0000 0800 0000 0301 0300 0100 0000 0100 0000 0601 0300 0100 0000 0000
0000 1101 0400 0100 0000 ba00 0000 1501 0300 0100 0000 0100 0000 1601 0400 0100
0000 $(le width) 1701 0400 0100 0000 $(le $((width * height))) 1a01 0500 0100 0000 0800 0000 1b01
0500 0100 0000 1000 0000 2801 0300 0100 0000 0200 0000 0000 0000
$(echo "${contents:10}" | xxd -p -r | LANG=C tr -C \
'\0\366\367\52\370\371\125\372\373\200\374\375\253\376\377\326' '\0' | LANG=C tr \
'\0\366\367\52\370\371\125\372\373\200\374\375\253\376\377\326' \
'\0\021\042\63\104\125\146\167\210\231\252\273\314\335\356\377' | xxd -p)
" | xxd -p -r > "$thedest"
}
# mount a partition
mountpartition() {
local mounttype=$1
local slice=$2
local volume=$3
local mountpoint=""
mountpoint=$(mount | sed -n -E "/\/dev\/$slice on (.*) \(.*/s//\1/p")
if [[ -n "$mountpoint" ]]; then
echo -n "$mountpoint"
return 0
fi
local i=0
local startmountpoint="/Volumes/$volume"
mountpoint="$startmountpoint"
while ((1)); do
sudo mkdir "$mountpoint" 2> /dev/null && break
if ((i++ > 1000)); then
echo "Could not create mountpoint" 1>&2
return 1
fi
mountpoint="$startmountpoint$i"
done
sudo "mount$mounttype" "/dev/$slice" "$mountpoint" && echo -n "$mountpoint"
return $?
}
makemultilinedisklabel () {
if (( $# != 2 )); then
echo "# error: not enough arguments" 1>&2
echo "# usage: makemultilinedisklabel destinationpath disklabellines" 1>&2
return 1
fi
local folder="$1"
if [[ ! -d $folder ]]; then
echo '# error: "'"$folder"' is not a folder' 1>&2
echo "# usage: makemultilinedisklabel destinationpath disklabellines" 1>&2
return 1
fi
local lines="$2"
local alllines=""
local alllines2x=""
local tempfolder0=""
local tempfolder=""
tempfolder0="$(mktemp -d)"
# do the blessing on a temporary disk image so it doesn't do apfs stuff like copy the disk label to preboot of the current system
tempfolder="$(hdiutil create -volname disklabeltmp -size 600k -fs HFS+ -attach "$tempfolder0/tmp.dmg" | perl -nE 'if (/\/dev\/disk[0-9]+s[0-9]+[^\t]*\tApple_HFS[^\t]*\t(.*)/) { print $1 }')"
if [[ -z $tempfolder ]]; then
echo "# error creating temporary disk image" 1>&2
return 1
fi
if [[ ! -d $tempfolder ]]; then
echo "# error getting temporary disk image mount point: $tempfolder" 1>&2
return 1
fi
IFS=$'\n'
local linenum=0
local theline=""
local theoneline=""
for theline in $(echo "$lines"); do
sudo bless --folder "$tempfolder" --label "$theline" 2> /dev/null
if (( linenum )); then
alllines+="$(dump_label "$tempfolder/.disk_label" | sed '1,/[^.]/ {/^[.]*$/d; }')"$'\n'
alllines2x+="$(dump_label "$tempfolder/.disk_label_2x" | sed '1,/[^.]/ {/^[.]*$/d; }')"$'\n'
theoneline+=" $theline"
else
alllines+="$(dump_label "$tempfolder/.disk_label")"$'\n'
alllines2x+="$(dump_label "$tempfolder/.disk_label_2x")"$'\n'
theoneline="$theline"
fi
((linenum++))
done
local suffix=""
local thelines=""
for suffix in "" "_2x"; do
[[ -z $suffix ]] && thelines="$alllines" || thelines="$alllines2x"
local linewidths=""
local maxlinewidth=""
linewidths="$(echo "${thelines}" | tr '0-9a-f' '.' | sort -ur)"
maxlinewidth=$(echo "$linewidths" | sed -n '1p')
linewidths=$(echo "$linewidths" | sed '1d')
local centercommands=""
local linewidth=""
for linewidth in $(echo "$linewidths"); do
local left=${maxlinewidth:0:(${#maxlinewidth}-${#linewidth})/4*2}
local right=${maxlinewidth:0:(${#maxlinewidth}-${#linewidth}-${#left})}
centercommands+="s/^(${linewidth})$/$left\1$right/; "
done
thelines=$(echo "$thelines" | sed -E "$centercommands s/\./0/g")
printf "01%04x%04x$thelines" $((${#maxlinewidth} / 2)) "$(echo "$thelines" | wc -l)" | xxd -p -r > "$tempfolder/disk_label$suffix"
[[ -f "$folder/.disk_label$suffix" ]] && sudo chflags noschg "$folder/.disk_label$suffix"
sudo cp "$tempfolder/disk_label$suffix" "$folder/.disk_label$suffix"
sudo chflags schg "$folder/.disk_label$suffix"
done
[[ -f "$folder/.disk_label.contentDetails" ]] && sudo chflags noschg "$folder/.disk_label.contentDetails"
theoneline="${theoneline/🄳/ [D]}"
theoneline="${theoneline/🄿/ [P]}"
theoneline="${theoneline/🅁/ [R]}"
theoneline="${theoneline/🄱/ [B]}"
theoneline="${theoneline// / }"
printf "%s" "$theoneline" > "$tempfolder/disk_label.contentDetails"
sudo cp "$tempfolder/disk_label.contentDetails" "$folder/.disk_label.contentDetails"
sudo chflags schg "$folder/.disk_label.contentDetails"
hdiutil detach -quiet "${tempfolder}"
}
unsetall () {
local thevarprefix="$1" # prefix for variable names
if [[ -n $thevarprefix ]]; then
eval "$(set | sed -nE '/^('"$1"'[^=]*)=.*/s//unset \1/p')"
fi
}
patmatch () {
# substitute for [[ =~ ]] for Mac OS X 10.4
perl -0777 -ne '<>; exit !( $_ =~ /'"$1"'/ )'
}
fixapfsbooter () {
# Use Catalina for best results.
theoneapfsmountpoint="$1"
theline1="$2"
if [[ -z $theoneapfsmountpoint ]]; then
echo "#Error: missing parameters"
return 1
fi
if [[ -z $theline1 ]]; then
echo "#Error: missing second parameter"
return 1
fi
local diskutil_fab1_Error=""
local diskutil_fab1_APFSContainerReference=""
local diskutil_fab1_APFSVolumeGroupID=""
local diskutil_fab1_MountPoint=""
local diskutil_fab1_VolumeUUID=""
getdiskinfo "$theoneapfsmountpoint" "diskutil_fab1_"
if [[ -n $diskutil_fab1_Error ]]; then
echo "# Error: $diskutil_fab1_Error"
return 1
fi
mkdir -p /tmp/disklabeltemp
local iconsource="$theoneapfsmountpoint/.VolumeIcon.icns"
if [[ -f "$iconsource" ]]; then
echo "# Got icon at $iconsource"
if [[ -L "$iconsource" ]]; then
echo "# Making icon not a sym link"
cp -p "$iconsource" /tmp/disklabeltemp
sudo rm "$iconsource"
sudo cp -p /tmp/disklabeltemp/.VolumeIcon.icns "$iconsource"
fi
else
echo "# Did not find icon at $iconsource"
fi
local Recovery_MountPoint=""
local patAll="^(_|${diskutil_fab1_APFSVolumeGroupID}_System|${diskutil_fab1_APFSVolumeGroupID}_Data|_Preboot|_Recovery)="
local patSys="^_="
local patSys2="^${diskutil_fab1_APFSVolumeGroupID}_System="
local patData="^${diskutil_fab1_APFSVolumeGroupID}_Data="
local patPreboot="^_Preboot="
local patRecovery="^_Recovery="
local System_UUID="$diskutil_fab1_APFSVolumeGroupID"
if [[ -z $System_UUID ]]; then
System_UUID="$diskutil_fab1_VolumeUUID"
fi
for thepass in 0 1; do
for thedisk in $(getapfsroles "$diskutil_fab1_APFSContainerReference"); do
if patmatch "$patAll" <<< "$thedisk" ; then
echo "#================"
echo "# Doing $thedisk, pass $(( thepass+1 ))"
local thedevice="${thedisk#*=}"
diskutil mount "$thedevice" > /dev/null 2>&1
getdiskinfo "$thedevice" "diskutil_fab2_"
if [[ -z "$diskutil_fab2_MountPoint" ]]; then
echo "# Could not mount"
else
local issource=0
if [[ "$diskutil_fab2_MountPoint" == "$diskutil_fab1_MountPoint" ]]; then
issource=1
fi
local theletter=""
local thedest=""
if patmatch "$patSys" <<< "$thedisk"; then
if ((issource)); then
thedest="$diskutil_fab2_MountPoint/System/Library/CoreServices"
fi
elif patmatch "$patSys2" <<< "$thedisk"; then
thedest="$diskutil_fab2_MountPoint/System/Library/CoreServices"
elif patmatch "$patData" <<< "$thedisk"; then
theletter="🄳"
thedest="$diskutil_fab2_MountPoint/System/Library/CoreServices"
elif patmatch "$patPreboot" <<< "$thedisk"; then
theletter="🄿"
thedest="$diskutil_fab2_MountPoint/$System_UUID/System/Library/CoreServices"
if [[ ! -d $thedest ]]; then
thedest="$diskutil_fab2_MountPoint/$diskutil_fab1_VolumeUUID/System/Library/CoreServices"
fi
elif patmatch "$patRecovery" <<< "$thedisk"; then
theletter="🅁"
thedest="$diskutil_fab2_MountPoint/$System_UUID"
if [[ ! -d $thedest ]]; then
thedest="$diskutil_fab2_MountPoint/$diskutil_fab1_VolumeUUID"
fi
Recovery_MountPoint="$diskutil_fab2_MountPoint"
fi
if ((thepass == 0)); then
if ((issource)); then
echo "# Skipping icon copy"
elif [[ -f "$iconsource" ]]; then
if [[ -L "$diskutil_fab2_MountPoint/.VolumeIcon.icns" ]]; then
echo "# Removing icon sym link"
sudo rm "$diskutil_fab2_MountPoint/.VolumeIcon.icns"
fi
echo "# Doing icon copy"
sudo cp -p "$iconsource" "$diskutil_fab2_MountPoint" || echo "# Error copying icon"
else
echo "# No icon to copy"
fi
fi
if [[ -d $thedest ]]; then
echo "# Destination at $thedest"
if ((thepass == 0)); then
local version=""
local theversionfile="$thedest/SystemVersion.plist"
if [[ -f "$theversionfile" ]]; then
version=$(/usr/libexec/PlistBuddy -c 'Print :ProductVersion' "$theversionfile")
fi
local theline2=""
if [[ -n "$version" ]]; then
theline2="${version}${theletter}"
else
theline2="${theletter}"
fi
makemultilinedisklabel "/tmp/disklabeltemp" "${theline1}"$'\n'"${theline2}"
echo "# Setting the label to: [${theline1}][${theline2}]"
sudo find "$thedest" -name '.disk_label*' -exec chflags noschg {} \;
sudo cp /tmp/disklabeltemp/.disk_label* "$thedest"
sudo find "$thedest" -name '.disk_label*' -exec chflags schg {} \;
else
if [[ -f "$thedest/boot.efi" ]]; then
echo "# Blessing $thedest/boot.efi"
if ((usedirectbless)); then
sudo "$directbless" --folder "$thedest" --file "$thedest/boot.efi"
else
if [[ -z $theletter ]]; then
# Blessing the system volume will bless Preboot, so temporarily change it to Recovery
# but first must change Recovery to none. This only works in Catalina
diskutil apfs changeVolumeRole "$Recovery_MountPoint" clear || echo "# ERROR: Unexpected error attempting to change role of Recovery volume"
diskutil apfs changeVolumeRole "$diskutil_fab2_MountPoint" R || echo "# ERROR: Retry this from a different macOS Catalina system"
fi
sudo bless --folder "$thedest" --file "$thedest/boot.efi"
if [[ -z $theletter ]]; then
diskutil apfs changeVolumeRole "$diskutil_fab2_MountPoint" clear || echo "# ERROR: Retry this from a different macOS Catalina system"
diskutil apfs changeVolumeRole "$Recovery_MountPoint" R || echo "# ERROR: Unexpected error attempting to change role of Recovery volume"
fi
fi
fi
fi
else
echo "# Destination does not exist $thedest"
fi
fi # if mount
fi # if the disk
done # for thedisk
done # for thepass
echo "#================"
echo "# Done!"
}
unlockapfsbooter () {
# If you use fixapfsbooter to create disk labels for an APFS system, you should probably
# use this to unlock the disk label files in case the installer doesn't like them.
theoneapfsmountpoint="$1"
if [[ -z $theoneapfsmountpoint ]]; then
echo "#Error: missing parameters"
return 1
fi
local diskutil_fab1_Error=""
local diskutil_fab1_APFSContainerReference=""
local diskutil_fab1_APFSVolumeGroupID=""
local diskutil_fab1_MountPoint=""
local diskutil_fab1_VolumeUUID=""
getdiskinfo "$theoneapfsmountpoint" "diskutil_fab1_"
if [[ -n $diskutil_fab1_Error ]]; then
echo "# Error: $diskutil_fab1_Error"
return 1
fi
local patAll="^(_|${diskutil_fab1_APFSVolumeGroupID}_System|${diskutil_fab1_APFSVolumeGroupID}_Data|_Preboot|_Recovery)="
local patSys="^_="
local patSys2="^${diskutil_fab1_APFSVolumeGroupID}_System="
local patData="^${diskutil_fab1_APFSVolumeGroupID}_Data="
local patPreboot="^_Preboot="
local patRecovery="^_Recovery="
local System_UUID="$diskutil_fab1_APFSVolumeGroupID"
if [[ -z $System_UUID ]]; then
System_UUID="$diskutil_fab1_VolumeUUID"
fi
for thedisk in $(getapfsroles "$diskutil_fab1_APFSContainerReference"); do
if patmatch "$patAll" <<< "$thedisk" ; then
echo "#================"
echo "# Doing $thedisk"
local thedevice="${thedisk#*=}"
diskutil mount "$thedevice" > /dev/null 2>&1
getdiskinfo "$thedevice" "diskutil_fab2_"
if [[ -z "$diskutil_fab2_MountPoint" ]]; then
echo "# Could not mount"
else
local issource=0
if [[ "$diskutil_fab2_MountPoint" == "$diskutil_fab1_MountPoint" ]]; then
issource=1
fi
local thedest=""
if patmatch "$patSys" <<< "$thedisk"; then
if ((issource)); then
thedest="$diskutil_fab2_MountPoint/System/Library/CoreServices"
fi
elif patmatch "$patSys2" <<< "$thedisk"; then
thedest="$diskutil_fab2_MountPoint/System/Library/CoreServices"
elif patmatch "$patData" <<< "$thedisk"; then
thedest="$diskutil_fab2_MountPoint/System/Library/CoreServices"
elif patmatch "$patPreboot" <<< "$thedisk"; then
thedest="$diskutil_fab2_MountPoint/$System_UUID/System/Library/CoreServices"
if [[ ! -d $thedest ]]; then
thedest="$diskutil_fab2_MountPoint/$diskutil_fab1_VolumeUUID/System/Library/CoreServices"
fi
elif patmatch "$patRecovery" <<< "$thedisk"; then
thedest="$diskutil_fab2_MountPoint/$System_UUID"
if [[ ! -d $thedest ]]; then
thedest="$diskutil_fab2_MountPoint/$diskutil_fab1_VolumeUUID"
fi
fi
if [[ -d $thedest ]]; then
echo "# Unlocking these files:"
find "$thedest" -name '.disk_label*'
sudo find "$thedest" -name '.disk_label*' -exec chflags noschg {} \;
else
echo "# Destination does not exist $thedest"
fi
fi # if mount
fi # if the disk
done # for thedisk
echo "#================"
echo "# Done!"
}
getvolumeproperty () {
local slice="$1"
local property="$2"
local duinfo="$(diskutil info -plist "$slice" 2> /dev/null)"
eval "$(
{
for (( i=0 ; i < 2 ; i++ )); do
if patmatch "Disk Utility Tool" <<< "$duinfo"; then
diskutil info "$slice" | perl -ne 'while (<>) { if (/ +([^:]+): +"?(.*?)"?$/) { $name = $1; $value = $2; $name =~ s/ //g; print "\"" . $name . "\" => \"" . $value . "\"\n" } }'
break
else
printf "%s" "$duinfo" | plutil -p - 2> /dev/null && break || duinfo="Disk Utility Tool"
fi
done
} | sed -nE '/^ *"'"$property"'" => (.*)/s//echo \1/p'
)"
}
getvolumename () {
getvolumeproperty "$1" "VolumeName"
}
mountEFIpartitions () {
IFS=$'\n'
local diskinfo=""
for diskinfo in $(diskutil list | LANG=C sed -nE $'/^ *[0-9]+: +EFI (\xe2\x81\xa8)?(.*[^ \xa9])?(\xe2\x81\xa9)? +[0-9.]+ [^ ]?[Bi] +(disk[0-9]+s[0-9]+)$/''s//\2_\4/p'); do
local slice="${diskinfo##*_}"
local volume=""
volume="$(getvolumename "$slice")"
if [[ -z $volume ]]; then
volume="EFI" # Mac OS X 10.4.11 doesn't load EFI volume name
fi
#echo mountpartition "_msdos" "$slice" "$volume"
mountpartition "_msdos" "$slice" "$volume" > /dev/null
done
}
mountRecoveryHDpartitions() {
IFS=$'\n'
for diskinfo in $(diskutil list | LANG=C sed -nE $'/^ *[0-9]+: +Apple_Boot (\xe2\x81\xa8)?(Recovery HD)(\xe2\x81\xa9)? +[0-9.]+ [^ ]?[Bi] +(disk[0-9]+s[0-9]+)$/''s//\2_\4/p'); do
local slice="${diskinfo##*_}"
local volume=""
volume="$(getvolumename "$slice")"
mountpartition "_hfs" "$slice" "$volume" > /dev/null
done
}
mountRecoveryPartitions() {
IFS=$'\n'
for diskinfo in $(diskutil list | LANG=C sed -nE $'/^ *[0-9]+: +APFS Volume (\xe2\x81\xa8)?(Recovery)(\xe2\x81\xa9)? +[0-9.]+ [^ ]?[Bi] +(disk[0-9]+s[0-9]+)$/''s//\2_\4/p'); do
local slice="${diskinfo##*_}"
local volume=""
volume="$(getvolumename "$slice")"
mountpartition "_apfs" "$slice" "$volume" > /dev/null
done
}
mountPrebootPartitions() {
IFS=$'\n'
for diskinfo in $(diskutil list | LANG=C sed -nE $'/^ *[0-9]+: +APFS Volume (\xe2\x81\xa8)?(Preboot)(\xe2\x81\xa9)? +[0-9.]+ [^ ]?[Bi] +(disk[0-9]+s[0-9]+)$/''s//\2_\4/p'); do
local slice="${diskinfo##*_}"
local volume=""
volume="$(getvolumename "$slice")"
mountpartition "_apfs" "$slice" "$volume" > /dev/null
done
}
mounthfsimage () {
# macOS doesn't support mounting HFS anymore. To get around that,
# this command uses https://github.com/joevt/fusehfs.
# Useful for mounting SheepShaver.app disk image.
local theimage="$1"
theresult=$(hdiutil attach "$theimage" -readonly -nomount -noverify -noautofsck -plist)
if [[ -n $theresult ]]; then
local device=""
local volume_kind=""
eval "$(
plutil -convert json -o - - <<< "$theresult" | perl -e '
use JSON::PP; my $f = decode_json (<>)->{"system-entities"};
printf("device=\"%s\"\nvolume_kind=\"%s\"\n", $f->[0]->{"dev-entry"}, $f->[0]->{"volume-kind"})
'
)"
if [[ -z $volume_kind ]] || [[ $volume_kind = "hfs" ]]; then
local volume=""
local slice="${device/\/dev\//}"
volume="$(getvolumename "$slice")"
mountpartition "_fusefs_hfs" "$slice" "$volume" # > /dev/null
else
echo "# unexpected volume kind '$volume_kind'"
fi
fi
}
getblessinfo () {
local mountpoint="$1"
local thevarprefix="$2"
unsetall "$thevarprefix"
eval "$(
directbless -plist --info "$mountpoint" 2> /dev/null | plutil -convert json -o - - | perl -e '
use JSON::PP; my $f = decode_json (<>)->{"Finder Info"};
printf("'"$thevarprefix"'theblessfolder=\"%s\"\n'"$thevarprefix"'theblessfile=\"%s\"\n'"$thevarprefix"'theblessopen=\"%s\"\n", $f->[0]->{"Path"}, $f->[1]->{"Path"}, $f->[2]->{"Path"})
' 2> /dev/null
)"
}
dumpAllDiskLabels () {
IFS=$'\n'
for theslice in $(diskutil list | sed -nE '/.*(disk[0-9]+s[0-9]+)$/s//\1/p'); do
#echo "$theslice"
sudo diskutil mount "$theslice" > /dev/null 2>&1
local diskutil_dadl_DeviceIdentifier=""
getdiskinfo "$theslice" "diskutil_dadl_"
if [[ -n "$diskutil_dadl_MountPoint" ]]; then
echo "#==========="
echo "$diskutil_dadl_DeviceIdentifier $diskutil_dadl_MountPoint"
local theblessfolder=""
local theblessfile=""
local theblessopen=""
getblessinfo "$diskutil_dadl_MountPoint"
if [[ -n $theblessfile ]]; then
theblessfolder="$(dirname "$theblessfile")"
fi
for thefolder in $( {
find "$diskutil_dadl_MountPoint" -maxdepth 2 -name "boot.efi*" 2> /dev/null | sed -E '/\/boot.efi.*/s///'
ls -d "$diskutil_dadl_MountPoint/"*"/System/Library/CoreServices"
ls -d "$theblessfolder" "$diskutil_dadl_MountPoint/System/Library/CoreServices" "$diskutil_dadl_MountPoint/EFI/BOOT"
} 2> /dev/null | sort -u ); do
local version=""
local theversionfile="$thefolder/SystemVersion.plist"
if [[ -f "$theversionfile" ]]; then
version=$(/usr/libexec/PlistBuddy -c 'Print :ProductVersion' "$theversionfile")
fi
echo "${thefolder} ($version)"
[[ -f $thefolder/.disk_label.contentDetails ]] && echo "contentDetails: $(cat "$thefolder/.disk_label.contentDetails")" || echo " missing .disk_label.contentDetails"
[[ -f $thefolder/.disk_label_2x ]] && dump_label "$thefolder/.disk_label_2x" || echo " missing .disk_label_2x"
[[ -f $thefolder/.disk_label ]] && dump_label "$thefolder/.disk_label" || echo " missing .disk_label"
done
fi
done
}
makeLabelCommands () {
# Partitions need to be mounted first.
# Only includes Apple_HFS and Apple_Boot partitions.
IFS=$'\n'
local thefolder=""
for diskinfo in $(diskutil list | LANG=C sed -nE $'/^ *[0-9]+: +Apple_[HFSBoot]* (\xe2\x81\xa8)?([^ ].*[^ \xa9])(\xe2\x81\xa9)? +[0-9.]+ [^ ]?[Bi] +(disk[0-9]+s[0-9]+)$/''s//\2_\4/p'); do
local slice="${diskinfo##*_}"
local volume=""
volume="$(getvolumename "$slice")"
local version=""
local mountpoint=""
mountpoint=$(mountpartition "_hfs" "$slice" "$volume")
local theblessfolder=""
local theblessfile=""
local theblessopen=""
getblessinfo "$mountpoint"
if [[ -n $theblessfile ]]; then
thefolder="$(dirname "$theblessfile")"
if [[ "$thefolder" != /Volumes/*/System/Library/CoreServices ]]; then
local theversionfile="$thefolder/SystemVersion.plist"
if [[ -f "$theversionfile" ]]; then
version=$(/usr/libexec/PlistBuddy -c 'Print :ProductVersion' "$theversionfile")
fi
printf 'makemultilinedisklabel "%s" "%s"$%c\\n%c"%s"\n' "$thefolder" "${volume/Recovery HD/Recovery}" "'" "'" "$version"
fi
fi
done
# Scan the usual places first
IFS=$'\n'
for thefolder in $({ find /Volumes/*/System/Library/CoreServices -maxdepth 0 || true ; find /Volumes/*/EFI/BOOT -maxdepth 0 || true ; } 2> /dev/null); do
local volume=""
local version=""
volume="${thefolder/\/System\/Library\/CoreServices/}"
volume="${volume/\/EFI\/BOOT/}"
volume="$(getvolumename "$volume")"
local theversionfile="$thefolder/SystemVersion.plist"
if [[ -f "$theversionfile" ]]; then
version=$(/usr/libexec/PlistBuddy -c 'Print :ProductVersion' "$theversionfile")
fi
printf 'makemultilinedisklabel "%s" "%s"$%c\\n%c"%s"\n' "$thefolder" "${volume/Recovery HD/Recovery}" "'" "'" "$version"
done
}
clearOpenFoldersAll () {
# Clear open folder list of hfs partitions (finderinfo[2] in bless --info).
# If you have an HFS+ disk that has folders that open when you mount them (such as an installer), then this will stop that from happening.
IFS=$'\n'
clearOpenFolders $(getallhfsdisks)
}
clearOpenFolders () {
while (($#)); do
local slice="$1"
shift
local volume=""
volume="$(getvolumename "$slice")"
local mountpoint=""
mountpoint=$(mountpartition "_hfs" "$slice" "$volume")
echo "#$slice=$mountpoint,$volume ($version)"
local theblessfolder=""
local theblessfile=""
local theblessopen=""
getblessinfo "$mountpoint"
if [[ -n $theblessopen ]]; then
echo "Clearing open folder list"
if [[ -n $theblessfolder ]]; then
if [[ -n $theblessfile ]]; then
sudo "$directbless" --folder "$theblessfolder" --file "$theblessfile"
else
sudo "$directbless" --folder "$theblessfolder"
fi
else
if [[ -n $theblessfile ]]; then
sudo "$directbless" --mount "$mountpoint" --file "$theblessfile" --openfolder ""
else
sudo "$directbless" --unbless "$mountpoint"
fi
fi
fi
done
}
getdiskinfo () {
local thedisk="$1" # the disk you want info for (either a mountpoint like "/" or a device "disk0s1")
local thevarprefix="$2" # prefix for variable names
# note: if thevarprefix is empty then you should clear any variables you want to use from this result beforehand
unsetall "$thevarprefix"
eval "$(showdiskinfo "$thedisk" "$thevarprefix")"
}
showdiskinfo () {
local thedisk="$1" # the disk you want info for (either a mountpoint like "/" or a device "disk0s1")
local thevarprefix="$2" # prefix for variable names
diskutil info -plist "$thedisk" 2> /dev/null | plutil -p - | perl -nE 'if (s/ *"([^"]+)" => "?(.*?)"?$/'"$thevarprefix"'\1=\2/) { s/"/\\"/g; s/([^=]+=)(.*)/\1"\2"/; print $_ }'
}
getapfsroles () {
# $1 = the apfs container
local patVolumeGroup="^(Data|System)="
for theapfsvolume in $(
diskutil apfs list -plist "$1" | plutil -convert json -o - - | perl -e '
use JSON::PP; my $apfs = decode_json (<>);
foreach my $volume (@{$apfs->{Containers}[0]{Volumes}}) {print $volume->{Roles}[0] . "=" . $volume->{DeviceIdentifier} . "\n";}
'
); do
diskutil_role_APFSVolumeGroupID=""
if patmatch "$patVolumeGroup" <<< "$theapfsvolume"; then
getdiskinfo "${theapfsvolume#*=}" "diskutil_role_"
fi
echo "${diskutil_role_APFSVolumeGroupID}_${theapfsvolume}"
done
}
setbootername () {
local thedisk="$1"
local NewBooterName="$2"
local diskutil_sbn_Root_BooterDeviceIdentifier=""
getdiskinfo "$thedisk" diskutil_sbn_Root_
if [[ -n $diskutil_sbn_Root_BooterDeviceIdentifier ]]; then
diskutil mount "$diskutil_sbn_Root_BooterDeviceIdentifier"
local diskutil_sbn_Root_VolumeUUID=""
local diskutil_sbn_Root_APFSVolumeGroupID=""
getdiskinfo "$diskutil_sbn_Root_BooterDeviceIdentifier" "diskutil_sbn_Preboot_"
if [[ -n $diskutil_sbn_Preboot_MountPoint ]]; then
local theUUID=""
if [[ -d $diskutil_sbn_Preboot_MountPoint/$diskutil_sbn_Root_VolumeUUID ]]; then
theUUID=$diskutil_sbn_Root_VolumeUUID
echo "using VolumeUUID"
elif [[ -d $diskutil_sbn_Preboot_MountPoint/$diskutil_sbn_Root_APFSVolumeGroupID ]]; then
theUUID=$diskutil_sbn_Root_APFSVolumeGroupID
echo "using APFSVolumeGroupID"
else
echo "unknown UUID in booter"
ls -l "$diskutil_sbn_Preboot_MountPoint"
return 1
fi
local thefile="$diskutil_sbn_Preboot_MountPoint/$theUUID/System/Library/CoreServices/.disk_label.contentDetails"
if [[ -f "$thefile" ]]; then
echo "$thefile already exists: $(cat "$thefile")"
else
sudo bash -c "echo \"$NewBooterName\" > \"$thefile\""
echo "$thefile is created"
fi
else
echo "$diskutil_sbn_Root_BooterDeviceIdentifier is not mounted"
fi
else
echo "No booter device"
fi
}
doperlondisks () {
local thefilter="$1"
local theperl="$2"
{
if [[ -z $thefilter ]]; then
diskutil list -plist
else
diskutil list -plist "$thefilter"
fi
} | plutil -convert json -o - - | perl -e '
use JSON::PP; my $disks = decode_json (<>)->{AllDisksAndPartitions};
foreach my $disk (@$disks) {
foreach my $type ("APFSVolumes","Partitions") {
foreach my $part (@{$disk->{$type}}) {
'"$theperl"'
}
}
}
'
}
doperlonmounteddisks () {
doperlondisks "$1" '
if (exists $part->{MountPoint}) {
'"$2"'
}
'
}
getallmounteddisks () {
# not all disks are mounted at /Volumes so use diskutil to find them
if [[ "$1" == "-d" ]]; then
doperlonmounteddisks "" 'print $part->{DeviceIdentifier} . ":" . $part->{MountPoint} . "\n";'
else
doperlonmounteddisks "" 'print $part->{MountPoint} . "\n";'
fi
}
getalldisks () {
doperlondisks "$1" 'print $part->{DeviceIdentifier} . "\n";'
}
getallhfsdisks () {
doperlondisks "$1" '
if ($part->{Content} eq "Apple_HFS") {
print $part->{DeviceIdentifier} . "\n";
}
'
}
getallefidisks () {
doperlondisks "$1" '
if ($part->{Content} eq "EFI") {
print $part->{DeviceIdentifier} . "\n";
}
'
}
setfinderinfoflag () {
local thepath="$1"
local thechar="$2"
local thebit="$3"
local thevalue="$4"
local thefinderinfo=""
local newfinderinfo=""
local thedescription=""
thefinderinfo=$(xattr -px com.apple.FinderInfo "$thepath" 2> /dev/null | xxd -p -r | xxd -p -c 32)
if [[ -z "$thefinderinfo" ]]; then
thefinderinfo="0000000000000000000000000000000000000000000000000000000000000000"
thedescription="nonexistant "
fi
newfinderinfo=${thefinderinfo:0:$thechar}$( printf "%x" $(( ${thefinderinfo:$thechar:1} & (~(1<<(thebit))) | (thevalue<<(thebit)) )) )${thefinderinfo:$((thechar+1))}
if [[ "$newfinderinfo" != "$thefinderinfo" ]]; then
echo "# modifying ${thedescription}FinderInfo $newfinderinfo $thepath"
sudo xattr -wx com.apple.FinderInfo "$newfinderinfo" "$thepath"
else
echo "# unchanged ${thedescription}FinderInfo $newfinderinfo $thepath"
fi
}
getfinderinfoflag () {
local thepath="$1"
local thechar="$2"
local thebit="$3"
local thefinderinfo=""
thefinderinfo=$(xattr -px com.apple.FinderInfo "$thepath" 2> /dev/null | xxd -p -r | xxd -p -c 32)
echo $(( ( 0x0${thefinderinfo:$thechar:1} >> thebit ) & 1 ))
}
setfoldercustomicon () {
# if the folder is a mount point then the icon should be in datafork of ".VolumeIcon.icns"
# otherwise the icon should be in a icns resource in "Icon\n"
setfinderinfoflag "$1" 17 2 1
}
isfileinvisible () {
getfinderinfoflag "$1" 16 2
}
setfilevisible () {
setfinderinfoflag "$1" 16 2 0
}
setfileinvisible () {
setfinderinfoflag "$1" 16 2 1
}
fixcustomiconflagofallvolumes () {
IFS=$'\n'
for themount in $(getallmounteddisks); do
if [[ -f "$themount/.VolumeIcon.icns" ]]; then
setfoldercustomicon "$themount"
fi
done
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment