Last active
March 16, 2024 10:31
-
-
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 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 16, 2024 | |
# 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="" | |
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" | |
[[ -n $thepath ]] && printf " => \"%s\"" "$thepath" | |
} | |
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" | |
(( (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" | |
} | |
} | |
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")" | |
for PARTDRIVE in $(echo $DRIVELIST); do | |
DRIVE=$(expr "$PARTDRIVE" : '.*_\([^_]*\)') | |
DTYPE=$(expr "$PARTDRIVE" : '\(.*\)_[^_]*') | |
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 | |
MOUNTPOINTS="" | |
if [[ -n $INPUTSLICE ]]; then | |
DEVLIST="$(echo "$diskutiloutput" | sed -n -E "/^.* (disk[0-9]+s${INPUTSLICE})$/s//\1/p")" | |
else | |
DEVLIST="$(echo "$diskutiloutput" | sed -n -E "/^.* (disk[0-9]+s[0-9]+)$/s//\1/p")" | |
fi | |
for THEDEV in $(echo $DEVLIST); do | |
echo "---------------------------------------------" | |
partinfo="$(diskutil info "$THEDEV")" | |
echo "$partinfo" | |
MOUNTPOINTS="${MOUNTPOINTS}${THEDEV}_$(sed -nE '/ *Mount Point: *(.*)/s//\1/p' <<< "$partinfo")"$'\n' | |
done | |
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 | |
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]*_\(.*\)') | |
DEVICEID=$(stat -f "%r" "/dev/${DRIVE}s$PNUM") | |
POFFSETS="_0" | |
while [[ -n $POFFSETS ]]; do | |
POFFSET=$( expr "$POFFSETS" : '_\([0-9]*\)') | |
POFFSETS=$( expr "$POFFSETS" : '_[0-9]*\(_.*\)') | |
if (( POFFSET >= PLENGTH )); then | |
continue | |
fi | |
blockbytes=$(dd if="/dev/$DRIVE" bs="$BLOCKSIZE" skip=$((PSTART + POFFSET)) count=1 2> /dev/null | xxd -p -c 9999) | |
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} == "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})) | |
blockSize=$((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" "$blockSize" | |
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 * (blockSize / 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
March 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.
July 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
,aliases
options
(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.
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.