feat(core): resolve symlinks on file write (#233)

* feat(core): resolve symlinks on file write

Instead of using the passed filename as the rename target, use
Path.resolve for resolving any symlinks.

This allows to use symlinks for the nvchecker database files that point
somewhere else. Before this commit nvchecker simply replaced the symlink
with an actual file, now we resolve the targets first and only replace
the actual file that is pointed to.

---------

Signed-off-by: Levente Polyak <levente@leventepolyak.net>
Co-authored-by: Andreas 'Segaja' Schleifer <webmaster@segaja.de>
Co-authored-by: lilydjwg <lilydjwg@gmail.com>
This commit is contained in:
Levente Polyak 2023-10-27 11:18:05 +02:00 committed by GitHub
parent c401d239b2
commit 9221a476c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -111,11 +111,12 @@ def process_common_arguments(args: argparse.Namespace) -> bool:
return True
return False
def safe_overwrite(fname: str, data: Union[bytes, str], *,
def safe_overwrite(file: Path, data: Union[bytes, str], *,
method: str = 'write', mode: str = 'w', encoding: Optional[str] = None) -> None:
# FIXME: directory has no read perm
# FIXME: symlinks and hard links
tmpname = fname + '.tmp'
# FIXME: hard links
resolved_path = file.resolve()
tmpname = str(resolved_path) + '.tmp'
# if not using "with", write can fail without exception
with open(tmpname, mode, encoding=encoding) as f:
getattr(f, method)(data)
@ -123,7 +124,7 @@ def safe_overwrite(fname: str, data: Union[bytes, str], *,
f.flush()
os.fsync(f.fileno())
# if the above write failed (because disk is full etc), the old data should be kept
os.rename(tmpname, fname)
os.rename(tmpname, resolved_path)
def read_verfile(file: Path) -> VersData:
try:
@ -150,7 +151,7 @@ def write_verfile(file: Path, versions: VersData) -> None:
indent=2,
ensure_ascii=False,
) + '\n'
safe_overwrite(str(file), data)
safe_overwrite(file, data)
class Options(NamedTuple):
ver_files: Optional[Tuple[Path, Path]]