]> arthur.barton.de Git - bup.git/blob - cmd/meta-cmd.py
README: add 0.30.x to CI status table
[bup.git] / cmd / meta-cmd.py
1 #!/bin/sh
2 """": # -*-python-*-
3 bup_python="$(dirname "$0")/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
5 """
6 # end of bup preamble
7
8 # Copyright (C) 2010 Rob Browning
9 #
10 # This code is covered under the terms of the GNU Library General
11 # Public License as described in the bup LICENSE file.
12
13 # TODO: Add tar-like -C option.
14
15 from __future__ import absolute_import
16 import sys
17 from bup import metadata
18 from bup import options
19 from bup.compat import argv_bytes
20 from bup.io import byte_stream
21 from bup.helpers import handle_ctrl_c, log, saved_errors
22
23
24 def open_input(name):
25     if not name or name == b'-':
26         return byte_stream(sys.stdin)
27     return open(name, 'rb')
28
29
30 def open_output(name):
31     if not name or name == b'-':
32         sys.stdout.flush()
33         return byte_stream(sys.stdout)
34     return open(name, 'wb')
35
36
37 optspec = """
38 bup meta --create [OPTION ...] <PATH ...>
39 bup meta --list [OPTION ...]
40 bup meta --extract [OPTION ...]
41 bup meta --start-extract [OPTION ...]
42 bup meta --finish-extract [OPTION ...]
43 bup meta --edit [OPTION ...] <PATH ...>
44 --
45 c,create       write metadata for PATHs to stdout (or --file)
46 t,list         display metadata
47 x,extract      perform --start-extract followed by --finish-extract
48 start-extract  build tree matching metadata provided on standard input (or --file)
49 finish-extract finish applying standard input (or --file) metadata to filesystem
50 edit           alter metadata; write to stdout (or --file)
51 f,file=        specify source or destination file
52 R,recurse      recurse into subdirectories
53 xdev,one-file-system  don't cross filesystem boundaries
54 numeric-ids    apply numeric IDs (user, group, etc.) rather than names
55 symlinks       handle symbolic links (default is true)
56 paths          include paths in metadata (default is true)
57 set-uid=       set metadata uid (via --edit)
58 set-gid=       set metadata gid (via --edit)
59 set-user=      set metadata user (via --edit)
60 unset-user     remove metadata user (via --edit)
61 set-group=     set metadata group (via --edit)
62 unset-group    remove metadata group (via --edit)
63 v,verbose      increase log output (can be used more than once)
64 q,quiet        don't show progress meter
65 """
66
67 handle_ctrl_c()
68
69 o = options.Options(optspec)
70 (opt, flags, remainder) = o.parse(['--paths', '--symlinks', '--recurse']
71                                   + sys.argv[1:])
72
73 opt.verbose = opt.verbose or 0
74 opt.quiet = opt.quiet or 0
75 metadata.verbose = opt.verbose - opt.quiet
76 opt.file = argv_bytes(opt.file) if opt.file else None
77
78 action_count = sum([bool(x) for x in [opt.create, opt.list, opt.extract,
79                                       opt.start_extract, opt.finish_extract,
80                                       opt.edit]])
81 if action_count > 1:
82     o.fatal("bup: only one action permitted: --create --list --extract --edit")
83 if action_count == 0:
84     o.fatal("bup: no action specified")
85
86 if opt.create:
87     if len(remainder) < 1:
88         o.fatal("no paths specified for create")
89     output_file = open_output(opt.file)
90     metadata.save_tree(output_file,
91                        [argv_bytes(r) for r in remainder],
92                        recurse=opt.recurse,
93                        write_paths=opt.paths,
94                        save_symlinks=opt.symlinks,
95                        xdev=opt.xdev)
96 elif opt.list:
97     if len(remainder) > 0:
98         o.fatal("cannot specify paths for --list")
99     src = open_input(opt.file)
100     metadata.display_archive(src, open_output(b'-'))
101 elif opt.start_extract:
102     if len(remainder) > 0:
103         o.fatal("cannot specify paths for --start-extract")
104     src = open_input(opt.file)
105     metadata.start_extract(src, create_symlinks=opt.symlinks)
106 elif opt.finish_extract:
107     if len(remainder) > 0:
108         o.fatal("cannot specify paths for --finish-extract")
109     src = open_input(opt.file)
110     metadata.finish_extract(src, restore_numeric_ids=opt.numeric_ids)
111 elif opt.extract:
112     if len(remainder) > 0:
113         o.fatal("cannot specify paths for --extract")
114     src = open_input(opt.file)
115     metadata.extract(src,
116                      restore_numeric_ids=opt.numeric_ids,
117                      create_symlinks=opt.symlinks)
118 elif opt.edit:
119     if len(remainder) < 1:
120         o.fatal("no paths specified for edit")
121     output_file = open_output(opt.file)
122
123     unset_user = False # True if --unset-user was the last relevant option.
124     unset_group = False # True if --unset-group was the last relevant option.
125     for flag in flags:
126         if flag[0] == '--set-user':
127             unset_user = False
128         elif flag[0] == '--unset-user':
129             unset_user = True
130         elif flag[0] == '--set-group':
131             unset_group = False
132         elif flag[0] == '--unset-group':
133             unset_group = True
134
135     for path in remainder:
136         f = open(argv_bytes(path), 'rb')
137         try:
138             for m in metadata._ArchiveIterator(f):
139                 if opt.set_uid is not None:
140                     try:
141                         m.uid = int(opt.set_uid)
142                     except ValueError:
143                         o.fatal("uid must be an integer")
144
145                 if opt.set_gid is not None:
146                     try:
147                         m.gid = int(opt.set_gid)
148                     except ValueError:
149                         o.fatal("gid must be an integer")
150
151                 if unset_user:
152                     m.user = b''
153                 elif opt.set_user is not None:
154                     m.user = argv_bytes(opt.set_user)
155
156                 if unset_group:
157                     m.group = b''
158                 elif opt.set_group is not None:
159                     m.group = argv_bytes(opt.set_group)
160
161                 m.write(output_file)
162         finally:
163             f.close()
164
165
166 if saved_errors:
167     log('WARNING: %d errors encountered.\n' % len(saved_errors))
168     sys.exit(1)
169 else:
170     sys.exit(0)