Skip to content

Commit

Permalink
fix: writeAsarFile — use close event instead of end
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Mar 5, 2017
1 parent 7bf594d commit 3f41497
Show file tree
Hide file tree
Showing 18 changed files with 330 additions and 377 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"lerna": "2.0.0-beta.37",
"lerna": "2.0.0-beta.38",
"packages": [
"packages/*"
],
Expand Down
14 changes: 6 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@
"///": "all dependencies for all packages (hoisted)",
"dependencies": {
"7zip-bin": "^2.0.4",
"ajv": "^5.0.2-beta",
"ajv-keywords": "^2.0.1-beta.0",
"ajv": "^5.0.3-beta",
"ajv-keywords": "^2.0.1-beta.1",
"archiver": "^1.3.0",
"asar": "~0.13.0",
"aws-sdk": "^2.22.0",
"bluebird-lst": "^1.0.1",
"chalk": "^1.1.3",
Expand All @@ -44,7 +43,7 @@
"ini": "^1.3.4",
"is-ci": "^1.0.10",
"isbinaryfile": "^3.0.2",
"js-yaml": "^3.8.1",
"js-yaml": "^3.8.2",
"mime": "^1.3.4",
"minimatch": "^3.0.3",
"node-emoji": "^1.5.1",
Expand All @@ -56,10 +55,10 @@
"sanitize-filename": "^1.6.1",
"semver": "^5.3.0",
"stat-mode": "^0.2.2",
"tunnel-agent": "^0.4.3",
"tunnel-agent": "^0.6.0",
"update-notifier": "^2.1.0",
"uuid-1345": "^0.99.6",
"yargs": "^6.6.0"
"yargs": "^7.0.1"
},
"devDependencies": {
"@types/electron": "^1.4.33",
Expand All @@ -84,11 +83,10 @@
"jest-environment-node-debug": "^2.0.0",
"jest-junit": "^1.2.0",
"jsdoc": "^3.4.3",
"lerna": "2.0.0-beta.37",
"path-sort": "^0.1.0",
"source-map-support": "^0.4.11",
"ts-babel": "^1.4.4",
"tslint": "^4.5.0",
"tslint": "^4.5.1",
"typescript": "^2.2.1",
"typescript-json-schema": "0.10.0",
"whitespace": "^2.1.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/electron-builder-util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"source-map-support": "^0.4.11",
"7zip-bin": "^2.0.4",
"ini": "^1.3.4",
"tunnel-agent": "^0.4.3"
"tunnel-agent": "^0.6.0"
},
"typings": "./out/electron-builder-util.d.ts"
}
9 changes: 4 additions & 5 deletions packages/electron-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@
"bugs": "/~https://github.com/electron-userland/electron-builder/issues",
"homepage": "/~https://github.com/electron-userland/electron-builder",
"dependencies": {
"ajv": "^5.0.2-beta",
"ajv-keywords": "^2.0.1-beta.0",
"ajv": "^5.0.3-beta",
"ajv-keywords": "^2.0.1-beta.1",
"7zip-bin": "^2.0.4",
"asar": "~0.13.0",
"bluebird-lst": "^1.0.1",
"chalk": "^1.1.3",
"chromium-pickle-js": "^0.2.0",
Expand All @@ -62,7 +61,7 @@
"hosted-git-info": "^2.2.0",
"is-ci": "^1.0.10",
"isbinaryfile": "^3.0.2",
"js-yaml": "^3.8.1",
"js-yaml": "^3.8.2",
"minimatch": "^3.0.3",
"normalize-package-data": "^2.3.5",
"parse-color": "^1.0.0",
Expand All @@ -71,7 +70,7 @@
"semver": "^5.3.0",
"update-notifier": "^2.1.0",
"uuid-1345": "^0.99.6",
"yargs": "^6.6.0",
"yargs": "^7.0.1",
"node-forge": "^0.7.0"
},
"typings": "./out/electron-builder.d.ts",
Expand Down
168 changes: 168 additions & 0 deletions packages/electron-builder/src/asar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { createFromBuffer } from "chromium-pickle-js"
import { close, open, read, readFile, Stats } from "fs-extra-p"
import * as path from "path"
const UINT64 = require("cuint").UINT64

export class Node {
// we don't use Map because later it will be stringified
files?: { [key: string]: Node }

unpacked?: boolean

size: number
offset: number

executable?: boolean

link?: string
}

export class AsarFilesystem {
private offset = UINT64(0)

constructor (readonly src: string, readonly header = new Node(), readonly headerSize: number = -1) {
if (this.header.files == null) {
this.header.files = {}
}
}

searchNodeFromDirectory(p: string): Node {
let node = this.header
for (const dir of p.split(path.sep)) {
if (dir !== ".") {
node = node.files![dir]!
}
}
return node
}

getOrCreateNode(p: string): Node {
p = path.relative(this.src, p)
if (p == null || p.length === 0) {
return this.header
}

const name = path.basename(p)
const dirNode = this.searchNodeFromDirectory(path.dirname(p))
if (dirNode.files == null) {
dirNode.files = {}
}

let result = dirNode.files[name]
if (result == null) {
result = new Node()
dirNode.files[name] = result
}
return result
}

insertDirectory(p: string, unpacked: boolean = false) {
const node = this.getOrCreateNode(p)
node.files = {}
if (unpacked) {
node.unpacked = unpacked
}
return node.files
}

insertFileNode(node: Node, stat: Stats, file: string) {
if (node.size > 4294967295) {
throw new Error(`${file}: file size can not be larger than 4.2GB`)
}

node.offset = this.offset.toString()
if (process.platform !== "win32" && (stat.mode & 0o100)) {
node.executable = true
}
this.offset.add(UINT64(node.size))
}

// listFiles() {
// const files: Array<string> = []
// const fillFilesFromHeader = (p: string, json: Node) => {
// if (json.files == null) {
// return
// }
//
// for (const f in json.files) {
// const fullPath = path.join(p, f)
// files.push(fullPath)
// fillFilesFromHeader(fullPath, json.files[f]!)
// }
// }
//
// fillFilesFromHeader("/", this.header)
// return files
// }

getNode(p: string) {
const node = this.searchNodeFromDirectory(path.dirname(p))
return node.files![path.basename(p)]
}

getFile(p: string, followLinks: boolean = true): Node {
const info = this.getNode(p)!
// if followLinks is false we don't resolve symlinks
return followLinks && info.link != null ? this.getFile(info.link) : info
}

async readJson(file: string): Promise<Buffer> {
return JSON.parse((await this.readFile(file)).toString())
}

async readFile(file: string): Promise<Buffer> {
return await readFileFromAsar(this, file, this.getFile(file))
}
}

export async function readAsar(archive: string): Promise<AsarFilesystem> {
const fd = await open(archive, "r")
let size
let headerBuf
try {
const sizeBuf = new Buffer(8)
if (await read(fd, sizeBuf, 0, 8, <any>null) !== 8) {
throw new Error("Unable to read header size")
}

const sizePickle = createFromBuffer(sizeBuf)
size = sizePickle.createIterator().readUInt32()
headerBuf = new Buffer(size)
if (await read(fd, headerBuf, 0, size, <any>null) !== size) {
throw new Error("Unable to read header")
}
}
finally {
await close(fd)
}

const headerPickle = createFromBuffer(headerBuf!)
const header = headerPickle.createIterator().readString()
return new AsarFilesystem(archive, JSON.parse(header), size)
}

export async function readAsarJson(archive: string, file: string): Promise<Buffer> {
const fs = await readAsar(archive)
return await fs.readJson(file)
}

async function readFileFromAsar(filesystem: AsarFilesystem, filename: string, info: Node): Promise<Buffer> {
let buffer = new Buffer(info.size)
if (info.size <= 0) {
return buffer
}

if (info.unpacked) {
return await readFile(path.join(`${filesystem.src}.unpacked`, filename))
}

const fd = await open(filesystem.src, "r")
try {
const offset = 8 + filesystem.headerSize + parseInt(<any>info.offset)
await read(fd, buffer, 0, info.size, offset)
}
finally {
await close(fd)
}
return buffer
}
45 changes: 17 additions & 28 deletions packages/electron-builder/src/asarUtil.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { AsarFileInfo, listPackage, statFile } from "asar"
import BluebirdPromise from "bluebird-lst"
import { debug } from "electron-builder-util"
import { deepAssign } from "electron-builder-util/out/deepAssign"
import { CONCURRENCY, FileCopier, Filter, MAX_FILE_REQUESTS, statOrNull, walk } from "electron-builder-util/out/fs"
import { log } from "electron-builder-util/out/log"
import { createReadStream, createWriteStream, ensureDir, readFile, readJson, readlink, stat, Stats, writeFile } from "fs-extra-p"
import * as path from "path"
import { AsarFilesystem, Node, readAsar } from "./asar"
import { AsarOptions } from "./metadata"

const isBinaryFile: any = BluebirdPromise.promisify(require("isbinaryfile"))
const pickle = require ("chromium-pickle-js")
const Filesystem = require("asar/lib/filesystem")
const UINT64 = require("cuint").UINT64

const NODE_MODULES_PATTERN = `${path.sep}node_modules${path.sep}`

Expand Down Expand Up @@ -51,7 +49,7 @@ function writeUnpackedFiles(filesToUnpack: Array<UnpackedFileTask>, fileCopier:

class AsarPackager {
private readonly toPack: Array<string> = []
private readonly fs = new Filesystem(this.src)
private readonly fs = new AsarFilesystem(this.src)
private readonly changedFiles = new Map<string, string>()
private readonly outFile: string

Expand Down Expand Up @@ -188,7 +186,7 @@ class AsarPackager {
const stat = metadata.get(file)!
if (stat.isFile()) {
const fileParent = path.dirname(file)
const dirNode = this.fs.searchNodeFromPath(fileParent)
const dirNode = this.fs.getOrCreateNode(fileParent)
const packageDataPromise = fileIndexToModulePackageData.get(i)
let newData: string | null = null
if (packageDataPromise == null) {
Expand All @@ -213,7 +211,7 @@ class AsarPackager {
}

const fileSize = newData == null ? stat.size : Buffer.byteLength(newData)
const node = this.fs.searchNodeFromPath(file)
const node = this.fs.getOrCreateNode(file)
node.size = fileSize
if (dirNode.unpacked || (this.unpackPattern != null && this.unpackPattern(file, stat))) {
node.unpacked = true
Expand All @@ -234,18 +232,8 @@ class AsarPackager {
if (newData != null) {
this.changedFiles.set(file, newData)
}

if (fileSize > 4294967295) {
throw new Error(`${file}: file size can not be larger than 4.2GB`)
}

node.offset = this.fs.offset.toString()
//noinspection JSBitwiseOperatorUsage
if (process.platform !== "win32" && stat.mode & 0x40) {
node.executable = true
}
this.fs.insertFileNode(node, stat, file)
this.toPack.push(file)
this.fs.offset.add(UINT64(fileSize))
}
}
else if (stat.isDirectory()) {
Expand All @@ -268,7 +256,7 @@ class AsarPackager {
this.fs.insertDirectory(file, unpacked)
}
else if (stat.isSymbolicLink()) {
this.fs.searchNodeFromPath(file).link = (<any>stat).relativeLink
this.fs.getOrCreateNode(file).link = (<any>stat).relativeLink
}
}

Expand All @@ -289,7 +277,7 @@ class AsarPackager {
const writeStream = createWriteStream(this.outFile)
return new BluebirdPromise((resolve, reject) => {
writeStream.on("error", reject)
writeStream.once("finish", resolve)
writeStream.on("close", resolve)
writeStream.write(sizeBuf)

let w: (list: Array<any>, index: number) => void
Expand Down Expand Up @@ -386,23 +374,24 @@ export async function checkFileInArchive(asarFile: string, relativeFile: string,
return new Error(`${messagePrefix} "${relativeFile}" in the "${asarFile}" ${text}`)
}

let stat: AsarFileInfo | null
let fs
try {
fs = await readAsar(asarFile)
}
catch (e) {
throw error(`is corrupted: ${e}`)
}

let stat: Node | null
try {
stat = statFile(asarFile, relativeFile)
stat = fs.getFile(relativeFile)
}
catch (e) {
const fileStat = await statOrNull(asarFile)
if (fileStat == null) {
throw error(`does not exist. Seems like a wrong configuration.`)
}

try {
listPackage(asarFile)
}
catch (e) {
throw error(`is corrupted: ${e}`)
}

// asar throws error on access to undefined object (info.link)
stat = null
}
Expand Down
Loading

0 comments on commit 3f41497

Please sign in to comment.