mirror of
https://github.com/borgbackup/borg.git
synced 2024-12-25 09:19:31 +00:00
secure_erase: avoid collateral damage, fixes #6768
if a hardlink copy of a repo was made and a new repo config shall be saved, do NOT fill in random garbage before deleting the previous repo config, because that would damage the hardlink copy.
This commit is contained in:
parent
eed5038922
commit
ba1f8926cc
2 changed files with 18 additions and 8 deletions
|
@ -189,13 +189,23 @@ def scandir_inorder(*, path, fd=None):
|
||||||
return sorted(os.scandir(arg), key=scandir_keyfunc)
|
return sorted(os.scandir(arg), key=scandir_keyfunc)
|
||||||
|
|
||||||
|
|
||||||
def secure_erase(path):
|
def secure_erase(path, *, avoid_collateral_damage):
|
||||||
"""Attempt to securely erase a file by writing random data over it before deleting it."""
|
"""Attempt to securely erase a file by writing random data over it before deleting it.
|
||||||
|
|
||||||
|
If avoid_collateral_damage is True, we only secure erase if the total link count is 1,
|
||||||
|
otherwise we just do a normal "delete" (unlink) without first overwriting it with random.
|
||||||
|
This avoids other hardlinks pointing to same inode as <path> getting damaged, but might be less secure.
|
||||||
|
A typical scenario where this is useful are quick "hardlink copies" of bigger directories.
|
||||||
|
|
||||||
|
If avoid_collateral_damage is False, we always secure erase.
|
||||||
|
If there are hardlinks pointing to the same inode as <path>, they will contain random garbage afterwards.
|
||||||
|
"""
|
||||||
with open(path, 'r+b') as fd:
|
with open(path, 'r+b') as fd:
|
||||||
length = os.stat(fd.fileno()).st_size
|
st = os.stat(fd.fileno())
|
||||||
fd.write(os.urandom(length))
|
if not (st.st_nlink > 1 and avoid_collateral_damage):
|
||||||
fd.flush()
|
fd.write(os.urandom(st.st_size))
|
||||||
os.fsync(fd.fileno())
|
fd.flush()
|
||||||
|
os.fsync(fd.fileno())
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -306,7 +306,7 @@ def save_config(self, path, config):
|
||||||
|
|
||||||
if os.path.isfile(old_config_path):
|
if os.path.isfile(old_config_path):
|
||||||
logger.warning("Old config file not securely erased on previous config update")
|
logger.warning("Old config file not securely erased on previous config update")
|
||||||
secure_erase(old_config_path)
|
secure_erase(old_config_path, avoid_collateral_damage=True)
|
||||||
|
|
||||||
if os.path.isfile(config_path):
|
if os.path.isfile(config_path):
|
||||||
link_error_msg = ("Failed to securely erase old repository config file (hardlinks not supported). "
|
link_error_msg = ("Failed to securely erase old repository config file (hardlinks not supported). "
|
||||||
|
@ -333,7 +333,7 @@ def save_config(self, path, config):
|
||||||
"read-only repositories." % (e.strerror, e.filename))
|
"read-only repositories." % (e.strerror, e.filename))
|
||||||
|
|
||||||
if os.path.isfile(old_config_path):
|
if os.path.isfile(old_config_path):
|
||||||
secure_erase(old_config_path)
|
secure_erase(old_config_path, avoid_collateral_damage=True)
|
||||||
|
|
||||||
def save_key(self, keydata):
|
def save_key(self, keydata):
|
||||||
assert self.config
|
assert self.config
|
||||||
|
|
Loading…
Reference in a new issue