Skip to content

Commit 1ca38d5

Browse files
committed
Fix Patchless for cloudOS 26.4
by dynamically matching and downloading the required apfs_sealvolume version.
1 parent 045d805 commit 1ca38d5

4 files changed

Lines changed: 109 additions & 44 deletions

File tree

scripts/fw_prepare.sh

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,87 @@ extract() {
410410
cp -R "$cache" "$out"
411411
}
412412

413+
download_apfs_sealvolume() {
414+
local src="$1"
415+
IOS_VERSION="$(basename "$src" | awk -F_ '{print $2}')"
416+
filename="apfs_sealvolume_$IOS_VERSION"
417+
418+
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
419+
TOOLS_PREFIX="$PROJECT_DIR/.tools"
420+
421+
if [[ -f "$TOOLS_PREFIX/$filename" ]]; then
422+
echo "$filename already present"
423+
else
424+
echo "Downloading $filename"
425+
TMP_DIR="$(mktemp -d)"
426+
427+
# List matching macOS version to the iOS version
428+
while IFS= read -r url; do
429+
bn="$(basename "$url")"
430+
ver="${bn#*_}"
431+
ver="${ver%%_*}"
432+
433+
[[ "$ver" == "$IOS_VERSION" ]] || continue
434+
435+
selected_src="$url"
436+
BUILD="$(awk -F_ '{print $3}' <<<"$bn")"
437+
break
438+
done < <(
439+
ipsw download appledb \
440+
--os macOS \
441+
--version $IOS_VERSION \
442+
--urls
443+
)
444+
445+
# Download BuildManifest first
446+
ipsw download appledb \
447+
--os macOS \
448+
--build $BUILD \
449+
--pattern "^BuildManifest.plist\$" \
450+
--output "$TMP_DIR"
451+
452+
BUILD_MANIFEST="$(find "$TMP_DIR" -name BuildManifest.plist -print -quit)"
453+
if [ -z "$BUILD_MANIFEST" ]; then
454+
echo "Failed to locate BuildManifest.plist"
455+
rm -rf "$TMP_DIR"
456+
exit 1
457+
fi
458+
459+
RAMDISK_PATH="$(/usr/bin/plutil -extract 'BuildIdentities.0.Manifest.RestoreRamDisk.Info.Path' raw -o - "$BUILD_MANIFEST")"
460+
if [ -z "$RAMDISK_PATH" ]; then
461+
echo "Failed to read RestoreRamDisk path from BuildManifest"
462+
rm -rf "$TMP_DIR"
463+
exit 1
464+
fi
465+
466+
# Download the ramdisk referenced by BuildManifest
467+
ipsw download appledb \
468+
--os macOS \
469+
--build $BUILD \
470+
--pattern "$RAMDISK_PATH" \
471+
--output "$TMP_DIR"
472+
473+
RAMDISK_IM4P="$(find "$TMP_DIR" -path "*${RAMDISK_PATH}" -print -quit)"
474+
if [ -z "$RAMDISK_IM4P" ]; then
475+
echo "Failed to locate downloaded ramdisk: $RAMDISK_PATH"
476+
rm -rf "$TMP_DIR"
477+
exit 1
478+
fi
479+
480+
RAMDISK="$TMP_DIR/ramdisk.dmg"
481+
ipsw img4 im4p extract --output "$RAMDISK" "$RAMDISK_IM4P"
482+
483+
MOUNT=$(hdiutil attach -readonly -nobrowse "$RAMDISK" | awk 'END{ print$NF}')
484+
cp "$MOUNT/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs_sealvolume" \
485+
"$TOOLS_PREFIX/$filename"
486+
hdiutil detach "$MOUNT" >/dev/null 2>&1 || true
487+
rm -rf "$TMP_DIR"
488+
echo " Downloaded: $TOOLS_PREFIX/$filename"
489+
echo " Resigning $filename"
490+
codesign --force --sign - "$TOOLS_PREFIX/$filename"
491+
fi
492+
}
493+
413494
LIST_FIRMWARES="${LIST_FIRMWARES:-0}"
414495
IPHONE_DEVICE="${IPHONE_DEVICE:-$DEFAULT_IPHONE_DEVICE}"
415496
IPHONE_VERSION="${IPHONE_VERSION:-}"
@@ -537,6 +618,13 @@ echo ""
537618
fetch "$IPHONE_SOURCE" "$IPHONE_IPSW_PATH"
538619
fetch "$CLOUDOS_SOURCE" "$CLOUDOS_IPSW_PATH"
539620

621+
VARIANT="${VARIANT:-}"
622+
if [[ "$VARIANT" == "less" ]]; then
623+
download_apfs_sealvolume "$IPHONE_SOURCE"
624+
else
625+
echo "==> Downloading apfs sealvolume (skipped — patchless variant only)"
626+
fi
627+
540628
IPHONE_CACHE="${IPSW_DIR}/${IPHONE_DIR}"
541629
CLOUDOS_CACHE="${IPSW_DIR}/${CLOUDOS_DIR}"
542630

scripts/setup_machine.sh

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,11 +1053,7 @@ main() {
10531053
install_brew_deps
10541054
ensure_python_linked
10551055

1056-
if [[ "$LESS_MODE" -eq 1 ]]; then
1057-
VARIANT=less run_make "Project setup" setup_tools
1058-
else
1059-
run_make "Project setup" setup_tools
1060-
fi
1056+
run_make "Project setup" setup_tools
10611057
run_make "Project setup" build
10621058
fi
10631059

@@ -1066,10 +1062,11 @@ main() {
10661062
export PATH="$PROJECT_ROOT/.venv/bin:$PATH"
10671063

10681064
run_make "Firmware prep" vm_new
1069-
run_make "Firmware prep" fw_prepare
10701065
if [[ "$LESS_MODE" -eq 0 ]]; then
1066+
run_make "Firmware prep" fw_prepare
10711067
run_make "Firmware patch" "$fw_patch_target"
10721068
else
1069+
VARIANT=less run_make "Firmware prep" fw_prepare
10731070
run_make_sudo "Firmware patch" "$fw_patch_target"
10741071
fi
10751072

scripts/setup_tools.sh

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ensure_repo_submodule() {
2525

2626
# ── Brew packages ──────────────────────────────────────────────
2727

28-
echo "[1/5] Checking brew packages..."
28+
echo "[1/4] Checking brew packages..."
2929

3030
BREW_PACKAGES=(aria2 gnu-tar openssl@3 ldid-procursus sshpass zstd)
3131
BREW_MISSING=()
@@ -45,7 +45,7 @@ fi
4545

4646
# ── Trustcache ─────────────────────────────────────────────────
4747

48-
echo "[2/5] trustcache"
48+
echo "[2/4] trustcache"
4949

5050
TRUSTCACHE_BIN="$TOOLS_PREFIX/bin/trustcache"
5151
if [[ -x "$TRUSTCACHE_BIN" ]]; then
@@ -74,7 +74,7 @@ fi
7474

7575
# ── insert_dylib ───────────────────────────────────────────────
7676

77-
echo "[3/5] insert_dylib"
77+
echo "[3/4] insert_dylib"
7878

7979
INSERT_DYLIB_BIN="$TOOLS_PREFIX/bin/insert_dylib"
8080
if [[ -x "$INSERT_DYLIB_BIN" ]]; then
@@ -90,41 +90,8 @@ fi
9090

9191
# ── Python venv ────────────────────────────────────────────────
9292

93-
echo "[4/5] Python venv"
93+
echo "[4/4] Python venv"
9494
zsh "$SCRIPT_DIR/setup_venv.sh"
9595

96-
# ── APFS sealvolume (patchless variant only) ──────────────────────
97-
98-
VARIANT="${VARIANT:-}"
99-
100-
if [[ "$VARIANT" == "less" ]]; then
101-
echo "[5/5] apfs sealvolume"
102-
if [[ -f "$TOOLS_PREFIX/apfs_sealvolume" ]]; then
103-
echo " apfs_sealvolume already present"
104-
else
105-
TMP_DIR="$(mktemp -d)"
106-
ipsw download appledb \
107-
--os macOS \
108-
--build 25D2140 \
109-
--pattern "094-33864-054.dmg" \
110-
--output "$TMP_DIR"
111-
112-
RAMDISK_IM4P="$TMP_DIR/25D2140__MacOS/094-33864-054.dmg"
113-
RAMDISK="$TMP_DIR/ramdisk.dmg"
114-
ipsw img4 im4p extract --output "$RAMDISK" "$RAMDISK_IM4P"
115-
116-
MOUNT=$(hdiutil attach -readonly -nobrowse "$RAMDISK" | awk 'END{ print$NF}')
117-
cp "$MOUNT/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs_sealvolume" \
118-
"$TOOLS_PREFIX/apfs_sealvolume"
119-
hdiutil detach "$MOUNT" >/dev/null 2>&1 || true
120-
rm -rf "$TMP_DIR"
121-
echo " Downloaded: $TOOLS_PREFIX/apfs_sealvolume"
122-
echo " Resigning apfs_sealvolume"
123-
codesign --force --sign - "$TOOLS_PREFIX/apfs_sealvolume"
124-
fi
125-
else
126-
echo "[5/5] apfs sealvolume (skipped — patchless variant only)"
127-
fi
128-
12996
echo ""
13097
echo "All tools installed."

sources/FirmwarePatcher/Filesystem/CryptexFilesystemPatcher.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ public final class CryptexFilesystemPatcher: Patcher {
394394
return im4pPath
395395
}
396396

397+
func identify_apfs_sealvolume() throws -> URL {
398+
let iosVersion = try getProductVersion()
399+
return self.vphoneCliDirectory.appending(path: ".tools/apfs_sealvolume_\(iosVersion)")
400+
}
401+
397402
func createDigestAndHash(filesystem: URL, mtree: URL, remap: Bool) throws -> (URL, URL) {
398403
let (device, mount) = try attachImage(path: filesystem)
399404
defer { try? detachImage(deviceNode: device) }
@@ -423,7 +428,7 @@ public final class CryptexFilesystemPatcher: Patcher {
423428
FileManager.default.createFile(atPath: mtreeRemapPath.path, contents: remapContent.data(using: .utf8))
424429

425430
try unmount(mount: mount)
426-
let sealvolume = self.vphoneCliDirectory.appending(path: ".tools/apfs_sealvolume")
431+
let sealvolume = try identify_apfs_sealvolume()
427432
_ = try runProcess(sealvolume.path, [
428433
"-R", mtreeRemapPath.path,
429434
"-U", digestDbPath.path, // Save digest records
@@ -615,6 +620,14 @@ public final class CryptexFilesystemPatcher: Patcher {
615620
return remaining.joined(separator: "/")
616621
}
617622

623+
func getProductVersion() throws -> String {
624+
var root = try parsePlist(data: buildManiest)
625+
guard var productVersion = root["ProductVersion"] as? String else {
626+
throw FirmwareManifest.ManifestError.missingKey("ProductVersion in BuildManifest")
627+
}
628+
return productVersion
629+
}
630+
618631
func getTrustcachePath() throws -> String {
619632
let path = self.restoreDir.appending(path: "iPhone-BuildManifest.plist")
620633
let manifest = try getBuildIdentityManifest(path: path)

0 commit comments

Comments
 (0)