Update util.writeFile to write to tmp file before rename

This commit is contained in:
Nick O'Leary 2020-08-13 17:17:40 +01:00
parent bcd85b11a1
commit b0b2c32654
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
1 changed files with 36 additions and 18 deletions

View File

@ -17,7 +17,6 @@
var fs = require('fs-extra'); var fs = require('fs-extra');
var fspath = require('path'); var fspath = require('path');
var when = require('when'); var when = require('when');
var nodeFn = require('when/node/function');
var log = require("@node-red/util").log; // TODO: separate module var log = require("@node-red/util").log; // TODO: separate module
@ -75,39 +74,58 @@ function readFile(path,backupPath,emptyResponse,type) {
module.exports = { module.exports = {
/** /**
* Write content to a file using UTF8 encoding. * Write content to a file using UTF8 encoding.
* This forces a fsync before completing to ensure * This forces a fsync before completing to ensure
* the write hits disk. * the write hits disk.
*/ */
writeFile: function(path,content,backupPath) { writeFile: function(path,content,backupPath) {
var backupPromise;
if (backupPath) { if (backupPath) {
if (fs.existsSync(path)) { backupPromise = fs.copy(path,backupPath);
fs.renameSync(path,backupPath); } else {
} backupPromise = Promise.resolve();
} }
return when.promise(function(resolve,reject) {
fs.ensureDir(fspath.dirname(path), (err)=>{ const dirname = fspath.dirname(path);
if (err) { const tempFile = `${path}.$$$`;
reject(err);
return; return backupPromise.then(() => {
} if (backupPath) {
var stream = fs.createWriteStream(path); log.trace(`utils.writeFile - copied ${path} TO ${backupPath}`)
}
return fs.ensureDir(dirname)
}).then(() => {
return new Promise(function(resolve,reject) {
var stream = fs.createWriteStream(tempFile);
stream.on('open',function(fd) { stream.on('open',function(fd) {
stream.write(content,'utf8',function() { stream.write(content,'utf8',function() {
fs.fsync(fd,function(err) { fs.fsync(fd,function(err) {
if (err) { if (err) {
log.warn(log._("storage.localfilesystem.fsync-fail",{path: path, message: err.toString()})); log.warn(log._("storage.localfilesystem.fsync-fail",{path: tempFile, message: err.toString()}));
} }
stream.end(resolve); stream.end(resolve);
}); });
}); });
}); });
stream.on('error',function(err) { stream.on('error',function(err) {
log.warn(log._("storage.localfilesystem.fsync-fail",{path: tempFile, message: err.toString()}));
reject(err); reject(err);
}); });
}); });
}).then(() => {
log.trace(`utils.writeFile - written content to ${tempFile}`)
return new Promise(function(resolve,reject) {
fs.rename(tempFile,path,err => {
if (err) {
log.warn(log._("storage.localfilesystem.fsync-fail",{path: path, message: err.toString()}));
return reject(err);
}
log.trace(`utils.writeFile - renamed ${tempFile} to ${path}`)
resolve();
})
});
}); });
}, },
readFile: readFile, readFile: readFile,
parseJSON: parseJSON parseJSON: parseJSON