Skip to content

Commit f90c7dd

Browse files
committed
Stop unsymlinking destinations that are symlinks
Fixes: #5
1 parent 629265c commit f90c7dd

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

index.js

+10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ module.exports = function writeFile (filename, data, options, callback) {
1515
options = null
1616
}
1717
if (!options) options = {}
18+
fs.realpath(filename, function (_, realname) {
19+
_writeFile(realname || filename, data, options, callback)
20+
})
21+
}
22+
function _writeFile (filename, data, options, callback) {
1823
var tmpfile = getTmpname(filename)
1924

2025
if (options.mode && options.chown) {
@@ -51,6 +56,11 @@ module.exports = function writeFile (filename, data, options, callback) {
5156

5257
module.exports.sync = function writeFileSync (filename, data, options) {
5358
if (!options) options = {}
59+
try {
60+
filename = fs.realpathSync(filename)
61+
} catch (ex) {
62+
// it's ok, it'll happen on a not yet existing file
63+
}
5464
var tmpfile = getTmpname(filename)
5565

5666
try {

test/basic.js

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ var test = require('tap').test
33
var requireInject = require('require-inject')
44
var writeFileAtomic = requireInject('../index', {
55
'graceful-fs': {
6+
realpath: function (filename, cb) {
7+
return cb(null, filename)
8+
},
69
writeFile: function (tmpfile, data, options, cb) {
710
if (/nowrite/.test(tmpfile)) return cb(new Error('ENOWRITE'))
811
cb()
@@ -27,6 +30,9 @@ var writeFileAtomic = requireInject('../index', {
2730
if (/nostat/.test(tmpfile)) return cb(new Error('ENOSTAT'))
2831
cb()
2932
},
33+
realpathSync: function (filename, cb) {
34+
return filename
35+
},
3036
writeFileSync: function (tmpfile, data, options) {
3137
if (/nowrite/.test(tmpfile)) throw new Error('ENOWRITE')
3238
},

test/integration.js

+26
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ test('writes simple file (async)', function (t) {
8484
})
8585
})
8686

87+
test('writes to symlinks without clobbering (async)', function (t) {
88+
t.plan(5)
89+
var file = tmpFile()
90+
var link = tmpFile()
91+
fs.writeFileSync(file, '42')
92+
fs.symlinkSync(file, link)
93+
didWriteFileAtomic(t, currentUser(), link, '43', function (err) {
94+
t.ifError(err, 'no error')
95+
t.is(readFile(file), '43', 'target content ok')
96+
t.is(readFile(link), '43', 'link content ok')
97+
t.ok(fs.lstatSync(link).isSymbolicLink(), 'link is link')
98+
})
99+
})
100+
87101
test('runs chown on given file (async)', function (t) {
88102
var file = tmpFile()
89103
didWriteFileAtomic(t, {uid: 42, gid: 43}, file, '42', { chown: { uid: 42, gid: 43 } }, function (err) {
@@ -156,6 +170,18 @@ test('writes simple file (sync)', function (t) {
156170
t.is(readFile(file), '42')
157171
})
158172

173+
test('writes to symlinks without clobbering (sync)', function (t) {
174+
t.plan(4)
175+
var file = tmpFile()
176+
var link = tmpFile()
177+
fs.writeFileSync(file, '42')
178+
fs.symlinkSync(file, link)
179+
didWriteFileAtomicSync(t, currentUser(), link, '43')
180+
t.is(readFile(file), '43', 'target content ok')
181+
t.is(readFile(link), '43', 'link content ok')
182+
t.ok(fs.lstatSync(link).isSymbolicLink(), 'link is link')
183+
})
184+
159185
test('runs chown on given file (sync)', function (t) {
160186
t.plan(1)
161187
var file = tmpFile()

0 commit comments

Comments
 (0)