summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-10-07 19:57:03 +0200
committerChristian Breunig <christian@breunig.cc>2026-10-31 09:58:29 +0000
commit30b999c5a2b6ce67734e882942609a5e4a4d84cf (patch)
tree2f8b1fb25ab18a3a6361d4458b406932b1386a4f /python
parent710d0a19b25262fe77a6ebd4a62a5c427a1efa6f (diff)
downloadvyos-1x-30b999c5a2b6ce67734e882942609a5e4a4d84cf.tar.gz
vyos-1x-30b999c5a2b6ce67734e882942609a5e4a4d84cf.zip
interfaces: T3871: make the interface map survive a crash, not just a rename
Replacing the map was atomic but not durable: neither its contents nor the rename were forced to the disk, so a machine losing power shortly after a boot could come back to an empty or truncated file. Names are then derived from the hardware again, which only gives the same answer where they happened to follow slot order - anywhere else an address moves to another cable. Force the contents out before the file is put in place, and the directory entry after, so what comes back is either the names it had before or the ones just written.
Diffstat (limited to 'python')
-rw-r--r--python/vyos/ifconfig/ifname_store.py13
1 files changed, 12 insertions, 1 deletions
diff --git a/python/vyos/ifconfig/ifname_store.py b/python/vyos/ifconfig/ifname_store.py
index 5a0a29398..b0ee3df35 100644
--- a/python/vyos/ifconfig/ifname_store.py
+++ b/python/vyos/ifconfig/ifname_store.py
@@ -289,7 +289,9 @@ def config_dir_is_mounted(path: Path = None) -> bool:
def save_store(store: dict, path: Path = None) -> bool:
- """Write atomically, so an interrupted boot leaves no half-written file.
+ """Write atomically and durably, so a boot cut short leaves either the
+ previous names or the new ones, never a half-written file and never a
+ rename the disk has not seen.
Returns False without writing on a degraded boot: names are still
resolved for this boot, they just must not be persisted.
@@ -305,6 +307,8 @@ def save_store(store: dict, path: Path = None) -> bool:
with os.fdopen(fd, 'w') as f:
json.dump(store, f, indent=2, sort_keys=True)
f.write('\n')
+ f.flush()
+ os.fsync(f.fileno())
os.chmod(tmp, 0o664)
try:
os.chown(tmp, -1, get_cfg_group_id())
@@ -312,6 +316,13 @@ def save_store(store: dict, path: Path = None) -> bool:
# not fatal, the store is read by root at boot
pass
os.replace(tmp, path)
+ # the rename itself has to reach the disk, or a boot cut short here
+ # comes back to the name it had before
+ dir_fd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
+ try:
+ os.fsync(dir_fd)
+ finally:
+ os.close(dir_fd)
except Exception:
Path(tmp).unlink(missing_ok=True)
raise