diff --git a/lib/common/asar.js b/lib/common/asar.js index 5d57c07d2f..288b438674 100644 --- a/lib/common/asar.js +++ b/lib/common/asar.js @@ -1,59 +1,77 @@ (function () { const asar = process.binding('atom_common_asar') + const assert = require('assert') const {Buffer} = require('buffer') const childProcess = require('child_process') const path = require('path') const util = require('util') - const hasProp = {}.hasOwnProperty - - // Cache asar archive objects. - const cachedArchives = {} - - // asar error types - const NOT_FOUND = 'NOT_FOUND' - const NOT_DIR = 'NOT_DIR' - const NO_ACCESS = 'NO_ACCESS' - const INVALID_ARCHIVE = 'INVALID_ARCHIVE' - - const envNoAsar = process.env.ELECTRON_NO_ASAR && process.type !== 'browser' && process.type !== 'renderer' + const envNoAsar = process.env.ELECTRON_NO_ASAR && + process.type !== 'browser' && + process.type !== 'renderer' const isAsarDisabled = () => process.noAsar || envNoAsar - const getOrCreateArchive = p => { - let archive = cachedArchives[p] - if (archive != null) return archive + /** + * @param {!Function} functionToCall + * @param {!Array|undefined} args + */ + const nextTick = (functionToCall, args = []) => { + process.nextTick(() => functionToCall(...args)) + } - archive = asar.createArchive(p) - if (!archive) return false + // Cache asar archive objects. + const cachedArchives = new Map() - cachedArchives[p] = archive - return archive + const getOrCreateArchive = archivePath => { + const isCached = cachedArchives.has(archivePath) + if (isCached) { + return cachedArchives.get(archivePath) + } + + const newArchive = asar.createArchive(archivePath) + if (!newArchive) return null + + cachedArchives.set(archivePath, newArchive) + return newArchive } // Clean cache on quit. process.on('exit', () => { - for (let p in cachedArchives) { - if (!hasProp.call(cachedArchives, p)) continue - cachedArchives[p].destroy() + for (const archive of cachedArchives.values()) { + archive.destroy() } + cachedArchives.clear() }) - // Separate asar package's path from full path. - const splitPath = p => { - // shortcut to disable asar. - if (isAsarDisabled()) return {isAsar: false} - if (Buffer.isBuffer(p)) p = p.toString() - if (typeof p !== 'string') return {isAsar: false} - if (p.endsWith('.asar')) return {isAsar: true, asarPath: p, filePath: ''} + const ASAR_EXTENSION = '.asar' - p = path.normalize(p) - const index = p.lastIndexOf(`.asar${path.sep}`) + // Separate asar package's path from full path. + const splitPath = archivePathOrBuffer => { + // Shortcut for disabled asar. + if (isAsarDisabled()) return {isAsar: false} + + // Check for a bad argument type. + let archivePath = archivePathOrBuffer + if (Buffer.isBuffer(archivePathOrBuffer)) { + archivePath = archivePathOrBuffer.toString() + } + if (typeof archivePath !== 'string') return {isAsar: false} + + if (archivePath.endsWith(ASAR_EXTENSION)) { + return {isAsar: true, asarPath: archivePath, filePath: ''} + } + + archivePath = path.normalize(archivePath) + const index = archivePath.lastIndexOf(`${ASAR_EXTENSION}${path.sep}`) if (index === -1) return {isAsar: false} + // E.g. for "//some/path/to/archive.asar/then/internal.file"... return { isAsar: true, - asarPath: p.substr(0, index + 5), - filePath: p.substr(index + 6) + // "//some/path/to/archive.asar" + asarPath: archivePath.substr(0, index + ASAR_EXTENSION.length), + // "then/internal.file" (with a path separator excluded) + filePath: archivePath.substr(index + ASAR_EXTENSION.length + 1) } } @@ -80,105 +98,124 @@ } return new Stats( - 1, // dev - mode, // mode - 1, // nlink - uid, - gid, - 0, // rdev - undefined, // blksize - ++nextInode, // ino - stats.size, - undefined, // blocks, - msec(stats.atime), // atim_msec - msec(stats.mtime), // mtim_msec - msec(stats.ctime), // ctim_msec - msec(stats.birthtime) // birthtim_msec + 1, // dev + mode, // mode + 1, // nlink + uid, + gid, + 0, // rdev + undefined, // blksize + ++nextInode, // ino + stats.size, + undefined, // blocks, + msec(stats.atime), // atim_msec + msec(stats.mtime), // mtim_msec + msec(stats.ctime), // ctim_msec + msec(stats.birthtime) // birthtim_msec ) } - const createError = (errorType, {asarPath, filePath, callback} = {}) => { + const AsarError = { + NOT_FOUND: 'NOT_FOUND', + NOT_DIR: 'NOT_DIR', + NO_ACCESS: 'NO_ACCESS', + INVALID_ARCHIVE: 'INVALID_ARCHIVE' + } + + const createError = (errorType, {asarPath, filePath} = {}) => { let error switch (errorType) { - case NOT_FOUND: + case AsarError.NOT_FOUND: error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`) error.code = 'ENOENT' error.errno = -2 break - case NOT_DIR: + case AsarError.NOT_DIR: error = new Error('ENOTDIR, not a directory') error.code = 'ENOTDIR' error.errno = -20 break - case NO_ACCESS: + case AsarError.NO_ACCESS: error = new Error(`EACCES: permission denied, access '${filePath}'`) error.code = 'EACCES' error.errno = -13 break - case INVALID_ARCHIVE: + case AsarError.INVALID_ARCHIVE: error = new Error(`Invalid package ${asarPath}`) break default: - throw new Error('invalid error type') + assert.fail(`Invalid error type "${errorType}" passed to createError.`) } - if (typeof callback !== 'function') throw error - process.nextTick(() => callback(error)) + return error } - const overrideAPISync = function (module, name, arg) { - if (arg == null) arg = 0 + const overrideAPISync = function (module, name, pathArgumentIndex) { + if (pathArgumentIndex == null) pathArgumentIndex = 0 const old = module[name] module[name] = function () { - const p = arguments[arg] - const {isAsar, asarPath, filePath} = splitPath(p) + const pathArgument = arguments[pathArgumentIndex] + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return old.apply(this, arguments) const archive = getOrCreateArchive(asarPath) - if (!archive) createError(INVALID_ARCHIVE, {asarPath}) + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, {asarPath}) const newPath = archive.copyFileOut(filePath) - if (!newPath) createError(NOT_FOUND, {asarPath, filePath}) + if (!newPath) throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) - arguments[arg] = newPath + arguments[pathArgumentIndex] = newPath return old.apply(this, arguments) } } - const overrideAPI = function (module, name, arg) { - if (arg == null) arg = 0 + const overrideAPI = function (module, name, pathArgumentIndex) { + if (pathArgumentIndex == null) pathArgumentIndex = 0 const old = module[name] module[name] = function () { - const p = arguments[arg] - const {isAsar, asarPath, filePath} = splitPath(p) - console.log(`${isAsar}, ${asarPath}, ${filePath}`) + const pathArgument = arguments[pathArgumentIndex] + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return old.apply(this, arguments) const callback = arguments[arguments.length - 1] - if (typeof callback !== 'function') return overrideAPISync(module, name, arg) + if (typeof callback !== 'function') { + return overrideAPISync(module, name, pathArgumentIndex) + } const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } const newPath = archive.copyFileOut(filePath) - if (!newPath) return createError(NOT_FOUND, {asarPath, filePath, callback}) + if (!newPath) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } - arguments[arg] = newPath + arguments[pathArgumentIndex] = newPath return old.apply(this, arguments) } if (old[util.promisify.custom]) { module[name][util.promisify.custom] = function () { - const p = arguments[arg] - const {isAsar, asarPath, filePath} = splitPath(p) + const pathArgument = arguments[pathArgumentIndex] + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return old[util.promisify.custom].apply(this, arguments) const archive = getOrCreateArchive(asarPath) - if (!archive) return new Promise(() => createError(INVALID_ARCHIVE, {asarPath})) + if (!archive) { + return Promise.reject(createError(AsarError.INVALID_ARCHIVE, {asarPath})) + } const newPath = archive.copyFileOut(filePath) - if (!newPath) return new Promise(() => createError(NOT_FOUND, {asarPath, filePath})) + if (!newPath) { + return Promise.reject(createError(AsarError.NOT_FOUND, {asarPath, filePath})) + } - arguments[arg] = newPath + arguments[pathArgumentIndex] = newPath return old[util.promisify.custom].apply(this, arguments) } } @@ -194,60 +231,69 @@ const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt` const logPath = path.join(require('os').tmpdir(), logFilename) logFDs[asarPath] = fs.openSync(logPath, 'a') - console.log(`Logging ${asarPath} access to ${logPath}`) } fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`) } const {lstatSync} = fs - fs.lstatSync = p => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return lstatSync(p) + fs.lstatSync = pathArgument => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return lstatSync(pathArgument) const archive = getOrCreateArchive(asarPath) - if (!archive) createError(INVALID_ARCHIVE, {asarPath}) + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, {asarPath}) const stats = archive.stat(filePath) - if (!stats) createError(NOT_FOUND, {asarPath, filePath}) + if (!stats) throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) + return asarStatsToFsStats(stats) } const {lstat} = fs - fs.lstat = (p, callback) => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return lstat(p, callback) + fs.lstat = (pathArgument, callback) => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return lstat(pathArgument, callback) const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } - const stats = getOrCreateArchive(asarPath).stat(filePath) - if (!stats) return createError(NOT_FOUND, {asarPath, filePath, callback}) + const stats = archive.stat(filePath) + if (!stats) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } - process.nextTick(() => callback(null, asarStatsToFsStats(stats))) + const fsStats = asarStatsToFsStats(stats) + nextTick(callback, [null, fsStats]) } const {statSync} = fs - fs.statSync = p => { - const {isAsar} = splitPath(p) - if (!isAsar) return statSync(p) + fs.statSync = pathArgument => { + const {isAsar} = splitPath(pathArgument) + if (!isAsar) return statSync(pathArgument) // Do not distinguish links for now. - return fs.lstatSync(p) + return fs.lstatSync(pathArgument) } const {stat} = fs - fs.stat = (p, callback) => { - const {isAsar} = splitPath(p) - if (!isAsar) return stat(p, callback) + fs.stat = (pathArgument, callback) => { + const {isAsar} = splitPath(pathArgument) + if (!isAsar) return stat(pathArgument, callback) // Do not distinguish links for now. - process.nextTick(() => fs.lstat(p, callback)) + process.nextTick(() => fs.lstat(pathArgument, callback)) } const {statSyncNoException} = fs - fs.statSyncNoException = p => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return statSyncNoException(p) + fs.statSyncNoException = pathArgument => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return statSyncNoException(pathArgument) const archive = getOrCreateArchive(asarPath) if (!archive) return false @@ -259,35 +305,43 @@ } const {realpathSync} = fs - fs.realpathSync = function (p) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.realpathSync = function (pathArgument) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return realpathSync.apply(this, arguments) const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath}) + if (!archive) { + throw createError(AsarError.INVALID_ARCHIVE, {asarPath}) + } - const real = archive.realpath(filePath) - if (real === false) createError(NOT_FOUND, {asarPath, filePath}) + const fileRealPath = archive.realpath(filePath) + if (fileRealPath === false) { + throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) + } - return path.join(realpathSync(asarPath), real) + return path.join(realpathSync(asarPath), fileRealPath) } - fs.realpathSync.native = function (p) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.realpathSync.native = function (pathArgument) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return realpathSync.native.apply(this, arguments) const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath}) + if (!archive) { + throw createError(AsarError.INVALID_ARCHIVE, {asarPath}) + } - const real = archive.realpath(filePath) - if (real === false) createError(NOT_FOUND, {asarPath, filePath}) + const fileRealPath = archive.realpath(filePath) + if (fileRealPath === false) { + throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) + } - return path.join(realpathSync.native(asarPath), real) + return path.join(realpathSync.native(asarPath), fileRealPath) } const {realpath} = fs - fs.realpath = function (p, cache, callback) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.realpath = function (pathArgument, cache, callback) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return realpath.apply(this, arguments) if (typeof cache === 'function') { @@ -296,63 +350,95 @@ } const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } - const real = archive.realpath(filePath) - if (real === false) return createError(NOT_FOUND, {asarPath, filePath, callback}) + const fileRealPath = archive.realpath(filePath) + if (fileRealPath === false) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } - return realpath(asarPath, (err, p) => { - return (err) ? callback(err) : callback(null, path.join(p, real)) + realpath(asarPath, (error, archiveRealPath) => { + if (error === null) { + const fullPath = path.join(archiveRealPath, fileRealPath) + callback(null, fullPath) + } else { + callback(error) + } }) } - fs.realpath.native = function (p, cache, callback) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.realpath.native = function (pathArgument, cache, callback) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return realpath.native.apply(this, arguments) if (typeof cache === 'function') { callback = cache - cache = void 0 + cache = undefined } const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } - const real = archive.realpath(filePath) - if (real === false) return createError(NOT_FOUND, {asarPath, filePath, callback}) + const fileRealPath = archive.realpath(filePath) + if (fileRealPath === false) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } - return realpath.native(asarPath, (err, p) => { - return (err) ? callback(err) : callback(null, path.join(p, real)) + realpath.native(asarPath, (error, archiveRealPath) => { + if (error === null) { + const fullPath = path.join(archiveRealPath, fileRealPath) + callback(null, fullPath) + } else { + callback(error) + } }) } const {exists} = fs - fs.exists = (p, callback) => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return exists(p, callback) + fs.exists = (pathArgument, callback) => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return exists(pathArgument, callback) const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } - // Disabled due to false positive in StandardJS - // eslint-disable-next-line standard/no-callback-literal - process.nextTick(() => callback(archive.stat(filePath) !== false)) + const pathExists = (archive.stat(filePath) !== false) + nextTick(callback, [pathExists]) } - fs.exists[util.promisify.custom] = p => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return exists[util.promisify.custom](p) + fs.exists[util.promisify.custom] = pathArgument => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return exists[util.promisify.custom](pathArgument) const archive = getOrCreateArchive(asarPath) - if (!archive) return new Promise(() => createError(INVALID_ARCHIVE, {asarPath})) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + return Promise.reject(error) + } return Promise.resolve(archive.stat(filePath) !== false) } const {existsSync} = fs - fs.existsSync = p => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return existsSync(p) + fs.existsSync = pathArgument => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return existsSync(pathArgument) const archive = getOrCreateArchive(asarPath) if (!archive) return false @@ -361,8 +447,8 @@ } const {access} = fs - fs.access = function (p, mode, callback) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.access = function (pathArgument, mode, callback) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return access.apply(this, arguments) if (typeof mode === 'function') { @@ -371,48 +457,75 @@ } const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } const info = archive.getFileInfo(filePath) - if (!info) return createError(NOT_FOUND, {asarPath, filePath, callback}) + if (!info) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } if (info.unpacked) { const realPath = archive.copyFileOut(filePath) return fs.access(realPath, mode, callback) } - const stats = getOrCreateArchive(asarPath).stat(filePath) - if (!stats) return createError(NOT_FOUND, {asarPath, filePath, callback}) + const stats = archive.stat(filePath) + if (!stats) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } - if (mode & fs.constants.W_OK) return createError(NO_ACCESS, {asarPath, filePath, callback}) + if (mode & fs.constants.W_OK) { + const error = createError(AsarError.NO_ACCESS, {asarPath, filePath}) + nextTick(callback, [error]) + return + } - process.nextTick(() => callback()) + nextTick(callback) } const {accessSync} = fs - fs.accessSync = function (p, mode) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.accessSync = function (pathArgument, mode) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return accessSync.apply(this, arguments) + if (mode == null) mode = fs.constants.F_OK const archive = getOrCreateArchive(asarPath) - if (!archive) createError(INVALID_ARCHIVE, {asarPath}) + if (!archive) { + throw createError(AsarError.INVALID_ARCHIVE, {asarPath}) + } const info = archive.getFileInfo(filePath) - if (!info) createError(NOT_FOUND, {asarPath, filePath}) + if (!info) { + throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) + } + if (info.unpacked) { const realPath = archive.copyFileOut(filePath) return fs.accessSync(realPath, mode) } - const stats = getOrCreateArchive(asarPath).stat(filePath) - if (!stats) createError(NOT_FOUND, {asarPath, filePath}) - if (mode & fs.constants.W_OK) createError(NO_ACCESS, {asarPath, filePath}) + const stats = archive.stat(filePath) + if (!stats) { + throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) + } + + if (mode & fs.constants.W_OK) { + throw createError(AsarError.NO_ACCESS, {asarPath, filePath}) + } } const {readFile} = fs - fs.readFile = function (p, options, callback) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.readFile = function (pathArgument, options, callback) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return readFile.apply(this, arguments) if (typeof options === 'function') { @@ -428,11 +541,24 @@ const {encoding} = options const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } const info = archive.getFileInfo(filePath) - if (!info) return createError(NOT_FOUND, {asarPath, filePath, callback}) - if (info.size === 0) return process.nextTick(() => callback(null, encoding ? '' : Buffer.alloc(0))) + if (!info) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } + + if (info.size === 0) { + nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]) + return + } + if (info.unpacked) { const realPath = archive.copyFileOut(filePath) return fs.readFile(realPath, options, callback) @@ -440,7 +566,11 @@ const buffer = Buffer.alloc(info.size) const fd = archive.getFd() - if (!(fd >= 0)) return createError(NOT_FOUND, {asarPath, filePath, callback}) + if (!(fd >= 0)) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } logASARAccess(asarPath, filePath, info.offset) fs.read(fd, buffer, 0, info.size, info.offset, error => { @@ -449,15 +579,16 @@ } const {readFileSync} = fs - fs.readFileSync = function (p, options) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.readFileSync = function (pathArgument, options) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return readFileSync.apply(this, arguments) const archive = getOrCreateArchive(asarPath) - if (!archive) createError(INVALID_ARCHIVE, {asarPath}) + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, {asarPath}) const info = archive.getFileInfo(filePath) - if (!info) createError(NOT_FOUND, {asarPath, filePath}) + if (!info) throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) + if (info.size === 0) return (options) ? '' : Buffer.alloc(0) if (info.unpacked) { const realPath = archive.copyFileOut(filePath) @@ -475,7 +606,7 @@ const {encoding} = options const buffer = Buffer.alloc(info.size) const fd = archive.getFd() - if (!(fd >= 0)) createError(NOT_FOUND, {asarPath, filePath}) + if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) logASARAccess(asarPath, filePath, info.offset) fs.readSync(fd, buffer, 0, info.size, info.offset) @@ -483,37 +614,49 @@ } const {readdir} = fs - fs.readdir = function (p, callback) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.readdir = function (pathArgument, callback) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return readdir.apply(this, arguments) const archive = getOrCreateArchive(asarPath) - if (!archive) return createError(INVALID_ARCHIVE, {asarPath, callback}) + if (!archive) { + const error = createError(AsarError.INVALID_ARCHIVE, {asarPath}) + nextTick(callback, [error]) + return + } const files = archive.readdir(filePath) - if (!files) return createError(NOT_FOUND, {asarPath, filePath, callback}) + if (!files) { + const error = createError(AsarError.NOT_FOUND, {asarPath, filePath}) + nextTick(callback, [error]) + return + } - process.nextTick(() => callback(null, files)) + nextTick(callback, [null, files]) } const {readdirSync} = fs - fs.readdirSync = function (p) { - const {isAsar, asarPath, filePath} = splitPath(p) + fs.readdirSync = function (pathArgument) { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) if (!isAsar) return readdirSync.apply(this, arguments) const archive = getOrCreateArchive(asarPath) - if (!archive) createError(INVALID_ARCHIVE, {asarPath}) + if (!archive) { + throw createError(AsarError.INVALID_ARCHIVE, {asarPath}) + } const files = archive.readdir(filePath) - if (!files) createError(NOT_FOUND, {asarPath, filePath}) + if (!files) { + throw createError(AsarError.NOT_FOUND, {asarPath, filePath}) + } return files } const {internalModuleReadJSON} = process.binding('fs') - process.binding('fs').internalModuleReadJSON = p => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return internalModuleReadJSON(p) + process.binding('fs').internalModuleReadJSON = pathArgument => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return internalModuleReadJSON(pathArgument) const archive = getOrCreateArchive(asarPath) if (!archive) return @@ -536,9 +679,9 @@ } const {internalModuleStat} = process.binding('fs') - process.binding('fs').internalModuleStat = p => { - const {isAsar, asarPath, filePath} = splitPath(p) - if (!isAsar) return internalModuleStat(p) + process.binding('fs').internalModuleStat = pathArgument => { + const {isAsar, asarPath, filePath} = splitPath(pathArgument) + if (!isAsar) return internalModuleStat(pathArgument) // -ENOENT const archive = getOrCreateArchive(asarPath) @@ -557,20 +700,27 @@ // widely used. if (process.platform === 'win32') { const {mkdir} = fs - fs.mkdir = (p, mode, callback) => { - if (typeof mode === 'function') callback = mode + fs.mkdir = (pathArgument, mode, callback) => { + if (typeof mode === 'function') { + callback = mode + mode = undefined + } - const {isAsar, filePath} = splitPath(p) - if (isAsar && filePath.length) return createError(NOT_DIR, {callback}) + const {isAsar, filePath} = splitPath(pathArgument) + if (isAsar && filePath.length > 0) { + const error = createError(AsarError.NOT_DIR) + nextTick(callback, [error]) + return + } - mkdir(p, mode, callback) + mkdir(pathArgument, mode, callback) } const {mkdirSync} = fs - fs.mkdirSync = function (p, mode) { - const {isAsar, filePath} = splitPath(p) - if (isAsar && filePath.length) createError(NOT_DIR) - return mkdirSync(p, mode) + fs.mkdirSync = function (pathArgument, mode) { + const {isAsar, filePath} = splitPath(pathArgument) + if (isAsar && filePath.length) throw createError(AsarError.NOT_DIR) + return mkdirSync(pathArgument, mode) } }