diff options
author | Filip Weiss <me@fiws.net> | 2014-09-12 14:43:19 +0400 |
---|---|---|
committer | Forrest L Norvell <forrest@npmjs.com> | 2014-09-19 09:42:28 +0400 |
commit | 17c941a2d583210fe97ed47e2968d94ce9f774ba (patch) | |
tree | b4ec50ca5d84cf93f688d0c10a9cfdeba14ffe1c /node_modules/write-file-atomic | |
parent | f2d2190aa365d22378d03afab0da13f95614a583 (diff) |
use writeFileAtomic instead of fs.writeFile fixes #6163
Diffstat (limited to 'node_modules/write-file-atomic')
-rw-r--r-- | node_modules/write-file-atomic/.npmignore | 4 | ||||
-rw-r--r-- | node_modules/write-file-atomic/README.md | 44 | ||||
-rw-r--r-- | node_modules/write-file-atomic/index.js | 45 | ||||
-rw-r--r-- | node_modules/write-file-atomic/package.json | 55 | ||||
-rw-r--r-- | node_modules/write-file-atomic/test/basic.js | 97 |
5 files changed, 245 insertions, 0 deletions
diff --git a/node_modules/write-file-atomic/.npmignore b/node_modules/write-file-atomic/.npmignore new file mode 100644 index 000000000..454382637 --- /dev/null +++ b/node_modules/write-file-atomic/.npmignore @@ -0,0 +1,4 @@ +*~ +DEADJOE +.#* +node_modules
\ No newline at end of file diff --git a/node_modules/write-file-atomic/README.md b/node_modules/write-file-atomic/README.md new file mode 100644 index 000000000..26e434d19 --- /dev/null +++ b/node_modules/write-file-atomic/README.md @@ -0,0 +1,44 @@ +write-file-atomic +----------------- + +This is an extension for node's `fs.writeFile` that makes its operation +atomic and allows you set ownership (uid/gid of the file). + +### var writeFileAtomic = require('write-file-atomic')<br>writeFileAtomic(filename, data, [options], callback) + +* filename **String** +* data **String** | **Buffer** +* options **Object** + * chown **Object** + * uid **Number** + * gid **Number** + * encoding **String** | **Null** default = 'utf8' + * mode **Number** default = 438 (aka 0666 in Octal) +callback **Function** + +Atomically and asynchronously writes data to a file, replacing the file if it already +exists. data can be a string or a buffer. + +The file is initially named `filename + "." + md5hex(__filename, process.pid, ++invocations)`. +If writeFile completes successfully then, if passed the **chown** option it will change +the ownership of the file. Finally it renames the file back to the filename you specified. If +it encounters errors at any of these steps it will attempt to unlink the temporary file and then +pass the error back to the caller. + +If provided, the **chown** option requires both **uid** and **gid** properties or else +you'll get an error. + +The **encoding** option is ignored if **data** is a buffer. It defaults to 'utf8'. + +Example: + +```javascript +writeFileAtomic('message.txt', 'Hello Node', {chown:{uid:100,gid:50}}, function (err) { + if (err) throw err; + console.log('It\'s saved!'); +}); +``` + +### var writeFileAtomicSync = require('write-file-atomic').sync<br>writeFileAtomicSync(filename, data, [options]) + +The synchronous version of **writeFileAtomic**. diff --git a/node_modules/write-file-atomic/index.js b/node_modules/write-file-atomic/index.js new file mode 100644 index 000000000..5f103a382 --- /dev/null +++ b/node_modules/write-file-atomic/index.js @@ -0,0 +1,45 @@ +'use strict' +var fs = require('fs'); +var chain = require('slide').chain; +var crypto = require('crypto'); + +var md5hex = function () { + var hash = crypto.createHash('md5'); + for (var ii=0; ii<arguments.length; ++ii) hash.update(''+arguments[ii]) + return hash.digest('hex') +} +var invocations = 0; +var getTmpname = function (filename) { + return filename + "." + md5hex(__filename, process.pid, ++invocations) +} + +module.exports = function writeFile(filename, data, options, callback) { + if (options instanceof Function) { + callback = options; + options = null; + } + if (!options) options = {}; + var tmpfile = getTmpname(filename); + chain([ + [fs, fs.writeFile, tmpfile, data, options], + options.chown && [fs, fs.chown, tmpfile, options.chown.uid, options.chown.gid], + [fs, fs.rename, tmpfile, filename] + ], function (err) { + err ? fs.unlink(tmpfile, function () { callback(err) }) + : callback() + }) +} + +module.exports.sync = function writeFileSync(filename, data, options) { + if (!options) options = {}; + var tmpfile = getTmpname(filename); + try { + fs.writeFileSync(tmpfile, data, options); + if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid); + fs.renameSync(tmpfile, filename); + } + catch (err) { + try { fs.unlinkSync(tmpfile) } catch(e) {} + throw err; + } +}
\ No newline at end of file diff --git a/node_modules/write-file-atomic/package.json b/node_modules/write-file-atomic/package.json new file mode 100644 index 000000000..a41a63d58 --- /dev/null +++ b/node_modules/write-file-atomic/package.json @@ -0,0 +1,55 @@ +{ + "name": "write-file-atomic", + "version": "1.0.3", + "description": "Write files in an atomic fashion w/configurable ownership", + "main": "index.js", + "scripts": { + "test": "tap test/*.js" + }, + "repository": { + "type": "git", + "url": "git@github.com:iarna/write-file-atomic.git" + }, + "keywords": [ + "writeFile", + "atomic" + ], + "author": { + "name": "Rebecca Turner", + "email": "me@re-becca.org", + "url": "http://re-becca.org" + }, + "license": "ISC", + "bugs": { + "url": "https://github.com/iarna/write-file-atomic/issues" + }, + "homepage": "https://github.com/iarna/write-file-atomic", + "dependencies": { + "slide": "^1.1.5" + }, + "devDependencies": { + "require-inject": "^1.1.0", + "tap": "^0.4.12" + }, + "gitHead": "5e85cfc7b38e4f0659b15b1c1d979e6a4a7bf6d5", + "_id": "write-file-atomic@1.0.3", + "_shasum": "597fcb824a0d1ad337db90b7028d2772e9e8e7af", + "_from": "write-file-atomic@>=1.0.3 <1.1.0", + "_npmVersion": "2.0.0", + "_npmUser": { + "name": "iarna", + "email": "me@re-becca.org" + }, + "maintainers": [ + { + "name": "iarna", + "email": "me@re-becca.org" + } + ], + "dist": { + "shasum": "597fcb824a0d1ad337db90b7028d2772e9e8e7af", + "tarball": "http://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.0.3.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.0.3.tgz" +} diff --git a/node_modules/write-file-atomic/test/basic.js b/node_modules/write-file-atomic/test/basic.js new file mode 100644 index 000000000..3e0316dc4 --- /dev/null +++ b/node_modules/write-file-atomic/test/basic.js @@ -0,0 +1,97 @@ +"use strict"; +var test = require('tap').test; +var requireInject = require('require-inject'); +var writeFileAtomic = requireInject('../index', { + fs: { + writeFile: function (tmpfile, data, options, cb) { + if (/nowrite/.test(tmpfile)) return cb('ENOWRITE'); + cb(); + }, + chown: function (tmpfile, uid, gid, cb) { + if (/nochown/.test(tmpfile)) return cb('ENOCHOWN'); + cb(); + }, + rename: function (tmpfile, filename, cb) { + if (/norename/.test(tmpfile)) return cb('ENORENAME'); + cb(); + }, + unlink: function (tmpfile, cb) { + if (/nounlink/.test(tmpfile)) return cb('ENOUNLINK'); + cb(); + }, + writeFileSync: function (tmpfile, data, options) { + if (/nowrite/.test(tmpfile)) throw 'ENOWRITE'; + }, + chownSync: function (tmpfile, uid, gid) { + if (/nochown/.test(tmpfile)) throw 'ENOCHOWN'; + }, + renameSync: function (tmpfile, filename) { + if (/norename/.test(tmpfile)) throw 'ENORENAME'; + }, + unlinkSync: function (tmpfile) { + if (/nounlink/.test(tmpfile)) throw 'ENOUNLINK'; + }, + } +}); +var writeFileAtomicSync = writeFileAtomic.sync; + +test('async tests', function (t) { + t.plan(7); + writeFileAtomic('good', 'test', {mode: '0777'}, function (err) { + t.notOk(err, 'No errors occur when passing in options'); + }); + writeFileAtomic('good', 'test', function (err) { + t.notOk(err, 'No errors occur when NOT passing in options'); + }); + writeFileAtomic('nowrite', 'test', function (err) { + t.is(err, 'ENOWRITE', 'writeFile failures propagate'); + }); + writeFileAtomic('nochown', 'test', {chown: {uid:100,gid:100}}, function (err) { + t.is(err, 'ENOCHOWN', 'Chown failures propagate'); + }); + writeFileAtomic('nochown', 'test', function (err) { + t.notOk(err, 'No attempt to chown when no uid/gid passed in'); + }); + writeFileAtomic('norename', 'test', function (err) { + t.is(err, 'ENORENAME', 'Rename errors propagate'); + }); + writeFileAtomic('norename nounlink', 'test', function (err) { + t.is(err, 'ENORENAME', 'Failure to unlink the temp file does not clobber the original error'); + }); +}); + +test('sync tests', function (t) { + t.plan(7); + var throws = function (shouldthrow, msg, todo) { + var err; + try { todo() } catch (e) { err = e } + t.is(shouldthrow,err,msg); + } + var noexception = function (msg, todo) { + var err; + try { todo() } catch (e) { err = e } + t.notOk(err,msg); + } + + noexception('No errors occur when passing in options',function (){ + writeFileAtomicSync('good', 'test', {mode: '0777'}); + }) + noexception('No errors occur when NOT passing in options',function (){ + writeFileAtomicSync('good', 'test'); + }); + throws('ENOWRITE', 'writeFile failures propagate', function () { + writeFileAtomicSync('nowrite', 'test'); + }); + throws('ENOCHOWN', 'Chown failures propagate', function () { + writeFileAtomicSync('nochown', 'test', {chown: {uid:100,gid:100}}); + }); + noexception('No attempt to chown when no uid/gid passed in', function (){ + writeFileAtomicSync('nochown', 'test'); + }); + throws('ENORENAME', 'Rename errors propagate', function (){ + writeFileAtomicSync('norename', 'test'); + }); + throws('ENORENAME', 'Failure to unlink the temp file does not clobber the original error', function (){ + writeFileAtomicSync('norename nounlink', 'test'); + }); +}); |