From 585ff9062cbf92ca65fcc50607e8f2b751f9d05e Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Thu, 26 Nov 2015 08:48:47 -0400 Subject: [PATCH] :bug: Fix missing execution permission bit in execFile override Consider an electron application that uses `execFile` to run a script that lives within the application code base: ```coffee child_process = require 'child_process' child_process.execFile __dirname + '/script.sh', (error) -> throw error if error? ``` An application like this will fail when being packaged in an `asar` with an following error: ``` Error: spawn EACCES ``` Electron overrides certain `fs` functions to make them work within an `asar` package. In the case of `execFile`, the file to be executed is extracted from the `asar` package into a temporary file and ran from there. The problem is that during the extraction, the original permissions of the file are lost. We workaround this by: 1. Extending `asar.stat` to return whether a file is executable or not, which is information that's already saved in the `asar` header. 2. Setting execution permissions on the extracted file if the above property holds true. Fixes: https://github.com/atom/electron/issues/3512 --- atom/common/api/atom_api_asar.cc | 1 + atom/common/asar/archive.cc | 3 +++ atom/common/asar/archive.h | 1 + atom/common/lib/asar.coffee | 11 +++++++++++ 4 files changed, 16 insertions(+) diff --git a/atom/common/api/atom_api_asar.cc b/atom/common/api/atom_api_asar.cc index 4ea7d8c5c362..489118bd70af 100644 --- a/atom/common/api/atom_api_asar.cc +++ b/atom/common/api/atom_api_asar.cc @@ -54,6 +54,7 @@ class Archive : public mate::Wrappable { mate::Dictionary dict(isolate, v8::Object::New(isolate)); dict.Set("size", stats.size); dict.Set("offset", stats.offset); + dict.Set("executable", stats.executable); dict.Set("isFile", stats.is_file); dict.Set("isDirectory", stats.is_directory); dict.Set("isLink", stats.is_link); diff --git a/atom/common/asar/archive.cc b/atom/common/asar/archive.cc index 969f958956ca..61b22e9013f6 100644 --- a/atom/common/asar/archive.cc +++ b/atom/common/asar/archive.cc @@ -107,6 +107,9 @@ bool FillFileInfoWithNode(Archive::FileInfo* info, return false; info->offset += header_size; + info->executable = false; + node->GetBoolean("executable", &info->executable); + return true; } diff --git a/atom/common/asar/archive.h b/atom/common/asar/archive.h index f2ff2f76d676..fb52c6265d31 100644 --- a/atom/common/asar/archive.h +++ b/atom/common/asar/archive.h @@ -27,6 +27,7 @@ class Archive { struct FileInfo { FileInfo() : size(0), offset(0) {} bool unpacked; + bool executable; uint32 size; uint64 offset; }; diff --git a/atom/common/lib/asar.coffee b/atom/common/lib/asar.coffee index f7eeceb3f314..fba3faed8ce2 100644 --- a/atom/common/lib/asar.coffee +++ b/atom/common/lib/asar.coffee @@ -1,5 +1,6 @@ asar = process.binding 'atom_common_asar' child_process = require 'child_process' +fs = require 'fs' path = require 'path' util = require 'util' @@ -83,6 +84,11 @@ overrideAPISync = (module, name, arg = 0) -> newPath = archive.copyFileOut filePath notFoundError asarPath, filePath unless newPath + stat = archive.stat filePath + + if stat.executable + fs.chmodSync(newPath, 0o755) + arguments[arg] = newPath old.apply this, arguments @@ -102,6 +108,11 @@ overrideAPI = (module, name, arg = 0) -> newPath = archive.copyFileOut filePath return notFoundError asarPath, filePath, callback unless newPath + stat = archive.stat filePath + + if stat.executable + fs.chmodSync(newPath, 0o755) + arguments[arg] = newPath old.apply this, arguments