mirror of https://github.com/borgbackup/borg.git
fix backing up FIFOs. fixes #4394
The O_NONBLOCK caused EAGAIN errors borg did not deal with, so we better block and wait until read data is available from e.g. a fifo. Fixed the --read-special test so it creates a FIFO, feeds it from a thread and backs it up with borg. Then extracts and checks if correct data has been backed up. Also: renamed flags_follow to flags_special_follow (its only intended use is on symlinks to special files with --read-special).
This commit is contained in:
parent
382875692b
commit
cd857b6d8f
|
@ -66,7 +66,7 @@ from .helpers import ChunkIteratorFileWrapper
|
|||
from .helpers import popen_with_error_handling, prepare_subprocess_env
|
||||
from .helpers import dash_open
|
||||
from .helpers import umount
|
||||
from .helpers import flags_root, flags_dir, flags_follow
|
||||
from .helpers import flags_root, flags_dir, flags_special_follow, flags_special
|
||||
from .helpers import msgpack
|
||||
from .nanorst import rst_to_terminal
|
||||
from .patterns import ArgparsePatternAction, ArgparseExcludeFileAction, ArgparsePatternFileAction, parse_exclude_pattern
|
||||
|
@ -646,7 +646,7 @@ class Archiver:
|
|||
special = is_special(st_target.st_mode)
|
||||
if special:
|
||||
status = fso.process_file(path=path, parent_fd=parent_fd, name=name, st=st_target,
|
||||
cache=cache, flags=flags_follow)
|
||||
cache=cache, flags=flags_special_follow)
|
||||
else:
|
||||
status = fso.process_symlink(path=path, parent_fd=parent_fd, name=name, st=st)
|
||||
elif stat.S_ISFIFO(st.st_mode):
|
||||
|
@ -654,19 +654,22 @@ class Archiver:
|
|||
if not read_special:
|
||||
status = fso.process_fifo(path=path, parent_fd=parent_fd, name=name, st=st)
|
||||
else:
|
||||
status = fso.process_file(path=path, parent_fd=parent_fd, name=name, st=st, cache=cache)
|
||||
status = fso.process_file(path=path, parent_fd=parent_fd, name=name, st=st,
|
||||
cache=cache, flags=flags_special)
|
||||
elif stat.S_ISCHR(st.st_mode):
|
||||
if not dry_run:
|
||||
if not read_special:
|
||||
status = fso.process_dev(path=path, parent_fd=parent_fd, name=name, st=st, dev_type='c')
|
||||
else:
|
||||
status = fso.process_file(path=path, parent_fd=parent_fd, name=name, st=st, cache=cache)
|
||||
status = fso.process_file(path=path, parent_fd=parent_fd, name=name, st=st,
|
||||
cache=cache, flags=flags_special)
|
||||
elif stat.S_ISBLK(st.st_mode):
|
||||
if not dry_run:
|
||||
if not read_special:
|
||||
status = fso.process_dev(path=path, parent_fd=parent_fd, name=name, st=st, dev_type='b')
|
||||
else:
|
||||
status = fso.process_file(path=path, parent_fd=parent_fd, name=name, st=st, cache=cache)
|
||||
status = fso.process_file(path=path, parent_fd=parent_fd, name=name, st=st,
|
||||
cache=cache, flags=flags_special)
|
||||
elif stat.S_ISSOCK(st.st_mode):
|
||||
# Ignore unix sockets
|
||||
return
|
||||
|
|
|
@ -202,9 +202,10 @@ def O_(*flags):
|
|||
return result
|
||||
|
||||
|
||||
flags_base = O_('BINARY', 'NONBLOCK', 'NOCTTY')
|
||||
flags_follow = flags_base | O_('RDONLY')
|
||||
flags_normal = flags_base | O_('RDONLY', 'NOFOLLOW')
|
||||
flags_base = O_('BINARY', 'NOCTTY', 'RDONLY')
|
||||
flags_special = flags_base | O_('NOFOLLOW') # BLOCK == wait when reading devices or fifos
|
||||
flags_special_follow = flags_base # BLOCK == wait when reading symlinked devices or fifos
|
||||
flags_normal = flags_base | O_('NONBLOCK', 'NOFOLLOW')
|
||||
flags_noatime = flags_normal | O_('NOATIME')
|
||||
flags_root = O_('RDONLY')
|
||||
flags_dir = O_('DIRECTORY', 'RDONLY', 'NOFOLLOW')
|
||||
|
|
|
@ -1780,19 +1780,38 @@ class ArchiverTestCase(ArchiverTestCaseBase):
|
|||
output = self.cmd('create', '--list', '--filter=AM', self.repository_location + '::test3', 'input')
|
||||
self.assert_in('file1', output)
|
||||
|
||||
def test_create_read_special(self):
|
||||
self.create_regular_file('regular', size=1024)
|
||||
os.symlink(os.path.join(self.input_path, 'file'), os.path.join(self.input_path, 'link_regular'))
|
||||
if are_fifos_supported():
|
||||
os.mkfifo(os.path.join(self.input_path, 'fifo'))
|
||||
os.symlink(os.path.join(self.input_path, 'fifo'), os.path.join(self.input_path, 'link_fifo'))
|
||||
@pytest.mark.skipif(not are_fifos_supported(), reason='FIFOs not supported')
|
||||
def test_create_read_special_symlink(self):
|
||||
from threading import Thread
|
||||
|
||||
def fifo_feeder(fifo_fn, data):
|
||||
fd = os.open(fifo_fn, os.O_WRONLY)
|
||||
try:
|
||||
os.write(fd, data)
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||
archive = self.repository_location + '::test'
|
||||
self.cmd('create', '--read-special', archive, 'input')
|
||||
output = self.cmd('list', archive)
|
||||
assert 'input/link_regular -> ' in output # not pointing to special file: archived as symlink
|
||||
if are_fifos_supported():
|
||||
assert 'input/link_fifo\n' in output # pointing to a special file: archived following symlink
|
||||
data = b'foobar' * 1000
|
||||
|
||||
fifo_fn = os.path.join(self.input_path, 'fifo')
|
||||
link_fn = os.path.join(self.input_path, 'link_fifo')
|
||||
os.mkfifo(fifo_fn)
|
||||
os.symlink(fifo_fn, link_fn)
|
||||
|
||||
t = Thread(target=fifo_feeder, args=(fifo_fn, data))
|
||||
t.start()
|
||||
try:
|
||||
self.cmd('create', '--read-special', archive, 'input/link_fifo')
|
||||
finally:
|
||||
t.join()
|
||||
with changedir('output'):
|
||||
self.cmd('extract', archive)
|
||||
fifo_fn = 'input/link_fifo'
|
||||
with open(fifo_fn, 'rb') as f:
|
||||
extracted_data = f.read()
|
||||
assert extracted_data == data
|
||||
|
||||
def test_create_read_special_broken_symlink(self):
|
||||
os.symlink('somewhere doesnt exist', os.path.join(self.input_path, 'link'))
|
||||
|
|
Loading…
Reference in New Issue