diff options
author | Florian Dold <florian.dold@gmail.com> | 2017-12-27 19:33:54 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2017-12-27 19:34:16 +0100 |
commit | 0e6de2c31dbf8c21277481f112e99c52b913940f (patch) | |
tree | 91789032de3b8eec9d789acd1323f25fc5d08422 /node_modules/write-file-atomic/index.js | |
parent | ceda0da31ad542c598c68146ae0712ca03df3d71 (diff) | |
download | wallet-core-0e6de2c31dbf8c21277481f112e99c52b913940f.tar.xz |
node_modules
Diffstat (limited to 'node_modules/write-file-atomic/index.js')
-rw-r--r-- | node_modules/write-file-atomic/index.js | 177 |
1 files changed, 121 insertions, 56 deletions
diff --git a/node_modules/write-file-atomic/index.js b/node_modules/write-file-atomic/index.js index c677ee962..3b5607d15 100644 --- a/node_modules/write-file-atomic/index.js +++ b/node_modules/write-file-atomic/index.js @@ -2,10 +2,13 @@ module.exports = writeFile module.exports.sync = writeFileSync module.exports._getTmpname = getTmpname // for testing +module.exports._cleanupOnExit = cleanupOnExit var fs = require('graceful-fs') -var chain = require('slide').chain var MurmurHash3 = require('imurmurhash') +var onExit = require('signal-exit') +var path = require('path') +var activeFiles = {} var invocations = 0 function getTmpname (filename) { @@ -16,75 +19,134 @@ function getTmpname (filename) { .result() } +function cleanupOnExit (tmpfile) { + return function () { + try { + fs.unlinkSync(typeof tmpfile === 'function' ? tmpfile() : tmpfile) + } catch (_) {} + } +} + function writeFile (filename, data, options, callback) { if (options instanceof Function) { callback = options options = null } if (!options) options = {} - fs.realpath(filename, function (_, realname) { - _writeFile(realname || filename, data, options, callback) - }) -} -function _writeFile (filename, data, options, callback) { - var tmpfile = getTmpname(filename) - if (options.mode && options.chown) { - return thenWriteFile() - } else { - // Either mode or chown is not explicitly set - // Default behavior is to copy it from original file - return fs.stat(filename, function (err, stats) { - if (err || !stats) return thenWriteFile() + var Promise = options.Promise || global.Promise + var truename + var fd + var tmpfile + var removeOnExit = cleanupOnExit(() => tmpfile) + var absoluteName = path.resolve(filename) - options = Object.assign({}, options) - if (!options.mode) { - options.mode = stats.mode - } - if (!options.chown && process.getuid) { - options.chown = { uid: stats.uid, gid: stats.gid } - } - return thenWriteFile() - }) - } + new Promise(function serializeSameFile (resolve) { + // make a queue if it doesn't already exist + if (!activeFiles[absoluteName]) activeFiles[absoluteName] = [] - function thenWriteFile () { - chain([ - [writeFileAsync, tmpfile, data, options.mode, options.encoding || 'utf8'], - options.chown && [fs, fs.chown, tmpfile, options.chown.uid, options.chown.gid], - options.mode && [fs, fs.chmod, tmpfile, options.mode], - [fs, fs.rename, tmpfile, filename] - ], function (err) { - err ? fs.unlink(tmpfile, function () { callback(err) }) - : callback() + activeFiles[absoluteName].push(resolve) // add this job to the queue + if (activeFiles[absoluteName].length === 1) resolve() // kick off the first one + }).then(function getRealPath () { + return new Promise(function (resolve) { + fs.realpath(filename, function (_, realname) { + truename = realname || filename + tmpfile = getTmpname(truename) + resolve() + }) }) - } + }).then(function stat () { + return new Promise(function stat (resolve) { + if (options.mode && options.chown) resolve() + else { + // Either mode or chown is not explicitly set + // Default behavior is to copy it from original file + fs.stat(truename, function (err, stats) { + if (err || !stats) resolve() + else { + options = Object.assign({}, options) - // doing this instead of `fs.writeFile` in order to get the ability to - // call `fsync`. - function writeFileAsync (file, data, mode, encoding, cb) { - fs.open(file, 'w', options.mode, function (err, fd) { - if (err) return cb(err) + if (!options.mode) { + options.mode = stats.mode + } + if (!options.chown && process.getuid) { + options.chown = { uid: stats.uid, gid: stats.gid } + } + resolve() + } + }) + } + }) + }).then(function thenWriteFile () { + return new Promise(function (resolve, reject) { + fs.open(tmpfile, 'w', options.mode, function (err, _fd) { + fd = _fd + if (err) reject(err) + else resolve() + }) + }) + }).then(function write () { + return new Promise(function (resolve, reject) { if (Buffer.isBuffer(data)) { - return fs.write(fd, data, 0, data.length, 0, syncAndClose) + fs.write(fd, data, 0, data.length, 0, function (err) { + if (err) reject(err) + else resolve() + }) } else if (data != null) { - return fs.write(fd, String(data), 0, String(encoding), syncAndClose) - } else { - return syncAndClose() - } - function syncAndClose (err) { - if (err) return cb(err) - if (options.fsync !== false) { - fs.fsync(fd, function (err) { - if (err) return cb(err) - fs.close(fd, cb) - }) - } else { - fs.close(fd, cb) - } - } + fs.write(fd, String(data), 0, String(options.encoding || 'utf8'), function (err) { + if (err) reject(err) + else resolve() + }) + } else resolve() }) - } + }).then(function syncAndClose () { + if (options.fsync !== false) { + return new Promise(function (resolve, reject) { + fs.fsync(fd, function (err) { + if (err) reject(err) + else fs.close(fd, resolve) + }) + }) + } + }).then(function chown () { + if (options.chown) { + return new Promise(function (resolve, reject) { + fs.chown(tmpfile, options.chown.uid, options.chown.gid, function (err) { + if (err) reject(err) + else resolve() + }) + }) + } + }).then(function chmod () { + if (options.mode) { + return new Promise(function (resolve, reject) { + fs.chmod(tmpfile, options.mode, function (err) { + if (err) reject(err) + else resolve() + }) + }) + } + }).then(function rename () { + return new Promise(function (resolve, reject) { + fs.rename(tmpfile, truename, function (err) { + if (err) reject(err) + else resolve() + }) + }) + }).then(function success () { + removeOnExit() + callback() + }).catch(function fail (err) { + removeOnExit() + fs.unlink(tmpfile, function () { + callback(err) + }) + }).then(function checkQueue () { + activeFiles[absoluteName].shift() // remove the element added by serializeSameFile + if (activeFiles[absoluteName].length > 0) { + activeFiles[absoluteName][0]() // start next job if one is pending + } else delete activeFiles[absoluteName] + }) } function writeFileSync (filename, data, options) { @@ -114,6 +176,7 @@ function writeFileSync (filename, data, options) { } } + var removeOnExit = onExit(cleanupOnExit(tmpfile)) var fd = fs.openSync(tmpfile, 'w', options.mode) if (Buffer.isBuffer(data)) { fs.writeSync(fd, data, 0, data.length, 0) @@ -127,7 +190,9 @@ function writeFileSync (filename, data, options) { if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid) if (options.mode) fs.chmodSync(tmpfile, options.mode) fs.renameSync(tmpfile, filename) + removeOnExit() } catch (err) { + removeOnExit() try { fs.unlinkSync(tmpfile) } catch (e) {} throw err } |