From 7ae125f042d6705390b627b5116ec67394028dd9 Mon Sep 17 00:00:00 2001 From: Rob Browning Date: Sun, 7 Aug 2011 18:01:08 +0100 Subject: [PATCH] Make "bup meta -tvv" output identical to "bup xstat". Move the bits of xstat-cmd.py that generate the detailed metadata representation to metadata.py and xstat.py to support their use from both "bup xstat" and "bup meta -tvv". Add size to detailed metadata description when available. Signed-off-by: Rob Browning Reviewed-by: Zoran Zaric --- cmd/xstat-cmd.py | 86 ++++--------------------------------- lib/bup/metadata.py | 101 +++++++++++++++++++++++++++++++++++++++++--- lib/bup/xstat.py | 10 +++++ t/test-meta.sh | 2 +- 4 files changed, 116 insertions(+), 83 deletions(-) diff --git a/cmd/xstat-cmd.py b/cmd/xstat-cmd.py index edd01f6..11b0b58 100755 --- a/cmd/xstat-cmd.py +++ b/cmd/xstat-cmd.py @@ -7,17 +7,6 @@ import sys, stat, errno from bup import metadata, options, xstat from bup.helpers import handle_ctrl_c, saved_errors, add_error, log - -def fstimestr(fstime): - (s, ns) = xstat.fstime_to_timespec(fstime) - if(s < 0): - s += 1 - if ns == 0: - return '%d' % s - else: - return '%d.%09d' % (s, ns) - - optspec = """ bup xstat pathinfo [OPTION ...] -- @@ -28,21 +17,7 @@ include-fields= include comma-separated fields (definitive if first) """ target_filename = '' -all_fields = frozenset(['path', - 'mode', - 'link-target', - 'rdev', - 'uid', - 'gid', - 'owner', - 'group', - 'atime', - 'mtime', - 'ctime', - 'linux-attr', - 'linux-xattr', - 'posix1e-acl']) -active_fields = all_fields +active_fields = metadata.all_fields handle_ctrl_c() @@ -54,14 +29,14 @@ for flag, value in flags: if flag == '--exclude-fields': exclude_fields = frozenset(value.split(',')) for f in exclude_fields: - if not f in all_fields: + if not f in metadata.all_fields: o.fatal(f + ' is not a valid field name') active_fields = active_fields - exclude_fields treat_include_fields_as_definitive = False elif flag == '--include-fields': include_fields = frozenset(value.split(',')) for f in include_fields: - if not f in all_fields: + if not f in metadata.all_fields: o.fatal(f + ' is not a valid field name') if treat_include_fields_as_definitive: active_fields = include_fields @@ -73,6 +48,7 @@ opt.verbose = opt.verbose or 0 opt.quiet = opt.quiet or 0 metadata.verbose = opt.verbose - opt.quiet +first_path = True for path in remainder: try: m = metadata.from_path(path, archive_path = path) @@ -82,55 +58,11 @@ for path in remainder: continue else: raise - if 'path' in active_fields: - print 'path:', m.path - if 'mode' in active_fields: - print 'mode: %s (%s)' % (oct(m.mode), - xstat.mode_str(m.mode)) - if 'link-target' in active_fields and stat.S_ISLNK(m.mode): - print 'link-target:', m.symlink_target - if 'rdev' in active_fields: - print 'rdev:', m.rdev - if 'uid' in active_fields: - print 'uid:', m.uid - if 'gid' in active_fields: - print 'gid:', m.gid - if 'owner' in active_fields: - print 'owner:', m.owner - if 'group' in active_fields: - print 'group:', m.group - if 'atime' in active_fields: - # If we don't have utimensat, that means we have to use - # utime(), and utime() has no way to set the mtime/atime of a - # symlink. Thus, the mtime/atime of a symlink is meaningless, - # so let's not report it. (That way scripts comparing - # before/after won't trigger.) - if xstat.lutime or not stat.S_ISLNK(m.mode): - print 'atime: ' + fstimestr(m.atime) - else: - print 'atime: 0' - if 'mtime' in active_fields: - if xstat.lutime or not stat.S_ISLNK(m.mode): - print 'mtime: ' + fstimestr(m.mtime) - else: - print 'mtime: 0' - if 'ctime' in active_fields: - print 'ctime: ' + fstimestr(m.ctime) - if 'linux-attr' in active_fields and m.linux_attr: - print 'linux-attr:', hex(m.linux_attr) - if 'linux-xattr' in active_fields and m.linux_xattr: - for name, value in m.linux_xattr: - print 'linux-xattr: %s -> %s' % (name, repr(value)) - if 'posix1e-acl' in active_fields and m.posix1e_acl and metadata.posix1e: - flags = metadata.posix1e.TEXT_ABBREVIATE - if stat.S_ISDIR(m.mode): - acl = m.posix1e_acl[0] - default_acl = m.posix1e_acl[2] - print acl.to_any_text('posix1e-acl: ', '\n', flags) - print acl.to_any_text('posix1e-acl-default: ', '\n', flags) - else: - acl = m.posix1e_acl[0] - print acl.to_any_text('posix1e-acl: ', '\n', flags) + if metadata.verbose >= 0: + if not first_path: + print + print metadata.detailed_str(m, active_fields) + first_path = False if saved_errors: log('WARNING: %d errors encountered.\n' % len(saved_errors)) diff --git a/lib/bup/metadata.py b/lib/bup/metadata.py index c76a25f..4f9e162 100644 --- a/lib/bup/metadata.py +++ b/lib/bup/metadata.py @@ -4,7 +4,7 @@ # # This code is covered under the terms of the GNU Library General # Public License as described in the bup LICENSE file. -import errno, os, sys, stat, pwd, grp, struct, re +import errno, os, sys, stat, time, pwd, grp, struct, re from cStringIO import StringIO from bup import vint, xstat from bup.drecurse import recursive_dirlist @@ -690,6 +690,88 @@ def _set_up_path(meta, create_symlinks=True): meta.create_path(meta.path, create_symlinks=create_symlinks) +all_fields = frozenset(['path', + 'mode', + 'link-target', + 'rdev', + 'size', + 'uid', + 'gid', + 'owner', + 'group', + 'atime', + 'mtime', + 'ctime', + 'linux-attr', + 'linux-xattr', + 'posix1e-acl']) + + +def detailed_str(meta, fields = None): + # FIXME: should optional fields be omitted, or empty i.e. "rdev: + # 0", "link-target:", etc. + if not fields: + fields = all_fields + + result = [] + if 'path' in fields: + result.append('path: ' + meta.path) + if 'mode' in fields: + result.append('mode: %s (%s)' % (oct(meta.mode), + xstat.mode_str(meta.mode))) + if 'link-target' in fields and stat.S_ISLNK(meta.mode): + result.append('link-target: ' + meta.symlink_target) + if 'rdev' in fields: + if meta.rdev: + result.append('rdev: %d,%d' % (os.major(meta.rdev), + os.minor(meta.rdev))) + else: + result.append('rdev: 0') + if 'size' in fields and meta.size: + result.append('size: ' + str(meta.size)) + if 'uid' in fields: + result.append('uid: ' + str(meta.uid)) + if 'gid' in fields: + result.append('gid: ' + str(meta.gid)) + if 'owner' in fields: + result.append('owner: ' + meta.owner) + if 'group' in fields: + result.append('group: ' + meta.group) + if 'atime' in fields: + # If we don't have xstat.lutime, that means we have to use + # utime(), and utime() has no way to set the mtime/atime of a + # symlink. Thus, the mtime/atime of a symlink is meaningless, + # so let's not report it. (That way scripts comparing + # before/after won't trigger.) + if xstat.lutime or not stat.S_ISLNK(meta.mode): + result.append('atime: ' + xstat.fstime_to_sec_str(meta.atime)) + else: + result.append('atime: 0') + if 'mtime' in fields: + if xstat.lutime or not stat.S_ISLNK(meta.mode): + result.append('mtime: ' + xstat.fstime_to_sec_str(meta.mtime)) + else: + result.append('mtime: 0') + if 'ctime' in fields: + result.append('ctime: ' + xstat.fstime_to_sec_str(meta.ctime)) + if 'linux-attr' in fields and meta.linux_attr: + result.append('linux-attr: ' + hex(meta.linux_attr)) + if 'linux-xattr' in fields and meta.linux_xattr: + for name, value in meta.linux_xattr: + result.append('linux-xattr: %s -> %s' % (name, repr(value))) + if 'posix1e-acl' in fields and meta.posix1e_acl and posix1e: + flags = posix1e.TEXT_ABBREVIATE + if stat.S_ISDIR(meta.mode): + acl = meta.posix1e_acl[0] + default_acl = meta.posix1e_acl[2] + result.append(acl.to_any_text('posix1e-acl: ', '\n', flags)) + result.append(acl.to_any_text('posix1e-acl-default: ', '\n', flags)) + else: + acl = meta.posix1e_acl[0] + result.append(acl.to_any_text('posix1e-acl: ', '\n', flags)) + return '\n'.join(result) + + class _ArchiveIterator: def next(self): try: @@ -705,10 +787,19 @@ class _ArchiveIterator: def display_archive(file): - for meta in _ArchiveIterator(file): - if verbose: - print meta.path # FIXME - else: + if verbose > 1: + first_item = True + for meta in _ArchiveIterator(file): + if not first_item: + print + print detailed_str(meta) + first_item = False + elif verbose >= 0: + for meta in _ArchiveIterator(file): + if not meta.path: + print >> sys.stderr, \ + 'bup: cannot list path for metadata without path' + sys.exit(1) print meta.path diff --git a/lib/bup/xstat.py b/lib/bup/xstat.py index 7fcbe8a..be6f062 100644 --- a/lib/bup/xstat.py +++ b/lib/bup/xstat.py @@ -38,6 +38,16 @@ def fstime_to_timespec(ns): return nsecs_to_timespec(ns) +def fstime_to_sec_str(fstime): + (s, ns) = fstime_to_timespec(fstime) + if(s < 0): + s += 1 + if ns == 0: + return '%d' % s + else: + return '%d.%09d' % (s, ns) + + if _have_bup_utime_ns: def utime(path, times): """Times must be provided as (atime_ns, mtime_ns).""" diff --git a/t/test-meta.sh b/t/test-meta.sh index 5ce48d5..e94a85b 100755 --- a/t/test-meta.sh +++ b/t/test-meta.sh @@ -20,7 +20,7 @@ genstat() ( export PATH="$TOP:$PATH" # pick up bup # Skip atime (test elsewhere) to avoid the observer effect. - find . | sort | xargs bup xstat --exclude-fields ctime,atime + find . | sort | xargs bup xstat --exclude-fields ctime,atime,size ) } -- 2.39.2