blob: 7ae3426eea98d4584a9fa7e425e4a0d7594f9e9b [file] [log] [blame]
Gilad Arnoldb92167f2015-07-15 16:49:00 -07001#!/usr/bin/python2
Gilad Arnold553b0ec2013-01-26 01:00:39 -08002#
Amin Hassanif94b6432018-01-26 17:39:47 -08003# Copyright (C) 2013 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Gilad Arnold553b0ec2013-01-26 01:00:39 -080017
18"""Command-line tool for checking and applying Chrome OS update payloads."""
19
Gilad Arnoldb92167f2015-07-15 16:49:00 -070020from __future__ import print_function
21
Amin Hassani52b60392017-12-19 10:53:24 -080022# pylint: disable=import-error
23import argparse
Gilad Arnold553b0ec2013-01-26 01:00:39 -080024import os
25import sys
26
Gilad Arnold553b0ec2013-01-26 01:00:39 -080027lib_dir = os.path.join(os.path.dirname(__file__), 'lib')
28if os.path.exists(lib_dir) and os.path.isdir(lib_dir):
29 sys.path.insert(1, lib_dir)
30import update_payload
31
32
33_TYPE_FULL = 'full'
34_TYPE_DELTA = 'delta'
35
36
Gilad Arnold4fbe4092013-04-17 10:00:55 -070037def ParseArguments(argv):
Gilad Arnold553b0ec2013-01-26 01:00:39 -080038 """Parse and validate command-line arguments.
39
40 Args:
Gilad Arnold4fbe4092013-04-17 10:00:55 -070041 argv: command-line arguments to parse (excluding the program name)
Gilad Arnoldb92167f2015-07-15 16:49:00 -070042
Gilad Arnold553b0ec2013-01-26 01:00:39 -080043 Returns:
Amin Hassani52b60392017-12-19 10:53:24 -080044 Returns the arguments returned by the argument parser.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080045 """
Amin Hassani52b60392017-12-19 10:53:24 -080046 parser = argparse.ArgumentParser(
47 description=('Applies a Chrome OS update PAYLOAD to src_kern and '
48 'src_root emitting dst_kern and dst_root, respectively. '
49 'src_kern and src_root are only needed for delta payloads. '
Gilad Arnold4fbe4092013-04-17 10:00:55 -070050 'When no partitions are provided, verifies the payload '
51 'integrity.'),
52 epilog=('Note: a payload may verify correctly but fail to apply, and '
53 'vice versa; this is by design and can be thought of as static '
54 'vs dynamic correctness. A payload that both verifies and '
55 'applies correctly should be safe for use by the Chrome OS '
56 'Update Engine. Use --check to verify a payload prior to '
Amin Hassani52b60392017-12-19 10:53:24 -080057 'applying it.'),
58 formatter_class=argparse.RawDescriptionHelpFormatter
59 )
Gilad Arnold553b0ec2013-01-26 01:00:39 -080060
Amin Hassani52b60392017-12-19 10:53:24 -080061 check_args = parser.add_argument_group('Checking payload integrity')
62 check_args.add_argument('-c', '--check', action='store_true', default=False,
63 help=('force payload integrity check (e.g. before '
64 'applying)'))
65 check_args.add_argument('-D', '--describe', action='store_true',
66 default=False,
67 help='Print a friendly description of the payload.')
68 check_args.add_argument('-r', '--report', metavar='FILE',
69 help="dump payload report (`-' for stdout)")
70 check_args.add_argument('-t', '--type', dest='assert_type',
71 help='assert the payload type',
72 choices=[_TYPE_FULL, _TYPE_DELTA])
73 check_args.add_argument('-z', '--block-size', metavar='NUM', default=0,
74 type=int,
75 help='assert a non-default (4096) payload block size')
76 check_args.add_argument('-u', '--allow-unhashed', action='store_true',
77 default=False, help='allow unhashed operations')
78 check_args.add_argument('-d', '--disabled_tests', default=(), metavar='',
79 help=('space separated list of tests to disable. '
80 'allowed options include: ' +
81 ', '.join(update_payload.CHECKS_TO_DISABLE)),
82 choices=update_payload.CHECKS_TO_DISABLE)
83 check_args.add_argument('-k', '--key', metavar='FILE',
84 help=('override standard key used for signature '
85 'validation'))
86 check_args.add_argument('-m', '--meta-sig', metavar='FILE',
87 help='verify metadata against its signature')
88 check_args.add_argument('-p', '--root-part-size', metavar='NUM',
89 default=0, type=int,
90 help='override rootfs partition size auto-inference')
91 check_args.add_argument('-P', '--kern-part-size', metavar='NUM',
92 default=0, type=int,
93 help='override kernel partition size auto-inference')
Gilad Arnold553b0ec2013-01-26 01:00:39 -080094
Amin Hassani52b60392017-12-19 10:53:24 -080095 apply_args = parser.add_argument_group('Applying payload')
96 # TODO(ahassani): Extent extract-bsdiff to puffdiff too.
97 apply_args.add_argument('-x', '--extract-bsdiff', action='store_true',
98 default=False,
99 help=('use temp input/output files with BSDIFF '
100 'operations (not in-place)'))
101 apply_args.add_argument('--bspatch-path', metavar='FILE',
102 help='use the specified bspatch binary')
103 apply_args.add_argument('--puffpatch-path', metavar='FILE',
104 help='use the specified puffpatch binary')
105 apply_args.add_argument('--dst_kern', metavar='FILE',
106 help='destination kernel partition file')
107 apply_args.add_argument('--dst_root', metavar='FILE',
108 help='destination root partition file')
109 apply_args.add_argument('--src_kern', metavar='FILE',
110 help='source kernel partition file')
111 apply_args.add_argument('--src_root', metavar='FILE',
112 help='source root partition file')
Gilad Arnold272a4992013-05-08 13:12:53 -0700113
Amin Hassani52b60392017-12-19 10:53:24 -0800114 parser.add_argument('payload', metavar='PAYLOAD', help='the payload file')
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800115
Gilad Arnold4fbe4092013-04-17 10:00:55 -0700116 # Parse command-line arguments.
Amin Hassani52b60392017-12-19 10:53:24 -0800117 args = parser.parse_args(argv)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700118
Gilad Arnold4fbe4092013-04-17 10:00:55 -0700119 # There are several options that imply --check.
Amin Hassani52b60392017-12-19 10:53:24 -0800120 args.check = (args.check or args.report or args.assert_type or
121 args.block_size or args.allow_unhashed or
122 args.disabled_tests or args.meta_sig or args.key or
123 args.root_part_size or args.kern_part_size)
Gilad Arnold4fbe4092013-04-17 10:00:55 -0700124
Amin Hassani52b60392017-12-19 10:53:24 -0800125 # Check the arguments, enforce payload type accordingly.
126 if (args.src_kern is None) != (args.src_root is None):
127 parser.error('--src_kern and --src_root should be given together')
128 if (args.dst_kern is None) != (args.dst_root is None):
129 parser.error('--dst_kern and --dst_root should be given together')
130
131 if args.dst_kern and args.dst_root:
132 if args.src_kern and args.src_root:
133 if args.assert_type == _TYPE_FULL:
134 parser.error('%s payload does not accept source partition arguments'
135 % _TYPE_FULL)
136 else:
137 args.assert_type = _TYPE_DELTA
138 else:
139 if args.assert_type == _TYPE_DELTA:
140 parser.error('%s payload requires source partitions arguments'
141 % _TYPE_DELTA)
142 else:
143 args.assert_type = _TYPE_FULL
144 else:
Amin Hassania5489022018-01-26 11:23:26 -0800145 # Not applying payload.
Amin Hassani52b60392017-12-19 10:53:24 -0800146 if args.extract_bsdiff:
Gilad Arnold272a4992013-05-08 13:12:53 -0700147 parser.error('--extract-bsdiff can only be used when applying payloads')
Amin Hassani52b60392017-12-19 10:53:24 -0800148 if args.bspatch_path:
Gilad Arnold21a02502013-08-22 16:59:48 -0700149 parser.error('--bspatch-path can only be used when applying payloads')
Amin Hassani52b60392017-12-19 10:53:24 -0800150 if args.puffpatch_path:
Amin Hassani6be71682017-12-01 10:46:45 -0800151 parser.error('--puffpatch-path can only be used when applying payloads')
Gilad Arnold4fbe4092013-04-17 10:00:55 -0700152
Don Garrett30027fd2013-05-01 16:56:16 -0700153 # By default, look for a metadata-signature file with a name based on the name
Gilad Arnold9b90c932013-05-22 17:12:56 -0700154 # of the payload we are checking. We only do it if check was triggered.
Amin Hassani52b60392017-12-19 10:53:24 -0800155 if args.check and not args.meta_sig:
156 default_meta_sig = args.payload + '.metadata-signature'
Don Garrett30027fd2013-05-01 16:56:16 -0700157 if os.path.isfile(default_meta_sig):
Amin Hassani52b60392017-12-19 10:53:24 -0800158 args.meta_sig = default_meta_sig
159 print('Using default metadata signature', args.meta_sig, file=sys.stderr)
Don Garrett30027fd2013-05-01 16:56:16 -0700160
Amin Hassani52b60392017-12-19 10:53:24 -0800161 return args
Gilad Arnold4fbe4092013-04-17 10:00:55 -0700162
163
164def main(argv):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800165 # Parse and validate arguments.
Amin Hassani52b60392017-12-19 10:53:24 -0800166 args = ParseArguments(argv[1:])
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800167
Amin Hassani52b60392017-12-19 10:53:24 -0800168 with open(args.payload) as payload_file:
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800169 payload = update_payload.Payload(payload_file)
170 try:
171 # Initialize payload.
172 payload.Init()
173
Amin Hassani52b60392017-12-19 10:53:24 -0800174 if args.describe:
Don Garrett432d6012013-05-10 15:01:36 -0700175 payload.Describe()
176
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800177 # Perform payload integrity checks.
Amin Hassani52b60392017-12-19 10:53:24 -0800178 if args.check:
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800179 report_file = None
180 do_close_report_file = False
Gilad Arnold7a7edfd2013-05-22 17:21:58 -0700181 metadata_sig_file = None
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800182 try:
Amin Hassani52b60392017-12-19 10:53:24 -0800183 if args.report:
184 if args.report == '-':
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800185 report_file = sys.stdout
186 else:
Amin Hassani52b60392017-12-19 10:53:24 -0800187 report_file = open(args.report, 'w')
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800188 do_close_report_file = True
Gilad Arnold03959b72013-05-07 17:08:18 -0700189
Amin Hassani52b60392017-12-19 10:53:24 -0800190 metadata_sig_file = args.meta_sig and open(args.meta_sig)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800191 payload.Check(
Amin Hassani52b60392017-12-19 10:53:24 -0800192 pubkey_file_name=args.key,
Gilad Arnold4f8c17c2013-05-04 22:57:45 -0700193 metadata_sig_file=metadata_sig_file,
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800194 report_out_file=report_file,
Amin Hassani52b60392017-12-19 10:53:24 -0800195 assert_type=args.assert_type,
196 block_size=int(args.block_size),
197 rootfs_part_size=args.root_part_size,
198 kernel_part_size=args.kern_part_size,
199 allow_unhashed=args.allow_unhashed,
200 disabled_tests=args.disabled_tests)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800201 finally:
Gilad Arnold7a7edfd2013-05-22 17:21:58 -0700202 if metadata_sig_file:
203 metadata_sig_file.close()
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800204 if do_close_report_file:
205 report_file.close()
206
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800207 # Apply payload.
Amin Hassani52b60392017-12-19 10:53:24 -0800208 if args.dst_root or args.dst_kern:
209 dargs = {'bsdiff_in_place': not args.extract_bsdiff}
210 if args.bspatch_path:
211 dargs['bspatch_path'] = args.bspatch_path
212 if args.puffpatch_path:
213 dargs['puffpatch_path'] = args.puffpatch_path
214 if args.assert_type == _TYPE_DELTA:
215 dargs['old_kernel_part'] = args.src_kern
216 dargs['old_rootfs_part'] = args.src_root
Gilad Arnold272a4992013-05-08 13:12:53 -0700217
Amin Hassani52b60392017-12-19 10:53:24 -0800218 payload.Apply(args.dst_kern, args.dst_root, **dargs)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800219
220 except update_payload.PayloadError, e:
221 sys.stderr.write('Error: %s\n' % e)
222 return 1
223
224 return 0
225
226
227if __name__ == '__main__':
228 sys.exit(main(sys.argv))