mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-11 01:41:57 -04:00
Merge pull request #6301 from ThomasWaldmann/safe-unlink-master
safe_unlink (was: truncate_and_unlink)
This commit is contained in:
commit
da763cedda
5 changed files with 34 additions and 20 deletions
|
|
@ -26,7 +26,7 @@ from .helpers import yes
|
|||
from .helpers import remove_surrogates
|
||||
from .helpers import ProgressIndicatorPercent, ProgressIndicatorMessage
|
||||
from .helpers import set_ec, EXIT_WARNING
|
||||
from .helpers import truncate_and_unlink
|
||||
from .helpers import safe_unlink
|
||||
from .helpers import msgpack
|
||||
from .item import ArchiveItem, ChunkListEntry
|
||||
from .crypto.key import PlaintextKey
|
||||
|
|
@ -774,7 +774,7 @@ class LocalCache(CacheStatsMixin):
|
|||
filename=bin_to_hex(archive_id) + '.compact') as fd:
|
||||
chunk_idx.write(fd)
|
||||
except Exception:
|
||||
truncate_and_unlink(fn_tmp)
|
||||
safe_unlink(fn_tmp)
|
||||
else:
|
||||
os.rename(fn_tmp, fn)
|
||||
|
||||
|
|
|
|||
|
|
@ -205,12 +205,12 @@ def secure_erase(path):
|
|||
os.unlink(path)
|
||||
|
||||
|
||||
def truncate_and_unlink(path):
|
||||
def safe_unlink(path):
|
||||
"""
|
||||
Truncate and then unlink *path*.
|
||||
Safely unlink (delete) *path*.
|
||||
|
||||
Do not create *path* if it does not exist.
|
||||
Open *path* for truncation in r+b mode (=O_RDWR|O_BINARY).
|
||||
If we run out of space while deleting the file, we try truncating it first.
|
||||
BUT we truncate only if path is the only hardlink referring to this content.
|
||||
|
||||
Use this when deleting potentially large files when recovering
|
||||
from a VFS error such as ENOSPC. It can help a full file system
|
||||
|
|
@ -218,13 +218,27 @@ def truncate_and_unlink(path):
|
|||
in repository.py for further explanations.
|
||||
"""
|
||||
try:
|
||||
with open(path, 'r+b') as fd:
|
||||
fd.truncate()
|
||||
except OSError as err:
|
||||
if err.errno != errno.ENOTSUP:
|
||||
os.unlink(path)
|
||||
except OSError as unlink_err:
|
||||
if unlink_err.errno != errno.ENOSPC:
|
||||
# not free space related, give up here.
|
||||
raise
|
||||
# don't crash if the above ops are not supported.
|
||||
os.unlink(path)
|
||||
# we ran out of space while trying to delete the file.
|
||||
st = os.stat(path)
|
||||
if st.st_nlink > 1:
|
||||
# rather give up here than cause collateral damage to the other hardlink.
|
||||
raise
|
||||
# no other hardlink! try to recover free space by truncating this file.
|
||||
try:
|
||||
# Do not create *path* if it does not exist, open for truncation in r+b mode (=O_RDWR|O_BINARY).
|
||||
with open(path, 'r+b') as fd:
|
||||
fd.truncate()
|
||||
except OSError:
|
||||
# truncate didn't work, so we still have the original unlink issue - give up:
|
||||
raise unlink_err
|
||||
else:
|
||||
# successfully truncated the file, try again deleting it:
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
def dash_open(path, mode):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import os
|
|||
import socket
|
||||
import uuid
|
||||
|
||||
from borg.helpers import truncate_and_unlink
|
||||
from borg.helpers import safe_unlink
|
||||
from borg.platformflags import is_win32
|
||||
|
||||
"""
|
||||
|
|
@ -212,7 +212,7 @@ class SaveFile:
|
|||
def __enter__(self):
|
||||
from .. import platform
|
||||
try:
|
||||
truncate_and_unlink(self.tmppath)
|
||||
safe_unlink(self.tmppath)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
self.fd = platform.SyncFile(self.tmppath, self.binary)
|
||||
|
|
@ -222,7 +222,7 @@ class SaveFile:
|
|||
from .. import platform
|
||||
self.fd.close()
|
||||
if exc_type is not None:
|
||||
truncate_and_unlink(self.tmppath)
|
||||
safe_unlink(self.tmppath)
|
||||
return
|
||||
os.replace(self.tmppath, self.path)
|
||||
platform.sync_dir(os.path.dirname(self.path))
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from .helpers import get_limited_unpacker
|
|||
from .helpers import replace_placeholders
|
||||
from .helpers import sysinfo
|
||||
from .helpers import format_file_size
|
||||
from .helpers import truncate_and_unlink
|
||||
from .helpers import safe_unlink
|
||||
from .helpers import prepare_subprocess_env
|
||||
from .logger import create_logger, setup_logging
|
||||
from .helpers import msgpack
|
||||
|
|
@ -1150,7 +1150,7 @@ class RepositoryCache(RepositoryNoCache):
|
|||
fd.write(packed)
|
||||
except OSError as os_error:
|
||||
try:
|
||||
truncate_and_unlink(file)
|
||||
safe_unlink(file)
|
||||
except FileNotFoundError:
|
||||
pass # open() could have failed as well
|
||||
if os_error.errno == errno.ENOSPC:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ from .helpers import Error, ErrorWithTraceback, IntegrityError, format_file_size
|
|||
from .helpers import Location
|
||||
from .helpers import ProgressIndicatorPercent
|
||||
from .helpers import bin_to_hex
|
||||
from .helpers import secure_erase, truncate_and_unlink
|
||||
from .helpers import secure_erase, safe_unlink
|
||||
from .helpers import Manifest
|
||||
from .helpers import msgpack
|
||||
from .locking import Lock, LockError, LockErrorT
|
||||
|
|
@ -1342,7 +1342,7 @@ class LoggedIO:
|
|||
if segment > transaction_id:
|
||||
if segment in self.fds:
|
||||
del self.fds[segment]
|
||||
truncate_and_unlink(filename)
|
||||
safe_unlink(filename)
|
||||
count += 1
|
||||
else:
|
||||
break
|
||||
|
|
@ -1450,7 +1450,7 @@ class LoggedIO:
|
|||
if segment in self.fds:
|
||||
del self.fds[segment]
|
||||
try:
|
||||
truncate_and_unlink(self.segment_filename(segment))
|
||||
safe_unlink(self.segment_filename(segment))
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue