From bfba0dba5630cbafdb35f782836b383ae94c2c36 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Tue, 10 Oct 2023 14:33:22 +0000 Subject: [PATCH] storage: make partial writes pos sanity-check more robust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return value of f.write and f.seek cannot be compared when using open() in text mode: ``` >>> import os >>> s = "aĆ”" >>> >>> with open("a1", "w", encoding='utf-8') as f: ... a = f.write(s) ... pos = f.seek(0, os.SEEK_END) ... print(a, pos) ... 2 3 >>> >>> with open("a2", "wb") as f: ... a = f.write(s.encode('utf-8')) ... pos = f.seek(0, os.SEEK_END) ... print(a, pos) ... 3 3 ``` Was getting errors on Windows, probably due to `\r\n` vs `\n`? ``` 20231010T121334.522573Z | ERROR | util.CallbackManager | cb errored. event='adb_set_up_to_date'. exc=AssertionError((2471475, 2522998)) Traceback (most recent call last): File "...\electrum\electrum\wallet.py", line 497, in on_event_adb_set_up_to_date self.save_db() File "...\electrum\electrum\wallet.py", line 403, in save_db self.db.write() File "...\electrum\electrum\json_db.py", line 48, in wrapper return func(self, *args, **kwargs) File "...\electrum\electrum\json_db.py", line 389, in write self._append_pending_changes() File "...\electrum\electrum\json_db.py", line 48, in wrapper return func(self, *args, **kwargs) File "...\electrum\electrum\json_db.py", line 400, in _append_pending_changes self.storage.append(s) File "...\electrum\electrum\storage.py", line 110, in append assert pos == self.pos, (self.pos, pos) AssertionError: (2471475, 2522998) ``` --- electrum/storage.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/electrum/storage.py b/electrum/storage.py index 81a079b60..a941d5d1f 100644 --- a/electrum/storage.py +++ b/electrum/storage.py @@ -69,8 +69,8 @@ class WalletStorage(Logger): except IOError as e: raise StorageReadWriteError(e) from e if self.file_exists(): - with open(self.path, "r", encoding='utf-8') as f: - self.raw = f.read() + with open(self.path, "rb") as f: + self.raw = f.read().decode("utf-8") self.pos = f.seek(0, os.SEEK_END) self.init_pos = self.pos self._encryption_version = self._init_encryption_version() @@ -86,8 +86,9 @@ class WalletStorage(Logger): def write(self, data: str) -> None: s = self.encrypt_before_writing(data) temp_path = "%s.tmp.%s" % (self.path, os.getpid()) - with open(temp_path, "w", encoding='utf-8') as f: - self.pos = f.write(s) + with open(temp_path, "wb") as f: + f.write(s.encode("utf-8")) + self.pos = f.seek(0, os.SEEK_END) f.flush() os.fsync(f.fileno()) try: @@ -105,10 +106,11 @@ class WalletStorage(Logger): def append(self, data: str) -> None: """ append data to file. for the moment, only non-encrypted file""" assert not self.is_encrypted() - with open(self.path, "r+", encoding='utf-8') as f: + with open(self.path, "rb+") as f: pos = f.seek(0, os.SEEK_END) assert pos == self.pos, (self.pos, pos) - self.pos += f.write(data) + f.write(data.encode("utf-8")) + self.pos = f.seek(0, os.SEEK_END) f.flush() os.fsync(f.fileno())