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