• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2'use strict';
3
4// This tests that fs.access and fs.accessSync works as expected
5// and the errors thrown from these APIs include the desired properties
6
7const common = require('../common');
8if (!common.isWindows && process.getuid() === 0)
9  common.skip('as this test should not be run as `root`');
10
11if (common.isIBMi)
12  common.skip('IBMi has a different access permission mechanism');
13
14const assert = require('assert');
15const fs = require('fs');
16const path = require('path');
17
18const { internalBinding } = require('internal/test/binding');
19const { UV_ENOENT } = internalBinding('uv');
20
21const tmpdir = require('../common/tmpdir');
22const doesNotExist = path.join(tmpdir.path, '__this_should_not_exist');
23const readOnlyFile = path.join(tmpdir.path, 'read_only_file');
24const readWriteFile = path.join(tmpdir.path, 'read_write_file');
25
26function createFileWithPerms(file, mode) {
27  fs.writeFileSync(file, '');
28  fs.chmodSync(file, mode);
29}
30
31tmpdir.refresh();
32createFileWithPerms(readOnlyFile, 0o444);
33createFileWithPerms(readWriteFile, 0o666);
34
35/*
36 * On non-Windows supported platforms, fs.access(readOnlyFile, W_OK, ...)
37 * always succeeds if node runs as the super user, which is sometimes the
38 * case for tests running on our continuous testing platform agents.
39 *
40 * In this case, this test tries to change its process user id to a
41 * non-superuser user so that the test that checks for write access to a
42 * read-only file can be more meaningful.
43 *
44 * The change of user id is done after creating the fixtures files for the same
45 * reason: the test may be run as the superuser within a directory in which
46 * only the superuser can create files, and thus it may need superuser
47 * privileges to create them.
48 *
49 * There's not really any point in resetting the process' user id to 0 after
50 * changing it to 'nobody', since in the case that the test runs without
51 * superuser privilege, it is not possible to change its process user id to
52 * superuser.
53 *
54 * It can prevent the test from removing files created before the change of user
55 * id, but that's fine. In this case, it is the responsibility of the
56 * continuous integration platform to take care of that.
57 */
58let hasWriteAccessForReadonlyFile = false;
59if (!common.isWindows && process.getuid() === 0) {
60  hasWriteAccessForReadonlyFile = true;
61  try {
62    process.setuid('nobody');
63    hasWriteAccessForReadonlyFile = false;
64  } catch {
65  }
66}
67
68assert.strictEqual(typeof fs.F_OK, 'number');
69assert.strictEqual(typeof fs.R_OK, 'number');
70assert.strictEqual(typeof fs.W_OK, 'number');
71assert.strictEqual(typeof fs.X_OK, 'number');
72
73const throwNextTick = (e) => { process.nextTick(() => { throw e; }); };
74
75fs.access(__filename, common.mustCall(function(...args) {
76  assert.deepStrictEqual(args, [null]);
77}));
78fs.promises.access(__filename)
79  .then(common.mustCall())
80  .catch(throwNextTick);
81fs.access(__filename, fs.R_OK, common.mustCall(function(...args) {
82  assert.deepStrictEqual(args, [null]);
83}));
84fs.promises.access(__filename, fs.R_OK)
85  .then(common.mustCall())
86  .catch(throwNextTick);
87fs.access(readOnlyFile, fs.F_OK | fs.R_OK, common.mustCall(function(...args) {
88  assert.deepStrictEqual(args, [null]);
89}));
90fs.promises.access(readOnlyFile, fs.F_OK | fs.R_OK)
91  .then(common.mustCall())
92  .catch(throwNextTick);
93
94{
95  const expectedError = (err) => {
96    assert.notStrictEqual(err, null);
97    assert.strictEqual(err.code, 'ENOENT');
98    assert.strictEqual(err.path, doesNotExist);
99  };
100  fs.access(doesNotExist, common.mustCall(expectedError));
101  fs.promises.access(doesNotExist)
102    .then(common.mustNotCall(), common.mustCall(expectedError))
103    .catch(throwNextTick);
104}
105
106{
107  function expectedError(err) {
108    assert.strictEqual(this, undefined);
109    if (hasWriteAccessForReadonlyFile) {
110      assert.ifError(err);
111    } else {
112      assert.notStrictEqual(err, null);
113      assert.strictEqual(err.path, readOnlyFile);
114    }
115  }
116  fs.access(readOnlyFile, fs.W_OK, common.mustCall(expectedError));
117  fs.promises.access(readOnlyFile, fs.W_OK)
118    .then(common.mustNotCall(), common.mustCall(expectedError))
119    .catch(throwNextTick);
120}
121
122{
123  const expectedError = (err) => {
124    assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE');
125    assert.ok(err instanceof TypeError);
126    return true;
127  };
128  assert.throws(
129    () => { fs.access(100, fs.F_OK, common.mustNotCall()); },
130    expectedError
131  );
132
133  fs.promises.access(100, fs.F_OK)
134    .then(common.mustNotCall(), common.mustCall(expectedError))
135    .catch(throwNextTick);
136}
137
138assert.throws(
139  () => {
140    fs.access(__filename, fs.F_OK);
141  },
142  {
143    code: 'ERR_INVALID_CALLBACK',
144    name: 'TypeError'
145  });
146
147assert.throws(
148  () => {
149    fs.access(__filename, fs.F_OK, {});
150  },
151  {
152    code: 'ERR_INVALID_CALLBACK',
153    name: 'TypeError'
154  });
155
156// Regular access should not throw.
157fs.accessSync(__filename);
158const mode = fs.F_OK | fs.R_OK | fs.W_OK;
159fs.accessSync(readWriteFile, mode);
160
161assert.throws(
162  () => { fs.accessSync(doesNotExist); },
163  (err) => {
164    assert.strictEqual(err.code, 'ENOENT');
165    assert.strictEqual(err.path, doesNotExist);
166    assert.strictEqual(
167      err.message,
168      `ENOENT: no such file or directory, access '${doesNotExist}'`
169    );
170    assert.strictEqual(err.constructor, Error);
171    assert.strictEqual(err.syscall, 'access');
172    assert.strictEqual(err.errno, UV_ENOENT);
173    return true;
174  }
175);
176
177assert.throws(
178  () => { fs.accessSync(Buffer.from(doesNotExist)); },
179  (err) => {
180    assert.strictEqual(err.code, 'ENOENT');
181    assert.strictEqual(err.path, doesNotExist);
182    assert.strictEqual(
183      err.message,
184      `ENOENT: no such file or directory, access '${doesNotExist}'`
185    );
186    assert.strictEqual(err.constructor, Error);
187    assert.strictEqual(err.syscall, 'access');
188    assert.strictEqual(err.errno, UV_ENOENT);
189    return true;
190  }
191);
192