mirror of
https://github.com/borgbackup/borg.git
synced 2025-01-01 04:37:34 +00:00
import-tar/export-tar: add xattr support for PAX format, #2521
This commit is contained in:
parent
2ed5d6b101
commit
e0fdaa440f
4 changed files with 44 additions and 1 deletions
|
@ -1487,6 +1487,17 @@ def s_to_ns(s):
|
||||||
if name in ph:
|
if name in ph:
|
||||||
ns = s_to_ns(ph[name])
|
ns = s_to_ns(ph[name])
|
||||||
setattr(item, name, ns)
|
setattr(item, name, ns)
|
||||||
|
xattrs = StableDict()
|
||||||
|
for key, value in ph.items():
|
||||||
|
if key.startswith(SCHILY_XATTR):
|
||||||
|
key = key.removeprefix(SCHILY_XATTR)
|
||||||
|
# the tarfile code gives us str keys and str values,
|
||||||
|
# but we need bytes keys and bytes values.
|
||||||
|
bkey = key.encode("utf-8", errors="surrogateescape")
|
||||||
|
bvalue = value.encode("utf-8", errors="surrogateescape")
|
||||||
|
xattrs[bkey] = bvalue
|
||||||
|
if xattrs:
|
||||||
|
item.xattrs = xattrs
|
||||||
yield item, status
|
yield item, status
|
||||||
# if we get here, "with"-block worked ok without error/exception, the item was processed ok...
|
# if we get here, "with"-block worked ok without error/exception, the item was processed ok...
|
||||||
self.add_item(item, stats=self.stats)
|
self.add_item(item, stats=self.stats)
|
||||||
|
|
|
@ -211,6 +211,13 @@ def item_to_paxheaders(format, item):
|
||||||
if hasattr(item, name):
|
if hasattr(item, name):
|
||||||
ns = getattr(item, name)
|
ns = getattr(item, name)
|
||||||
ph[name] = str(ns / 1e9)
|
ph[name] = str(ns / 1e9)
|
||||||
|
if hasattr(item, "xattrs"):
|
||||||
|
for bkey, bvalue in item.xattrs.items():
|
||||||
|
# we have bytes key and bytes value, but the tarfile code
|
||||||
|
# expects str key and str value.
|
||||||
|
key = SCHILY_XATTR + bkey.decode("utf-8", errors="surrogateescape")
|
||||||
|
value = bvalue.decode("utf-8", errors="surrogateescape")
|
||||||
|
ph[key] = value
|
||||||
if format == "BORG": # BORG format additions
|
if format == "BORG": # BORG format additions
|
||||||
ph["BORG.item.version"] = "1"
|
ph["BORG.item.version"] = "1"
|
||||||
# BORG.item.meta - just serialize all metadata we have:
|
# BORG.item.meta - just serialize all metadata we have:
|
||||||
|
@ -355,6 +362,7 @@ def build_parser_tar(self, subparsers, common_parser, mid_common_parser):
|
||||||
| BORG | BORG specific, like PAX | all as supported by borg |
|
| BORG | BORG specific, like PAX | all as supported by borg |
|
||||||
+--------------+---------------------------+----------------------------+
|
+--------------+---------------------------+----------------------------+
|
||||||
| PAX | POSIX.1-2001 (pax) format | GNU + atime/ctime/mtime ns |
|
| PAX | POSIX.1-2001 (pax) format | GNU + atime/ctime/mtime ns |
|
||||||
|
| | | + xattrs |
|
||||||
+--------------+---------------------------+----------------------------+
|
+--------------+---------------------------+----------------------------+
|
||||||
| GNU | GNU tar format | mtime s, no atime/ctime, |
|
| GNU | GNU tar format | mtime s, no atime/ctime, |
|
||||||
| | | no ACLs/xattrs/bsdflags |
|
| | | no ACLs/xattrs/bsdflags |
|
||||||
|
|
|
@ -121,6 +121,9 @@
|
||||||
# similar to above, but for bigger granularity / clock differences
|
# similar to above, but for bigger granularity / clock differences
|
||||||
TIME_DIFFERS2_NS = 3000000000
|
TIME_DIFFERS2_NS = 3000000000
|
||||||
|
|
||||||
|
# tar related
|
||||||
|
SCHILY_XATTR = "SCHILY.xattr." # xattr key prefix in tar PAX headers
|
||||||
|
|
||||||
# return codes returned by borg command
|
# return codes returned by borg command
|
||||||
EXIT_SUCCESS = 0 # everything done, no problems
|
EXIT_SUCCESS = 0 # everything done, no problems
|
||||||
EXIT_WARNING = 1 # reached normal end of operation, but there were issues (generic warning)
|
EXIT_WARNING = 1 # reached normal end of operation, but there were issues (generic warning)
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from ... import xattr
|
||||||
from ...constants import * # NOQA
|
from ...constants import * # NOQA
|
||||||
from .. import changedir
|
from .. import changedir
|
||||||
from . import assert_dirs_equal, _extract_hardlinks_setup, cmd, create_test_files, requires_hardlinks, RK_ENCRYPTION
|
from . import assert_dirs_equal, _extract_hardlinks_setup, cmd, requires_hardlinks, RK_ENCRYPTION
|
||||||
|
from . import create_test_files, create_regular_file
|
||||||
from . import generate_archiver_tests
|
from . import generate_archiver_tests
|
||||||
|
|
||||||
pytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds="local,remote,binary") # NOQA
|
pytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds="local,remote,binary") # NOQA
|
||||||
|
@ -219,3 +221,22 @@ def test_roundtrip_pax_borg(archivers, request):
|
||||||
with changedir(archiver.output_path):
|
with changedir(archiver.output_path):
|
||||||
cmd(archiver, "extract", "dst")
|
cmd(archiver, "extract", "dst")
|
||||||
assert_dirs_equal("input", "output/input")
|
assert_dirs_equal("input", "output/input")
|
||||||
|
|
||||||
|
|
||||||
|
def test_roundtrip_pax_xattrs(archivers, request):
|
||||||
|
archiver = request.getfixturevalue(archivers)
|
||||||
|
if not xattr.is_enabled(archiver.input_path):
|
||||||
|
pytest.skip("xattrs not supported")
|
||||||
|
create_regular_file(archiver.input_path, "file")
|
||||||
|
original_path = os.path.join(archiver.input_path, "file")
|
||||||
|
xa_key, xa_value = b"user.xattrtest", b"not valid utf-8: \xff"
|
||||||
|
xattr.setxattr(original_path.encode(), xa_key, xa_value)
|
||||||
|
cmd(archiver, "repo-create", "--encryption=none")
|
||||||
|
cmd(archiver, "create", "src", "input")
|
||||||
|
cmd(archiver, "export-tar", "src", "xattrs.tar", "--tar-format=PAX")
|
||||||
|
cmd(archiver, "import-tar", "dst", "xattrs.tar")
|
||||||
|
with changedir(archiver.output_path):
|
||||||
|
cmd(archiver, "extract", "dst")
|
||||||
|
extracted_path = os.path.abspath("input/file")
|
||||||
|
xa_value_extracted = xattr.getxattr(extracted_path.encode(), xa_key)
|
||||||
|
assert xa_value_extracted == xa_value
|
||||||
|
|
Loading…
Reference in a new issue