Debugging Electron to run shell scripts in a packaged application
I’ve been working on Etcher, a cross-platform Electron application to flash OS images.
In Windows, the maximum length of a file path is 260
characters.
To work around this limitation, Electron encourages developers to package their
applications for distribution using a tar-like format called asar
.
For asar
s to work, Electron patches some functions from the Node.js API to
treat an asar
package as a virtual
directory.
One of the patched functions is
child_process.execFile
,
which when called on a file inside an asar
archive, will extract the file
into a temporary location and execute it from there.
Etcher makes use of an npm
module called drivelist
to list the
connected drives. This module executes some shell scripts using
child_process.execFile
, and parses back the results.
We get the following mysterious error as soon as we package the application
inside an asar
and try running drivelist
:
Error: spawn EACCES
The problem is that asar
discards execution permissions from the files it
archives. For example:
# Install the command line utility tool
$ npm install -g asar
# Create a directory containing an executable file
$ mkdir asar_test
$ touch asar_test/foo
$ chmod 755 asar_test/foo
# Check that the file has execution permissions for the everyone
$ ls -l asar_test/foo
-rwxr-xr-x 1 jviotti staff 0 Nov 26 21:54 asar_test/foo*
# Pack the directory and extract the resulting archive
$ asar pack asar_test app.asar
$ asar extract app.asar output
# The file indeed lost its execution permissions
$ ls -l output/foo
-rw-r--r-- 1 jviotti staff 0 Nov 26 21:57 asar_test/foo
By looking closer at asar
, we can see that it stores a serialized 8 bytes
JSON header at the beginning of the
archive that indexes the files and
directories stored in the archive along with boolean executable
properties,
which are set to true
if the files have execution permissions at the point
where the archive was
created:
if process.platform isnt 'win32' and stat.mode & 0o100
node.executable = true
We can fix this by consulting the executable
property using the stat
method
provided by asar
as part of the child_process.execFile
Electron function,
and adding the execution permission using fs.chmod
after the file is
extracted into a temporary location.
See the following pull request that implements this fix: https://github.com/atom/electron/pull/3595
After the above fix, the application runs flawlessly on UNIX based operating systems, however, we get a new error on Windows:
Error: spawn UNKNOWN
By taking another closer look at Electron and asar
, we can see that Electron
extracts files out of the archive appending a generic .tmp
extension. This
comes from Chromium’s
CreateTemporaryFile
utility, which makes use of GetTempFileName
from the Windows
API,
which according to the docs:
The
GetTempFileName
function creates a temporary file name of the following form:
<path>\<pre><uuuu>.TMP
Windows fails with an UNKNOWN
error as it doesn’t know how to execute a
.tmp
file. The solution is to append the file extension when extracting files
out of the asar
, to we get a .bat
extension after .tmp
.
See the following pull request that implements this fix: https://github.com/atom/electron/pull/3648