From 8396c822c01d9ca0139d54c38e550884094fda3f Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:03:11 -0800 Subject: [PATCH] Update config json files atomically --- ACKNOWLEDGMENTS.md | 4 ++++ app/base_config.ts | 3 ++- package.json | 2 ++ ts/test-node/app/base_config_test.ts | 34 +++++++++++++--------------- yarn.lock | 15 ++++++++++++ 5 files changed, 39 insertions(+), 19 deletions(-) diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index 0c00a68262..47decc88db 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -3640,6 +3640,10 @@ Signal Desktop makes use of the following open source projects. END OF TERMS AND CONDITIONS +## write-file-atomic + + License: ISC + ## zod MIT License diff --git a/app/base_config.ts b/app/base_config.ts index 350339243b..821518884a 100644 --- a/app/base_config.ts +++ b/app/base_config.ts @@ -1,7 +1,8 @@ // Copyright 2018 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { readFileSync, writeFileSync, unlinkSync } from 'fs'; +import { readFileSync, unlinkSync } from 'fs'; +import { sync as writeFileSync } from 'write-file-atomic'; import { get } from 'lodash'; import { set } from 'lodash/fp'; diff --git a/package.json b/package.json index edbf8fe597..b757d5e695 100644 --- a/package.json +++ b/package.json @@ -190,6 +190,7 @@ "uuid": "3.3.2", "uuid-browser": "3.1.0", "websocket": "1.0.34", + "write-file-atomic": "5.0.1", "zod": "3.22.3" }, "devDependencies": { @@ -264,6 +265,7 @@ "@types/terser-webpack-plugin": "5.0.3", "@types/uuid": "3.4.4", "@types/websocket": "1.0.0", + "@types/write-file-atomic": "4.0.3", "@types/yargs": "17.0.7", "@typescript-eslint/eslint-plugin": "6.18.1", "@typescript-eslint/parser": "6.18.1", diff --git a/ts/test-node/app/base_config_test.ts b/ts/test-node/app/base_config_test.ts index 43a755f973..e4b002fd35 100644 --- a/ts/test-node/app/base_config_test.ts +++ b/ts/test-node/app/base_config_test.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { tmpdir } from 'os'; -import { chmodSync, mkdirSync, unlinkSync, writeFileSync } from 'fs'; +import { chmodSync, rmdirSync, writeFileSync, mkdtempSync } from 'fs'; import { pathExists, readJsonSync } from 'fs-extra'; import { v4 as generateGuid } from 'uuid'; @@ -13,15 +13,19 @@ import type { ConfigType } from '../../../app/base_config'; import { start } from '../../../app/base_config'; describe('base_config', () => { + let targetDir: string; let targetPath: string; beforeEach(() => { - targetPath = path.join(tmpdir(), `${generateGuid()}.json`); + targetDir = mkdtempSync(path.join(tmpdir(), 'base_config')); + targetPath = path.join(targetDir, `${generateGuid()}.json`); }); afterEach(() => { try { - unlinkSync(targetPath); + chmodSync(targetDir, 0o755); + chmodSync(targetPath, 0o755); + rmdirSync(targetDir, { recursive: true }); } catch (err) { assert.strictEqual(err.code, 'ENOENT'); } @@ -89,7 +93,7 @@ describe('base_config', () => { } writeFileSync(targetPath, JSON.stringify({ foo: 123 })); - chmodSync(targetPath, 0); + chmodSync(targetDir, 0); const { _getCachedValue } = start({ name: 'test', targetPath, @@ -163,7 +167,7 @@ describe('base_config', () => { throwOnFilesystemErrors: true, }); config.set('foo', 123); - chmodSync(targetPath, 0); + rmdirSync(targetDir, { recursive: true }); assert.throws(() => config.set('foo', 456)); assert.strictEqual(config.get('foo'), 123); @@ -181,7 +185,7 @@ describe('base_config', () => { throwOnFilesystemErrors: false, }); config.set('foo', 123); - chmodSync(targetPath, 0); + rmdirSync(targetDir, { recursive: true }); config.set('bar', 456); @@ -234,16 +238,13 @@ describe('base_config', () => { // We put the config file in a directory, then remove all permissions from that // directory. This should prevent removal. - const directory = path.join(tmpdir(), generateGuid()); - const configFile = path.join(directory, 'test_config.json'); - mkdirSync(directory, { recursive: true }); - writeFileSync(configFile, JSON.stringify({ foo: 123 })); + writeFileSync(targetPath, JSON.stringify({ foo: 123 })); const config = start({ name: 'test', - targetPath: configFile, + targetPath, throwOnFilesystemErrors: true, }); - chmodSync(directory, 0); + chmodSync(targetDir, 0); assert.throws(() => config.remove()); @@ -258,16 +259,13 @@ describe('base_config', () => { } // See above. - const directory = path.join(tmpdir(), generateGuid()); - const configFile = path.join(directory, 'test_config.json'); - mkdirSync(directory, { recursive: true }); - writeFileSync(configFile, JSON.stringify({ foo: 123 })); + writeFileSync(targetPath, JSON.stringify({ foo: 123 })); const config = start({ name: 'test', - targetPath: configFile, + targetPath, throwOnFilesystemErrors: false, }); - chmodSync(directory, 0); + chmodSync(targetDir, 0); config.remove(); diff --git a/yarn.lock b/yarn.lock index d40adb7cf4..d06dbc1a2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5792,6 +5792,13 @@ dependencies: "@types/node" "*" +"@types/write-file-atomic@4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/write-file-atomic/-/write-file-atomic-4.0.3.tgz#bda169b8369022e2c87028671fa4b742c08d98c9" + integrity sha512-qdo+vZRchyJIHNeuI1nrpsLw+hnkgqP/8mlaN6Wle/NKhydHmUN9l4p3ZE8yP90AJNJW4uB8HQhedb4f1vNayQ== + dependencies: + "@types/node" "*" + "@types/ws@^8.5.1": version "8.5.4" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" @@ -20166,6 +20173,14 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^4.0.1" + write-file-atomic@^1.1.4: version "1.3.4" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f"