Browse Source

xattr: use fd for get_all

when processing regular files, use a fd to query xattrs.

when the file was modified and we chunked it, we have it open anyways.

if not, we open the file once and then query xattrs, in the hope that
this is more efficient than the path based calls.

guess it is less prone to race conditions in any case.
Thomas Waldmann 7 years ago
parent
commit
113b0eabec
1 changed files with 13 additions and 5 deletions
  1. 13 5
      src/borg/archive.py

+ 13 - 5
src/borg/archive.py

@@ -947,11 +947,11 @@ class MetadataCollector:
             attrs['group'] = gid2group(st.st_gid)
             attrs['group'] = gid2group(st.st_gid)
         return attrs
         return attrs
 
 
-    def stat_ext_attrs(self, st, path):
+    def stat_ext_attrs(self, st, path, fd=None):
         attrs = {}
         attrs = {}
         bsdflags = 0
         bsdflags = 0
         with backup_io('extended stat'):
         with backup_io('extended stat'):
-            xattrs = xattr.get_all(path, follow_symlinks=False)
+            xattrs = xattr.get_all(fd or path, follow_symlinks=False)
             if not self.nobsdflags:
             if not self.nobsdflags:
                 bsdflags = get_flags(path, st)
                 bsdflags = get_flags(path, st)
             acl_get(path, attrs, st, self.numeric_owner)
             acl_get(path, attrs, st, self.numeric_owner)
@@ -961,9 +961,9 @@ class MetadataCollector:
             attrs['bsdflags'] = bsdflags
             attrs['bsdflags'] = bsdflags
         return attrs
         return attrs
 
 
-    def stat_attrs(self, st, path):
+    def stat_attrs(self, st, path, fd=None):
         attrs = self.stat_simple_attrs(st)
         attrs = self.stat_simple_attrs(st)
-        attrs.update(self.stat_ext_attrs(st, path))
+        attrs.update(self.stat_ext_attrs(st, path, fd=fd))
         return attrs
         return attrs
 
 
 
 
@@ -1119,6 +1119,7 @@ class FilesystemObjectProcessors:
 
 
     def process_file(self, path, st, cache):
     def process_file(self, path, st, cache):
         with self.create_helper(path, st, None) as (item, status, hardlinked, hardlink_master):  # no status yet
         with self.create_helper(path, st, None) as (item, status, hardlinked, hardlink_master):  # no status yet
+            md = None
             is_special_file = is_special(st.st_mode)
             is_special_file = is_special(st.st_mode)
             if not hardlinked or hardlink_master:
             if not hardlinked or hardlink_master:
                 if not is_special_file:
                 if not is_special_file:
@@ -1152,12 +1153,19 @@ class FilesystemObjectProcessors:
                         fh = Archive._open_rb(path)
                         fh = Archive._open_rb(path)
                     with os.fdopen(fh, 'rb') as fd:
                     with os.fdopen(fh, 'rb') as fd:
                         self.process_file_chunks(item, cache, self.stats, self.show_progress, backup_io_iter(self.chunker.chunkify(fd, fh)))
                         self.process_file_chunks(item, cache, self.stats, self.show_progress, backup_io_iter(self.chunker.chunkify(fd, fh)))
+                        md = self.metadata_collector.stat_attrs(st, path, fd=fh)
                     if not is_special_file:
                     if not is_special_file:
                         # we must not memorize special files, because the contents of e.g. a
                         # we must not memorize special files, because the contents of e.g. a
                         # block or char device will change without its mtime/size/inode changing.
                         # block or char device will change without its mtime/size/inode changing.
                         cache.memorize_file(path_hash, st, [c.id for c in item.chunks])
                         cache.memorize_file(path_hash, st, [c.id for c in item.chunks])
                 self.stats.nfiles += 1
                 self.stats.nfiles += 1
-            item.update(self.metadata_collector.stat_attrs(st, path))
+            if md is None:
+                fh = Archive._open_rb(path)
+                try:
+                    md = self.metadata_collector.stat_attrs(st, path, fd=fh)
+                finally:
+                    os.close(fh)
+            item.update(md)
             item.get_size(memorize=True)
             item.get_size(memorize=True)
             if is_special_file:
             if is_special_file:
                 # we processed a special file like a regular file. reflect that in mode,
                 # we processed a special file like a regular file. reflect that in mode,