Last active
August 28, 2025 07:03
-
-
Save joevt/a99e3af71343d8242e0078ab4af39b6c to your computer and use it in GitHub Desktop.
A script to help with diagnosing legacy BIOS boot issues on Macs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # Get Partition Info from all disks | |
| # | |
| # Written by joevt updated March 20, 2025 | |
| # Patches marked "rgh" July, 2010, to dump information beyond the | |
| # four bios partitions | |
| # | |
| # sudo ./dumpvols.sh > dumpvols_result.txt 2>&1 | |
| # | |
| # Based on johnsock's AHCI Master Boot Record Patch. | |
| # | |
| # This script is freely distributable. | |
| # | |
| if [ ! "$USER" = "root" ]; then | |
| echo WARNING: This script must be run as root. | |
| exit | |
| fi | |
| INPUTDISK="$1" | |
| INPUTDISK="${INPUTDISK#/dev/}" | |
| INPUTSLICE=$(expr "$INPUTDISK" : 'disk[0-9]*s\([0-9]*\)') | |
| if [[ -n $INPUTDISK ]]; then | |
| disklist="$(diskutil list "$INPUTDISK" | perl -p -e 's|^/|\n/|')" | |
| apfslist="$(diskutil apfs list | sed -nE "/^\|/s// /; /^ $/s///; /\+.*$1 /,/^$/ { /^$/d ; p ; }" 2> /dev/null)" | |
| ALLDISKS=0 | |
| else | |
| disklist="$(diskutil list | perl -p -e 's|^/|\n/|')" | |
| apfslist="$(diskutil apfs list 2> /dev/null)" | |
| ALLDISKS=1 | |
| fi | |
| [[ "${apfslist:0:4}" == "Disk" ]] && apfslist="" | |
| #echo "disklist = <<${disklist}>>" | |
| #echo "apfslist = <<${apfslist}>>" | |
| MBRHASHES=" | |
| Windows_XP=d88d4f2dbc2c662db7d7d60ebf9e3e00 | |
| Windows_Vista=12c9d7ff4914c88a7f9eadf9211b861b | |
| Windows_7=118eb70c44cb69284e5d8aa93096831e | |
| None_all_zeros=4ebc676ce4896613a3a9df6e2a1c77ae | |
| MSDOS_or_FreeDOS_on_FreeDOS=93dd2ee87a995e36cbab0c2d5c2f041a | |
| Windows_XP_On_BootCamp2_with_AHCI_patch=c3fb54174bc479899d4ef6e45308dc18 | |
| Windows_XP_On_BootCamp2=cb4dabdd862da0508083b05814e198b8 | |
| Windows_Vista_on_XP_with_AHCI_patch=4979e1b9d70738759280f6962c2ad298 | |
| Windows_7_on_XP=7db43c1425e9ff3077dd62b776a50419 | |
| Windows_7_with_AHCI_patch=a243d3475531b0f6f0d7199043c2eb5c | |
| Windows_7_on_XP_with_AHCI_patch=24977c27865adec9ac8298f1a8d214bf | |
| Apple_Partition_Map_Block0_Driver_Descriptor_Map_390721968_blocks_and_Tiger_OS_9_drivers=087f35e49a39d52a46e3a94882869f51 | |
| " | |
| #All VBR hashes use all the bytes starting from offset 0x60. It may be better to use a different range for each type of VBR... | |
| # GRUB2 hash was incorrectly named GRUB #rgh | |
| # added GRUB hash from observation of kununtu 9.04 #rgh | |
| # added Windows 8 | |
| VBRHASHES=" | |
| None_all_zeros=acf496fff71230daa6985a701f83ce49 | |
| NTFS_No_Loader=3be27456483746abb85cd614381dac5e | |
| NTFS_Windows_XP_NTLDR=dd1728a59343b9fa9458d80657f68771 | |
| NTFS_Windows_Vista_BOOTMGR=d1c278b56eeea9536d0eb5898fe6a0b5 | |
| NTFS_Windows_8_BOOTMGR=771df8b803f96296854316d3d5918812 | |
| GRUB_from_Ubuntu=332469fa146db809f58d6392e2e0bdce | |
| GRUB2_from_Ubuntu=9c249066e1eb9c842d8acdfe6d23a2e3 | |
| FAT32_FRDOS4.1=98050815221ee147066f99f806c23203 | |
| FAT32_FRDOS4.1_or_MSWIN4.1_BOOTMGR=aec608de8ac709d91e6f3340b643ab4d | |
| FAT32_NTFS_NTLDR=d9c3d66e975d2b3b122353951a598b32 | |
| FAT12_BSD_4.4_BOOTMGR=4cc92970d5a3350bc72d2b975e28b915 | |
| FAT16_Non_system_disk=2a3d0f51ad246f115aa7d37891788857 | |
| FAT32_Non_system_disk_or_EFI=34f2d1f3c3ecce5c00cae8f0b82c922b | |
| HFS_boot_block_0=28f5bc1563fbaedeb8dabbd4ef9eb4c2 | |
| APM_Driver_SCSI_patch=44dc8729f17b4de891fc93d8db04e393 | |
| APM_Driver_SCSI_chained_real=f13d0837e944353d07121dcda47f01c0 | |
| APM_Driver_ATA_patch=e1dd2732d4f0cfc3f79357f1bae992c7 | |
| APM_Driver_ATA_chained_real=4ccc33e59537e50e7831fc499a8672a5 | |
| " | |
| # APM drivers created by Leopard Install Mac OS 9 Drivers | |
| FindHash () { | |
| # $1: name of hashed contents | |
| # $2: list of known hashes | |
| # $3: the hash to search for | |
| DIDFIND=0 | |
| for CURHASH in $2; do | |
| THEHASH=$(expr "$CURHASH" : '[^=]*=\(.*\)') | |
| THEOS=$(expr "$CURHASH" : '^\([^=]*\)=') | |
| if [ "$3" = "$THEHASH" ]; then | |
| DIDFIND=1 | |
| break | |
| fi | |
| done | |
| echo "" | |
| if [ $DIDFIND = 1 ]; then | |
| echo "$1: $THEOS" | |
| else | |
| echo "$1: Unrecognized (hash=$3)" | |
| fi | |
| } | |
| pascalstring () { | |
| # $1 pascal string bytes | |
| local thebytes; | |
| local thechars; | |
| local thelen; | |
| thebytes="$1" | |
| thelen=$((0x${thebytes:0:2})) | |
| thechars=$(sed -E 's/(00)+$//' <<< "${thebytes:2}" | xxd -p -r | tr '\0' '.') | |
| if (( thelen != ${#thechars} )); then | |
| printf "len:%d? " "$thelen" | |
| fi | |
| printf "\"%s\"" "$thechars" | |
| } | |
| GetFileInfo="GetFileInfo" | |
| command -v GetFileInfo > /dev/null 2>&1 || { | |
| GetFileInfo="/Developer/Tools/GetFileInfo" | |
| } | |
| inodetopath () { | |
| "$GetFileInfo" -P "/.vol/$1/$2" 2> /dev/null | sed -nE '/^(file|directory): "(.*)"/s//\2/p' | |
| } | |
| inodestring () { | |
| local deviceid="$1" | |
| local inode="$2" | |
| local thepath; | |
| thepath="$(inodetopath "$deviceid" "$inode")" | |
| printf "%d" "$inode" | |
| if [[ -n $thepath ]]; then | |
| printf " => \"%s\"" "$thepath" | |
| elif ((inode == 2)); then | |
| printf " => root directory of volume" | |
| fi | |
| } | |
| forkdatastring () { | |
| local forkdata="$1" | |
| local extentslist | |
| if [[ -n ${forkdata//0/} ]]; then | |
| extentslist=$( | |
| extents="${forkdata:32}" | |
| while [[ -n $extents ]]; do | |
| if ((0x${extents:0:16})); then | |
| printf "(%d,%d)," $((0x${extents:0:8})) $((0x${extents:8:8})) | |
| else | |
| printf "," | |
| fi | |
| extents="${extents:16}" | |
| done | |
| ) | |
| printf "logicalSize:%d clumpSize:%d totalBlocks:%d extents:(%s)" $((0x${forkdata:0:16})) $((0x${forkdata:16:8})) $((0x${forkdata:24:8})) "${extentslist%"${extentslist##*[!,]}"}" | |
| fi | |
| } | |
| extentstring () { | |
| extents="$1" | |
| extentslist=$( | |
| while [[ -n $extents ]]; do | |
| if ((0x${extents:0:16})); then | |
| printf "(%d,%d)," $((0x${extents:0:4})) $((0x${extents:4:4})) | |
| else | |
| printf "," | |
| fi | |
| extents="${extents:8}" | |
| done | |
| ) | |
| printf "(%s)" "${extentslist%"${extentslist##*[!,]}"}" | |
| } | |
| dumpencoding () { | |
| local message="$1" | |
| local encoding="$2" | |
| ((encoding)) && { | |
| printf "%s 0x%08x" "$message" "$encoding" | |
| # 656e63 = enc | |
| (( (encoding & 0x656e6300) == 0x656e6300 )) && { | |
| case $((encoding & 0xff)) in | |
| 0) printf " = kTextEncodingMacRoman" ;; | |
| 1) printf " = kTextEncodingMacJapanese" ;; | |
| 2) printf " = kTextEncodingMacChineseTrad" ;; | |
| 3) printf " = kTextEncodingMacKorean" ;; | |
| 4) printf " = kTextEncodingMacArabic" ;; | |
| 5) printf " = kTextEncodingMacHebrew" ;; | |
| 6) printf " = kTextEncodingMacGreek" ;; | |
| 7) printf " = kTextEncodingMacCyrillic" ;; | |
| 9) printf " = kTextEncodingMacDevanagari" ;; | |
| 10) printf " = kTextEncodingMacGurmukhi" ;; | |
| 11) printf " = kTextEncodingMacGujarati" ;; | |
| 12) printf " = kTextEncodingMacOriya" ;; | |
| 13) printf " = kTextEncodingMacBengali" ;; | |
| 14) printf " = kTextEncodingMacTamil" ;; | |
| 15) printf " = kTextEncodingMacTelugu" ;; | |
| 16) printf " = kTextEncodingMacKannada" ;; | |
| 17) printf " = kTextEncodingMacMalayalam" ;; | |
| 18) printf " = kTextEncodingMacSinhalese" ;; | |
| 19) printf " = kTextEncodingMacBurmese" ;; | |
| 20) printf " = kTextEncodingMacKhmer" ;; | |
| 21) printf " = kTextEncodingMacThai" ;; | |
| 22) printf " = kTextEncodingMacLaotian" ;; | |
| 23) printf " = kTextEncodingMacGeorgian" ;; | |
| 24) printf " = kTextEncodingMacArmenian" ;; | |
| 25) printf " = kTextEncodingMacChineseSimp" ;; | |
| 26) printf " = kTextEncodingMacTibetan" ;; | |
| 27) printf " = kTextEncodingMacMongolian" ;; | |
| 28) printf " = kTextEncodingMacEthiopic" ;; | |
| 29) printf " = kTextEncodingMacCentralEurRoman" ;; | |
| 30) printf " = kTextEncodingMacVietnamese" ;; | |
| 31) printf " = kTextEncodingMacExtArabic" ;; | |
| 33) printf " = kTextEncodingMacSymbol" ;; | |
| 34) printf " = kTextEncodingMacDingbats" ;; | |
| 35) printf " = kTextEncodingMacTurkish" ;; | |
| 36) printf " = kTextEncodingMacCroatian" ;; | |
| 37) printf " = kTextEncodingMacIcelandic" ;; | |
| 38) printf " = kTextEncodingMacRomanian" ;; | |
| 39) printf " = kTextEncodingMacCeltic" ;; | |
| 40) printf " = kTextEncodingMacGaelic" ;; | |
| 41) printf " = kTextEncodingMacKeyboardGlyphs" ;; | |
| esac | |
| } | |
| printf "\n" | |
| } || { | |
| printf "%s 0x%08x" "$message" "$encoding" | |
| printf "\n" | |
| } | |
| } | |
| command -v python > /dev/null 2>&1 && python=python || python=python3 | |
| vsdbtouuid0 () { | |
| "$python" -c ' | |
| import uuid ; from hashlib import md5 | |
| kFSUUIDNamespaceSHA1 = uuid.UUID("b3e20f39-f292-11d6-97a4-00306543ecac") | |
| digest = md5(kFSUUIDNamespaceSHA1.bytes + bytes.fromhex("'"$1"'"), usedforsecurity=False).digest() | |
| print ("%s" % uuid.UUID(bytes=digest[:16], version=3)) | |
| ' | |
| } | |
| vsdbtouuid () { | |
| local theuuid="" | |
| theuuid=$(printf "b3e20f39f29211d697a400306543ecac%s" "$1" | xxd -p -r | md5 | tr "a-f" "A-F" ) | |
| printf "%s" "${theuuid:0:8}-${theuuid:8:4}-3${theuuid:13:3}-$(printf "%X" $(((0x${theuuid:16:1} & 3) | 8)))${theuuid:17:3}-${theuuid:20:12}" | |
| } | |
| (( ALLDISKS )) && echo "$disklist" | |
| echo "$apfslist" | |
| DRIVELIST="$(sed -n -E " | |
| /^[ ]*0: [ ]{0,25}([A-Za-z_]+)[ ]*[+*][0-9.]+ [TGMK][iB][ ]+(disk[0-9]+)$/s//\1_\2/p; | |
| /^[ ]*0: [ ]{0,26} .* [*+][0-9.]+ [TGMK][iB][ ]+(disk[0-9]+)$/s//whole_\1/p | |
| " <<< "$disklist")" | |
| #echo "DRIVELIST = <<${DRIVELIST}>>" | |
| for PARTDRIVE in $(echo $DRIVELIST); do | |
| DRIVE=$(expr "$PARTDRIVE" : '.*_\([^_]*\)') | |
| DTYPE=$(expr "$PARTDRIVE" : '\(.*\)_[^_]*') | |
| #echo "DRIVE = <<$DRIVE>>" | |
| #echo "DTYPE = <<$DTYPE>>" | |
| echo "===============================================================================" | |
| diskutiloutput="$(sed -nE '/\/dev\/'"$DRIVE"'( |$)/,/^$/ p' <<< "${disklist}")" | |
| echo "$diskutiloutput" | |
| echo "---------------------------------------------" | |
| diskinfo="$(diskutil info "$DRIVE")" | |
| echo "$diskinfo" | |
| BLOCKSIZE=$(sed -nE '/^ *Device Block Size: +([0-9]+).*/s//\1/p' <<< "$diskinfo") | |
| if [[ -z $BLOCKSIZE ]]; then | |
| BLOCKSIZE=512 | |
| fi | |
| #echo "BLOCKSIZE = <<$BLOCKSIZE>>" | |
| TOTALSIZE=$(( | |
| $(diskutil info -plist $DRIVE | perl -0777 -ne 'if (m|<key>TotalSize</key>\s*<integer>(\d+)</integer>|) { print $1 }') | |
| / BLOCKSIZE | |
| )) | |
| #echo "TOTALSIZE = <<$TOTALSIZE>>" | |
| MOUNTPOINTS="" | |
| if [[ -n $INPUTSLICE ]]; then | |
| SLICELIST="$(echo "$diskutiloutput" | sed -n -E "/^.* (disk[0-9]+s${INPUTSLICE})$/s//\1/p")" | |
| else | |
| SLICELIST="$(echo "$diskutiloutput" | sed -n -E "/^.* (disk[0-9]+s[0-9]+)$/s//\1/p")" | |
| fi | |
| #echo "SLICELIST = <<$SLICELIST>>" | |
| for THESLICE in $(echo $SLICELIST); do | |
| echo "---------------------------------------------" | |
| partinfo="$(diskutil info "$THESLICE")" | |
| echo "$partinfo" | |
| MOUNTPOINTS="${MOUNTPOINTS}${THESLICE}_$(sed -nE '/ *Mount Point: *(.*)/s//\1/p' <<< "$partinfo")"$'\n' | |
| done | |
| echo "MOUNTPOINTS: $MOUNTPOINTS" | |
| echo "---------------------------------------------" | |
| PARTLIST="" | |
| blockbytes=$(dd if="/dev/$DRIVE" bs=512 count=1 2> /dev/null | xxd -p -c 9999) | |
| if [ "$DTYPE" = "Apple_partition_scheme" ]; then | |
| pdiskoutput="$(pdisk -r -l "/dev/$DRIVE" 2>&1)" | |
| pdiskoutput2="$(pdisk -r -l -f "/dev/$DRIVE" 2>&1)" | |
| echo "$pdiskoutput" | |
| [[ "$pdiskoutput" != "$pdiskoutput2" ]] && echo "$pdiskoutput2" | |
| i=0 | |
| if [[ ${blockbytes:0:4} == "4552" ]]; then # 'ER' | |
| sbBlkSize=$((0x${blockbytes:4:4})) | |
| sbBlkCount=$((0x${blockbytes:8:8})) | |
| sbDevType=$((0x${blockbytes:16:4})) | |
| sbDevId=$((0x${blockbytes:20:4})) | |
| sbData=$((0x${blockbytes:24:8})) | |
| sbDrvrCount=$((0x${blockbytes:32:4})) | |
| echo | |
| echo "APM Block 0 contents" | |
| printf "000: sbSig : 'ER' = sbSIGWord\n" | |
| printf "002: sbBlkSize : %d\n" "$sbBlkSize" | |
| printf "004: sbBlkCount : %d = %d MB\n" "$sbBlkCount" $((sbBlkCount*sbBlkSize / 1000 / 1000)) | |
| ((sbDevType != 0)) && printf "008: sbDevType : %d\n" "$sbDevType" | |
| ((sbDevId != 0)) && printf "00a: sbDevId : %d\n" "$sbDevId" | |
| ((sbData != 0)) && printf "00c: sbData : %d\n" "$sbData" | |
| ((sbDrvrCount > 10)) && printf "010: sbDrvrCount: %d\n" "$sbDrvrCount" | |
| for ((i=0; i < sbDrvrCount; i++)); do | |
| ddBlock=$((0x${blockbytes:36+i*16:8})) | |
| ddSize=$((0x${blockbytes:44+i*16:4})) | |
| ddType=$((0x${blockbytes:48+i*16:4})) | |
| printf "%03x: DDMap[%d]:%4d @ %-4d 0x%04x = %-27s\n" $((0x012 + i*8)) "$i" "$ddSize" "$ddBlock" "$ddType" "$( | |
| { | |
| ((ddType == 0x0001)) && printf "kDriverTypeMacSCSI" ; } || | |
| { | |
| ((ddType == 0x0701)) && printf "kDriverTypeMacATA" ; } || | |
| { | |
| ((ddType == 0xFFFF)) && printf "kDriverTypeMacSCSIChained"; } || | |
| { | |
| ((ddType == 0xF8FF)) && printf "kDriverTypeMacATAChained" ; } || | |
| printf "?" | |
| )" | |
| done | |
| ddPad="${blockbytes:52+i*16}" | |
| [[ -n ${ddPad//0/} ]] && printf "ddPad: %s\n" "$ddPad" | |
| echo | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| else | |
| # use the same bytes as MBR hash even though Block0 contents are totally different | |
| HASH=$(xxd -p -r <<< "${blockbytes:0:440*2}" | xxd -p | md5) | |
| FindHash "Block0 contents" "$MBRHASHES" "$HASH" | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| fi | |
| PARTTYPE="APM" | |
| PARTLIST=$(echo "$pdiskoutput" | sed -nE "/ *[0-9]+: +Apple_Free /d; /[0-9]+: +[0-9]+ @ [0-9]+, type=0x/d; / *([0-9]+): +([^ ]+) .* ([0-9]+) @ ([0-9]+).*/s//\1_\4_\3_\2/p") | |
| elif [ "$DTYPE" = "GUID_partition_scheme" ] || [ "$DTYPE" = "FDisk_partition_scheme" ]; then | |
| gptoutput="$(gpt -r show "$DRIVE" 2>&1)" | |
| gptoutput2="$(gpt -r show -l "$DRIVE" 2>&1)" | |
| fdiskoutput="$(fdisk "/dev/r$DRIVE")" | |
| echo | |
| echo "$gptoutput" | |
| ! grep -q "illegal option -- l" <<< "$gptoutput2" && { | |
| echo | |
| echo "$gptoutput2" | |
| } | |
| echo | |
| echo "$fdiskoutput" | |
| HASH=$(xxd -p -r <<< "${blockbytes:0:440*2}" | xxd -p | md5) | |
| FindHash "MBR contents" "$MBRHASHES" "$HASH" | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| if [ "$DTYPE" = "FDisk_partition_scheme" ]; then | |
| PARTLIST=$(echo "$fdiskoutput" | sed -n -E "/^[ \*]+([0-9])\: ([0-9A-F]{2}) .*\[[ ]*([0-9]+) \-[ ]+([1-9][0-9]*)\].*$/s//\1_\3_\4_\2/p") | |
| PARTTYPE="MBR" | |
| else | |
| blockbytes=$(dd if="/dev/$DRIVE" bs="$BLOCKSIZE" skip=1 count=1 2> /dev/null | xxd -p -c 9999) | |
| HASH=$(xxd -p -r <<< "${blockbytes:96*2:416*2}" | xxd -p | md5) | |
| FindHash "GPT Header @ 1: GPT Header contents" "$VBRHASHES" "$HASH" | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| PARTLIST=$(echo "$gptoutput" | sed -n -E "/^[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+GPT part \- (.*)$/s//\3_\1_\2_\4/p") | |
| PARTTYPE="GPT" | |
| fi | |
| elif [ "$DTYPE" = "whole" ]; then | |
| HASH=$(xxd -p -r <<< "${blockbytes:0:416*2}" | xxd -p | md5) | |
| FindHash "0 @ 0: VBR contents" "$VBRHASHES" "$HASH" | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| else | |
| echo "Unknown partition scheme" | |
| fi | |
| if [[ -n $INPUTSLICE ]]; then | |
| PARTLIST="$(sed -nE "/^${INPUTSLICE}_/p" <<< "$PARTLIST")" | |
| fi | |
| if [[ -z $PARTLIST ]]; then | |
| PARTLIST="0_0_$TOTALSIZE" | |
| fi | |
| #echo "PARTLIST = <<${PARTLIST}>>" | |
| for THEPART in $(echo $PARTLIST); do | |
| PNUM=$( expr "$THEPART" : '\([0-9]*\)_') | |
| PSTART=$( expr "$THEPART" : '[0-9]*_\([0-9]*\)_[0-9]*') | |
| PLENGTH=$(expr "$THEPART" : '[0-9]*_[0-9]*_\([0-9]*\)') | |
| PTYPE=$( expr "$THEPART" : '[0-9]*_[0-9]*_[0-9]*_\(.*\)') | |
| if ((PNUM)); then | |
| DEVICEID=$(stat -f "%r" "/dev/${DRIVE}s$PNUM") | |
| else | |
| DEVICEID=$(stat -f "%r" "/dev/${DRIVE}") | |
| fi | |
| #echo "PNUM = <<$PNUM>>" | |
| #echo "PSTART = <<$PSTART>>" | |
| #echo "PLENGTH = <<$PLENGTH>>" | |
| #echo "PTYPE = <<$PTYPE>>" | |
| #echo "DEVICEID = <<$DEVICEID>>" | |
| POFFSETS="_0" | |
| while [[ -n $POFFSETS ]]; do | |
| POFFSET=$( expr "$POFFSETS" : '_\([0-9]*\)') | |
| POFFSETS=$( expr "$POFFSETS" : '_[0-9]*\(_.*\)') | |
| #echo "POFFSET: <<$POFFSET>>" | |
| if (( POFFSET >= PLENGTH )); then | |
| continue | |
| fi | |
| blockbytes=$( | |
| { | |
| dd if="/dev/$DRIVE" bs="$BLOCKSIZE" skip=$((PSTART + POFFSET)) count=1 2> /dev/null || \ | |
| dd if="/dev/r$DRIVE" bs="$BLOCKSIZE" skip=$((PSTART + POFFSET)) count=1 2> /dev/null | |
| } | xxd -p -c 9999 | |
| ) | |
| #echo "blockbytes: <<$blockbytes>>" | |
| if [[ $PTYPE = "Apple_Patches" ]]; then | |
| # Patches # https://developer.apple.com/library/archive/technotes/tn/tn1189.html#SecretsOfThePartitionMap | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): Driver Patches" | |
| numPatchBlocks=$((0x${blockbytes:0:4})) | |
| numPatches=$((0x${blockbytes:4:4})) | |
| printf "000: numPatchBlocks: %d\n" "$numPatchBlocks" | |
| byteoffset=4; | |
| for (( i=0; i < numPatches; i++ )); do | |
| printf "%03x: PatchDescriptor[%d]:\n" "$byteoffset" "$i" | |
| patchSig=$(sed -E 's/(00)+$//' <<< "${blockbytes:$byteoffset*2:8}" | xxd -p -r | tr '\0' '.') | |
| majorVers=$((0x${blockbytes:$byteoffset*2+8:4})); | |
| minorVers=$((0x${blockbytes:$byteoffset*2+12:4})); | |
| flags=$((0x${blockbytes:$byteoffset*2+16:8})); | |
| patchOffset=$((0x${blockbytes:$byteoffset*2+24:8})); | |
| patchSize=$((0x${blockbytes:$byteoffset*2+32:8})); | |
| patchCRC=$((0x${blockbytes:$byteoffset*2+40:8})); | |
| patchDescriptorLen=$((0x${blockbytes:$byteoffset*2+48:8})); | |
| patchNameLen=$((0x${blockbytes:$byteoffset*2+56:2})); | |
| patchName=$(pascalstring "${blockbytes:$byteoffset*2+56:$patchNameLen*2+2}") | |
| patchVendorLen=$((0x${blockbytes:$byteoffset*2+122:2})); | |
| patchVendor=$(pascalstring "${blockbytes:$byteoffset*2+122:$patchVendorLen*2+2}") | |
| patchPad="${blockbytes:$byteoffset*2+124+$patchVendorLen*2:$patchDescriptorLen*2 - (124+$patchVendorLen*2)}" | |
| printf "%03x: patchSig : '%s'\n" $((byteoffset+ 0)) "$patchSig" | |
| printf "%03x: majorVers : %d\n" $((byteoffset+ 4)) "$majorVers" | |
| printf "%03x: minorVers : %d\n" $((byteoffset+ 6)) "$minorVers" | |
| printf "%03x: flags : 0x%08x" $((byteoffset+ 8)) "$flags" | |
| if (( flags )); then | |
| flagtext=$( | |
| ((flags & 1)) && printf ",kRequiredPatch" | |
| ((flags & ~1)) && printf ",0x%08x?" $((flags & ~1)) | |
| ) | |
| printf " = %s" "${flagtext:1}" | |
| fi | |
| printf "\n" | |
| printf "%03x: patchOffset : %d\n" $((byteoffset+12)) "$patchOffset" | |
| printf "%03x: patchSize : %d\n" $((byteoffset+16)) "$patchSize" | |
| printf "%03x: patchCRC : 0x%08x\n" $((byteoffset+20)) "$patchCRC" | |
| printf "%03x: patchDescriptorLen : %d\n" $((byteoffset+24)) "$patchDescriptorLen" | |
| printf "%03x: patchName : %s\n" $((byteoffset+28)) "$patchName" | |
| printf "%03x: patchVendor : %s\n" $((byteoffset+61)) "$patchVendor" | |
| [[ -n $patchPad ]] && printf "%03x: patchPad : %s\n" $((byteoffset+62+patchVendorLen)) "$patchPad" | |
| ((byteoffset+=patchDescriptorLen)) | |
| done # for numPatches | |
| echo | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| elif [[ ${blockbytes:0:4} == "504d" ]]; then # 'PM' | |
| first_pmMapBlkCnt=$((0x${blockbytes:8:8})) | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): Partition Map contents (${first_pmMapBlkCnt} partitions)" | |
| for ((i=0;i<PLENGTH;i++)); do | |
| if (( i > 0 )); then | |
| blockbytes=$(dd if="/dev/$DRIVE" bs="$BLOCKSIZE" skip=$((PSTART + POFFSET + i)) count=1 2> /dev/null | xxd -p -c 9999) | |
| fi | |
| if [[ -n ${blockbytes//0/} ]]; then | |
| # Partition | |
| pmSig="${blockbytes:0:4}" | |
| pmSigPad=$((0x${blockbytes:4:4})) | |
| pmMapBlkCnt=$((0x${blockbytes:8:8})) | |
| pmPyPartStart=$((0x${blockbytes:16:8})) | |
| pmPartBlkCnt=$((0x${blockbytes:24:8})) | |
| pmPartName=$(sed -E 's/(00)+$//' <<< "${blockbytes:32:64}" | xxd -p -r | tr '\0' '.') | |
| pmParType=$(sed -E 's/(00)+$//' <<< "${blockbytes:96:64}" | xxd -p -r | tr '\0' '.') | |
| pmLgDataStart=$((0x${blockbytes:160:8})) | |
| pmDataCnt=$((0x${blockbytes:168:8})) | |
| pmPartStatus=$((0x${blockbytes:176:8})) | |
| pmLgBootStart=$((0x${blockbytes:184:8})) | |
| pmBootSize=$((0x${blockbytes:192:8})) | |
| pmBootAddr=$((0x${blockbytes:200:8})) | |
| pmBootAddr2=$((0x${blockbytes:208:8})) | |
| pmBootEntry=$((0x${blockbytes:216:8})) | |
| pmBootEntry2=$((0x${blockbytes:224:8})) | |
| pmBootCksum=$((0x${blockbytes:232:8})) | |
| pmProcessor=$(sed -E 's/(00)+$//' <<< "${blockbytes:240:32}" | xxd -p -r | tr '\0' '.') | |
| pmPad1="${blockbytes:272:256}" | |
| pmPad2=$(sed -E 's/(00)+$//' <<< "${blockbytes:528}") | |
| printf "%2d:" "$((i+1))" | |
| printf " %10d @ %-10d" \ | |
| "$pmPartBlkCnt" \ | |
| "$pmPyPartStart" | |
| [[ $pmSig != 504d ]] && printf " Sig:0x%s?" "$pmSig" | |
| [[ -n $pmParType ]] && printf " Type:\"%s\"" "$pmParType" | |
| [[ -n $pmPartName ]] && printf " Name:\"%s\"" "$pmPartName" | |
| (( pmSigPad )) && printf " SigPad:0x%04X" "$pmSigPad" | |
| (( pmMapBlkCnt != first_pmMapBlkCnt )) && printf " MapBlkCnt:%d" "$pmMapBlkCnt" | |
| (( pmLgDataStart )) && printf " LgDataStart:%d" "$pmLgDataStart" | |
| blockbytes2=${blockbytes:160} | |
| if [[ -n ${blockbytes2//0/} ]]; then | |
| (( pmDataCnt != pmPartBlkCnt )) && printf " DataCnt:%d" "$pmDataCnt" | |
| (( pmPartStatus )) && { | |
| statustext=$( | |
| (( pmPartStatus & 0x00000001 )) && printf ",Valid" # AUX | |
| (( pmPartStatus & 0x00000002 )) && printf ",Allocated" # AUX | |
| (( pmPartStatus & 0x00000004 )) && printf ",InUse" # AUX | |
| (( pmPartStatus & 0x00000008 )) && printf ",Bootable" # AUX | |
| (( pmPartStatus & 0x00000010 )) && printf ",Readable" # AUX | |
| (( pmPartStatus & 0x00000020 )) && printf ",Writeable" # AUX and Mac OS | |
| (( pmPartStatus & 0x00000040 )) && printf ",BootCodePositionIndependent" # AUX | |
| (( pmPartStatus & 0x00000080 )) && printf ",OSSpecific2" # ? | |
| (( pmPartStatus & 0x00000100 )) && printf ",ChainCompatible" # driver | |
| (( pmPartStatus & 0x00000200 )) && printf ",RealDeviceDriver" # driver | |
| (( pmPartStatus & 0x00000400 )) && printf ",CanChainToNext" # driver | |
| (( pmPartStatus & 0x40000000 )) && printf ",MountedAtStartup" # Mac OS | |
| (( pmPartStatus & 0x80000000 )) && printf ",Startup" # Mac OS | |
| (( pmPartStatus & 0x3FFFF800 )) && printf ",0x%x?" $((pmPartStatus & 0x3FFFF800)) | |
| ) | |
| printf " Status:%08X=%s" "$pmPartStatus" "${statustext:1}" | |
| } | |
| (( pmLgBootStart )) && printf " LgBootStart:%d" "$pmLgBootStart" | |
| (( pmBootSize )) && printf " BootSize:%d" "$pmBootSize" | |
| (( pmBootAddr )) && printf " BootAddr:0x%08X" "$pmBootAddr" | |
| (( pmBootAddr2 )) && printf " BootAddr2:0x%08X" "$pmBootAddr2" | |
| (( pmBootEntry )) && printf " BootEntry:0x%08X" "$pmBootEntry" | |
| (( pmBootEntry2 )) && printf " BootEntry2:0x%08X" "$pmBootEntry2" | |
| (( pmBootCksum )) && printf " BootCksum:0x%08X" "$pmBootCksum" | |
| [[ -n $pmProcessor ]] && printf " Processor:\"%s\"" "$pmProcessor" | |
| [[ -n ${pmPad1//0/} ]] && { | |
| printf " Pad1:%s" "$(sed -E 's/(00)+$//' <<< "${pmPad1}")" | |
| [ "${pmPad1:0:8}" = 70744452 ] && printf " = 'ptDR' = kPatchDriverSignature" | |
| [ "${pmPad1:0:8}" = 00010600 ] && printf "00 = kSCSIDriverSignature" | |
| [ "${pmPad1:0:8}" = 77696b69 ] && printf " = 'wiki' = kATADriverSignature" | |
| [ "${pmPad1:0:8}" = 43447672 ] && printf " = 'CDvr' = kSCSICDDriverSignature" | |
| [ "${pmPad1:0:8}" = 41545049 ] && printf " = 'ATPI' = kATAPIDriverSignature" | |
| [ "${pmPad1:0:8}" = 44535531 ] && printf " = 'DSU1' = kDriveSetupHFSSignature" | |
| } | |
| [[ -n $pmPad2 ]] && printf " Pad2:%s" "$pmPad2" | |
| fi | |
| printf "\n" | |
| fi | |
| done # for APM block | |
| elif [[ ${blockbytes:0:4} == "4c4b" ]]; then # 'LK' | |
| # Boot Blocks # https://developer.apple.com/library/archive/documentation/mac/pdf/Files/File_Manager.pdf | |
| # BootBlkHdr | |
| #bbID=$((0x${blockbytes:0:4})) | |
| bbEntry=$((0x${blockbytes:4:8})) | |
| bbVersion=$((0x${blockbytes:12:4})) | |
| bbPageFlags=$((0x${blockbytes:16:4})) | |
| bbSysName=$( pascalstring "${blockbytes:20:32}" ) | |
| bbShellName=$( pascalstring "${blockbytes:52:32}" ) | |
| bbDbg1Name=$( pascalstring "${blockbytes:84:32}" ) | |
| bbDbg2Name=$( pascalstring "${blockbytes:116:32}" ) | |
| bbScreenName=$(pascalstring "${blockbytes:148:32}" ) | |
| bbHelloName=$( pascalstring "${blockbytes:180:32}" ) | |
| bbScrapName=$( pascalstring "${blockbytes:212:32}" ) | |
| bbCntFCBs=$((0x${blockbytes:244:4})) | |
| bbCntEvts=$((0x${blockbytes:248:4})) | |
| bb128KSHeap=$((0x${blockbytes:252:8})) | |
| bb256KSHeap=$((0x${blockbytes:260:8})) | |
| bbSysHeapSize=$((0x${blockbytes:268:8})) | |
| filler=$((0x${blockbytes:276:4})) | |
| bbSysHeapExtra=$((0x${blockbytes:280:8})) | |
| bbSysHeapFract=$((0x${blockbytes:288:8})) | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): Boot Block Header contents" | |
| CODESTART=$((0x94)) | |
| printf "000: bbID : 'LK'\n" | |
| printf "002: bbEntry : 0x%08x" "$bbEntry" | |
| if (( (bbEntry & 0xffff0000) == 0x60000000 )); then | |
| CODESTART=$(((bbEntry & 0xffff) + 4)) | |
| printf " = BRA.S *+$%X to offset 0x%03x" $(( CODESTART - 2)) "$CODESTART" | |
| (( CODESTART - 2 == 0x8a )) && printf " (after old header)" | |
| (( CODESTART - 2 == 0x94 )) && printf " (after new header)" | |
| fi | |
| printf "\n" | |
| printf "006: bbVersion : 0x%04x" "$bbVersion" | |
| (( bbVersion )) && { | |
| printf " =" | |
| (( bbVersion & 0xff00 )) && { | |
| printf " flags:(" | |
| if (( bbVersion & 0x8000 )); then | |
| printf "new header" | |
| (( bbVersion & 0x4000 )) && printf ", execute boot code" | |
| (( bbVersion & 0x2000 )) && printf ", use relative system heap - bbSysHeapExtra and bbSysHeapFract instead of bbSysHeapSize" | |
| (( bbVersion & 0x1f00 )) && printf ", 0x%02x?" $(( (bbVersion & 0x1f00) >> 8 )) | |
| else | |
| printf "old header" | |
| (( bbVersion & 0x7f00 )) && printf ", 0x%02x?" $(( (bbVersion & 0x7f00) >> 8 )) | |
| fi | |
| printf ")" | |
| } | |
| (( bbVersion & 0xff )) && { | |
| (( bbVersion & 0xff00 )) && printf "," | |
| printf " version:%d.%d:(" $(( (bbVersion >> 4) & 15 )) $(( bbVersion & 15 )) | |
| (( (bbVersion & 0xff) < 0x15 )) && printf "ignore bb128KSHeap and bb256KSHeap and use default of bbSysHeapSize" | |
| (( (bbVersion & 0xff) >= 0x15 )) && printf "use bbSysHeapSize" # How is this different than < 0x15? the documentation for this is bad | |
| (( (bbVersion & 0xff) == 0xD )) && printf ", don't execute boot code" # I don't know if this is correct; the documentation for this is bad | |
| printf ")" | |
| } | |
| printf "\n" | |
| } | |
| ((bbPageFlags)) && { | |
| # https://developer.apple.com/library/archive/technotes/dv/dv_03.html#//apple_ref/doc/uid/DTS10002393 | |
| printf "008: bbPageFlags : 0x%04x =" "$bbPageFlags" | |
| if ((bbPageFlags & 0x8000)); then | |
| printf "allocate both video and sound buffers" | |
| else | |
| ((bbPageFlags > 0)) && printf "allocate only the secondary sound buffer" | |
| fi | |
| printf "\n" | |
| } | |
| printf "00a: bbSysName : %s\n" "$bbSysName" | |
| printf "01a: bbShellName : %s\n" "$bbShellName" | |
| printf "02a: bbDbg1Name : %s\n" "$bbDbg1Name" | |
| printf "03a: bbDbg2Name : %s\n" "$bbDbg2Name" | |
| printf "04a: bbScreenName : %s\n" "$bbScreenName" | |
| printf "05a: bbHelloName : %s\n" "$bbHelloName" | |
| printf "06a: bbScrapName : %s\n" "$bbScrapName" | |
| printf "07a: bbCntFCBs : %d\n" "$bbCntFCBs" | |
| printf "07c: bbCntEvts : %d\n" "$bbCntEvts" | |
| printf "07e: bb128KSHeap : %d\n" "$bb128KSHeap" | |
| printf "082: bb256KSHeap : %d\n" "$bb256KSHeap" | |
| printf "086: bbSysHeapSize : %d\n" "$bbSysHeapSize" | |
| ((CODESTART > 0x8a)) && printf "08a: filler : 0x%08x\n" "$filler" | |
| ((CODESTART > 0x8c)) && printf "08c: bbSysHeapExtra: 0x%08x\n" "$bbSysHeapExtra" | |
| ((CODESTART > 0x90)) && printf "090: bbSysHeapFract: 0x%08x\n" "$bbSysHeapFract" | |
| printf "%03x: boot code\n" "$CODESTART" | |
| echo | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| ((POFFSET++)) | |
| blockbytes=$(dd if="/dev/$DRIVE" bs="$BLOCKSIZE" skip=$((PSTART + POFFSET)) count=1 2> /dev/null | xxd -p -c 9999) | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): 2nd Boot Block contents" | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| ((POFFSET++)) | |
| POFFSETS="_${POFFSET}${POFFSETS}" | |
| #printf "queued first MDB after boot blocks: $POFFSETS\n" | |
| elif [[ ${blockbytes:0:4} == "D2D7" ]]; then # 'RW'+0x8080 | |
| # Master Directory Blocks # https://developer.apple.com/library/archive/documentation/mac/pdf/Files/File_Manager.pdf | |
| # MDB | |
| #drSigWord=${blockbytes:0x000*2:4} | |
| drCrDate=$((0x${blockbytes:0x002*2:8})) | |
| drLsBkUp=$((0x${blockbytes:0x006*2:8})) | |
| drAtrb=$((0x${blockbytes:0x00a*2:4})) | |
| drNmFls=$((0x${blockbytes:0x00c*2:4})) | |
| drDirSt=$((0x${blockbytes:0x00e*2:4})) | |
| drBlLen=$((0x${blockbytes:0x010*2:4})) | |
| drNmAlBlks=$((0x${blockbytes:0x012*2:4})) | |
| drAlBlkSiz=$((0x${blockbytes:0x014*2:8})) | |
| drClpSiz=$((0x${blockbytes:0x018*2:8})) | |
| drAlBlSt=$((0x${blockbytes:0x01c*2:4})) | |
| drNxtfNum=$((0x${blockbytes:0x01e*2:8})) | |
| drFreeBks=$((0x${blockbytes:0x022*2:4})) | |
| drVN="$(pascalstring "${blockbytes:0x024*2:28*2}")" | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): Master Directory Block contents" | |
| printf "000: drSigWord : 'RW'+0x8080 = kMFSSigWord\n" | |
| printf "002: drCrDate : %s\n" "$( ((drCrDate)) && date -r $((drCrDate-2082844800)))" | |
| printf "006: drLsBkUp : %s\n" "$( ((drLsBkUp )) && date -r $((drLsBkUp -2082844800)))" | |
| ((drAtrb)) && { | |
| attributestext=$( | |
| ((drAtrb & 0x0080)) && printf ",VolumeHardwareLock" | |
| ((drAtrb & 0x0100)) && printf ",VolumeUnmounted" | |
| ((drAtrb & 0x0200)) && printf ",VolumeSparedBlocks" | |
| ((drAtrb & 0x0400)) && printf ",VolumeNoCacheRequired" | |
| ((drAtrb & 0x0800)) && printf ",BootVolumeInconsistent" | |
| ((drAtrb & 0x1000)) && printf ",CatalogNodeIDsReused" | |
| ((drAtrb & 0x2000)) && printf ",VolumeJournaled" | |
| ((drAtrb & 0x4000)) && printf ",VolumeInconsistent" | |
| ((drAtrb & 0x8000)) && printf ",VolumeSoftwareLock" | |
| ((drAtrb & 0x007f)) && printf ",0x%04x?" $((drAtrb & 0x007f)) | |
| ) | |
| printf "00a: drAtrb : 0x%04x = %s\n" "$drAtrb" "${attributestext:1}" | |
| } | |
| printf "00c: drNmFls : %d\n" "$drNmFls" | |
| printf "00e: drDirSt : %d\n" "$drDirSt" | |
| printf "010: drBlLen : %d\n" "$drBlLen" | |
| printf "012: drNmAlBlks : %d\n" "$drNmAlBlks" | |
| printf "014: drAlBlkSiz : %d\n" "$drAlBlkSiz" | |
| printf "018: drClpSiz : %d\n" "$drClpSiz" | |
| printf "01c: drAlBlSt : %d\n" "$drAlBlSt" | |
| printf "01e: drNxtfNum : %d\n" "$drNxtfNum" | |
| printf "022: drFreeBks : %d\n" "$drFreeBks" | |
| printf "024: drVN : %s\n" "$drVN" | |
| echo | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| if ((POFFSET < drNmAlBlks * (drAlBlkSiz / 512) - 2)); then | |
| # queue up location of alternate Master Directory Block | |
| POFFSETS="_$((PLENGTH-2))_$((PLENGTH-1))${POFFSETS}" | |
| #printf "queued 2alternate MDB: $POFFSETS \n" | |
| fi | |
| elif [[ ${blockbytes:0:4} == "4244" ]]; then # 'BD' | |
| # Master Directory Blocks # https://developer.apple.com/library/archive/documentation/mac/pdf/Files/File_Manager.pdf | |
| # MDB | |
| #drSigWord=${blockbytes:0x000*2:4} | |
| drCrDate=$((0x${blockbytes:0x002*2:8})) | |
| drLsMod=$((0x${blockbytes:0x006*2:8})) | |
| drAtrb=$((0x${blockbytes:0x00a*2:4})) | |
| drNmFls=$((0x${blockbytes:0x00c*2:4})) | |
| drVBMSt=$((0x${blockbytes:0x00e*2:4})) | |
| drAllocPtr=$((0x${blockbytes:0x010*2:4})) | |
| drNmAlBlks=$((0x${blockbytes:0x012*2:4})) | |
| drAlBlkSiz=$((0x${blockbytes:0x014*2:8})) | |
| drClpSiz=$((0x${blockbytes:0x018*2:8})) | |
| drAlBlSt=$((0x${blockbytes:0x01c*2:4})) | |
| drNxtCNID=$((0x${blockbytes:0x01e*2:8})) | |
| drFreeBks=$((0x${blockbytes:0x022*2:4})) | |
| drVN="$(pascalstring "${blockbytes:0x024*2:28*2}")" | |
| drVolBkUp=$((0x${blockbytes:0x040*2:8})) | |
| drVSeqNum=$((0x${blockbytes:0x044*2:4})) | |
| drWrCnt=$((0x${blockbytes:0x046*2:8})) | |
| drXTClpSiz=$((0x${blockbytes:0x04a*2:8})) | |
| drCTClpSiz=$((0x${blockbytes:0x04e*2:8})) | |
| drNmRtDirs=$((0x${blockbytes:0x052*2:4})) | |
| drFilCnt=$((0x${blockbytes:0x054*2:8})) | |
| drDirCnt=$((0x${blockbytes:0x058*2:8})) | |
| drFndrInfo="${blockbytes:0x05c*2:64}" | |
| drVCSize=$((0x${blockbytes:0x07c*2:4})) | |
| drVBMCSize=$((0x${blockbytes:0x07e*2:4})) | |
| drCtlCSize=$((0x${blockbytes:0x080*2:4})) | |
| drEmbedSigWord="$drVCSize" | |
| drEmbedExtent_startBlock="$drVBMCSize" | |
| drEmbedExtent_blockCount="$drCtlCSize" | |
| drXTFlSize=$((0x${blockbytes:0x082*2:8})) | |
| drXTExtRec="${blockbytes:0x086*2:24}" | |
| drCTFlSize=$((0x${blockbytes:0x092*2:8})) | |
| drCTExtRec="${blockbytes:0x096*2:24}" | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): Master Directory Block contents" | |
| printf "000: drSigWord : 'BD' = kHFSSigWord\n" | |
| printf "002: drCrDate : %s\n" "$( ((drCrDate)) && date -r $((drCrDate-2082844800)))" | |
| printf "006: drLsMod : %s\n" "$( ((drLsMod )) && date -r $((drLsMod -2082844800)))" | |
| ((drAtrb)) && { | |
| attributestext=$( | |
| ((drAtrb & 0x0080)) && printf ",VolumeHardwareLock" | |
| ((drAtrb & 0x0100)) && printf ",VolumeUnmounted" | |
| ((drAtrb & 0x0200)) && printf ",VolumeSparedBlocks" | |
| ((drAtrb & 0x0400)) && printf ",VolumeNoCacheRequired" | |
| ((drAtrb & 0x0800)) && printf ",BootVolumeInconsistent" | |
| ((drAtrb & 0x1000)) && printf ",CatalogNodeIDsReused" | |
| ((drAtrb & 0x2000)) && printf ",VolumeJournaled" | |
| ((drAtrb & 0x4000)) && printf ",VolumeInconsistent" | |
| ((drAtrb & 0x8000)) && printf ",VolumeSoftwareLock" | |
| ((drAtrb & 0x007f)) && printf ",0x%04x?" $((drAtrb & 0x007f)) | |
| ) | |
| printf "00a: drAtrb : 0x%04x = %s\n" "$drAtrb" "${attributestext:1}" | |
| } | |
| printf "00c: drNmFls : %d\n" "$drNmFls" | |
| printf "00e: drVBMSt : %d\n" "$drVBMSt" | |
| printf "010: drAllocPtr : %d\n" "$drAllocPtr" | |
| printf "012: drNmAlBlks : %d\n" "$drNmAlBlks" | |
| printf "014: drAlBlkSiz : %d\n" "$drAlBlkSiz" | |
| printf "018: drClpSiz : %d\n" "$drClpSiz" | |
| printf "01c: drAlBlSt : %d\n" "$drAlBlSt" | |
| printf "01e: drNxtCNID : %d\n" "$drNxtCNID" | |
| printf "022: drFreeBks : %d\n" "$drFreeBks" | |
| printf "024: drVN : %s\n" "$drVN" | |
| printf "040: drVolBkUp : %s\n" "$( ((drVolBkUp)) && date -r $((drVolBkUp-2082844800)))" | |
| printf "044: drVSeqNum : %d\n" "$drVSeqNum" | |
| printf "046: drWrCnt : %d\n" "$drWrCnt" | |
| printf "04a: drXTClpSiz : %d\n" "$drXTClpSiz" | |
| printf "04e: drCTClpSiz : %d\n" "$drCTClpSiz" | |
| printf "052: drNmRtDirs : %d\n" "$drNmRtDirs" | |
| printf "054: drFilCnt : %d\n" "$drFilCnt" | |
| printf "058: drDirCnt : %d\n" "$drDirCnt" | |
| printf "05c: drFndrInfo:\n" | |
| printf "05c: Blessed System Folder : %s\n" "$(inodestring "$DEVICEID" "$((0x${drFndrInfo: 0: 8}))" )" | |
| printf "060: Blessed System File : %s\n" "$(inodestring "$DEVICEID" "$((0x${drFndrInfo: 8: 8}))" )" | |
| printf "064: Open-folder linked list : %s\n" "$(inodestring "$DEVICEID" "$((0x${drFndrInfo:16: 8}))" )" | |
| printf "068: Alternate OS blessed file/folder : %s\n" "$(inodestring "$DEVICEID" "$((0x${drFndrInfo:24: 8}))" )" | |
| dumpencoding "06c: Text encoding :" "$((0x${drFndrInfo:32: 8}))" | |
| printf "070: OS X blessed folder : %s\n" "$(inodestring "$DEVICEID" "$((0x${drFndrInfo:40: 8}))" )" | |
| printf "074: 64-bit VSDB volume id : 0x%016X => Volume UUID: %s\n" $((0x${drFndrInfo:48:16})) "$(vsdbtouuid "${drFndrInfo:48:16}")" | |
| if (( drEmbedSigWord == 0x482b )); then | |
| printf "07c: drEmbedSigWord: %d = 'H+' = kHFSPlusSigWord\n" "$drEmbedSigWord" | |
| printf "07e: drEmbedExtent.startBlock : %d\n" "$drEmbedExtent_startBlock" | |
| printf "080: drEmbedExtent.blockCount : %d\n" "$drEmbedExtent_blockCount" | |
| else | |
| printf "07c: drVCSize : %d\n" "$drVCSize" | |
| printf "07e: drVBMCSize : %d\n" "$drVBMCSize" | |
| printf "080: drCtlCSize : %d\n" "$drCtlCSize" | |
| fi | |
| printf "082: drXTFlSize : %d\n" "$drXTFlSize" | |
| printf "086: drXTExtRec : %s\n" "$(extentstring "$drXTExtRec")" | |
| printf "092: drCTFlSize : %d\n" "$drCTFlSize" | |
| printf "096: drCTExtRec : %s\n" "$(extentstring "$drCTExtRec")" | |
| echo | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| if ((POFFSET < drNmAlBlks * (drAlBlkSiz / 512) - 2)); then | |
| # queue up location of alternate Master Directory Block | |
| POFFSETS="_$((PLENGTH-2))_$((PLENGTH-1))${POFFSETS}" | |
| #printf "queued 2alternate MDB: $POFFSETS \n" | |
| if (( drEmbedSigWord == 0x482b )); then | |
| # queue up location of wrapped HFS+ partition | |
| hfsplusblock=$(( drAlBlSt + drEmbedExtent_startBlock * (drAlBlkSiz / 512) )) | |
| POFFSETS="${POFFSETS}_$((hfsplusblock))_$((hfsplusblock+1))_$((hfsplusblock+2))" | |
| #printf "queued 3wrapped: $POFFSETS \n" | |
| fi | |
| fi | |
| elif [[ ${blockbytes:0:4} == "482b" ]] || [[ ${blockbytes:0:4} == "4858" ]]; then # 'H+' or 'HX' | |
| # Master Directory Blocks # https://developer.apple.com/library/archive/documentation/mac/pdf/Files/File_Manager.pdf | |
| # HFS Plus Volume Header | |
| signature=$((0x${blockbytes:0x000*2:4})) | |
| signatureChars=$(sed -E 's/(00)+$//' <<< "${blockbytes:0x000*2:4}" | xxd -p -r | tr '\0' '.') | |
| version=$((0x${blockbytes:0x002*2:4})) | |
| attributes=$((0x${blockbytes:0x004*2:8})) | |
| lastMountedVersion=$((0x${blockbytes:0x008*2:8})) | |
| lastMountedVersionChars=$(sed -E 's/(00)+$//' <<< "${blockbytes:0x008*2:8}" | xxd -p -r | tr '\0' '.') | |
| journalInfoBlock=$((0x${blockbytes:0x00c*2:8})) | |
| createDate=$((0x${blockbytes:0x010*2:8})) | |
| modifyDate=$((0x${blockbytes:0x014*2:8})) | |
| backupDate=$((0x${blockbytes:0x018*2:8})) | |
| checkedDate=$((0x${blockbytes:0x01c*2:8})) | |
| fileCount=$((0x${blockbytes:0x020*2:8})) | |
| folderCount=$((0x${blockbytes:0x024*2:8})) | |
| mdbblockSize=$((0x${blockbytes:0x028*2:8})) | |
| totalBlocks=$((0x${blockbytes:0x02c*2:8})) | |
| freeBlocks=$((0x${blockbytes:0x030*2:8})) | |
| nextAllocation=$((0x${blockbytes:0x034*2:8})) | |
| rsrcClumpSize=$((0x${blockbytes:0x038*2:8})) | |
| dataClumpSize=$((0x${blockbytes:0x03c*2:8})) | |
| nextCatalogID=$((0x${blockbytes:0x040*2:8})) | |
| writeCount=$((0x${blockbytes:0x044*2:8})) | |
| encodingsBitmap=$((0x${blockbytes:0x048*2:16})) | |
| finderInfo="${blockbytes:0x050*2:64}" | |
| allocationFile="${blockbytes:0x070*2:160}" | |
| extentsFile="${blockbytes:0x0c0*2:160}" | |
| catalogFile="${blockbytes:0x110*2:160}" | |
| attributesFile="${blockbytes:0x160*2:160}" | |
| startupFile="${blockbytes:0x1b0*2:160}" | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): HFS Plus Volume Header contents" | |
| printf "000: signature : '%s'" "$signatureChars" | |
| ((signature == 0x482b )) && printf " = kHFSPlusSigWord" | |
| ((signature == 0x4858 )) && printf " = kHFSXSigWord" | |
| printf "\n" | |
| printf "002: version : %d" "$version" | |
| ((version == 4 )) && printf " = kHFSPlusVersion" | |
| ((version == 5 )) && printf " = kHFSXVersion" | |
| printf "\n" | |
| ((attributes)) && { | |
| attributestext=$( | |
| ((attributes & 0x00000080)) && printf ",VolumeHardwareLock" | |
| ((attributes & 0x00000100)) && printf ",VolumeUnmounted" | |
| ((attributes & 0x00000200)) && printf ",VolumeSparedBlocks" | |
| ((attributes & 0x00000400)) && printf ",VolumeNoCacheRequired" | |
| ((attributes & 0x00000800)) && printf ",BootVolumeInconsistent" | |
| ((attributes & 0x00001000)) && printf ",CatalogNodeIDsReused" | |
| ((attributes & 0x00002000)) && printf ",VolumeJournaled" | |
| ((attributes & 0x00004000)) && printf ",VolumeInconsistent" | |
| ((attributes & 0x00008000)) && printf ",VolumeSoftwareLock" | |
| ((attributes & 0x40000000)) && printf ",ContentProtection" | |
| ((attributes & 0x80000000)) && printf ",UnusedNodeFix" | |
| ((attributes & 0x3fff007f)) && printf ",0x%08x?" $((drAtrb & 0x3fff007f)) | |
| ) | |
| printf "004: attributes : 0x%08x = %s\n" "$attributes" "${attributestext:1}" | |
| } | |
| printf "008: lastMountedVersion : 0x%08x = '%s'" "$lastMountedVersion" "$lastMountedVersionChars" | |
| ((lastMountedVersion == 0x31302E30 )) && printf " = kHFSPlusMountVersion" # '10.0'; (Mac OS 8.1 to 9.2.2 is '8.10') | |
| ((lastMountedVersion == 0x4846534a )) && printf " = kHFSJMountVersion" | |
| ((lastMountedVersion == 0x46534b21 )) && printf " = kFSKMountVersion" | |
| printf "\n" | |
| ((journalInfoBlock || (attributes & 0x00002000) )) && printf "00c: journalInfoBlock : %d\n" "$journalInfoBlock" | |
| printf "010: createDate : %s\n" "$( ((createDate )) && date -r $((createDate -2082844800)))" | |
| printf "014: modifyDate : %s\n" "$( ((modifyDate )) && date -r $((modifyDate -2082844800)))" | |
| printf "018: backupDate : %s\n" "$( ((backupDate )) && date -r $((backupDate -2082844800)))" | |
| printf "01c: checkedDate : %s\n" "$( ((checkedDate)) && date -r $((checkedDate-2082844800)))" | |
| printf "020: fileCount : %d\n" "$fileCount" | |
| printf "024: folderCount : %d\n" "$folderCount" | |
| printf "028: blockSize : %d\n" "$mdbblockSize" | |
| printf "02c: totalBlocks : %d\n" "$totalBlocks" | |
| printf "030: freeBlocks : %d\n" "$freeBlocks" | |
| printf "034: nextAllocation : %d\n" "$nextAllocation" | |
| printf "038: rsrcClumpSize : %d\n" "$rsrcClumpSize" | |
| printf "03c: dataClumpSize : %d\n" "$dataClumpSize" | |
| printf "040: nextCatalogID : %d\n" "$nextCatalogID" | |
| printf "044: writeCount : %d\n" "$writeCount" | |
| ((encodingsBitmap)) && { | |
| encodings=$( | |
| ((encodingsBitmap & (1<< 0))) && printf ",MacRoman" | |
| ((encodingsBitmap & (1<< 1))) && printf ",MacJapanese" | |
| ((encodingsBitmap & (1<< 2))) && printf ",MacChineseTrad" | |
| ((encodingsBitmap & (1<< 3))) && printf ",MacKorean" | |
| ((encodingsBitmap & (1<< 4))) && printf ",MacArabic" | |
| ((encodingsBitmap & (1<< 5))) && printf ",MacHebrew" | |
| ((encodingsBitmap & (1<< 6))) && printf ",MacGreek" | |
| ((encodingsBitmap & (1<< 7))) && printf ",MacCyrillic" | |
| ((encodingsBitmap & (1<< 9))) && printf ",MacDevanagari" | |
| ((encodingsBitmap & (1<<10))) && printf ",MacGurmukhi" | |
| ((encodingsBitmap & (1<<11))) && printf ",MacGujarati" | |
| ((encodingsBitmap & (1<<12))) && printf ",MacOriya" | |
| ((encodingsBitmap & (1<<13))) && printf ",MacBengali" | |
| ((encodingsBitmap & (1<<14))) && printf ",MacTamil" | |
| ((encodingsBitmap & (1<<15))) && printf ",MacTelugu" | |
| ((encodingsBitmap & (1<<16))) && printf ",MacKannada" | |
| ((encodingsBitmap & (1<<17))) && printf ",MacMalayalam" | |
| ((encodingsBitmap & (1<<18))) && printf ",MacSinhalese" | |
| ((encodingsBitmap & (1<<19))) && printf ",MacBurmese" | |
| ((encodingsBitmap & (1<<20))) && printf ",MacKhmer" | |
| ((encodingsBitmap & (1<<21))) && printf ",MacThai" | |
| ((encodingsBitmap & (1<<22))) && printf ",MacLaotian" | |
| ((encodingsBitmap & (1<<23))) && printf ",MacGeorgian" | |
| ((encodingsBitmap & (1<<24))) && printf ",MacArmenian" | |
| ((encodingsBitmap & (1<<25))) && printf ",MacChineseSimp" | |
| ((encodingsBitmap & (1<<26))) && printf ",MacTibetan" | |
| ((encodingsBitmap & (1<<27))) && printf ",MacMongolian" | |
| ((encodingsBitmap & (1<<28))) && printf ",MacEthiopic" | |
| ((encodingsBitmap & (1<<29))) && printf ",MacCentralEurRoman" | |
| ((encodingsBitmap & (1<<30))) && printf ",MacVietnamese" | |
| ((encodingsBitmap & (1<<31))) && printf ",MacExtArabic" | |
| ((encodingsBitmap & (1<<33))) && printf ",MacSymbol" | |
| ((encodingsBitmap & (1<<34))) && printf ",MacDingbats" | |
| ((encodingsBitmap & (1<<35))) && printf ",MacTurkish" | |
| ((encodingsBitmap & (1<<36))) && printf ",MacCroatian" | |
| ((encodingsBitmap & (1<<37))) && printf ",MacIcelandic" | |
| ((encodingsBitmap & (1<<38))) && printf ",MacRomanian" | |
| ((encodingsBitmap & (1<<48))) && printf ",MacUkrainian" | |
| ((encodingsBitmap & (1<<49))) && printf ",MacFarsi" | |
| ((encodingsBitmap & (0xfffcff81 << 32))) && printf ",0x%016x?" $((encodingsBitmap & (0xfffcff81 << 32))) | |
| ) | |
| printf "048: encodingsBitmap : 0x%016x = %s" "$encodingsBitmap" "${encodings:1}" | |
| } | |
| printf "\n" | |
| printf "050: finderInfo:\n" | |
| printf "050: Blessed System Folder : %s\n" "$(inodestring "$DEVICEID" "$((0x${finderInfo: 0: 8}))" )" | |
| printf "054: Blessed System File : %s\n" "$(inodestring "$DEVICEID" "$((0x${finderInfo: 8: 8}))" )" | |
| printf "058: Open-folder linked list : %s\n" "$(inodestring "$DEVICEID" "$((0x${finderInfo:16: 8}))" )" | |
| printf "05c: Alternate OS blessed file/folder : %s\n" "$(inodestring "$DEVICEID" "$((0x${finderInfo:24: 8}))" )" | |
| dumpencoding "060: Text encoding :" "$((0x${finderInfo:32: 8}))" | |
| printf "064: OS X blessed folder : %s\n" "$(inodestring "$DEVICEID" "$((0x${finderInfo:40: 8}))" )" | |
| printf "068: 64-bit VSDB volume id : 0x%016X => Volume UUID: %s\n" $(((0x${finderInfo:48:8} << 32) | 0x${finderInfo:56:8} )) "$(vsdbtouuid "${finderInfo:48:16}")" | |
| printf "070: allocationFile : %s\n" "$(forkdatastring "$allocationFile" )" | |
| printf "0c0: extentsFile : %s\n" "$(forkdatastring "$extentsFile" )" | |
| printf "110: catalogFile : %s\n" "$(forkdatastring "$catalogFile" )" | |
| printf "160: attributesFile : %s\n" "$(forkdatastring "$attributesFile" )" | |
| printf "1b0: startupFile : %s\n" "$(forkdatastring "$startupFile" )" | |
| echo | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| if ((POFFSET < PLENGTH-4)); then | |
| # queue up location of alternate HFS Plus Volume Header | |
| # HFS+ volume starts at POFFSET - 2 | |
| endoffset=$((POFFSET - 2 + totalBlocks * (mdbblockSize / 512))) | |
| POFFSETS="_$((endoffset - 2))_$((endoffset - 1))${POFFSETS}" | |
| #printf "queued alternate HFS+ Header: $POFFSETS\n" | |
| fi | |
| else | |
| if (( 0x${blockbytes:0x20*2:8} == 0x4e585342 )); then # 'NXSB' | |
| echo | |
| echo "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): APFS Container contents" | |
| else | |
| HASH=$(xxd -p -r <<< "${blockbytes:96*2:416*2}" | xxd -p | md5) | |
| FindHash "$PARTTYPE $PNUM @ ${PSTART}$( ((POFFSET > 0)) && printf "+%d" "$POFFSET" ): $( | |
| ((POFFSET==0)) && printf "VBR " | |
| )contents" "$VBRHASHES" "$HASH" | |
| fi | |
| if [[ -n ${blockbytes//0/} ]]; then | |
| xxd -p -r <<< "$blockbytes" | xxd -c 16 | |
| elif [[ -z $POFFSETS ]] && (( POFFSET < 2 )); then | |
| POFFSETS="_$((POFFSET+1))" | |
| #printf "queued after zero block: $POFFSETS\n" | |
| fi | |
| fi | |
| done # while POFFSETS | |
| done # while THEPART | |
| done | |
| #exit | |
| if [[ -n $INPUTDISK ]]; then | |
| echo "===============================================================================" | |
| ioreg -w 0 -c IOMedia | sed -n -E "/^[ \|]*\+\-o (.*) <class IOMedia[^a-zA-Z]/,/^[ \|]+ }$/p" | { | |
| sed -n -E "/^[ \|]*\+\-o (.*)/s//\1/p;/^[ \|]* \| (.*)/s//\1/p;" | |
| } | { | |
| perl -0777 -n -e 'while (/(^.*? <class IOMedia.*\n\{\n( .*\n)*^ "BSD Name" = "('"$INPUTDISK"'[^\d]|'"${INPUTDISK/s[0-9]*/}"'").*\n( .*\n)*?^}\n)/mg) { printf "$1\n" }' | |
| } | |
| echo "===============================================================================" | |
| else | |
| echo "===============================================================================" | |
| ioreg -w 0 -c IOMedia | sed -n -E "/^[ \|]*\+\-o (.*) <class IOMedia[^a-zA-Z]/,/^[ \|]+ }$/p" | sed -n -E "/^[ \|]*\+\-o (.*)/s//\1/p;/^[ \|]* \| (.*)/s//\1/p;" | |
| echo "===============================================================================" | |
| bless --verbose --getBoot | |
| echo "===============================================================================" | |
| bless --verbose --info | |
| echo "===============================================================================" | |
| #ioreg -rw 0 -n AppleEFINVRAM 2> /dev/null | sed -n -E "/^[ \|]+[ ]+(\".*)$/s//\1/p;" | |
| ioreg -lw0 -p IODeviceTree 2> /dev/null | sed -nE '/o chosen/,/}$/p ; /o option/,/}$/p ; /o aliases/,/}$/p ' | sed -nE "/^[ \|]+[ ]+(\".*)$/s// \1/p; / +\+\-o (.*) +<class ([^,]+).*/s//\1 <\2>/p" | |
| echo "===============================================================================" | |
| fi |
Updates
Aug 28, 2025:
- Fixes and improvements.
Mar 20, 2025:
- Add MFS partition support.
- Add support for whole disk partition (no partition map).
Mar 16, 2024:
- Fixes for Mac OS X 10.5 Leopard.
- Fixed an issue where embedded HFS+ info was not output when a block is unexpectedly not all zeros.
Jul 28, 2023:
- Bug fixes.
May 20, 2023:
- Added 64-bit VSDB volume id to Volume UUID conversion for HFS and HFS+ partitions.
Dec 2, 2022:
- Fixes for Mac OS X 10.4 Tiger.
- Added the following (related to Mac booting from Open Firmware or EFI):
- Apple Partition Map
- APM Patch Driver
- HFS/HFS+ Boot Blocks
- HFS Wrapper
- HFS Master Directory Block
- HFS+ Volume Header
chosen,aliasesoptions(was previously output only for Intel Macs)
- Pass a disk name (e.g.
disk0) or a disk slice (e.g.disk0s2) to reduce the output to a single drive or partition.
what is b3e20f39f29211d697a400306543ecac ?
what is b3e20f39f29211d697a400306543ecac ?
It is b3e20f39-f292-11d6-97a4-00306543ecac or HFS_UUID_NAMESPACE_ID - a 128-bit UUID which is prepended to the 64-bit VSDB volume id of a HFS partition. The 192-bit result is passed to MD5 to generate a 128-bit Mac OS X volume UUID.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Download
Run
Notes
Run the command before and after making partition changes. Use a different file name each time. This way you can compare the files to see what changed and to make sure the changes are correct.
This script does not list boot files. Do that manually for each partition you think should be bootable. The boot code in a VBR (also know as PBR) should show what file it's looking to boot. For Windows 7 and later, it should be BOOTMGR. That file should exist in the root directory with a Boot folder containing a BCD folder containing a BCD file. The BCD file contains a menu of items to be bootable and boot options. Use EasyBCD.exe to edit the BCD of any partition. The files may be invisible in macOS - type Command-Shift-Period to show invisible files in the Finder.