backups: Properly cleanup after downloading an archive

Closes: #2354.

- Process spawned for raw IO should be collected using wait(). Closing the
specially created pipe early causes an exception in the child process when it
tries to write to stderr the JSON result of executed method.

Tests:

- Monitor the output of 'journalctl -f'. Download and archive. Without the
patch, an error is printed as described in the bug. With the but no such error
is printed. Downloaded file is the same in both cases.

- Writing a log message in cleanup_func shows that the process has been waited
for and the FDs have been closed.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2025-01-06 15:27:40 -08:00 committed by James Valleroy
parent 03484bd026
commit d710ab0790
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808

View File

@ -196,6 +196,9 @@ class BaseBorgRepository(abc.ABC):
"""Override to call read() instead of readline()."""
chunk = self.read(io.DEFAULT_BUFFER_SIZE)
if not chunk:
if getattr(self, 'cleanup_func'):
self.cleanup_func()
raise StopIteration
return chunk
@ -204,12 +207,27 @@ class BaseBorgRepository(abc.ABC):
self._get_archive_path(archive_name),
self._get_encryption_passpharse(), _raw_output=True)
os.close(read_fd) # Don't use the pipe for communication, just stdout
# Write the method request with args to the process
proc.stdin.write(input_)
proc.stdin.close()
proc.stderr.close() # writing to stderr in child will cause SIGPIPE
return BufferedReader(proc.stdout)
def _cleanup_func():
"""After the process has been read from, cleanup the process."""
try:
if proc.stdout:
proc.stdout.close()
if proc.stderr:
proc.stderr.close()
proc.wait(30)
os.close(read_fd)
except Exception:
logger.exception('Closing process failed after download')
reader = BufferedReader(proc.stdout)
reader.cleanup_func = _cleanup_func
return reader
def _get_archive_path(self, archive_name):
"""Return full borg path for an archive."""