mirror of
https://github.com/borgbackup/borg.git
synced 2025-01-25 00:38:58 +00:00
Added xattr support
This commit is contained in:
parent
68bd3d3e3d
commit
fe663e3232
2 changed files with 35 additions and 9 deletions
|
@ -5,6 +5,7 @@
|
|||
import socket
|
||||
import stat
|
||||
import sys
|
||||
from xattr import xattr, XATTR_NOFOLLOW
|
||||
|
||||
from . import NS_ARCHIVE_METADATA, NS_ARCHIVE_ITEMS, NS_ARCHIVE_CHUNKS, NS_CHUNK
|
||||
from .chunkifier import chunkify
|
||||
|
@ -144,6 +145,13 @@ def extract_item(self, item, dest=None):
|
|||
raise Exception('Unknown archive item type %r' % item['mode'])
|
||||
|
||||
def restore_attrs(self, path, item, symlink=False):
|
||||
if item['xattrs']:
|
||||
try:
|
||||
xa = xattr(path, XATTR_NOFOLLOW)
|
||||
for k, v in item['xattrs'].items():
|
||||
xa.set(k, v)
|
||||
except IOError:
|
||||
pass
|
||||
if have_lchmod:
|
||||
os.lchmod(path, item['mode'])
|
||||
elif not symlink:
|
||||
|
@ -179,28 +187,33 @@ def delete(self, cache):
|
|||
self.store.commit()
|
||||
cache.save()
|
||||
|
||||
def stat_attrs(self, st):
|
||||
def stat_attrs(self, st, path):
|
||||
try:
|
||||
xattrs = dict(xattr(path, XATTR_NOFOLLOW))
|
||||
except IOError:
|
||||
xattrs = None
|
||||
return {
|
||||
'mode': st.st_mode,
|
||||
'uid': st.st_uid, 'user': uid2user(st.st_uid),
|
||||
'gid': st.st_gid, 'group': gid2group(st.st_gid),
|
||||
'atime': st.st_atime, 'mtime': st.st_mtime,
|
||||
'xattrs': xattrs,
|
||||
}
|
||||
|
||||
def process_dir(self, path, st):
|
||||
item = {'path': path.lstrip('/\\:')}
|
||||
item.update(self.stat_attrs(st))
|
||||
item.update(self.stat_attrs(st, path))
|
||||
self.items.append(item)
|
||||
|
||||
def process_fifo(self, path, st):
|
||||
item = {'path': path.lstrip('/\\:')}
|
||||
item.update(self.stat_attrs(st))
|
||||
item.update(self.stat_attrs(st, path))
|
||||
self.items.append(item)
|
||||
|
||||
def process_symlink(self, path, st):
|
||||
source = os.readlink(path)
|
||||
item = {'path': path.lstrip('/\\:'), 'source': source}
|
||||
item.update(self.stat_attrs(st))
|
||||
item.update(self.stat_attrs(st, path))
|
||||
self.items.append(item)
|
||||
|
||||
def process_file(self, path, st, cache):
|
||||
|
@ -240,7 +253,7 @@ def process_file(self, path, st, cache):
|
|||
size += len(chunk)
|
||||
cache.memorize_file_chunks(path_hash, st, ids)
|
||||
item = {'path': safe_path, 'chunks': chunks, 'size': size}
|
||||
item.update(self.stat_attrs(st))
|
||||
item.update(self.stat_attrs(st, path))
|
||||
self.items.append(item)
|
||||
|
||||
def process_chunk2(self, id, cache):
|
||||
|
|
21
darc/test.py
21
darc/test.py
|
@ -6,6 +6,7 @@
|
|||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
from xattr import xattr, XATTR_NOFOLLOW
|
||||
|
||||
from . import store
|
||||
from .archiver import Archiver
|
||||
|
@ -57,25 +58,37 @@ def create_regual_file(self, name, size=0):
|
|||
with open(filename, 'wb') as fd:
|
||||
fd.write('X' * size)
|
||||
|
||||
def get_xattrs(self, path):
|
||||
try:
|
||||
return dict(xattr(path, XATTR_NOFOLLOW))
|
||||
except IOError:
|
||||
return {}
|
||||
|
||||
def diff_dirs(self, dir1, dir2):
|
||||
diff = filecmp.dircmp(dir1, dir2)
|
||||
self.assertEqual(diff.left_only, [])
|
||||
self.assertEqual(diff.right_only, [])
|
||||
self.assertEqual(diff.diff_files, [])
|
||||
for filename in diff.common:
|
||||
s1 = os.lstat(os.path.join(dir1, filename))
|
||||
s2 = os.lstat(os.path.join(dir2, filename))
|
||||
path1 = os.path.join(dir1, filename)
|
||||
path2 = os.path.join(dir2, filename)
|
||||
s1 = os.lstat(path1)
|
||||
s2 = os.lstat(path2)
|
||||
attrs = ['st_mode', 'st_uid', 'st_gid']
|
||||
# We can't restore symlink atime/mtime right now
|
||||
if not os.path.islink(os.path.join(dir1, filename)):
|
||||
if not os.path.islink(path1):
|
||||
attrs.append('st_mtime')
|
||||
d1 = [filename] + [getattr(s1, a) for a in attrs]
|
||||
d2 = [filename] + [getattr(s2, a) for a in attrs]
|
||||
d1.append(self.get_xattrs(path1))
|
||||
d2.append(self.get_xattrs(path2))
|
||||
self.assertEqual(d1, d2)
|
||||
|
||||
def test_basic_functionality(self):
|
||||
self.create_regual_file('file1', size=1024*80)
|
||||
self.create_regual_file('dir2/file2', size=1024*80)
|
||||
x = xattr(os.path.join(self.input_path, 'file1'))
|
||||
x.set('user:foo', 'bar')
|
||||
os.symlink('somewhere', os.path.join(self.input_path, 'link1'))
|
||||
os.mkfifo(os.path.join(self.input_path, 'fifo1'))
|
||||
self.darc('create', self.store_path + '::test', 'input')
|
||||
|
@ -87,7 +100,7 @@ def test_corrupted_store(self):
|
|||
self.create_src_archive('test')
|
||||
self.darc('verify', self.store_path + '::test')
|
||||
fd = open(os.path.join(self.tmpdir, 'store', 'bands', '0', '0'), 'r+')
|
||||
fd.seek(1000)
|
||||
fd.seek(100)
|
||||
fd.write('X')
|
||||
fd.close()
|
||||
self.darc('verify', self.store_path + '::test', exit_code=1)
|
||||
|
|
Loading…
Reference in a new issue