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