1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24const assert = require('assert'); 25const util = require('util'); 26const fs = require('fs'); 27const url = require('url'); 28 29const tmpdir = require('../common/tmpdir'); 30tmpdir.refresh(); 31 32const lpath = `${tmpdir.path}/symlink`; 33fs.symlinkSync('unoent-entry', lpath); 34 35function stat_resource(resource, statSync = fs.statSync) { 36 if (typeof resource === 'string') { 37 return statSync(resource); 38 } 39 const stats = fs.fstatSync(resource); 40 // Ensure mtime has been written to disk 41 // except for directories on AIX where it cannot be synced 42 if (common.isAIX && stats.isDirectory()) 43 return stats; 44 fs.fsyncSync(resource); 45 return fs.fstatSync(resource); 46} 47 48function check_mtime(resource, mtime, statSync) { 49 mtime = fs._toUnixTimestamp(mtime); 50 const stats = stat_resource(resource, statSync); 51 const real_mtime = fs._toUnixTimestamp(stats.mtime); 52 return mtime - real_mtime; 53} 54 55function expect_errno(syscall, resource, err, errno) { 56 assert( 57 err && (err.code === errno || err.code === 'ENOSYS'), 58 `FAILED: expect_errno ${util.inspect(arguments)}` 59 ); 60} 61 62function expect_ok(syscall, resource, err, atime, mtime, statSync) { 63 const mtime_diff = check_mtime(resource, mtime, statSync); 64 assert( 65 // Check up to single-second precision. 66 // Sub-second precision is OS and fs dependant. 67 !err && (mtime_diff < 2) || err && err.code === 'ENOSYS', 68 `FAILED: expect_ok ${util.inspect(arguments)} 69 check_mtime: ${mtime_diff}` 70 ); 71} 72 73const stats = fs.statSync(tmpdir.path); 74 75const asPath = (path) => path; 76const asUrl = (path) => url.pathToFileURL(path); 77 78const cases = [ 79 [asPath, new Date('1982-09-10 13:37')], 80 [asPath, new Date()], 81 [asPath, 123456.789], 82 [asPath, stats.mtime], 83 [asPath, '123456', -1], 84 [asPath, new Date('2017-04-08T17:59:38.008Z')], 85 [asUrl, new Date()], 86]; 87 88runTests(cases.values()); 89 90function runTests(iter) { 91 const { value, done } = iter.next(); 92 if (done) return; 93 94 // Support easy setting same or different atime / mtime values. 95 const [pathType, atime, mtime = atime] = value; 96 97 let fd; 98 // 99 // test async code paths 100 // 101 fs.utimes(pathType(tmpdir.path), atime, mtime, common.mustCall((err) => { 102 expect_ok('utimes', tmpdir.path, err, atime, mtime); 103 104 fs.lutimes(pathType(lpath), atime, mtime, common.mustCall((err) => { 105 expect_ok('lutimes', lpath, err, atime, mtime, fs.lstatSync); 106 107 fs.utimes(pathType('foobarbaz'), atime, mtime, common.mustCall((err) => { 108 expect_errno('utimes', 'foobarbaz', err, 'ENOENT'); 109 110 // don't close this fd 111 if (common.isWindows) { 112 fd = fs.openSync(tmpdir.path, 'r+'); 113 } else { 114 fd = fs.openSync(tmpdir.path, 'r'); 115 } 116 117 fs.futimes(fd, atime, mtime, common.mustCall((err) => { 118 expect_ok('futimes', fd, err, atime, mtime); 119 120 syncTests(); 121 122 setImmediate(common.mustCall(runTests), iter); 123 })); 124 })); 125 })); 126 })); 127 128 // 129 // test synchronized code paths, these functions throw on failure 130 // 131 function syncTests() { 132 fs.utimesSync(pathType(tmpdir.path), atime, mtime); 133 expect_ok('utimesSync', tmpdir.path, undefined, atime, mtime); 134 135 fs.lutimesSync(pathType(lpath), atime, mtime); 136 expect_ok('lutimesSync', lpath, undefined, atime, mtime, fs.lstatSync); 137 138 // Some systems don't have futimes 139 // if there's an error, it should be ENOSYS 140 try { 141 fs.futimesSync(fd, atime, mtime); 142 expect_ok('futimesSync', fd, undefined, atime, mtime); 143 } catch (ex) { 144 expect_errno('futimesSync', fd, ex, 'ENOSYS'); 145 } 146 147 let err; 148 try { 149 fs.utimesSync(pathType('foobarbaz'), atime, mtime); 150 } catch (ex) { 151 err = ex; 152 } 153 expect_errno('utimesSync', 'foobarbaz', err, 'ENOENT'); 154 155 err = undefined; 156 } 157} 158 159const expectTypeError = { 160 code: 'ERR_INVALID_ARG_TYPE', 161 name: 'TypeError' 162}; 163// utimes-only error cases 164{ 165 assert.throws( 166 () => fs.utimes(0, new Date(), new Date(), common.mustNotCall()), 167 expectTypeError 168 ); 169 assert.throws( 170 () => fs.utimesSync(0, new Date(), new Date()), 171 expectTypeError 172 ); 173} 174 175// shared error cases 176[false, {}, [], null, undefined].forEach((i) => { 177 assert.throws( 178 () => fs.utimes(i, new Date(), new Date(), common.mustNotCall()), 179 expectTypeError 180 ); 181 assert.throws( 182 () => fs.utimesSync(i, new Date(), new Date()), 183 expectTypeError 184 ); 185 assert.throws( 186 () => fs.futimes(i, new Date(), new Date(), common.mustNotCall()), 187 expectTypeError 188 ); 189 assert.throws( 190 () => fs.futimesSync(i, new Date(), new Date()), 191 expectTypeError 192 ); 193}); 194 195const expectRangeError = { 196 code: 'ERR_OUT_OF_RANGE', 197 name: 'RangeError', 198 message: 'The value of "fd" is out of range. ' + 199 'It must be >= 0 && <= 2147483647. Received -1' 200}; 201// futimes-only error cases 202{ 203 assert.throws( 204 () => fs.futimes(-1, new Date(), new Date(), common.mustNotCall()), 205 expectRangeError 206 ); 207 assert.throws( 208 () => fs.futimesSync(-1, new Date(), new Date()), 209 expectRangeError 210 ); 211} 212