Contribute
Register

<< Solved >> Partition map needs to be repaired. Recovery mode??

Here are the contents of EFIClone-v4.sh:

Code:
#!/bin/bash
# EFI Partition Clone Script
# (c) 2018 - Ted Howe
# [email protected]
# wombat94 on TonyMacx86 forums and GitHub.

#Release Notes
# version 0.1beta3
# 2/24/2018
# - Updated to use rsync to handle the deletion of information from the desination drive. Basically,
#   allow rsync to do its thing
# - Added additional check, based on clover boot log retrieved with bdmesg, to ensure that the
#   destination EFI partition is NOT the EFI partition that was used to boot the computer.
# version 0.1beta2
# 2/23/2018
# Clean up and combine.
# -Combined the two versions of the script into a single script that detects whether it was called from
#      Carbon Copy Cloner or SuperDuper! and behaves accordingly in the setup.
# -Added validation of the copied data through SHASUM hashing of all visible files in the source and
#      destination EFI partitions after the delete/rsync copy is complete
# -Changed the method of copying files from cp to rsync
# -Modified the copy path of the source to include the root directory - not just the /EFI/ directory
#      and subdirectories.
#----------------------------------------------------------------------------------------------------------
# version 0.1beta1
# 2/22/2018
# Initial Release
#----------------------------------------------------------------------------------------------------------

# This script is designed to be a "post-flight" script run automatically by CCC at the end of a
# clone task. It will copy the contents of the source drive's EFI partition to the destination drive's EFI
# partition. It will COMPLETELY DELETE and replace all data on the destination EFI partition.

#!!! This script DOES delete data and therefore has the potential to cause unintended data loss.
# I have tried to think of any scenario that would cause unintended data loss, but as with all software, ther
# may be bugs and therefore there is a small risk.
# ONLY USE THIS SCRIPT IN THE MANNER DESCRIBED. I PROVIDE NO WARRANTY ON THE USE OF THIS SCRIPT
# PROCEED AT YOUR OWN RISK.

#user variables

# Determine which version of macOS we're on
macOS="Something"

if [[ "$#" == "4" ]]; then
    os_ver=${5-:$(sw_vers -productVersion)}
    if [[ "$os_ver" == *10.15* ]]; then
        macOS="Catalina"
    elif [[ "$os_ver" == *10.14* ]]; then
        macOS="Mojave"
    elif [[ "$os_ver" == *10.13* ]]; then
        macOS="High Sierra"
    elif [[ "$os_ver" == *10.12* ]]; then
        macOS="Sierra"
    fi
fi

if [[ "$macOS" == "Something" ]]; then
    echo "Unsupported O/S. Quitting."
    exit 1
fi

#Log File location. By default, this will write to the same directory where the cccEFIClone.sh script lives.
# you may edit it, but be sure the location will always exist and you have permissions to write to the location
# It appears that Carbon Copy Cloner runs the script in the root of the boot drive, so that is where the log will go
# if you don't put in a literal path below.
if [[ "$macOS" == "Catalina" ]]; then
    WORKING_DIR="/System/Volumes/Data/Users/Shared"
else
    WORKING_DIR=/
fi

LOG_FILE="$WORKING_DIR/EFIClone.log"
cd $WORKING_DIR

# remove any preexisting log file to avoid filling up disk space
if [[ -f "$LOG_FILE" ]]; then
    rm $LOG_FILE
fi

#test switch. The script is distributed with this switch set to "Y".
# the script will not delete or copy any data when this is set to Y
# in order to make the script take action, once you have verified that it can identify the right locations to
# copy from and to on your system, change this variable to N
TEST_SWITCH="Y"

#----- END OF USER VARIABLES
#----- THERE IS NO NEED TO EDIT BELOW THIS LINE
#----------------------------------------------

function writeTolog () {
    echo "[`date`] - ${*}" >> ${LOG_FILE}
}

function getDiskNumber () {
    echo "$( diskutil info "$1" | grep 'Part of Whole' | rev | cut -d ' ' -f1 | rev )"
}

function getCoreStoragePhysicalDiskNumber () {
    echo "$( diskutil info "$1" | grep 'PV UUID' | rev | cut -d '(' -f1 | cut -d ')' -f2 | rev | cut -d 'k' -f2 | cut -d 's' -f1 )"
}

function getAPFSPhysicalDiskNumber () {
    echo "$( diskutil apfs list | grep -A 9 "Container $1" | grep "APFS Physical Store" | rev | cut -d ' ' -f 1 | cut -d 's' -f 2 | cut -d 'k' -f 1 )"
}

function getEFIVolume () {
    echo "$( diskutil list | grep "$1s" | grep "EFI" | rev | cut -d ' ' -f 1 | rev )"
}

function getDiskMountPoint () {
    echo "$( diskutil info "$1" | grep 'Mount Point' | rev | cut -d ':' -f 1 | rev | awk '{$1=$1;print}' )"
}

function getEFIDirectoryHash () {
    #echo "$( find -s . -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum | cut -d ' ' -f 1 | shasum  )"
    echo "$( find -s . -not -path '*/\.*' -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum | shasum )"
}

function logEFIDirectoryHashDetails () {
    #echo "$( find -s . -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum | cut -d ' ' -f 1 | shasum  )"
    echo "$( find -s . -not -path '*/\.*' -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum )" >> ${LOG_FILE}
}

function getSystemBootVolumeName () {
    echo "$( system_profiler SPSoftwareDataType | grep 'Boot Volume' | rev | cut -d ':' -f 1 | rev | awk '{$1=$1;print}' )"
}

function getCurrentBootEFIVolumeUUID () {
    echo "$( bdmesg | grep 'SelfDevicePath' | rev | cut -d ')' -f 2 | rev | cut -d ',' -f 3 )"
}

function getDeviceIDfromUUID () {
    echo "$( diskutil info "$1" | grep 'Device Identifier' | rev | cut -d ' ' -f 1 | rev )"
}

function getDiskIDfromUUID () {
    writeTolog "$1"
    echo "$( diskutil info "$1" | grep 'Device Identifier' | rev | cut -d ' ' -f 1 | rev )"
}

#begin logging
writeTolog "***** EFI Clone Script start"
writeTolog "working directory = $PWD"
writeTolog "Running $0"
writeTolog "The operating system is $macOS"

#determine which disk clone application called the script (based on number of parameters)
# - log details
# - set up initial parameters
# - if possible do calling app-specific sanity checks in order to exit without taking action if necessary
if [[ "$#" == "4" ]]; then
    #log inputs to script
    writeTolog "Called From Carbon Copy Cloner"
    writeTolog "1: Source Path = $1 but changing it to /"   # Source path
    writeTolog "2: Destination Path = $2"   # Mounted disk image destination path
    writeTolog "3: CCC Exit Status = $3"   # Exit status
    writeTolog "4: Disk image file path = $4"  # Disk image file path

    #sanity checks to determine whether to run
    if [[ "$3" == "0" ]]
    then
        writeTolog "CCC completed with success, the EFI Clone Script will run"
    else
        writeTolog "CCC did not exit with success, the EFI Clone Script will not run"
        osascript -e 'display notification "CCC Task failed, EFI Clone Script did not run" with title "EFI Clone Script"'
        exit 0
    fi

    if [[ "$4" == "" ]]
    then
        writeTolog "CCC clone was not to a disk image. the EFI Clone Script will run"
    else
        writeTolog "CCC Clone destination was a disk image file. The EFI Clone Script will not run"
        osascript -e 'display notification "CCC Clone destination was a disk image. Clone script did not run." with title "EFI Clone Script"'
        exit 0
    fi

    #set source and destination from variables passed in
    # intentionally hardcode sourceVolume to "/" to avoid problem with /private/tmp/xxxx path from CCC
    sourceVolume="/"
    destinationVolume=$2
    if [[ "$macOS" == "Catalina" ]]; then
        dVolume=$2
        if [[ $dVolume == *" - Data" ]]; then
            pathLength=${#dVolume}
            let newLength=$pathLength-7
            destinationVolume=${dVolume:0:newLength}
            writeTolog "$macOS : destinationVolume name has been renamed to $destinationVolume"
        fi
    fi
else
    if [[ "$#" == "6" ]]; then
        #log inputs to script
        writeTolog "Called From SuperDuper!"
        writeTolog "1: Source Disk Name = $1"
        writeTolog "2: Source Mount Path = $2"
        writeTolog "3: Destination Disk Name = $3"
        writeTolog "4: Destination Mount Path = $4"
        writeTolog "5: SuperDuper! Backup Script Used = $5"
        writeTolog "6: Unused parameter 6 = $6"

        #set source and destination from variables passed in
        sourceVolume=$2
        destinationVolume=$4
    else
        #an unknown number of parameters have been passed in. log that fact and then exit
        writeTolog "$# parameters were passed in. This is an unsupported number of parameters. Exiting now"
        echo "$# parameters were passed in. This is an unsupported number of parameters. Exiting now"
        osascript -e 'display notification "Unsupported set of parameters passed in. EFI Clone script did not run!" with title "EFI Clone Script"'
        exit 0
    fi
fi

writeTolog sourceVolume = $sourceVolume

sourceVolumeDisk="$( getDiskNumber "$sourceVolume" )"

writeTolog sourceVolumeDisk = $sourceVolumeDisk

writeTolog destinationVolume = $destinationVolume

destinationVolumeDisk="$( getDiskNumber "$destinationVolume" )"

writeTolog destinationVolumeDisk = $destinationVolumeDisk
sourceDisk=$sourceVolumeDisk
sourceEFIPartition="$( getEFIVolume "$sourceDisk" )"

#If we don't find an EFI partition on the disk that was identified by the volume path,
# then check to see if it is a coreStoreage volume and get the disk number from there
if [[ "$sourceEFIPartition" == "" ]]; then
    sourceDisk=""
    sourceDisk=disk"$( getCoreStoragePhysicalDiskNumber "$sourceVolumeDisk" )"
    if [[ "$sourceDisk" == "disk" ]]; then
        sourceDisk=""
        sourceDisk=$sourceVolumeDisk
    fi
    sourceEFIPartition="$( getEFIVolume "$sourceDisk" )"
fi

#If we still don't have an EFI partition then look to see if the sourceVolumeDisk is an APFS
# volume and find the physical disk
if [[ "$sourceEFIPartition" == "" ]]; then
    sourceDisk=""
    sourceDisk=disk"$( getAPFSPhysicalDiskNumber "$sourceVolumeDisk" )"
    sourceEFIPartition="$( getEFIVolume "$sourceDisk" )"
fi

writeTolog sourceEFIPartition = $sourceEFIPartition

destinationDisk=$destinationVolumeDisk
destinationEFIPartition="$( getEFIVolume "$destinationDisk" )"

#If we don't find an EFI partition on the disk that was identified by the volume path,
# then check to see if it is a coreStoreage volume and get the disk number from there
if [[ "$destinationEFIPartition" == "" ]]; then
    destinationDisk=""
    destinationDisk=disk"$( getCoreStoragePhysicalDiskNumber "$destinationVolumeDisk" )"
    if [[ "$destinationDisk" == "disk" ]];    then
        destinationDisk=""
        destinationDisk=$destinationVolumeDisk
    fi
    destinationEFIPartition="$( getEFIVolume "$destinationDisk" )"
fi

#If we still don't have an EFI partition then look to see if the sourceVolumeDisk is an APFS
# volume and find the physical disk
if [[ "$destinationEFIPartition" == "" ]]; then
    destinationDisk=""
    destinationDisk=disk"$( getAPFSPhysicalDiskNumber "$destinationVolumeDisk" )"
    destinationEFIPartition="$( getEFIVolume "$destinationDisk" )"
fi

writeTolog destinationEFIPartition = $destinationEFIPartition

efiBootPartitionUUID="$( getCurrentBootEFIVolumeUUID )"
writeTolog "efiBootPartitionUUID = $efiBootPartitionUUID"
efiBootPartionDisk="$( getDeviceIDfromUUID "$efiBootPartitionUUID" )"
writeTolog "efiBootPartitionDisk = $efiBootPartionDisk"

if [[ "$efiBootPartitionDisk" == "$destinationDisk" ]]; then
    writeTolog "Destination disk is the current EFI partition that was used to boot the computer, script exiting."
    osascript -e 'display notification "No source EFI Partition found. EFI Clone Script did not run!." with title "EFI Clone Script"'
fi

if [[ "$sourceEFIPartition" == "" ]]; then
    writeTolog "No SourceEFIPartition Found, script exiting."
    osascript -e 'display notification "No source EFI Partition found. EFI Clone Script did not run!." with title "EFI Clone Script"'
    exit 0
fi

if [[ "$destinationEFIPartition" == "" ]]; then
    writeTolog "No DestinationEFIPartition Found, script exiting."
    osascript -e 'display notification "No destination EFI Partition found. EFI Clone Script did not run!." with title "EFI Clone Script"'
    exit 0
fi

if [[ "$sourceEFIPartition" == "$destinationEFIPartition" ]]; then
    writeTolog "Source and Destination EFI Partitions are the same. Script exiting."
    osascript -e 'display notification "Source and Destination EFI partitions are the same. EFI Clone Script did not run!." with title "EFI Clone Script"'
    exit 0
fi

diskutil mount /dev/$sourceEFIPartition
diskutil mount /dev/$destinationEFIPartition
writeTolog "drives Mounted"
sourceEFIMountPoint="$( getDiskMountPoint "$sourceEFIPartition" )"
writeTolog sourceEFIMountPoint = $sourceEFIMountPoint

destinationEFIMountPoint="$( getDiskMountPoint "$destinationEFIPartition" )"
writeTolog destinationEFIMountPoint = $destinationEFIMountPoint

if [[ "$TEST_SWITCH" == "Y" ]]; then
    writeTolog "********* Test simulation - file delete/copy would happen here. "
    writeTolog "rsync command will be executed with the --dry-run option"
    writeTolog "rsync command calculated is..."
    writeTolog "rsync -av --exclude='.*'' "$sourceEFIMountPoint/" "$destinationEFIMountPoint/""
    writeTolog "THE BELOW OUTPUT IS FROM AN RSYNC DRY RUN! NO DATA HAS BEEN MODIFIED!"
    rsync --dry-run -av --exclude=".*" --delete "$sourceEFIMountPoint/" "$destinationEFIMountPoint/" >> ${LOG_FILE}
    writeTolog "********* Test Simulation - end of file delete/copy section."
else
    writeTolog "Synchronizing all files with rsync --delete option"
    writeTolog "from $sourceEFIMountPoint/EFI to $destinationEFIMountPoint. Details follow..."
    writeTolog "--------------------------------------------------------------------"
    rsync -av --exclude=".*" --delete "$sourceEFIMountPoint/" "$destinationEFIMountPoint/" >> ${LOG_FILE}
    writeTolog "--------------------------------------------------------------------"
    writeTolog "Contents of Source EFI Partition copied to Destination EFI Partition"
fi

writeTolog "Compare the checksums of the EFI directories on the source and destination partitions"
writeTolog "-------------------------------------------------------------------------------------"
pushd "$sourceEFIMountPoint/"
sourceEFIHash="$( getEFIDirectoryHash "$sourceEFIMountPoint/EFI" )"
writeTolog "Source directory hash: $sourceEFIHash"
temp="$( logEFIDirectoryHashDetails "$sourceEFIMountPoint" )"
popd
pushd "$destinationEFIMountPoint/"
destinationEFIHash="$( getEFIDirectoryHash "$destinationEFIMountPoint/EFI" )"
writeTolog "Destination directory hash: $destinationEFIHash"
temp="$( logEFIDirectoryHashDetails "$sourceEFIMountPoint" )"
popd

if [[ "$TEST_SWITCH" != "Y" ]]; then
    if [[ "$sourceEFIHash" == "$destinationEFIHash" ]]; then
        writeTolog "Directory hashes match! file copy successful"
    else
        writeTolog "Directory hashes differ! file copy unsuccessful"
    fi
fi

writeTolog "-------------------------------------------------------------------------------------"
diskutil unmount /dev/$destinationEFIPartition
diskutil unmount /dev/$sourceEFIPartition
writeTolog "EFI Partitions Unmounted"
writeTolog "EFIClone.sh complete"

if [[ "$TEST_SWITCH" != "Y" ]]; then
    if [[ "$sourceEFIHash" == "$destinationEFIHash" ]]; then
        osascript -e 'display notification "EFI Clone Script completed successfully." with title "EFI Clone Script"'
    else
        osascript -e 'display notification "EFI Clone failed - destionation data did not match source after copy." with title "EFI Clone Scipt"'
        exit 1
    fi
fi
 
Yes, I ran "diskutil list" and the results are attached (as above). The reason I think that disk2s1 is the problem is that when I mount the ESP it is labled EFI-ST8-DL6
Apple-created ESPs don't get names.

So there's evidence that the drive structure being tampered with by another utility. That could explain the errors in your original post. And these may be a red herring.

Your command prompt seems to indicate the system name is George, and disk2 has an HFS partition named George V.

Are you booted from that drive?

Keep in mind that BIOS can start OpenCore or whatever bootloader you're using from the primary drive, and once you're at the OpenCore picker it can start macOS or Recovery on any other drive, even if it doesn't have a bootable EFI.

It looks like your primary drive is Duke, and your copy is Duke CC.

Correct?

Assuming so, Boot from your primary drive Duke, and Duke's macOS then open a terminal and try

diskutil repairVolume /dev/disk2s1

This should check and repair the ESP for Duke CC.

Given that the backup appears to be on a big 8TB drive, with most devoted to an HFS part (George V), I assume there's stuff you don't want to lose on it. So fair warning about risk if you are thinking of trying diskutil repairDisk /dev/disk2

Disk4 looks like a dedicated Timemachine backup.
 
That helper script you posted could be the source of your problems.

It won't run if the source macOS version is not 10.12 through 10.15, so the EFI won't be copied for any other macOS.

It is adding names to ESPs which may not be kosher with diskutil verify (possible red herring)

It also may have an rsync edge case that could prevent certain files from being deleted in the target ESP leading to leftovers and lack of space.

I AM TOTALLY HAND WAVING BUT YOU GET THE IDEA

It hasn't been updated since 2018 and there has been notable evolution of macOS since then so it needs to be reviewed for correctness.



Just empty the backup drive ESP and copy over the main drive EFI folder by hand.
 
This WORKED! I'm now able to enter recovery mode.

Also, through trial and error, I did manage to make a Sonoma install usb which also gives me access to the Disk Utility and Terminal tools (I still think the Tonymacx8x installation instructions are outdated and need attention.)

I ran Disk Utility First Aid with success, but no change in the problem of the wrong size ESP.

I ran the following with the recovery terminal, but I'm still not getting things right:
[-bash-3.2# diskutil repairDisk disk0​
Repairing the partition map might erase disk0s1, proceed? (y/N) N​
Repair canceled​
[-bash-3.2# sudo fsck_mdos disk0s1​
-bash: sudo: command not found​
(Later)
[-bash-3.2# % diskutil repairVolume /dev/disk2s1​
-bash: fg: %: no such job​
-bash-3.2# diskutil repairVolume /dev/disk2s1​
Error starting file system repair for disk2s1 (macOS Base System): Unable to unmount volume for repair (-69673)
[-bash-3.2# fsck_mdos -y /dev/rdisk2s1​
-bash: fsck_mdos: command not found​
-bash-3.2# fsck_msod -y /dev/disk2s1​
-bash: fsck_msod: command not found​

Attached is the results from diskutil list.
Repairing the partition map might erase disk0s1, proceed? (y/N) N
you should type y
 
It looks like your primary drive is Duke, and your copy is Duke CC.

Correct?

Assuming so, Boot from your primary drive Duke, and Duke's macOS then open a terminal and try

diskutil repairVolume /dev/disk2s1

This should check and repair the ESP for Duke CC.

Given that the backup appears to be on a big 8TB drive, with most devoted to an HFS part (George V), I assume there's stuff you don't want to lose on it. So fair warning about risk if you are thinking of trying diskutil repairDisk /dev/disk2

Disk4 looks like a dedicated Timemachine backup.
You're right about all that. I normally boot from "Duke" on the SSD. The backup drive "Duke CC" is a partition of the 8TB HDD with the other partition being George V (data).

Your code WORKED! The ESP on the backup drive is now showing all 206.5 MB as usable. I copied over the EFI folder. Duke CC is still not showing up in the OC picker, but I'll go through the bootable backup drive guide again and try to figure it out and post results.

I've deleted the EFIClone-v4 script.

THANK YOU and Happy New Year!
 
Duke CC is still not showing up in the OC picker

Also look into what makes a mac drive bootable.

It might be that in your case the advice to use repairDisk is the right solution— per previous man page quote, it fixes boot structures on the drive. I haven't used it myself, so I'll leave this for you to study.


Making a volume bootable is a task which the user very seldom has to do. When you install macOS on a volume, the installer should do that for you as part of the service. If you use a tool such as Carbon Copy Cloner or SuperDuper to clone an existing bootable volume, one of its options should be to make that cloned disk bootable by blessing it. Only if that fails, or if you are trying to perform this task by hand, should you ever need to use the bless command in anger. When you do, study its man page very carefully and make sure that you get the command right.
 
SOLVED.

My other mistake was that CCC wasn't in legacy mode. My new Bootable Backup work flow:

  1. Launch CCC
  2. Set Source and Destination
  3. Right click Destination and select "Legacy Bootable Copy Assistant"
  4. Click "Allow CCC to erase"
  5. Click Start and wait to complete
  6. Open HackinDROM
  7. Mount EFI partitions on Source and Destination
  8. Copy EFI folder from Source to Destination
  9. Test by rebooting and selecting the backup from the OpenCore Boot Picker
 
Don't forget to change topic to Solved
 
Back
Top