Node: Node 10, npm gives an EPERM on every operation

Created on 17 Apr 2018  路  19Comments  路  Source: nodejs/node

Version: v10.0.0-nightly201804175eb9f3c91c
Platform: Windows 10 64-bit and 32-bit
Subsystem: npm

Both node and npm are installed in default location, and on the path.

This works as expected in Node 9, which is why I filed an issue here.

Any npm command results in the following error on several machines:

fs.js:111
    throw err;
    ^

Error: EPERM: operation not permitted, open 'C:\Program Files (x86)\nodejs\node_modules\npm\bin\npm-cli.js'
    at Object.fs.openSync (fs.js:545:3)
    at Object.fs.readFileSync (fs.js:451:33)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:688:20)
    at Module.load (internal/modules/cjs/loader.js:589:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:528:12)
    at Function.Module._load (internal/modules/cjs/loader.js:520:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:719:10)
    at startup (internal/bootstrap/node.js:229:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:576:3)
fs.js:111
    throw err;
    ^

Error: EPERM: operation not permitted, open 'C:\Program Files (x86)\nodejs\node_modules\npm\bin\npm-cli.js'
    at Object.fs.openSync (fs.js:545:3)
    at Object.fs.readFileSync (fs.js:451:33)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:688:20)
    at Module.load (internal/modules/cjs/loader.js:589:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:528:12)
    at Function.Module._load (internal/modules/cjs/loader.js:520:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:719:10)
    at startup (internal/bootstrap/node.js:229:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:576:3)
confirmed-bug libuv npm windows

All 19 comments

EPERM means "permissions issue." Could be a number of things, from an overzealous virus scanner to wrong file permissions.

My guess is wrong file permissions are set by the installer, since Node 9 (both npm 5.6 and 5.8) work as expected. File location doesn't seem to be changed between versions, and this happens consistently on multiple machines, and keeps happening even after an hour.

I can repro the same issue easily. I tried to debug npm, but turns out this is before it even gets to running npm (as can been seen from the stack above, where it's just trying to open the npm-cli.js file to load it).

I have the latest v10.x branch built locally, and stepping through the code it appears to be due to the recent libuv, which is requesting write access to any opened file. As npm (along with the rest of Node.js) is installed under C:\Program Files, then write access is not granted (unless running with Admin rights).

You can see the change causing this here: https://github.com/nodejs/node/commit/ae2b5bcb7c17a2d2a488f234c736201eed8200db#diff-4a14951fd12c46802543f3a8aa54a620R437 . If I comment out that exact line ("access |= FILE_WRITE_ATTRIBUTES;") and rebuild, npm runs without issue.

This seems fundamentally busted on Windows if you can't run npm commands. (Odd/concerning if this has been in the v10.x branch for 15 days without being caught!)

cc @nodejs/libuv @nodejs/platform-windows

Corresponding libuv PR: https://github.com/libuv/libuv/pull/1777

cc @bzoz

Can reproduce on Windows 7 x64.

  1. Create simple test.js file, then forbid write access via file property dialog (this should not be simple read-only property, but a security permission change).
  2. This file can be run by all current releases from v4 till v9.
  3. Last Nightly and v8-canary give EPERM: operation not permitted.

cc @jasnell as this can block v10 release.

PR to revert the libuv commit in https://github.com/libuv/libuv/pull/1800. Unless a better solution is available quickly, I'd like to land the revert and cut a new libuv release.

@vsemozhetbyt can you PR a failing test case and mark it blocked?

@cjihrig I am not sure. I can try, but if anybody is sure enough to make it quickly and confidently, please, do.

It's a bit late at night for me, but I think a rough test case would involve:

  1. Copying a .js file fixture to common.tmpDir
  2. Using the Windows icacls cli command to deny write access for the current user to the copied fixture
  3. Attempting to run the copied fixture with process.execPath

I suspect the complications will be getting the right parameters to icacls, and whether access needs to be restored so that the common.tmpDir can be cleaned up afterwards.

Ouch. @cjihrig ... we can float a patch if a libuv update cannot be landed this week. Really need to try to get the commits locked down by end of week.

Proposed release for tomorrow - https://github.com/libuv/libuv/issues/1801. In addition to fixing this, I'd really like to get the fix for #19903 out.

Trying also to read https://ss64.com/nt/icacls.html ...

Strange.
As I cannot find an easy way to get file owner name to give it to icacls, I am trying to manipulate overall write permission via well-known SID.

When I deny writing permissions for all via GUI dialog, I can run with v4-v9 and not with v10.
When I deny writing permissions for all via:

execFileSync('icacls', [scriptName, '/deny', '*S-1-1-0:W']);

I cannot run with any version (Error: Cannot find module ...), while the permissions are identical.

Will investigate more if nobody will succeed sooner.

I just wrote up the below test, and this is performing as expected for me (i.e. fails with the current bits, works after building with my fix above).

I can turn this into a test-case and submit if you like. (I haven't contributed a test before, but I'm happy to give it a go). Let me know if you have any guidance on where/how this test should live, and any consideration that should be added.

const os = require('os');
const fs = require('fs');
const path = require('path');
const cp = require('child_process');

if (os.type() == 'Windows_NT') {
    // mymod.js should be a module in the same folder with default (inherited) permissions
    const mymodPath = path.join(__dirname, "mymod.js");

    // Removed any inherited ACEs, and any explicitly granted ACEs for the current user
    cp.execSync(`icacls.exe "${mymodPath}" /inheritance:r /remove "%USERNAME%"`);
    // Grant the current user read & execute only
    cp.execSync(`icacls.exe "${mymodPath}" /grant "%USERNAME%":RX`);

    // Attempt to load the module. Will fail if write access is required
    const mymod = require('./mymod');

    // Remove the expliclty granted rights, and reenable inheritance
    cp.execSync(`icacls.exe "${mymodPath}" /remove "%USERNAME%" /inheritance:e`);
}

cc @nodejs/testing

Test up for review in #20116 (may want to merge this in with the fix to avoid any failing tests).

Reopening until the libuv update lands here :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

addaleax picture addaleax  路  3Comments

mcollina picture mcollina  路  3Comments

Brekmister picture Brekmister  路  3Comments

dfahlander picture dfahlander  路  3Comments

jmichae3 picture jmichae3  路  3Comments