• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
159// Ref: https://github.com/nodejs/node/issues/13255
160const path = `${tmpdir.path}/test-utimes-precision`;
161fs.writeFileSync(path, '');
162
163// Test Y2K38 for all platforms [except 'arm', 'OpenBSD', 'SunOS' and 'IBMi']
164if (!process.arch.includes('arm') &&
165  !common.isOpenBSD && !common.isSunOS && !common.isIBMi) {
166  const Y2K38_mtime = 2 ** 31;
167  fs.utimesSync(path, Y2K38_mtime, Y2K38_mtime);
168  const Y2K38_stats = fs.statSync(path);
169  assert.strictEqual(Y2K38_stats.mtime.getTime() / 1000, Y2K38_mtime);
170}
171
172if (common.isWindows) {
173  // This value would get converted to (double)1713037251359.9998
174  const truncate_mtime = 1713037251360;
175  fs.utimesSync(path, truncate_mtime / 1000, truncate_mtime / 1000);
176  const truncate_stats = fs.statSync(path);
177  assert.strictEqual(truncate_stats.mtime.getTime(), truncate_mtime);
178
179  // test Y2K38 for windows
180  // This value if treaded as a `signed long` gets converted to -2135622133469.
181  // POSIX systems stores timestamps in {long t_sec, long t_usec}.
182  // NTFS stores times in nanoseconds in a single `uint64_t`, so when libuv
183  // calculates (long)`uv_timespec_t.tv_sec` we get 2's complement.
184  const overflow_mtime = 2159345162531;
185  fs.utimesSync(path, overflow_mtime / 1000, overflow_mtime / 1000);
186  const overflow_stats = fs.statSync(path);
187  assert.strictEqual(overflow_stats.mtime.getTime(), overflow_mtime);
188}
189
190const expectTypeError = {
191  code: 'ERR_INVALID_ARG_TYPE',
192  name: 'TypeError'
193};
194// utimes-only error cases
195{
196  assert.throws(
197    () => fs.utimes(0, new Date(), new Date(), common.mustNotCall()),
198    expectTypeError
199  );
200  assert.throws(
201    () => fs.utimesSync(0, new Date(), new Date()),
202    expectTypeError
203  );
204}
205
206// shared error cases
207[false, {}, [], null, undefined].forEach((i) => {
208  assert.throws(
209    () => fs.utimes(i, new Date(), new Date(), common.mustNotCall()),
210    expectTypeError
211  );
212  assert.throws(
213    () => fs.utimesSync(i, new Date(), new Date()),
214    expectTypeError
215  );
216  assert.throws(
217    () => fs.futimes(i, new Date(), new Date(), common.mustNotCall()),
218    expectTypeError
219  );
220  assert.throws(
221    () => fs.futimesSync(i, new Date(), new Date()),
222    expectTypeError
223  );
224});
225
226const expectRangeError = {
227  code: 'ERR_OUT_OF_RANGE',
228  name: 'RangeError',
229  message: 'The value of "fd" is out of range. ' +
230           'It must be >= 0 && <= 2147483647. Received -1'
231};
232// futimes-only error cases
233{
234  assert.throws(
235    () => fs.futimes(-1, new Date(), new Date(), common.mustNotCall()),
236    expectRangeError
237  );
238  assert.throws(
239    () => fs.futimesSync(-1, new Date(), new Date()),
240    expectRangeError
241  );
242}
243