From 10077ae7500431816176840be1549cb5a9c8a2a5 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 21 Aug 2019 16:54:26 +0100 Subject: [PATCH] Use a more atomic process for writing context files Fixes #2271 --- .../runtime/lib/nodes/context/localfilesystem.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js b/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js index 10b4349b8..0f83573e8 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js @@ -137,6 +137,16 @@ function stringify(value) { return { json: result, circular: hasCircular }; } + +function writeFileAtomic(storagePath, content) { + // To protect against file corruption, write to a tmp file first and then + // rename to the destination file + let finalFile = storagePath + ".json"; + let tmpFile = finalFile + "."+Date.now()+".tmp"; + return fs.outputFile(tmpFile, content, "utf8").then(function() { + return fs.rename(tmpFile,finalFile); + }) +} function LocalFileSystem(config){ this.config = config; this.storageBaseDir = getBasePath(this.config); @@ -202,7 +212,7 @@ LocalFileSystem.prototype.open = function(){ delete self.knownCircularRefs[scope]; } log.debug("Flushing localfilesystem context scope "+scope); - promises.push(fs.outputFile(storagePath + ".json", stringifiedContext.json, "utf8")); + promises.push(writeFileAtomic(storagePath, stringifiedContext.json)) }); delete self._pendingWriteTimeout; return Promise.all(promises); @@ -319,7 +329,7 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) { } else { delete self.knownCircularRefs[scope]; } - return fs.outputFile(storagePath + ".json", stringifiedContext.json, "utf8"); + return writeFileAtomic(storagePath, stringifiedContext.json); }).then(function(){ if(typeof callback === "function"){ callback(null);