From 0ab1ac4168341ff9077d6ca955fe1de0d4b6e1ab Mon Sep 17 00:00:00 2001 From: codeking Date: Wed, 11 Feb 2026 09:48:08 +0100 Subject: [PATCH] Update file management-related logic --- core/Helpers.py | 19 +++++++++++++++++++ core/controllers/ApplicationController.py | 2 -- .../ApplicationVersionController.py | 4 ---- core/controllers/ConnectionController.py | 4 ---- core/models/BaseProfile.py | 10 +++------- core/models/Configuration.py | 10 +++------- core/models/session/SessionProfile.py | 7 +------ core/models/session/SessionState.py | 5 +---- core/models/system/SystemProfile.py | 2 -- core/models/system/SystemState.py | 5 +---- 10 files changed, 28 insertions(+), 40 deletions(-) create mode 100644 core/Helpers.py diff --git a/core/Helpers.py b/core/Helpers.py new file mode 100644 index 0000000..a094006 --- /dev/null +++ b/core/Helpers.py @@ -0,0 +1,19 @@ +import os + +def write_atomically(file_path, contents): + + staging_file_path = f'{file_path}~' + + try: + + with open(staging_file_path, 'w') as config_staging_file: + + config_staging_file.write(contents) + config_staging_file.flush() + os.fsync(config_staging_file.fileno()) + os.replace(staging_file_path, file_path) + + finally: + + if os.path.exists(staging_file_path): + os.remove(staging_file_path) diff --git a/core/controllers/ApplicationController.py b/core/controllers/ApplicationController.py index 2cd691f..ce9d165 100644 --- a/core/controllers/ApplicationController.py +++ b/core/controllers/ApplicationController.py @@ -87,9 +87,7 @@ class ApplicationController: Path(initialization_file_path).touch(exist_ok=True, mode=0o600 | stat.S_IEXEC) with open(initialization_file_path, 'w') as initialization_file: - initialization_file.write(initialization_file_contents) - initialization_file.close() if asynchronous: diff --git a/core/controllers/ApplicationVersionController.py b/core/controllers/ApplicationVersionController.py index 046a6fb..0193342 100644 --- a/core/controllers/ApplicationVersionController.py +++ b/core/controllers/ApplicationVersionController.py @@ -99,14 +99,10 @@ class ApplicationVersionController: raise FileIntegrityError('Application version file integrity could not be verified.') with tarfile.open(fileobj=response_buffer, mode = 'r:gz') as tar_file: - tar_file.extractall(application_version.get_installation_path()) - tar_file.close() with open(f'{application_version.get_installation_path()}/.sha3-512', 'w') as hash_file: - hash_file.write(f'{file_hash}\n') - hash_file.close() else: raise ConnectionError('The application version could not be downloaded.') diff --git a/core/controllers/ConnectionController.py b/core/controllers/ConnectionController.py index c66a4e1..6e017b4 100644 --- a/core/controllers/ConnectionController.py +++ b/core/controllers/ConnectionController.py @@ -340,9 +340,7 @@ class ConnectionController: Path(wireproxy_configuration_file_path).touch(exist_ok=True, mode=0o600) with open(wireproxy_configuration_file_path, 'w') as wireproxy_configuration_file: - wireproxy_configuration_file.write(f'WGConfig = {profile.get_wireguard_configuration_path()}\n\n[Socks5]\nBindAddress = 127.0.0.1:{str(port_number)}\n') - wireproxy_configuration_file.close() return subprocess.Popen((f'{Constants.HV_RUNTIME_DATA_HOME}/wireproxy/wireproxy', '-c', wireproxy_configuration_file_path), stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) @@ -379,9 +377,7 @@ class ConnectionController: proxychains_configuration_file_contents = proxychains_template_file.read().format(proxy_list=proxychains_proxy_list) with open(proxychains_configuration_file_path, 'w') as proxychains_configuration_file: - proxychains_configuration_file.write(proxychains_configuration_file_contents) - proxychains_configuration_file.close() return subprocess.Popen(('proxychains4', '-f', proxychains_configuration_file_path, 'microsocks', '-p', str(proxy_port_number)), stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) diff --git a/core/models/BaseProfile.py b/core/models/BaseProfile.py index 228ec6a..1add011 100644 --- a/core/models/BaseProfile.py +++ b/core/models/BaseProfile.py @@ -1,11 +1,11 @@ from abc import ABC, abstractmethod from core.Constants import Constants +from core.Helpers import write_atomically from core.models.Location import Location from core.models.Subscription import Subscription from core.models.session.ApplicationVersion import ApplicationVersion from dataclasses import dataclass, field, asdict from dataclasses_json import config, Exclude, dataclass_json -from json import JSONDecodeError from pathlib import Path from typing import Optional, Self import json @@ -59,11 +59,7 @@ class BaseProfile(ABC): os.makedirs(self.get_data_path(), exist_ok=True) config_file_path = f'{self.get_config_path()}/config.json' - - with open(config_file_path, 'w') as config_file: - - config_file.write(config_file_contents) - config_file.close() + write_atomically(config_file_path, config_file_contents) def delete_data(self): shutil.rmtree(self.get_data_path(), ignore_errors=True) @@ -160,7 +156,7 @@ class BaseProfile(ABC): try: profile = json.loads(config_file_contents) - except JSONDecodeError: + except ValueError: return None profile['id'] = id diff --git a/core/models/Configuration.py b/core/models/Configuration.py index 681b776..6852a5a 100644 --- a/core/models/Configuration.py +++ b/core/models/Configuration.py @@ -1,8 +1,8 @@ from core.Constants import Constants +from core.Helpers import write_atomically from dataclasses import dataclass, field from dataclasses_json import dataclass_json, config from datetime import datetime -from json import JSONDecodeError from marshmallow import fields from typing import Optional, Self from zoneinfo import ZoneInfo @@ -53,11 +53,7 @@ class Configuration: os.makedirs(Constants.HV_CONFIG_HOME, exist_ok=True) config_file_path = f'{Constants.HV_CONFIG_HOME}/config.json' - - with open(config_file_path, 'w') as config_file: - - config_file.write(config_file_contents) - config_file.close() + write_atomically(config_file_path, config_file_contents) @staticmethod def get(): @@ -69,7 +65,7 @@ class Configuration: try: configuration = json.loads(config_file_contents) - except JSONDecodeError: + except ValueError: sys.exit(1) # noinspection PyUnresolvedReferences diff --git a/core/models/session/SessionProfile.py b/core/models/session/SessionProfile.py index 03fd0bc..b2e7f9c 100644 --- a/core/models/session/SessionProfile.py +++ b/core/models/session/SessionProfile.py @@ -5,7 +5,6 @@ from core.models.session.ApplicationVersion import ApplicationVersion from core.models.session.ProxyConfiguration import ProxyConfiguration from core.models.session.SessionConnection import SessionConnection from dataclasses import dataclass -from json import JSONDecodeError from pathlib import Path from typing import Optional import json @@ -46,18 +45,14 @@ class SessionProfile(BaseProfile): proxy_configuration_file_path = self.get_proxy_configuration_path() with open(proxy_configuration_file_path, 'w') as proxy_configuration_file: - proxy_configuration_file.write(proxy_configuration_file_contents) - proxy_configuration_file.close() def attach_wireguard_configuration(self, wireguard_configuration): wireguard_configuration_file_path = self.get_wireguard_configuration_path() with open(wireguard_configuration_file_path, 'w') as wireguard_configuration_file: - wireguard_configuration_file.write(wireguard_configuration) - wireguard_configuration_file.close() def get_proxy_configuration_path(self): return f'{self.get_config_path()}/proxy.json' @@ -74,7 +69,7 @@ class SessionProfile(BaseProfile): try: proxy_configuration = json.loads(config_file_contents) - except JSONDecodeError: + except ValueError: return None proxy_configuration = ProxyConfiguration.from_dict(proxy_configuration) diff --git a/core/models/session/SessionState.py b/core/models/session/SessionState.py index eda4552..f24af9b 100644 --- a/core/models/session/SessionState.py +++ b/core/models/session/SessionState.py @@ -2,7 +2,6 @@ from core.Constants import Constants from core.models.session.NetworkPortNumbers import NetworkPortNumbers from dataclasses import dataclass, field from dataclasses_json import config, Exclude, dataclass_json -from json import JSONDecodeError from pathlib import Path from typing import Self import json @@ -33,9 +32,7 @@ class SessionState: Path(session_state_file_path).touch(exist_ok=True, mode=0o600) with open(session_state_file_path, 'w') as session_state_file: - session_state_file.write(session_state_file_contents) - session_state_file.close() @staticmethod def find_by_id(id: int): @@ -55,7 +52,7 @@ class SessionState: # noinspection PyUnresolvedReferences return SessionState.from_dict(session_state) - except (JSONDecodeError, AttributeError): + except (ValueError, AttributeError): shutil.rmtree(Path(state_path), ignore_errors=True) return None diff --git a/core/models/system/SystemProfile.py b/core/models/system/SystemProfile.py index 28fe0e9..bd00de4 100644 --- a/core/models/system/SystemProfile.py +++ b/core/models/system/SystemProfile.py @@ -31,9 +31,7 @@ class SystemProfile(BaseProfile): wireguard_configuration_file_backup_path = f'{self.get_config_path()}/wg.conf.bak' with open(wireguard_configuration_file_backup_path, 'w') as wireguard_configuration_file: - wireguard_configuration_file.write(wireguard_configuration) - wireguard_configuration_file.close() wireguard_configuration_is_attached = False failed_attempt_count = 0 diff --git a/core/models/system/SystemState.py b/core/models/system/SystemState.py index 99904b9..2187faa 100644 --- a/core/models/system/SystemState.py +++ b/core/models/system/SystemState.py @@ -1,7 +1,6 @@ from core.Constants import Constants from dataclasses import dataclass from dataclasses_json import dataclass_json -from json import JSONDecodeError from pathlib import Path from typing import Self import json @@ -22,9 +21,7 @@ class SystemState: Path(system_state_file_path).touch(exist_ok=True, mode=0o600) with open(system_state_file_path, 'w') as system_state_file: - system_state_file.write(system_state_file_contents) - system_state_file.close() @staticmethod def get(): @@ -37,7 +34,7 @@ class SystemState: # noinspection PyUnresolvedReferences return SystemState.from_dict(system_state_dict) - except (FileNotFoundError, JSONDecodeError, KeyError): + except (FileNotFoundError, ValueError, KeyError): return None @staticmethod