@@ -97,6 +97,7 @@ class HardwarePatchsetValidation(StrEnum):
9797 NVDA_DRV_MISSING = "Validation: nvda_drv(_vrl) variable missing"
9898 PATCHING_NOT_POSSIBLE = "Validation: Patching not possible"
9999 UNPATCHING_NOT_POSSIBLE = "Validation: Unpatching not possible"
100+ REPATCHING_NOT_SUPPORTED = "Validation: Root volume dirty, unpatch to continue"
100101
101102
102103class HardwarePatchsetDetection :
@@ -191,8 +192,61 @@ def _validation_check_filevault_is_enabled(self) -> bool:
191192 return False
192193
193194 return "FileVault is Off" not in subprocess .run (["/usr/bin/fdesetup" , "status" ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT ).stdout .decode ()
195+ def _is_root_volume_dirty (self ) -> bool :
196+ """
197+ Determine if system volume is not sealed
198+ """
199+ # macOS 11.0 introduced sealed system volumes
200+ if self ._xnu_major < os_data .big_sur .value :
201+ return False
202+
203+ try :
204+ content = plistlib .loads (subprocess .run (["/usr/sbin/diskutil" , "info" , "-plist" , "/" ], capture_output = True ).stdout )
205+ except plistlib .InvalidFileException :
206+ raise RuntimeError ("Failed to parse diskutil output." )
207+
208+ seal = content ["Sealed" ]
209+
210+ if "Broken" in seal :
211+ logging .error (f"System volume is tainted, unpatching is required" )
212+ return True
213+
214+ return False
215+ def _validation_check_repatching_is_possible (self ) -> bool :
216+ """
217+ Determine if repatching is not allowed
218+ """
219+ oclp_patch_path = "/System/Library/CoreServices/OpenCore-Legacy-Patcher.plist"
220+ if not Path (oclp_patch_path ).exists ():
221+ return self ._is_root_volume_dirty ()
194222
223+ oclp_plist = plistlib .load (open (oclp_patch_path , "rb" ))
195224
225+ if self ._constants .computer .oclp_sys_url != self ._constants .commit_info [2 ]:
226+ logging .error ("Installed patches are from different commit, unpatching is required" )
227+ return True
228+
229+ wireless_keys = {"Legacy Wireless" , "Modern Wireless" }
230+
231+ # Keep in sync with generate_patchset_plist
232+ metadata_keys = {
233+ "OpenCore Legacy Patcher" ,
234+ "PatcherSupportPkg" ,
235+ "Time Patched" ,
236+ "Commit URL" ,
237+ "Kernel Debug Kit Used" ,
238+ "Metal Library Used" ,
239+ "OS Version" ,
240+ "Custom Signature" ,
241+ }
242+
243+ existing_patches = set (oclp_plist ) - wireless_keys - metadata_keys
244+ if existing_patches :
245+ logging .error (f"Patch(es) already installed: { ', ' .join (existing_patches )} , unpatching is required" )
246+ return True
247+
248+ return False
249+
196250 def _validation_check_system_integrity_protection_enabled (self , configs : list [str ]) -> bool :
197251 """
198252 Determine if System Integrity Protection is enabled
@@ -508,6 +562,7 @@ def _detect(self) -> None:
508562 HardwarePatchsetValidation .SIP_ENABLED : self ._validation_check_system_integrity_protection_enabled (required_sip_configs ),
509563 HardwarePatchsetValidation .SECURE_BOOT_MODEL_ENABLED : self ._validation_check_secure_boot_model_enabled (),
510564 HardwarePatchsetValidation .AMFI_ENABLED : self ._validation_check_amfi_enabled (highest_amfi_level ),
565+ HardwarePatchsetValidation .REPATCHING_NOT_SUPPORTED : self ._validation_check_repatching_is_possible (),
511566 HardwarePatchsetValidation .WHATEVERGREEN_MISSING : self ._validation_check_whatevergreen_missing () if has_nvidia_web_drivers is True else False ,
512567 HardwarePatchsetValidation .FORCE_OPENGL_MISSING : self ._validation_check_force_opengl_missing () if has_nvidia_web_drivers is True else False ,
513568 HardwarePatchsetValidation .FORCE_COMPAT_MISSING : self ._validation_check_force_compat_missing () if has_nvidia_web_drivers is True else False ,
0 commit comments