Debugging Electron to run shell scripts in a packaged application
TL;DR: This article discusses a file permissions error on Electron that can affect spawning, and an upstream fix for it
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
.executable = true node
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