Skip to content

Commit 7c251d6

Browse files
authored
Merge pull request #679 from jprichardson/fix-max-call-stack
util/stat: fix max call stack size exceeded
2 parents 23b0c0c + 72278dc commit 7c251d6

File tree

2 files changed

+86
-7
lines changed

2 files changed

+86
-7
lines changed

lib/util/__tests__/stat.test.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
'use strict'
2+
3+
const fs = require(process.cwd())
4+
const os = require('os')
5+
const path = require('path')
6+
const assert = require('assert')
7+
const semver = require('semver')
8+
const stat = require('../stat.js')
9+
10+
const NODE_VERSION_WITH_BIGINT = '10.5.0'
11+
12+
/* global beforeEach, afterEach, describe, it */
13+
14+
describe('util/stat', () => {
15+
let TEST_DIR
16+
17+
beforeEach(done => {
18+
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'util-stat')
19+
fs.emptyDir(TEST_DIR, done)
20+
})
21+
22+
afterEach(done => fs.remove(TEST_DIR, done))
23+
24+
describe('should use stats with bigint type for node versions >= 10.5.0 and number type for older versions', done => {
25+
it('stat.checkPaths()', () => {
26+
const nodeVersion = process.versions.node
27+
const src = path.join(TEST_DIR, 'src')
28+
const dest = path.join(TEST_DIR, 'dest')
29+
fs.ensureFileSync(src)
30+
fs.ensureFileSync(dest)
31+
stat.checkPaths(src, dest, 'copy', (err, stats) => {
32+
assert.ifError(err)
33+
const { srcStat } = stats
34+
if (semver.gte(nodeVersion, NODE_VERSION_WITH_BIGINT)) {
35+
assert.strictEqual(typeof srcStat.ino, 'bigint')
36+
} else {
37+
assert.strictEqual(typeof srcStat.ino, 'number')
38+
}
39+
})
40+
})
41+
42+
it('stat.checkPathsSync()', () => {
43+
const nodeVersion = process.versions.node
44+
const src = path.join(TEST_DIR, 'src')
45+
const dest = path.join(TEST_DIR, 'dest')
46+
fs.ensureFileSync(src)
47+
fs.ensureFileSync(dest)
48+
const { srcStat } = stat.checkPathsSync(src, dest, 'copy')
49+
if (semver.gte(nodeVersion, NODE_VERSION_WITH_BIGINT)) {
50+
assert.strictEqual(typeof srcStat.ino, 'bigint')
51+
} else {
52+
assert.strictEqual(typeof srcStat.ino, 'number')
53+
}
54+
})
55+
})
56+
57+
describe('should stop at src or root path and not throw max call stack size error', done => {
58+
it('stat.checkParentPaths()', () => {
59+
const src = path.join(TEST_DIR, 'src')
60+
let dest = path.join(TEST_DIR, 'dest')
61+
fs.ensureFileSync(src)
62+
fs.ensureFileSync(dest)
63+
dest = path.basename(dest)
64+
const srcStat = fs.statSync(src)
65+
stat.checkParentPaths(src, srcStat, dest, 'copy', err => {
66+
assert.ifError(err)
67+
})
68+
})
69+
70+
it('stat.checkParentPathsSync()', () => {
71+
const src = path.join(TEST_DIR, 'src')
72+
let dest = path.join(TEST_DIR, 'dest')
73+
fs.ensureFileSync(src)
74+
fs.ensureFileSync(dest)
75+
dest = path.basename(dest)
76+
const srcStat = fs.statSync(src)
77+
stat.checkParentPathsSync(src, srcStat, dest, 'copy')
78+
})
79+
})
80+
})

lib/util/stat.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,9 @@ function checkPathsSync (src, dest, funcName) {
104104
// checks the src and dest inodes. It starts from the deepest
105105
// parent and stops once it reaches the src parent or the root path.
106106
function checkParentPaths (src, srcStat, dest, funcName, cb) {
107-
const destParent = path.dirname(dest)
108-
if (destParent &&
109-
(destParent === path.dirname(src) ||
110-
destParent === path.parse(destParent).root)
111-
) return cb()
107+
const srcParent = path.resolve(path.dirname(src))
108+
const destParent = path.resolve(path.dirname(dest))
109+
if (destParent === srcParent || destParent === path.parse(destParent).root) return cb()
112110
if (nodeSupportsBigInt()) {
113111
fs.stat(destParent, { bigint: true }, (err, destStat) => {
114112
if (err) {
@@ -135,8 +133,9 @@ function checkParentPaths (src, srcStat, dest, funcName, cb) {
135133
}
136134

137135
function checkParentPathsSync (src, srcStat, dest, funcName) {
138-
const destParent = path.dirname(dest)
139-
if (destParent && (destParent === path.dirname(src) || destParent === path.parse(destParent).root)) return
136+
const srcParent = path.resolve(path.dirname(src))
137+
const destParent = path.resolve(path.dirname(dest))
138+
if (destParent === srcParent || destParent === path.parse(destParent).root) return
140139
let destStat
141140
try {
142141
if (nodeSupportsBigInt()) {

0 commit comments

Comments
 (0)