Implement Root Volume backups

Closes https://github.com/dortania/OpenCore-Legacy-Patcher/issues/378
This commit is contained in:
Mykola Grymalyuk
2021-08-02 16:34:19 -06:00
parent 03f65d1766
commit 33653a7108
7 changed files with 151 additions and 30 deletions
+2
View File
@@ -3,6 +3,8 @@
## 0.2.5
- Implement Latebloom configuration via command line tool
- Implement Root Volume backups in addition to APFS snapshot reversions
- Backups applicable to machines with sealed APFS snapshots
## 0.2.4
+15 -16
View File
@@ -63,7 +63,7 @@ class OpenCoreLegacyPatcher:
parser.add_argument("--patch_sys_vol", help="Patches root volume", action="store_true", required=False)
parser.add_argument("--unpatch_sys_vol", help="Unpatches root volume, EXPERIMENTAL", action="store_true", required=False)
parser.add_argument("--terascale_2", help="Enable TeraScale 2 Acceleration", action="store_true", required=False)
#parser.add_argument("--patch_disk", action="store", help="Specifies disk to root patch", required=False)
# parser.add_argument("--patch_disk", action="store", help="Specifies disk to root patch", required=False)
parser.add_argument("--validate", help="Validate", action="store_true", required=False)
@@ -83,7 +83,7 @@ class OpenCoreLegacyPatcher:
self.constants.disk = args.disk
if args.validate:
self.validate()
#if args.patch_disk:
# if args.patch_disk:
# print(f"- Patch Disk set: {args.patch_disk}")
# self.constants.patch_disk = args.patch_disk
if args.verbose:
@@ -146,7 +146,7 @@ class OpenCoreLegacyPatcher:
print(f"- Set LateBloom range: {args.lb_range}")
except ValueError:
print(f"- Invalid LateBloom range: {args.lb_range}")
if args.lb_debug:
try:
self.constants.latebloom_debug = int(args.lb_debug)
@@ -157,7 +157,6 @@ class OpenCoreLegacyPatcher:
except ValueError:
print(f"- Invalid LateBloom range: {args.lb_debug}")
if args.support_all:
print("- Building for natively supported model")
self.constants.allow_oc_everywhere = True
@@ -210,21 +209,21 @@ If you plan to create the USB for another machine, please select the "Change Mod
# self.constants.secure_status = True # Monterey
self.constants.disable_amfi = False
elif host_is_target:
self.constants.sip_status = False # Unsigned kexts
self.constants.secure_status = False # Root volume modified
self.constants.disable_amfi = True # Unsigned binaries
self.constants.sip_status = False # Unsigned kexts
self.constants.secure_status = False # Root volume modified
self.constants.disable_amfi = True # Unsigned binaries
if model in ModelArray.ModernGPU:
if host_is_target and model in ["iMac13,1", "iMac13,3"] and self.computer.dgpu:
# Some models have a supported dGPU, others don't
print("- Detected Metal dGPU, overriding default configuration")
self.constants.sip_status = True
elif host_is_target:
self.constants.sip_status = False # Unsigned kexts
self.constants.secure_status = False # Modified root volume
# self.constants.disable_amfi = False # Signed bundles, Don't need to explicitly set currently
self.constants.sip_status = False # Unsigned kexts
self.constants.secure_status = False # Modified root volume
# self.constants.disable_amfi = False # Signed bundles, Don't need to explicitly set currently
if model == "MacBook8,1" and host_is_target:
# MacBook8,1 has an odd bug where it cannot install Monterey with Minimal spoofing
self.constants.serial_settings == "Moderate"
# MacBook8,1 has an odd bug where it cannot install Monterey with Minimal spoofing
self.constants.serial_settings == "Moderate"
if self.constants.latebloom_delay == 0:
self.constants.latebloom_delay, self.constants.latebloom_range, self.constants.latebloom_debug = Utilities.latebloom_detection(model)
@@ -240,14 +239,14 @@ If you plan to create the USB for another machine, please select the "Change Mod
def install_opencore(self):
Build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).copy_efi()
def validate(self):
# Runs through ocvalidate to check for errors
valid_dumps = [
ModelExample.MacBookPro.MacBookPro92_Stock,
#ModelExample.MacBookPro.MacBookPro171_Stock,
#ModelExample.Macmini.Macmini91_Stock,
# ModelExample.MacBookPro.MacBookPro171_Stock,
# ModelExample.Macmini.Macmini91_Stock,
ModelExample.iMac.iMac81_Stock,
ModelExample.iMac.iMac112_Stock,
ModelExample.iMac.iMac122_Upgraded,
@@ -269,7 +268,7 @@ If you plan to create the USB for another machine, please select the "Change Mod
raise Exception(f"Validation failed for predefined model: {model}")
else:
print(f"Validation succeeded for predefined model: {model}")
for model in valid_dumps:
self.constants.computer = model
self.computer = self.constants.computer
+2 -2
View File
@@ -133,8 +133,8 @@ system_profiler SPHardwareDataType | grep 'Model Identifier'
CliMenu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).allow_native_models,
],
[
f"Latebloom settings:\t\tDelay {self.constants.latebloom_delay}, Range {self.constants.latebloom_range}, Debug {self.constants.latebloom_debug}",
CliMenu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).latebloom_settings,
f"Latebloom settings:\t\tDelay {self.constants.latebloom_delay}, Range {self.constants.latebloom_range}, Debug {self.constants.latebloom_debug}",
CliMenu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).latebloom_settings,
],
["Advanced Patch Settings, for developers only", self.advanced_patcher_settings],
]
+1 -1
View File
@@ -678,9 +678,9 @@ class BuildOpenCore:
spoofed_model = self.model
if self.constants.override_smbios == "Default":
spoofed_model = self.smbios_set(self.model)
print(f"- Using Model ID: {spoofed_model}")
else:
spoofed_model = self.constants.override_smbios
print(f"- Using Model ID: {spoofed_model}")
try:
spoofed_board = self.constants.board_id[spoofed_model]
print(f"- Using Board ID: {spoofed_board}")
+97 -4
View File
@@ -64,6 +64,8 @@ class PatchSysVolume:
if Path(self.mount_extensions).exists():
print("- Root Volume is already mounted")
if patch is True:
if self.constants.detected_os < self.constants.big_sur or Utilities.check_seal() is True:
self.backup_volume()
self.patch_root_vol()
return True
else:
@@ -76,6 +78,8 @@ class PatchSysVolume:
if Path(self.mount_extensions).exists():
print("- Successfully mounted the Root Volume")
if patch is True:
if self.constants.detected_os < self.constants.big_sur or Utilities.check_seal() is True:
self.backup_volume()
self.patch_root_vol()
return True
else:
@@ -91,6 +95,95 @@ class PatchSysVolume:
if self.constants.gui_mode is False:
input("- Press [ENTER] to exit: ")
def backup_volume(self):
for location in SysPatchArray.BackupLocations:
Utilities.cls()
print("Backing up root volume before patching (This may take some time)")
print(f"- Attempting to backup {location}")
location_zip = f"{location}-Backup.zip"
location_zip_path = Path(self.mount_location) / Path(location_zip)
if location_zip_path.exists():
print(f"- Found existing {location_zip}, skipping")
else:
print(f"- Backing up {location}")
# cp -r ./Extensions ./Extensions-Backup
# ditto -c -k --sequesterRsrc --keepParent ./Extensions-Backup ./Extensions-Backup.zip
# rm -r ./Extensions-Backup
print("- Creating Backup folder")
Utilities.process_status(
self.elevated(
["cp", "-r", f"{self.mount_location}/{location}", f"{self.mount_location}/{location}-Backup"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
)
print("- Zipping Backup folder")
Utilities.process_status(
self.elevated(
["ditto", "-c", "-k", "--sequesterRsrc", "--keepParent", f"{self.mount_location}/{location}-Backup", f"{self.mount_location}/{location_zip}"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
)
print("- Removing Backup folder")
Utilities.process_status(
self.elevated(
["rm", "-r", f"{self.mount_location}/{location}-Backup"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
)
def manual_root_patch_revert(self):
print("- Attempting to revert patches")
if (Path(self.mount_location) / Path("System/Library/Extensions-Backup.zip")).exists():
print("- Verified manual unpatching is available")
for location in SysPatchArray.BackupLocations:
Utilities.cls()
print("Reverting root volume patches (This may take some time)")
print(f"- Attempting to unpatch {location}")
location_zip = f"{location}-Backup.zip"
location_zip_path = Path(self.mount_location) / Path(location_zip)
location_old_path = Path(self.mount_location) / Path(location)
if "PrivateFrameworks" in location:
copy_path = Path(self.mount_location) / Path("System/Library/PrivateFrameworks")
elif "Frameworks" in location:
copy_path = Path(self.mount_location) / Path("System/Library/Frameworks")
else:
copy_path = Path(self.mount_location) / Path("System/Library")
if location_zip_path.exists():
print(f"- Found {location_zip}")
print(f"- Unzipping {location_zip}")
Utilities.process_status(self.elevated(["unzip", location_zip_path, "-d", copy_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
if location_old_path.exists():
print(f"- Renaming {location}")
Utilities.process_status(self.elevated(["mv", location_old_path, f"{location_old_path}-Patched"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
print(f"- Renaming {location}-Backup")
Utilities.process_status(self.elevated(["mv", f"{location_old_path}-Backup", location_old_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
print(f"- Removing {location_old_path}-Patched")
Utilities.process_status(self.elevated(["rm", "-r", f"{location_old_path}-Patched"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
# ditto will create a '__MACOSX' folder
# print("- Removing __MACOSX folder")
# Utilities.process_status(self.elevated(["rm", "-r", f"{copy_path}/__MACOSX"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
else:
print(f"- Failed to find {location_zip}, unable to unpatch")
self.rebuild_snapshot()
else:
print("- Could not find Extensions.zip, cannot manually unpatch root volume")
def unpatch_root_vol(self):
if self.constants.detected_os > self.constants.catalina:
print("- Reverting to last signed APFS snapshot")
@@ -99,13 +192,13 @@ class PatchSysVolume:
print("- Unable to revert root volume patches")
print("Reason for unpatch Failure:")
print(result.stdout.decode())
# print("- Failed to revert snapshot via bless, falling back on manual restoration")
# self.undo_root_patch()
print("- Failed to revert snapshot via bless, falling back on manual restoration")
self.manual_root_patch_revert()
else:
print("- Unpatching complete")
print("\nPlease reboot the machine for patches to take effect")
# else:
# self.undo_root_patch()
else:
self.manual_root_patch_revert()
def rebuild_snapshot(self):
if self.constants.gui_mode is False:
+15
View File
@@ -245,3 +245,18 @@ AddGeneralAccelMojave = [
"IOAcceleratorFamily2.kext",
"IOGraphicsFamily.kext",
]
BackupLocations = [
"System/Library/Extensions",
"System/Library/Frameworks/CoreDisplay.framework",
"System/Library/Frameworks/IOSurface.framework",
"System/Library/Frameworks/OpenGL.framework",
"System/Library/Frameworks/WebKit.framework",
"System/Library/LaunchDaemons",
"System/Library/PrivateFrameworks/DisplayServices.framework",
"System/Library/PrivateFrameworks/GPUSupport.framework",
"System/Library/PrivateFrameworks/SkyLight.framework",
"System/Library/PrivateFrameworks/IOAccelerator.framework",
"System/Library/PrivateFrameworks/AppleGVA.framework",
"System/Library/PrivateFrameworks/AppleGVACore.framework",
]
+19 -7
View File
@@ -65,6 +65,15 @@ def get_disk_path():
return root_mount_path
def check_seal():
# 'Snapshot Sealed' property is only listed on booted snapshots
sealed = subprocess.run(["diskutil", "apfs", "list"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if "Snapshot Sealed: Yes" in sealed.stdout.decode():
return True
else:
return False
def latebloom_detection(model):
if model in ["MacPro4,1", "MacPro5,1", "iMac7,1", "iMac8,1"]:
# These machines are more likely to experience boot hangs, increase delays to accomodate
@@ -73,15 +82,18 @@ def latebloom_detection(model):
lb_delay = "100"
lb_range = "1"
lb_debug = "1"
if get_nvram("boot-args", decode=False):
if "latebloom=" in get_nvram("boot-args", decode=False):
lb_delay = re.search(r"(?:[, ])latebloom=(\d+)", get_nvram("boot-args", decode=False))
boot_args = get_nvram("boot-args", decode=False)
# boot_args = "latebloom=200 lb_range=40 lb_debug=0 keepsyms=1 debug=0x100 -lilubetaall"
if boot_args:
# TODO: This crashes if latebloom=xxx is the very first entry in boot-args
if "latebloom=" in boot_args:
lb_delay = re.search(r"(?:[, ])latebloom=(\d+)", boot_args)
lb_delay = lb_delay[1]
if "lb_range=" in get_nvram("boot-args", decode=False):
lb_range = re.search(r"(?:[, ])lb_range=(\d+)", get_nvram("boot-args", decode=False))
if "lb_range=" in boot_args:
lb_range = re.search(r"(?:[, ])lb_range=(\d+)", boot_args)
lb_range = lb_range[1]
if "lb_debug=" in get_nvram("boot-args", decode=False):
lb_debug = re.search(r"(?:[, ])lb_debug=(\d+)", get_nvram("boot-args", decode=False))
if "lb_debug=" in boot_args:
lb_debug = re.search(r"(?:[, ])lb_debug=(\d+)", boot_args)
lb_debug = lb_debug[1]
return int(lb_delay), int(lb_range), int(lb_debug)