Skip to content

Commit 4b4f3cf

Browse files
committed
copy*(): use ino to check identical paths
1 parent 3eceb91 commit 4b4f3cf

13 files changed

+350
-240
lines changed

lib/copy-sync/__tests__/broken-symlink.test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ describe('copy-sync / broken symlink', () => {
2323

2424
afterEach(done => fse.remove(TEST_DIR, done))
2525

26-
it('should copy broken symlinks by default', () => {
27-
assert.doesNotThrow(() => copySync(src, out))
28-
assert.strictEqual(fs.readlinkSync(path.join(out, 'broken-symlink')), path.join(src, 'does-not-exist'))
26+
it('should error if symlink is broken', () => {
27+
assert.throws(() => copySync(src, out))
2928
})
3029

3130
it('should throw an error when dereference=true', () => {

lib/copy-sync/__tests__/copy-sync-dir.test.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ const path = require('path')
66
const assert = require('assert')
77
const crypto = require('crypto')
88

9-
/* global beforeEach, describe, it */
9+
/* global beforeEach, afterEach, describe, it */
1010

11-
describe('+ copySync()', () => {
11+
describe('+ copySync() / dir', () => {
1212
const SIZE = 16 * 64 * 1024 + 7
1313
let TEST_DIR
1414
let src, dest
@@ -20,6 +20,8 @@ describe('+ copySync()', () => {
2020
fs.emptyDir(TEST_DIR, done)
2121
})
2222

23+
afterEach(done => fs.remove(TEST_DIR, done))
24+
2325
describe('> when src is a directory', () => {
2426
describe('> when dest exists and is a file', () => {
2527
it('should throw error', () => {
@@ -70,13 +72,15 @@ describe('+ copySync()', () => {
7072
})
7173

7274
it('should preserve symbolic links', () => {
75+
const srcTarget = path.join(TEST_DIR, 'destination')
7376
fs.mkdirSync(src)
74-
fs.symlinkSync('destination', path.join(src, 'symlink'))
77+
fs.mkdirSync(srcTarget)
78+
fs.symlinkSync(srcTarget, path.join(src, 'symlink'))
7579

7680
fs.copySync(src, dest)
7781

7882
const link = fs.readlinkSync(path.join(dest, 'symlink'))
79-
assert.strictEqual(link, 'destination')
83+
assert.strictEqual(link, srcTarget)
8084
})
8185

8286
describe('> when the destination dir does not exist', () => {
@@ -183,14 +187,9 @@ describe('+ copySync()', () => {
183187
const dest = path.join(TEST_DIR, 'dest')
184188

185189
setTimeout(() => {
186-
fs.mkdirSync(src)
187-
fs.writeFileSync(path.join(src, 'somefile.html'), 'some data')
190+
fs.outputFileSync(path.join(src, 'somefile.html'), 'some data')
188191
fs.mkdirSync(dest)
189-
try {
190-
fs.copySync(src, dest, filter)
191-
} catch (err) {
192-
assert.ifError(err)
193-
}
192+
fs.copySync(src, dest, filter)
194193
assert(!fs.existsSync(path.join(dest, 'somefile.html')))
195194
done()
196195
}, 1000)

lib/copy-sync/__tests__/copy-sync-file.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ const crypto = require('crypto')
1010

1111
const SIZE = 16 * 64 * 1024 + 7
1212

13-
describe('+ copySync()', () => {
13+
describe('+ copySync() / file', () => {
1414
let TEST_DIR
1515

1616
beforeEach(done => {
17-
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync')
17+
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync-file')
1818
fs.emptyDir(TEST_DIR, done)
1919
})
2020

lib/copy-sync/__tests__/copy-sync-prevent-copying-identical.test.js

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,89 @@ describe('+ copySync() - prevent copying identical files and dirs', () => {
2323
it('should return an error if src and dest are the same', () => {
2424
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_copy_sync')
2525
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy_sync')
26+
fs.ensureFileSync(fileSrc)
27+
2628
try {
2729
fs.copySync(fileSrc, fileDest)
2830
} catch (err) {
2931
assert.strictEqual(err.message, 'Source and destination must not be the same.')
3032
}
3133
})
3234

35+
describe('dest with parent symlink', () => {
36+
describe('first parent is symlink', () => {
37+
it('should error when src is file', () => {
38+
const src = path.join(TEST_DIR, 'a', 'file.txt')
39+
const dest = path.join(TEST_DIR, 'b', 'file.txt')
40+
const srcParent = path.join(TEST_DIR, 'a')
41+
const destParent = path.join(TEST_DIR, 'b')
42+
fs.ensureFileSync(src)
43+
fs.ensureSymlinkSync(srcParent, destParent, 'dir')
44+
45+
try {
46+
fs.copySync(src, dest)
47+
} catch (err) {
48+
assert.strictEqual(err.message, 'Source and destination must not be the same.')
49+
} finally {
50+
assert(fs.existsSync(src))
51+
}
52+
})
53+
54+
it('should error when src is directory', () => {
55+
const src = path.join(TEST_DIR, 'a', 'foo')
56+
const dest = path.join(TEST_DIR, 'b', 'foo')
57+
const srcParent = path.join(TEST_DIR, 'a')
58+
const destParent = path.join(TEST_DIR, 'b')
59+
fs.ensureDirSync(src)
60+
fs.ensureSymlinkSync(srcParent, destParent, 'dir')
61+
62+
try {
63+
fs.copySync(src, dest)
64+
} catch (err) {
65+
assert.strictEqual(err.message, 'Source and destination must not be the same.')
66+
} finally {
67+
assert(fs.existsSync(src))
68+
}
69+
})
70+
})
71+
72+
describe('nested dest', () => {
73+
it('should error when src is file', () => {
74+
const src = path.join(TEST_DIR, 'a', 'dir', 'file.txt')
75+
const dest = path.join(TEST_DIR, 'b', 'dir', 'file.txt')
76+
const srcParent = path.join(TEST_DIR, 'a')
77+
const destParent = path.join(TEST_DIR, 'b')
78+
fs.ensureFileSync(src)
79+
fs.ensureSymlinkSync(srcParent, destParent, 'dir')
80+
81+
try {
82+
fs.copySync(src, dest)
83+
} catch (err) {
84+
assert.strictEqual(err.message, 'Source and destination must not be the same.')
85+
} finally {
86+
assert(fs.existsSync(src))
87+
}
88+
})
89+
90+
it('should error when src is directory', () => {
91+
const src = path.join(TEST_DIR, 'a', 'dir', 'foo')
92+
const dest = path.join(TEST_DIR, 'b', 'dir', 'foo')
93+
const srcParent = path.join(TEST_DIR, 'a')
94+
const destParent = path.join(TEST_DIR, 'b')
95+
fs.ensureDirSync(src)
96+
fs.ensureSymlinkSync(srcParent, destParent, 'dir')
97+
98+
try {
99+
fs.copySync(src, dest)
100+
} catch (err) {
101+
assert.strictEqual(err.message, 'Source and destination must not be the same.')
102+
} finally {
103+
assert(fs.existsSync(src))
104+
}
105+
})
106+
})
107+
})
108+
33109
// src is directory:
34110
// src is regular, dest is symlink
35111
// src is symlink, dest is regular
@@ -90,7 +166,7 @@ describe('+ copySync() - prevent copying identical files and dirs', () => {
90166
})
91167

92168
describe('>> when src and dest are symlinks that point to the exact same path', () => {
93-
it('should not copy and return', () => {
169+
it('should error src and dest are the same', () => {
94170
src = path.join(TEST_DIR, 'src')
95171
fs.mkdirsSync(src)
96172
const srcLink = path.join(TEST_DIR, 'src_symlink')
@@ -101,7 +177,11 @@ describe('+ copySync() - prevent copying identical files and dirs', () => {
101177
const srclenBefore = klawSync(srcLink).length
102178
const destlenBefore = klawSync(destLink).length
103179

104-
fs.copySync(srcLink, destLink)
180+
try {
181+
fs.copySync(srcLink, destLink)
182+
} catch (err) {
183+
assert.strictEqual(err.message, 'Source and destination must not be the same.')
184+
}
105185

106186
const srclenAfter = klawSync(srcLink).length
107187
assert.strictEqual(srclenAfter, srclenBefore, 'src length should not change')
@@ -146,8 +226,7 @@ describe('+ copySync() - prevent copying identical files and dirs', () => {
146226
describe(`>> when src is a symlink that points to a regular dest`, () => {
147227
it('should throw error', () => {
148228
dest = path.join(TEST_DIR, 'dest', 'somefile.txt')
149-
fs.ensureFileSync(dest)
150-
fs.writeFileSync(dest, 'some data')
229+
fs.outputFileSync(dest, 'some data')
151230

152231
const srcLink = path.join(TEST_DIR, 'src-symlink')
153232
fs.symlinkSync(dest, srcLink, 'file')
@@ -164,18 +243,21 @@ describe('+ copySync() - prevent copying identical files and dirs', () => {
164243
})
165244

166245
describe('>> when src and dest are symlinks that point to the exact same path', () => {
167-
it('should not copy and return', () => {
246+
it('should error src and dest are the same', () => {
168247
src = path.join(TEST_DIR, 'src', 'srcfile.txt')
169-
fs.ensureFileSync(src)
170-
fs.writeFileSync(src, 'src data')
248+
fs.outputFileSync(src, 'src data')
171249

172250
const srcLink = path.join(TEST_DIR, 'src_symlink')
173251
fs.symlinkSync(src, srcLink, 'file')
174252

175253
const destLink = path.join(TEST_DIR, 'dest_symlink')
176254
fs.symlinkSync(src, destLink, 'file')
177255

178-
fs.copySync(srcLink, destLink)
256+
try {
257+
fs.copySync(srcLink, destLink)
258+
} catch (err) {
259+
assert.strictEqual(err.message, 'Source and destination must not be the same.')
260+
}
179261

180262
const srcln = fs.readlinkSync(srcLink)
181263
assert.strictEqual(srcln, src)

lib/copy-sync/__tests__/copy-sync-prevent-copying-into-itself.test.js

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('+ copySync() - prevent copying into itself', () => {
3939
afterEach(done => fs.remove(TEST_DIR, done))
4040

4141
describe('> when source is a file', () => {
42-
it('should copy the file successfully even when dest parent is a subdir of src', () => {
42+
it('should copy the file successfully even if dest parent is a subdir of src', () => {
4343
const srcFile = path.join(TEST_DIR, 'src', 'srcfile.txt')
4444
const destFile = path.join(TEST_DIR, 'src', 'dest', 'destfile.txt')
4545
fs.writeFileSync(srcFile, dat0)
@@ -273,7 +273,7 @@ describe('+ copySync() - prevent copying into itself', () => {
273273
})
274274

275275
describe('>> when dest is a symlink', () => {
276-
it('should silently return when resolved dest path is exactly the same as resolved src path', () => {
276+
it('should error when resolved dest path is exactly the same as resolved src path', () => {
277277
const srcLink = path.join(TEST_DIR, 'src-symlink')
278278
fs.symlinkSync(src, srcLink, 'dir')
279279
const destLink = path.join(TEST_DIR, 'dest-symlink')
@@ -284,16 +284,21 @@ describe('+ copySync() - prevent copying into itself', () => {
284284
assert(srclenBefore > 2)
285285
assert(destlenBefore > 2)
286286

287-
fs.copySync(srcLink, destLink)
288-
const srclenAfter = klawSync(srcLink).length
289-
assert.strictEqual(srclenAfter, srclenBefore, 'src length should not change')
290-
const destlenAfter = klawSync(destLink).length
291-
assert.strictEqual(destlenAfter, destlenBefore, 'dest length should not change')
292-
293-
const srcln = fs.readlinkSync(srcLink)
294-
assert.strictEqual(srcln, src)
295-
const destln = fs.readlinkSync(destLink)
296-
assert.strictEqual(destln, src)
287+
try {
288+
fs.copySync(srcLink, destLink)
289+
} catch (err) {
290+
assert.strictEqual(err.message, 'Source and destination must not be the same.')
291+
} finally {
292+
const srclenAfter = klawSync(srcLink).length
293+
assert.strictEqual(srclenAfter, srclenBefore, 'src length should not change')
294+
const destlenAfter = klawSync(destLink).length
295+
assert.strictEqual(destlenAfter, destlenBefore, 'dest length should not change')
296+
297+
const srcln = fs.readlinkSync(srcLink)
298+
assert.strictEqual(srcln, src)
299+
const destln = fs.readlinkSync(destLink)
300+
assert.strictEqual(destln, src)
301+
}
297302
})
298303

299304
it('should error when resolved dest path is a subdir of resolved src path', () => {
@@ -309,10 +314,11 @@ describe('+ copySync() - prevent copying into itself', () => {
309314
try {
310315
fs.copySync(srcLink, destLink)
311316
} catch (err) {
312-
assert(err)
317+
assert.strictEqual(err.message, `Cannot copy '${src}' to a subdirectory of itself, '${resolvedDestPath}'.`)
318+
} finally {
319+
const destln = fs.readlinkSync(destLink)
320+
assert.strictEqual(destln, resolvedDestPath)
313321
}
314-
const destln = fs.readlinkSync(destLink)
315-
assert.strictEqual(destln, src)
316322
})
317323

318324
it('should error when resolved src path is a subdir of resolved dest path', () => {
@@ -322,18 +328,18 @@ describe('+ copySync() - prevent copying into itself', () => {
322328

323329
const dest = path.join(TEST_DIR, 'dest')
324330

325-
fs.mkdirSync(dest)
326-
327-
fs.symlinkSync(srcInDest, srcLink, 'dir')
328-
fs.symlinkSync(dest, destLink, 'dir')
331+
fs.ensureDirSync(srcInDest)
332+
fs.ensureSymlinkSync(srcInDest, srcLink, 'dir')
333+
fs.ensureSymlinkSync(dest, destLink, 'dir')
329334

330335
try {
331336
fs.copySync(srcLink, destLink)
332337
} catch (err) {
333338
assert.strictEqual(err.message, `Cannot overwrite '${dest}' with '${srcInDest}'.`)
339+
} finally {
340+
const destln = fs.readlinkSync(destLink)
341+
assert.strictEqual(destln, dest)
334342
}
335-
const destln = fs.readlinkSync(destLink)
336-
assert.strictEqual(destln, dest)
337343
})
338344
})
339345
})

0 commit comments

Comments
 (0)