read individual files rather than all at once

This commit is contained in:
leminlimez
2025-03-13 21:29:37 -04:00
parent 86bb3958de
commit fd4502dd4f
5 changed files with 44 additions and 23 deletions

View File

@@ -23,19 +23,35 @@ class BackupFile:
@dataclass @dataclass
class ConcreteFile(BackupFile): class ConcreteFile(BackupFile):
contents: bytes contents: bytes
src_path: Optional[str] = None
owner: int = 0 owner: int = 0
group: int = 0 group: int = 0
inode: Optional[int] = None inode: Optional[int] = None
mode: _FileMode = DEFAULT mode: _FileMode = DEFAULT
hash: bytes = None
size: int = None
def read_contents(self) -> bytes:
contents = self.contents
if self.contents == None:
with open(self.src_path, "rb") as in_file:
contents = in_file.read()
# prepopulate hash and size
self.hash = sha1(contents).digest()
self.size = len(contents)
return contents
def to_record(self) -> mbdb.MbdbRecord: def to_record(self) -> mbdb.MbdbRecord:
if self.inode is None: if self.inode is None:
self.inode = int.from_bytes(randbytes(8), "big") self.inode = int.from_bytes(randbytes(8), "big")
if self.hash == None or self.size == None:
self.read_contents()
return mbdb.MbdbRecord( return mbdb.MbdbRecord(
domain=self.domain, domain=self.domain,
filename=self.path, filename=self.path,
link="", link="",
hash=sha1(self.contents).digest(), hash=self.hash,
key=b"", key=b"",
mode=self.mode | _FileMode.S_IFREG, mode=self.mode | _FileMode.S_IFREG,
#unknown2=0, #unknown2=0,
@@ -46,7 +62,7 @@ class ConcreteFile(BackupFile):
mtime=int(datetime.now().timestamp()), mtime=int(datetime.now().timestamp()),
atime=int(datetime.now().timestamp()), atime=int(datetime.now().timestamp()),
ctime=int(datetime.now().timestamp()), ctime=int(datetime.now().timestamp()),
size=len(self.contents), size=self.size,
flags=4, flags=4,
properties=[] properties=[]
) )
@@ -126,7 +142,7 @@ class Backup:
if isinstance(file, ConcreteFile): if isinstance(file, ConcreteFile):
#print("Writing", file.path, "to", directory / sha1((file.domain + "-" + file.path).encode()).digest().hex()) #print("Writing", file.path, "to", directory / sha1((file.domain + "-" + file.path).encode()).digest().hex())
with open(directory / sha1((file.domain + "-" + file.path).encode()).digest().hex(), "wb") as f: with open(directory / sha1((file.domain + "-" + file.path).encode()).digest().hex(), "wb") as f:
f.write(file.contents) f.write(file.read_contents())
with open(directory / "Manifest.mbdb", "wb") as f: with open(directory / "Manifest.mbdb", "wb") as f:
f.write(self.generate_manifest_db().to_bytes()) f.write(self.generate_manifest_db().to_bytes())

View File

@@ -4,8 +4,9 @@ from pymobiledevice3.services.installation_proxy import InstallationProxyService
import os import os
class FileToRestore: class FileToRestore:
def __init__(self, contents: str, restore_path: str, domain: str = None, owner: int = 501, group: int = 501): def __init__(self, contents: str, restore_path: str, contents_path: str = None, domain: str = None, owner: int = 501, group: int = 501):
self.contents = contents self.contents = contents
self.contents_path = contents_path
self.restore_path = restore_path self.restore_path = restore_path
self.domain = domain self.domain = domain
self.owner = owner self.owner = owner
@@ -75,7 +76,8 @@ def concat_regular_file(file: FileToRestore, files_list: list[FileToRestore], la
file.domain, file.domain,
owner=file.owner, owner=file.owner,
group=file.group, group=file.group,
contents=file.contents contents=file.contents,
src_path=file.contents_path
)) ))
return new_last_domain, full_path return new_last_domain, full_path

View File

@@ -1,6 +1,6 @@
import traceback import traceback
import plistlib import plistlib
from pathlib import Path from tempfile import TemporaryDirectory
from PySide6.QtWidgets import QMessageBox from PySide6.QtWidgets import QMessageBox
from PySide6.QtCore import QSettings from PySide6.QtCore import QSettings
@@ -304,6 +304,7 @@ class DeviceManager:
# create the restore file list # create the restore file list
files_to_restore: dict[FileToRestore] = [ files_to_restore: dict[FileToRestore] = [
] ]
tmp_pb_dir = None # temporary directory for unzipping pb files
# set the plist keys # set the plist keys
if not resetting: if not resetting:
@@ -325,7 +326,8 @@ class DeviceManager:
if tweak.enabled and tweak.file_location.value.startswith("/var/mobile/"): if tweak.enabled and tweak.file_location.value.startswith("/var/mobile/"):
uses_domains = True uses_domains = True
elif isinstance(tweak, PosterboardTweak): elif isinstance(tweak, PosterboardTweak):
tweak.apply_tweak(files_to_restore=files_to_restore) tmp_pb_dir = TemporaryDirectory()
tweak.apply_tweak(files_to_restore=files_to_restore, output_dir=tmp_pb_dir.name)
else: else:
if gestalt_plist != None: if gestalt_plist != None:
gestalt_plist = tweak.apply_tweak(gestalt_plist) gestalt_plist = tweak.apply_tweak(gestalt_plist)
@@ -413,12 +415,16 @@ class DeviceManager:
update_label("Restoring to device...") update_label("Restoring to device...")
try: try:
restore_files(files=files_to_restore, reboot=self.auto_reboot, lockdown_client=self.data_singleton.current_device.ld) restore_files(files=files_to_restore, reboot=self.auto_reboot, lockdown_client=self.data_singleton.current_device.ld)
if tmp_pb_dir != None:
tmp_pb_dir.cleanup()
msg = "Your device will now restart." msg = "Your device will now restart."
if not self.auto_reboot: if not self.auto_reboot:
msg = "Please restart your device to see changes." msg = "Please restart your device to see changes."
QMessageBox.information(None, "Success!", "All done! " + msg) QMessageBox.information(None, "Success!", "All done! " + msg)
update_label("Success!") update_label("Success!")
except Exception as e: except Exception as e:
if tmp_pb_dir != None:
tmp_pb_dir.cleanup()
show_apply_error(e, update_label) show_apply_error(e, update_label)
## RESETTING MOBILE GESTALT ## RESETTING MOBILE GESTALT

View File

@@ -19,7 +19,7 @@ from tweaks.custom_gestalt_tweaks import CustomGestaltTweaks, ValueTypeStrings
from tweaks.daemons_tweak import Daemon from tweaks.daemons_tweak import Daemon
App_Version = "5.0" App_Version = "5.0"
App_Build = 1 App_Build = 2
class Page(Enum): class Page(Enum):
Home = 0 Home = 0

View File

@@ -2,7 +2,6 @@ from .tweak_classes import Tweak
from Sparserestore.restore import FileToRestore from Sparserestore.restore import FileToRestore
import os import os
import zipfile import zipfile
from tempfile import TemporaryDirectory
class PosterboardTweak(Tweak): class PosterboardTweak(Tweak):
def __init__(self): def __init__(self):
@@ -13,19 +12,18 @@ class PosterboardTweak(Tweak):
def recursive_add(self, files_to_restore: list[FileToRestore], curr_path: str, restore_path: str = "", isAdding: bool = False): def recursive_add(self, files_to_restore: list[FileToRestore], curr_path: str, restore_path: str = "", isAdding: bool = False):
for folder in sorted(os.listdir(curr_path)): for folder in sorted(os.listdir(curr_path)):
if folder == ".DS_Store" or folder == "__MACOSX": if folder.startswith('.') or folder == "__MACOSX":
continue continue
if isAdding: if isAdding:
# if file then add it, otherwise recursively call again # if file then add it, otherwise recursively call again
if os.path.isfile(os.path.join(curr_path, folder)): if os.path.isfile(os.path.join(curr_path, folder)):
try: try:
with open(os.path.join(curr_path, folder), "rb") as in_file: files_to_restore.append(FileToRestore(
contents = in_file.read() contents=None,
files_to_restore.append(FileToRestore( contents_path=os.path.join(curr_path, folder),
contents=contents, restore_path=f"{restore_path}/{folder}",
restore_path=f"{restore_path}/{folder}", domain=f"AppDomain-{self.bundle_id}"
domain=f"AppDomain-{self.bundle_id}" ))
))
except IOError: except IOError:
print(f"Failed to open file: {folder}") # TODO: Add QDebug equivalent print(f"Failed to open file: {folder}") # TODO: Add QDebug equivalent
else: else:
@@ -38,7 +36,7 @@ class PosterboardTweak(Tweak):
else: else:
self.recursive_add(files_to_restore, os.path.join(curr_path, folder), isAdding=False) self.recursive_add(files_to_restore, os.path.join(curr_path, folder), isAdding=False)
def apply_tweak(self, files_to_restore: list[FileToRestore]): def apply_tweak(self, files_to_restore: list[FileToRestore], output_dir: str):
# unzip the file # unzip the file
if not self.enabled: if not self.enabled:
return return
@@ -52,8 +50,7 @@ class PosterboardTweak(Tweak):
return return
elif self.zip_path == None: elif self.zip_path == None:
return return
with TemporaryDirectory() as output_dir: with zipfile.ZipFile(self.zip_path, 'r') as zip_ref:
with zipfile.ZipFile(self.zip_path, 'r') as zip_ref: zip_ref.extractall(output_dir)
zip_ref.extractall(output_dir) # add the files
# add the files self.recursive_add(files_to_restore, curr_path=output_dir)
self.recursive_add(files_to_restore, curr_path=output_dir)