daemon: split standardize_path from daemon._wallets keying
- standardize_path is made more lenient: it no longer calls os.path.realpath, as that was causing issues on Windows with some mounted drives - daemon._wallets is still keyed on the old strict standardize_path, but filesystem operations in WalletStorage use the new lenient standardize_path. - daemon._wallets is strict to forbid opening the same logical file twice concurrently - fs operations however work better on the non-resolved paths, so they use those closes https://github.com/spesmilo/electrum/issues/8495
This commit is contained in:
@@ -402,7 +402,7 @@ class Daemon(Logger):
|
|||||||
if not self.config.NETWORK_OFFLINE:
|
if not self.config.NETWORK_OFFLINE:
|
||||||
self.network = Network(config, daemon=self)
|
self.network = Network(config, daemon=self)
|
||||||
self.fx = FxThread(config=config)
|
self.fx = FxThread(config=config)
|
||||||
# path -> wallet; make sure path is standardized.
|
# wallet_key -> wallet
|
||||||
self._wallets = {} # type: Dict[str, Abstract_Wallet]
|
self._wallets = {} # type: Dict[str, Abstract_Wallet]
|
||||||
self._wallet_lock = threading.RLock()
|
self._wallet_lock = threading.RLock()
|
||||||
daemon_jobs = []
|
daemon_jobs = []
|
||||||
@@ -452,6 +452,17 @@ class Daemon(Logger):
|
|||||||
if self.config.LIGHTNING_USE_GOSSIP:
|
if self.config.LIGHTNING_USE_GOSSIP:
|
||||||
self.network.start_gossip()
|
self.network.start_gossip()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _wallet_key_from_path(path) -> str:
|
||||||
|
"""This does stricter path standardization than 'standardize_path'.
|
||||||
|
It is used for keying the _wallets dict, but not for the actual filesystem operations. (see #8495)
|
||||||
|
"""
|
||||||
|
path = standardize_path(path)
|
||||||
|
# also resolve symlinks and windows network mounts/etc:
|
||||||
|
path = os.path.realpath(path)
|
||||||
|
path = os.path.normcase(path)
|
||||||
|
return str(path)
|
||||||
|
|
||||||
def with_wallet_lock(func):
|
def with_wallet_lock(func):
|
||||||
def func_wrapper(self: 'Daemon', *args, **kwargs):
|
def func_wrapper(self: 'Daemon', *args, **kwargs):
|
||||||
with self._wallet_lock:
|
with self._wallet_lock:
|
||||||
@@ -461,9 +472,9 @@ class Daemon(Logger):
|
|||||||
@with_wallet_lock
|
@with_wallet_lock
|
||||||
def load_wallet(self, path, password, *, manual_upgrades=True) -> Optional[Abstract_Wallet]:
|
def load_wallet(self, path, password, *, manual_upgrades=True) -> Optional[Abstract_Wallet]:
|
||||||
path = standardize_path(path)
|
path = standardize_path(path)
|
||||||
|
wallet_key = self._wallet_key_from_path(path)
|
||||||
# wizard will be launched if we return
|
# wizard will be launched if we return
|
||||||
if path in self._wallets:
|
if wallet := self._wallets.get(wallet_key):
|
||||||
wallet = self._wallets[path]
|
|
||||||
return wallet
|
return wallet
|
||||||
wallet = self._load_wallet(path, password, manual_upgrades=manual_upgrades, config=self.config)
|
wallet = self._load_wallet(path, password, manual_upgrades=manual_upgrades, config=self.config)
|
||||||
if wallet is None:
|
if wallet is None:
|
||||||
@@ -503,13 +514,13 @@ class Daemon(Logger):
|
|||||||
@with_wallet_lock
|
@with_wallet_lock
|
||||||
def add_wallet(self, wallet: Abstract_Wallet) -> None:
|
def add_wallet(self, wallet: Abstract_Wallet) -> None:
|
||||||
path = wallet.storage.path
|
path = wallet.storage.path
|
||||||
path = standardize_path(path)
|
wallet_key = self._wallet_key_from_path(path)
|
||||||
self._wallets[path] = wallet
|
self._wallets[wallet_key] = wallet
|
||||||
run_hook('daemon_wallet_loaded', self, wallet)
|
run_hook('daemon_wallet_loaded', self, wallet)
|
||||||
|
|
||||||
def get_wallet(self, path: str) -> Optional[Abstract_Wallet]:
|
def get_wallet(self, path: str) -> Optional[Abstract_Wallet]:
|
||||||
path = standardize_path(path)
|
wallet_key = self._wallet_key_from_path(path)
|
||||||
return self._wallets.get(path)
|
return self._wallets.get(wallet_key)
|
||||||
|
|
||||||
@with_wallet_lock
|
@with_wallet_lock
|
||||||
def get_wallets(self) -> Dict[str, Abstract_Wallet]:
|
def get_wallets(self) -> Dict[str, Abstract_Wallet]:
|
||||||
@@ -531,8 +542,8 @@ class Daemon(Logger):
|
|||||||
@with_wallet_lock
|
@with_wallet_lock
|
||||||
async def _stop_wallet(self, path: str) -> bool:
|
async def _stop_wallet(self, path: str) -> bool:
|
||||||
"""Returns True iff a wallet was found."""
|
"""Returns True iff a wallet was found."""
|
||||||
path = standardize_path(path)
|
wallet_key = self._wallet_key_from_path(path)
|
||||||
wallet = self._wallets.pop(path, None)
|
wallet = self._wallets.pop(wallet_key, None)
|
||||||
if not wallet:
|
if not wallet:
|
||||||
return False
|
return False
|
||||||
await wallet.stop()
|
await wallet.stop()
|
||||||
|
|||||||
@@ -522,12 +522,13 @@ def assert_file_in_datadir_available(path, config_path):
|
|||||||
|
|
||||||
|
|
||||||
def standardize_path(path):
|
def standardize_path(path):
|
||||||
|
# note: os.path.realpath() is not used, as on Windows it can return non-working paths (see #8495).
|
||||||
|
# This means that we don't resolve symlinks!
|
||||||
return os.path.normcase(
|
return os.path.normcase(
|
||||||
os.path.realpath(
|
|
||||||
os.path.abspath(
|
os.path.abspath(
|
||||||
os.path.expanduser(
|
os.path.expanduser(
|
||||||
path
|
path
|
||||||
))))
|
)))
|
||||||
|
|
||||||
|
|
||||||
def get_new_wallet_name(wallet_folder: str) -> str:
|
def get_new_wallet_name(wallet_folder: str) -> str:
|
||||||
|
|||||||
Reference in New Issue
Block a user