diff options
| author | Christian Breunig <christian@breunig.cc> | 2026-10-07 19:57:03 +0200 |
|---|---|---|
| committer | Christian Breunig <christian@breunig.cc> | 2026-10-31 09:58:29 +0000 |
| commit | 30b999c5a2b6ce67734e882942609a5e4a4d84cf (patch) | |
| tree | 2f8b1fb25ab18a3a6361d4458b406932b1386a4f /python | |
| parent | 710d0a19b25262fe77a6ebd4a62a5c427a1efa6f (diff) | |
| download | vyos-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.py | 13 |
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 |
