diff options
author | Kat Marchán <kzm@sykosomatic.org> | 2017-03-10 00:37:13 +0300 |
---|---|---|
committer | Kat Marchán <kzm@sykosomatic.org> | 2017-03-10 03:08:26 +0300 |
commit | 0e105d9fb46bb00e2bcc32b13a23f018af5f7af8 (patch) | |
tree | 3ef4c24b18dde799c6d4fc84836c38f39f743c8e /node_modules/fs-write-stream-atomic | |
parent | 91dd09de11db15050215ab3094ef75ebf4da9848 (diff) |
fs-write-stream-atomic@1.0.10
Diffstat (limited to 'node_modules/fs-write-stream-atomic')
-rw-r--r-- | node_modules/fs-write-stream-atomic/index.js | 64 | ||||
-rw-r--r-- | node_modules/fs-write-stream-atomic/package.json | 62 | ||||
-rw-r--r-- | node_modules/fs-write-stream-atomic/test/rename-eperm.js | 154 |
3 files changed, 251 insertions, 29 deletions
diff --git a/node_modules/fs-write-stream-atomic/index.js b/node_modules/fs-write-stream-atomic/index.js index 59b50db6d..1690ff5ae 100644 --- a/node_modules/fs-write-stream-atomic/index.js +++ b/node_modules/fs-write-stream-atomic/index.js @@ -3,11 +3,12 @@ var Writable = require('readable-stream').Writable var util = require('util') var MurmurHash3 = require('imurmurhash') var iferr = require('iferr') +var crypto = require('crypto') function murmurhex () { var hash = MurmurHash3('') for (var ii = 0; ii < arguments.length; ++ii) { - hash.hash(hash + arguments[ii]) + hash.hash('' + arguments[ii]) } return hash.result() } @@ -36,6 +37,8 @@ function WriteStreamAtomic (path, options) { } Writable.call(this, options) + this.__isWin = options && options.hasOwnProperty('isWin') ? options.isWin : process.platform === 'win32' + this.__atomicTarget = path this.__atomicTmp = getTmpname(path) @@ -84,15 +87,64 @@ function handleClose (writeStream) { moveIntoPlace() } } + + function moveIntoPlace () { + fs.rename(writeStream.__atomicTmp, writeStream.__atomicTarget, iferr(trapWindowsEPERM, end)) + } + + function trapWindowsEPERM (err) { + if (writeStream.__isWin && + err.syscall && err.syscall === 'rename' && + err.code && err.code === 'EPERM' + ) { + checkFileHashes(err) + } else { + cleanup(err) + } + } + + function checkFileHashes (eperm) { + var inprocess = 2 + var tmpFileHash = crypto.createHash('sha512') + var targetFileHash = crypto.createHash('sha512') + + fs.createReadStream(writeStream.__atomicTmp) + .on('data', function (data, enc) { tmpFileHash.update(data, enc) }) + .on('error', fileHashError) + .on('end', fileHashComplete) + fs.createReadStream(writeStream.__atomicTarget) + .on('data', function (data, enc) { targetFileHash.update(data, enc) }) + .on('error', fileHashError) + .on('end', fileHashComplete) + + function fileHashError () { + if (inprocess === 0) return + inprocess = 0 + cleanup(eperm) + } + + function fileHashComplete () { + if (inprocess === 0) return + if (--inprocess) return + if (tmpFileHash.digest('hex') === targetFileHash.digest('hex')) { + return cleanup() + } else { + return cleanup(eperm) + } + } + } + function cleanup (err) { fs.unlink(writeStream.__atomicTmp, function () { - writeStream.emit('error', err) - writeStream.emit('close') + if (err) { + writeStream.emit('error', err) + writeStream.emit('close') + } else { + end() + } }) } - function moveIntoPlace () { - fs.rename(writeStream.__atomicTmp, writeStream.__atomicTarget, iferr(cleanup, end)) - } + function end () { // We have to use our parent class directly because we suppress `finish` // events fired via our own emit method. diff --git a/node_modules/fs-write-stream-atomic/package.json b/node_modules/fs-write-stream-atomic/package.json index e65fa32c0..f1526baa1 100644 --- a/node_modules/fs-write-stream-atomic/package.json +++ b/node_modules/fs-write-stream-atomic/package.json @@ -1,40 +1,56 @@ { "_args": [ [ - "fs-write-stream-atomic@1.0.8", - "/Users/rebecca/code/npm" + { + "raw": "fs-write-stream-atomic@latest", + "scope": null, + "escapedName": "fs-write-stream-atomic", + "name": "fs-write-stream-atomic", + "rawSpec": "latest", + "spec": "latest", + "type": "tag" + }, + "/Users/zkat/Documents/code/npm" ] ], - "_from": "fs-write-stream-atomic@1.0.8", - "_id": "fs-write-stream-atomic@1.0.8", + "_from": "fs-write-stream-atomic@latest", + "_id": "fs-write-stream-atomic@1.0.10", "_inCache": true, - "_installable": true, "_location": "/fs-write-stream-atomic", - "_nodeVersion": "4.2.2", + "_nodeVersion": "7.7.1", + "_npmOperationalInternal": { + "host": "packages-12-west.internal.npmjs.com", + "tmp": "tmp/fs-write-stream-atomic-1.0.10.tgz_1488925398888_0.6416559314820915" + }, "_npmUser": { - "email": "me@re-becca.org", - "name": "iarna" + "name": "iarna", + "email": "me@re-becca.org" }, - "_npmVersion": "3.5.2", + "_npmVersion": "4.4.1", "_phantomChildren": {}, "_requested": { - "name": "fs-write-stream-atomic", - "raw": "fs-write-stream-atomic@1.0.8", - "rawSpec": "1.0.8", + "raw": "fs-write-stream-atomic@latest", "scope": null, - "spec": "1.0.8", - "type": "version" + "escapedName": "fs-write-stream-atomic", + "name": "fs-write-stream-atomic", + "rawSpec": "latest", + "spec": "latest", + "type": "tag" }, "_requiredBy": [ - "/" + "#USER", + "/", + "/@npmcorp/move", + "/@npmcorp/move/@npmcorp/copy" ], - "_shasum": "e49aaddf288f87d46ff9e882f216a13abc40778b", + "_resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "_shasum": "b47df53493ef911df75731e70a9ded0189db40c9", "_shrinkwrap": null, - "_spec": "fs-write-stream-atomic@1.0.8", - "_where": "/Users/rebecca/code/npm", + "_spec": "fs-write-stream-atomic@latest", + "_where": "/Users/zkat/Documents/code/npm", "author": { - "email": "i@izs.me", "name": "Isaac Z. Schlueter", + "email": "i@izs.me", "url": "http://blog.izs.me/" }, "bugs": { @@ -56,10 +72,10 @@ "test": "test" }, "dist": { - "shasum": "e49aaddf288f87d46ff9e882f216a13abc40778b", - "tarball": "http://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.8.tgz" + "shasum": "b47df53493ef911df75731e70a9ded0189db40c9", + "tarball": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz" }, - "gitHead": "b55824ee4de7f1ca23784929d68b1b8f5edbf4a4", + "gitHead": "de157c0373a40fb5539640923cab9671cef08b12", "homepage": "https://github.com/npm/fs-write-stream-atomic", "license": "ISC", "main": "index.js", @@ -91,5 +107,5 @@ "scripts": { "test": "standard && tap --coverage test/*.js" }, - "version": "1.0.8" + "version": "1.0.10" } diff --git a/node_modules/fs-write-stream-atomic/test/rename-eperm.js b/node_modules/fs-write-stream-atomic/test/rename-eperm.js new file mode 100644 index 000000000..b1be0f318 --- /dev/null +++ b/node_modules/fs-write-stream-atomic/test/rename-eperm.js @@ -0,0 +1,154 @@ +'use strict' +var fs = require('graceful-fs') +var path = require('path') +var test = require('tap').test +var rimraf = require('rimraf') +var writeStream = require('../index.js') + +var target = path.resolve(__dirname, 'test-rename-eperm1') +var target2 = path.resolve(__dirname, 'test-rename-eperm2') +var target3 = path.resolve(__dirname, 'test-rename-eperm3') + +test('rename eperm none existing file', function (t) { + t.plan(2) + + var _rename = fs.rename + fs.existsSync = function (src) { + return true + } + fs.rename = function (src, dest, cb) { + // simulate a failure during rename where the file + // is renamed successfully but the process encounters + // an EPERM error and the target file does not exist + _rename(src, dest, function (e) { + var err = new Error('TEST BREAK') + err.syscall = 'rename' + err.code = 'EPERM' + cb(err) + }) + } + + var stream = writeStream(target, { isWin: true }) + var hadError = false + var calledFinish = false + stream.on('error', function (er) { + hadError = true + console.log('#', er) + }) + stream.on('finish', function () { + calledFinish = true + }) + stream.on('close', function () { + t.is(hadError, true, 'error was caught') + t.is(calledFinish, false, 'finish was called before close') + }) + stream.end() +}) + +// test existing file with diff. content +test('rename eperm existing file different content', function (t) { + t.plan(2) + + var _rename = fs.rename + fs.existsSync = function (src) { + return true + } + fs.rename = function (src, dest, cb) { + // simulate a failure during rename where the file + // is renamed successfully but the process encounters + // an EPERM error and the target file that has another content than the + // destination + _rename(src, dest, function (e) { + fs.writeFile(src, 'dest', function (writeErr) { + if (writeErr) { + return console.log('WRITEERR: ' + writeErr) + } + + fs.writeFile(target2, 'target', function (writeErr) { + if (writeErr) { + return console.log('WRITEERR: ' + writeErr) + } + + var err = new Error('TEST BREAK') + err.syscall = 'rename' + err.code = 'EPERM' + cb(err) + }) + }) + }) + } + + var stream = writeStream(target2, { isWin: true }) + var hadError = false + var calledFinish = false + stream.on('error', function (er) { + hadError = true + console.log('#', er) + }) + stream.on('finish', function () { + calledFinish = true + }) + stream.on('close', function () { + t.is(hadError, true, 'error was caught') + t.is(calledFinish, false, 'finish was called before close') + }) + stream.end() +}) + +// test existing file with the same content +// test existing file with diff. content +test('rename eperm existing file different content', function (t) { + t.plan(2) + + var _rename = fs.rename + fs.existsSync = function (src) { + return true + } + fs.rename = function (src, dest, cb) { + // simulate a failure during rename where the file + // is renamed successfully but the process encounters + // an EPERM error and the target file that has the same content than the + // destination + _rename(src, dest, function (e) { + fs.writeFile(src, 'target2', function (writeErr) { + if (writeErr) { + return console.log('WRITEERR: ' + writeErr) + } + + fs.writeFile(target3, 'target2', function (writeErr) { + if (writeErr) { + return console.log('WRITEERR: ' + writeErr) + } + + var err = new Error('TEST BREAK') + err.syscall = 'rename' + err.code = 'EPERM' + cb(err) + }) + }) + }) + } + + var stream = writeStream(target3, { isWin: true }) + var hadError = false + var calledFinish = false + stream.on('error', function (er) { + hadError = true + console.log('#', er) + }) + stream.on('finish', function () { + calledFinish = true + }) + stream.on('close', function () { + t.is(hadError, false, 'error was caught') + t.is(calledFinish, true, 'finish was called before close') + }) + stream.end() +}) + +test('cleanup', function (t) { + rimraf.sync(target) + rimraf.sync(target2) + rimraf.sync(target3) + t.end() +}) |