support downloading and unpacking GrapheneOS images

This commit is contained in:
Dmitry Muhomor 2023-10-07 17:44:19 +03:00 committed by Daniel Micay
parent 22256ea40b
commit 1d00936a4a
3 changed files with 39 additions and 9 deletions

View file

@ -388,7 +388,11 @@ async function unpackFactoryImage(factoryImagePath: string, image: DeviceImage,
// There's a TOCTOU race (file is accessed after check), but it affects all other generated files too. // There's a TOCTOU race (file is accessed after check), but it affects all other generated files too.
// Fixing it for this particular case is not worth the complexity increase // Fixing it for this particular case is not worth the complexity increase
} else { } else {
throw new Error(`SHA-256 mismatch for '${image.fileName}': expected ${image.sha256} got ${sha256}`) if (image.skipSha256Check) {
console.warn(`skipping SHA-256 check for ${image.fileName}, SHA-256: ${sha256}`)
} else {
throw new Error(`SHA-256 mismatch for '${image.fileName}': expected ${image.sha256} got ${sha256}`)
}
} }
let fd = await fs.open(factoryImagePath, 'r') let fd = await fs.open(factoryImagePath, 'r')
@ -400,9 +404,15 @@ async function unpackFactoryImage(factoryImagePath: string, image: DeviceImage,
let entry: yauzl.Entry = entryP let entry: yauzl.Entry = entryP
let entryName = entry.filename let entryName = entry.filename
let isInnerZip = let isInnerZip = false
entryName.includes(`-${image.buildId.toLowerCase()}/image-${image.deviceConfig.device.name}`) &&
entryName.endsWith(`-${image.buildId.toLowerCase()}.zip`) let deviceName = image.deviceConfig.device.name
if (image.isGrapheneOS) {
isInnerZip = entryName.includes(`/image-${deviceName}-`) && entryName.endsWith('.zip')
} else {
isInnerZip = entryName.includes(`-${image.buildId.toLowerCase()}/image-${deviceName}`) &&
entryName.endsWith(`-${image.buildId.toLowerCase()}.zip`)
}
if (!isInnerZip) { if (!isInnerZip) {
continue continue

View file

@ -5,6 +5,8 @@ import { DeviceConfig, getDeviceBuildId } from '../config/device'
import { IMAGE_DOWNLOAD_DIR } from '../config/paths' import { IMAGE_DOWNLOAD_DIR } from '../config/paths'
import { BuildIndex, DEFAULT_BASE_DOWNLOAD_URL, ImageType } from './build-index' import { BuildIndex, DEFAULT_BASE_DOWNLOAD_URL, ImageType } from './build-index'
const GRAPHENEOS_PSEUDO_BUILD_ID_PREFIX = 'gos-'
export class DeviceImage { export class DeviceImage {
constructor( constructor(
readonly deviceConfig: DeviceConfig, readonly deviceConfig: DeviceConfig,
@ -13,8 +15,9 @@ export class DeviceImage {
readonly fileName: string, readonly fileName: string,
readonly sha256: string, readonly sha256: string,
readonly url: string, readonly url: string,
) { readonly skipSha256Check: boolean,
} readonly isGrapheneOS: boolean,
) {}
getPath() { getPath() {
return path.join(IMAGE_DOWNLOAD_DIR, this.fileName) return path.join(IMAGE_DOWNLOAD_DIR, this.fileName)
@ -32,6 +35,15 @@ export class DeviceImage {
let deviceBuildId = getDeviceBuildId(deviceConfig, buildId) let deviceBuildId = getDeviceBuildId(deviceConfig, buildId)
let buildProps = index.get(deviceBuildId) let buildProps = index.get(deviceBuildId)
if (buildProps === undefined) { if (buildProps === undefined) {
if (buildId.startsWith(GRAPHENEOS_PSEUDO_BUILD_ID_PREFIX)) {
let fileName = deviceConfig.device.name + '-factory-' +
buildId.substring(GRAPHENEOS_PSEUDO_BUILD_ID_PREFIX.length) + '.zip'
let url = 'https://releases.grapheneos.org/' + fileName
return new DeviceImage(deviceConfig, type, buildId, fileName, 'no sha256', url, true, true)
}
throw new Error(`no images for '${deviceBuildId}'`) throw new Error(`no images for '${deviceBuildId}'`)
} }
@ -54,7 +66,7 @@ export class DeviceImage {
fileName = ending fileName = ending
url = DEFAULT_BASE_DOWNLOAD_URL + fileName url = DEFAULT_BASE_DOWNLOAD_URL + fileName
} }
return new DeviceImage(deviceConfig, type, buildId, fileName, sha256, url) return new DeviceImage(deviceConfig, type, buildId, fileName, sha256, url, false, false)
} }
static async getMissing(arr: DeviceImage[]) { static async getMissing(arr: DeviceImage[]) {

View file

@ -70,13 +70,21 @@ async function downloadImage(image: DeviceImage, outDir: string) {
let sha256Digest: string = sha256.digest('hex') let sha256Digest: string = sha256.digest('hex')
console.log('SHA-256: ' + sha256Digest) console.log('SHA-256: ' + sha256Digest)
assert(sha256Digest === image.sha256, 'SHA256 mismatch, expected ' + image.sha256) if (image.skipSha256Check) {
console.warn('skipping SHA-256 check for ' + completeOutFile)
} else {
assert(sha256Digest === image.sha256, 'SHA256 mismatch, expected ' + image.sha256)
}
await fs.rename(tmpOutFile, completeOutFile) await fs.rename(tmpOutFile, completeOutFile)
} }
function logTermsAndConditionsNotice(images: DeviceImage[]) { function logTermsAndConditionsNotice(images: DeviceImage[]) {
if (images.filter(i => i.type === ImageType.Factory || i.type === ImageType.Ota).length == 0) { if (
images.filter(i => {
return !i.isGrapheneOS && (i.type === ImageType.Factory || i.type === ImageType.Ota)
}).length == 0
) {
// vendor images show T&C notice themselves as part of unpacking // vendor images show T&C notice themselves as part of unpacking
return return
} }