1 # Copyright (C) 2010 Rob Browning
3 # This code is covered under the terms of the GNU Library General
4 # Public License as described in the bup LICENSE file.
6 # TODO: Add tar-like -C option.
8 from __future__ import absolute_import
11 from bup import metadata
12 from bup import options
13 from bup.compat import argv_bytes
14 from bup.io import byte_stream
15 from bup.helpers import log, saved_errors
19 if not name or name == b'-':
20 return byte_stream(sys.stdin)
21 return open(name, 'rb')
24 def open_output(name):
25 if not name or name == b'-':
27 return byte_stream(sys.stdout)
28 return open(name, 'wb')
32 bup meta --create [OPTION ...] <PATH ...>
33 bup meta --list [OPTION ...]
34 bup meta --extract [OPTION ...]
35 bup meta --start-extract [OPTION ...]
36 bup meta --finish-extract [OPTION ...]
37 bup meta --edit [OPTION ...] <PATH ...>
39 c,create write metadata for PATHs to stdout (or --file)
40 t,list display metadata
41 x,extract perform --start-extract followed by --finish-extract
42 start-extract build tree matching metadata provided on standard input (or --file)
43 finish-extract finish applying standard input (or --file) metadata to filesystem
44 edit alter metadata; write to stdout (or --file)
45 f,file= specify source or destination file
46 R,recurse recurse into subdirectories
47 xdev,one-file-system don't cross filesystem boundaries
48 numeric-ids apply numeric IDs (user, group, etc.) rather than names
49 symlinks handle symbolic links (default is true)
50 paths include paths in metadata (default is true)
51 set-uid= set metadata uid (via --edit)
52 set-gid= set metadata gid (via --edit)
53 set-user= set metadata user (via --edit)
54 unset-user remove metadata user (via --edit)
55 set-group= set metadata group (via --edit)
56 unset-group remove metadata group (via --edit)
57 v,verbose increase log output (can be used more than once)
58 q,quiet don't show progress meter
63 o = options.Options(optspec)
64 opt, flags, remainder = o.parse_bytes([b'--paths', b'--symlinks', b'--recurse']
67 opt.verbose = opt.verbose or 0
68 opt.quiet = opt.quiet or 0
69 metadata.verbose = opt.verbose - opt.quiet
70 opt.file = argv_bytes(opt.file) if opt.file else None
72 action_count = sum([bool(x) for x in [opt.create, opt.list, opt.extract,
73 opt.start_extract, opt.finish_extract,
76 o.fatal("bup: only one action permitted: --create --list --extract --edit")
78 o.fatal("bup: no action specified")
81 if len(remainder) < 1:
82 o.fatal("no paths specified for create")
83 output_file = open_output(opt.file)
84 metadata.save_tree(output_file,
85 [argv_bytes(r) for r in remainder],
87 write_paths=opt.paths,
88 save_symlinks=opt.symlinks,
91 if len(remainder) > 0:
92 o.fatal("cannot specify paths for --list")
93 src = open_input(opt.file)
94 metadata.display_archive(src, open_output(b'-'))
95 elif opt.start_extract:
96 if len(remainder) > 0:
97 o.fatal("cannot specify paths for --start-extract")
98 src = open_input(opt.file)
99 metadata.start_extract(src, create_symlinks=opt.symlinks)
100 elif opt.finish_extract:
101 if len(remainder) > 0:
102 o.fatal("cannot specify paths for --finish-extract")
103 src = open_input(opt.file)
104 metadata.finish_extract(src, restore_numeric_ids=opt.numeric_ids)
106 if len(remainder) > 0:
107 o.fatal("cannot specify paths for --extract")
108 src = open_input(opt.file)
109 metadata.extract(src,
110 restore_numeric_ids=opt.numeric_ids,
111 create_symlinks=opt.symlinks)
113 if len(remainder) < 1:
114 o.fatal("no paths specified for edit")
115 output_file = open_output(opt.file)
117 unset_user = False # True if --unset-user was the last relevant option.
118 unset_group = False # True if --unset-group was the last relevant option.
120 if flag[0] == '--set-user':
122 elif flag[0] == '--unset-user':
124 elif flag[0] == '--set-group':
126 elif flag[0] == '--unset-group':
129 for path in remainder:
130 f = open(argv_bytes(path), 'rb')
132 for m in metadata._ArchiveIterator(f):
133 if opt.set_uid is not None:
135 m.uid = int(opt.set_uid)
137 o.fatal("uid must be an integer")
139 if opt.set_gid is not None:
141 m.gid = int(opt.set_gid)
143 o.fatal("gid must be an integer")
147 elif opt.set_user is not None:
148 m.user = argv_bytes(opt.set_user)
152 elif opt.set_group is not None:
153 m.group = argv_bytes(opt.set_group)
161 log('WARNING: %d errors encountered.\n' % len(saved_errors))