summaryrefslogtreecommitdiff
path: root/payload_consumer/delta_performer.h
blob: c54316bd37ab6237f636bc712a058b9a47b7fdfe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
//
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_DELTA_PERFORMER_H_
#define UPDATE_ENGINE_PAYLOAD_CONSUMER_DELTA_PERFORMER_H_

#include <inttypes.h>

#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <base/time/time.h>
#include <brillo/secure_blob.h>
#include <google/protobuf/repeated_field.h>
#include <gtest/gtest_prod.h>  // for FRIEND_TEST

#include "update_engine/common/hash_calculator.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/payload_consumer/file_descriptor.h"
#include "update_engine/payload_consumer/file_writer.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/payload_consumer/partition_writer.h"
#include "update_engine/payload_consumer/payload_metadata.h"
#include "update_engine/payload_consumer/payload_verifier.h"
#include "update_engine/update_metadata.pb.h"

namespace chromeos_update_engine {

class DownloadActionDelegate;
class BootControlInterface;
class HardwareInterface;
class PrefsInterface;

// This class performs the actions in a delta update synchronously. The delta
// update itself should be passed in in chunks as it is received.
class DeltaPerformer : public FileWriter {
 public:
  // Defines the granularity of progress logging in terms of how many "completed
  // chunks" we want to report at the most.
  static const unsigned kProgressLogMaxChunks;
  // Defines a timeout since the last progress was logged after which we want to
  // force another log message (even if the current chunk was not completed).
  static const unsigned kProgressLogTimeoutSeconds;
  // These define the relative weights (0-100) we give to the different work
  // components associated with an update when computing an overall progress.
  // Currently they include the download progress and the number of completed
  // operations. They must add up to one hundred (100).
  static const unsigned kProgressDownloadWeight;
  static const unsigned kProgressOperationsWeight;
  static const uint64_t kCheckpointFrequencySeconds;

  DeltaPerformer(PrefsInterface* prefs,
                 BootControlInterface* boot_control,
                 HardwareInterface* hardware,
                 DownloadActionDelegate* download_delegate,
                 InstallPlan* install_plan,
                 InstallPlan::Payload* payload,
                 bool interactive)
      : prefs_(prefs),
        boot_control_(boot_control),
        hardware_(hardware),
        download_delegate_(download_delegate),
        install_plan_(install_plan),
        payload_(payload),
        interactive_(interactive) {
    CHECK(install_plan_);
  }

  // FileWriter's Write implementation where caller doesn't care about
  // error codes.
  bool Write(const void* bytes, size_t count) override {
    ErrorCode error;
    return Write(bytes, count, &error);
  }

  // FileWriter's Write implementation that returns a more specific |error| code
  // in case of failures in Write operation.
  bool Write(const void* bytes, size_t count, ErrorCode* error) override;

  // Wrapper around close. Returns 0 on success or -errno on error.
  // Closes both 'path' given to Open() and the kernel path.
  int Close() override;

  // Open the target and source (if delta payload) file descriptors for the
  // |current_partition_|. The manifest needs to be already parsed for this to
  // work. Returns whether the required file descriptors were successfully open.
  bool OpenCurrentPartition();

  // Closes the current partition file descriptors if open. Returns 0 on success
  // or -errno on error.
  int CloseCurrentPartition();

  // Returns |true| only if the manifest has been processed and it's valid.
  bool IsManifestValid();

  // Verifies the downloaded payload against the signed hash included in the
  // payload, against the update check hash and size using the public key and
  // returns ErrorCode::kSuccess on success, an error code on failure.
  // This method should be called after closing the stream. Note this method
  // skips the signed hash check if the public key is unavailable; it returns
  // ErrorCode::kSignedDeltaPayloadExpectedError if the public key is available
  // but the delta payload doesn't include a signature.
  ErrorCode VerifyPayload(const brillo::Blob& update_check_response_hash,
                          const uint64_t update_check_response_size);

  // Converts an ordered collection of Extent objects which contain data of
  // length full_length to a comma-separated string. For each Extent, the
  // string will have the start offset and then the length in bytes.
  // The length value of the last extent in the string may be short, since
  // the full length of all extents in the string is capped to full_length.
  // Also, an extent starting at kSparseHole, appears as -1 in the string.
  // For example, if the Extents are {1, 1}, {4, 2}, {kSparseHole, 1},
  // {0, 1}, block_size is 4096, and full_length is 5 * block_size - 13,
  // the resulting string will be: "4096:4096,16384:8192,-1:4096,0:4083"
  static bool ExtentsToBsdiffPositionsString(
      const google::protobuf::RepeatedPtrField<Extent>& extents,
      uint64_t block_size,
      uint64_t full_length,
      std::string* positions_string);

  // Returns true if a previous update attempt can be continued based on the
  // persistent preferences and the new update check response hash.
  static bool CanResumeUpdate(PrefsInterface* prefs,
                              const std::string& update_check_response_hash);

  // Resets the persistent update progress state to indicate that an update
  // can't be resumed. Performs a quick update-in-progress reset if |quick| is
  // true, otherwise resets all progress-related update state.
  // If |skip_dynamic_partititon_metadata_updated| is true, do not reset
  // dynamic-partition-metadata-updated.
  // Returns true on success, false otherwise.
  static bool ResetUpdateProgress(
      PrefsInterface* prefs,
      bool quick,
      bool skip_dynamic_partititon_metadata_updated = false);

  // Attempts to parse the update metadata starting from the beginning of
  // |payload|. On success, returns kMetadataParseSuccess. Returns
  // kMetadataParseInsufficientData if more data is needed to parse the complete
  // metadata. Returns kMetadataParseError if the metadata can't be parsed given
  // the payload.
  MetadataParseResult ParsePayloadMetadata(const brillo::Blob& payload,
                                           ErrorCode* error);

  void set_public_key_path(const std::string& public_key_path) {
    public_key_path_ = public_key_path;
  }

  void set_update_certificates_path(
      const std::string& update_certificates_path) {
    update_certificates_path_ = update_certificates_path;
  }

  // Return true if header parsing is finished and no errors occurred.
  bool IsHeaderParsed() const;

  // Checkpoints the update progress into persistent storage to allow this
  // update attempt to be resumed after reboot.
  // If |force| is false, checkpoint may be throttled.
  // Exposed for testing purposes.
  bool CheckpointUpdateProgress(bool force);

  // Compare |calculated_hash| with source hash in |operation|, return false and
  // dump hash and set |error| if don't match.
  // |source_fd| is the file descriptor of the source partition.
  static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
                                 const InstallOperation& operation,
                                 const FileDescriptorPtr source_fd,
                                 ErrorCode* error);

  // Initialize partitions and allocate required space for an update with the
  // given |manifest|. |update_check_response_hash| is used to check if the
  // previous call to this function corresponds to the same payload.
  // - Same payload: not make any persistent modifications (not write to disk)
  // - Different payload: make persistent modifications (write to disk)
  // In both cases, in-memory flags are updated. This function must be called
  // on the payload at least once (to update in-memory flags) before writing
  // (applying) the payload.
  // If error due to insufficient space, |required_size| is set to the required
  // size on the device to apply the payload.
  static bool PreparePartitionsForUpdate(
      PrefsInterface* prefs,
      BootControlInterface* boot_control,
      BootControlInterface::Slot target_slot,
      const DeltaArchiveManifest& manifest,
      const std::string& update_check_response_hash,
      uint64_t* required_size);

 protected:
  // Exposed as virtual for testing purposes.
  virtual std::unique_ptr<PartitionWriter> CreatePartitionWriter(
      const PartitionUpdate& partition_update,
      const InstallPlan::Partition& install_part,
      DynamicPartitionControlInterface* dynamic_control,
      size_t block_size,
      bool is_interactive,
      bool is_dynamic_partition);

  // return true if it has been long enough and a checkpoint should be saved.
  // Exposed for unittest purposes.
  virtual bool ShouldCheckpoint();

 private:
  friend class DeltaPerformerTest;
  friend class DeltaPerformerIntegrationTest;
  FRIEND_TEST(DeltaPerformerTest, BrilloMetadataSignatureSizeTest);
  FRIEND_TEST(DeltaPerformerTest, BrilloParsePayloadMetadataTest);
  FRIEND_TEST(DeltaPerformerTest, UsePublicKeyFromResponse);

  // Obtain the operation index for current partition. If all operations for
  // current partition is are finished, return # of operations. This is mostly
  // intended to be used by CheckpointUpdateProgress, where partition writer
  // needs to know the current operation number to properly checkpoint update.
  size_t GetPartitionOperationNum();

  // Parse and move the update instructions of all partitions into our local
  // |partitions_| variable based on the version of the payload. Requires the
  // manifest to be parsed and valid.
  bool ParseManifestPartitions(ErrorCode* error);

  // Appends up to |*count_p| bytes from |*bytes_p| to |buffer_|, but only to
  // the extent that the size of |buffer_| does not exceed |max|. Advances
  // |*cbytes_p| and decreases |*count_p| by the actual number of bytes copied,
  // and returns this number.
  size_t CopyDataToBuffer(const char** bytes_p, size_t* count_p, size_t max);

  // If |op_result| is false, emits an error message using |op_type_name| and
  // sets |*error| accordingly. Otherwise does nothing. Returns |op_result|.
  bool HandleOpResult(bool op_result,
                      const char* op_type_name,
                      ErrorCode* error);

  // Logs the progress of downloading/applying an update.
  void LogProgress(const char* message_prefix);

  // Update overall progress metrics, log as necessary.
  void UpdateOverallProgress(bool force_log, const char* message_prefix);

  // Returns true if enough of the delta file has been passed via Write()
  // to be able to perform a given install operation.
  bool CanPerformInstallOperation(const InstallOperation& operation);

  // Checks the integrity of the payload manifest. Returns true upon success,
  // false otherwise.
  ErrorCode ValidateManifest();

  // Validates that the hash of the blobs corresponding to the given |operation|
  // matches what's specified in the manifest in the payload.
  // Returns ErrorCode::kSuccess on match or a suitable error code otherwise.
  ErrorCode ValidateOperationHash(const InstallOperation& operation);

  // Returns true on success.
  bool PerformInstallOperation(const InstallOperation& operation);

  // These perform a specific type of operation and return true on success.
  // |error| will be set if source hash mismatch, otherwise |error| might not be
  // set even if it fails.
  bool PerformReplaceOperation(const InstallOperation& operation);
  bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
  bool PerformSourceCopyOperation(const InstallOperation& operation,
                                  ErrorCode* error);
  bool PerformSourceBsdiffOperation(const InstallOperation& operation,
                                    ErrorCode* error);
  bool PerformPuffDiffOperation(const InstallOperation& operation,
                                ErrorCode* error);

  // Extracts the payload signature message from the current |buffer_| if the
  // offset matches the one specified by the manifest. Returns whether the
  // signature was extracted.
  bool ExtractSignatureMessage();

  // Updates the payload hash calculator with the bytes in |buffer_|, also
  // updates the signed hash calculator with the first |signed_hash_buffer_size|
  // bytes in |buffer_|. Then discard the content, ensuring that memory is being
  // deallocated. If |do_advance_offset|, advances the internal offset counter
  // accordingly.
  void DiscardBuffer(bool do_advance_offset, size_t signed_hash_buffer_size);

  // Primes the required update state. Returns true if the update state was
  // successfully initialized to a saved resume state or if the update is a new
  // update. Returns false otherwise.
  bool PrimeUpdateState();

  // Get the public key to be used to verify metadata signature or payload
  // signature. Always use |public_key_path_| if exists, otherwise if the Omaha
  // response contains a public RSA key and we're allowed to use it (e.g. if
  // we're in developer mode), decode the key from the response and store it in
  // |out_public_key|. Returns false on failures.
  bool GetPublicKey(std::string* out_public_key);

  // Creates a PayloadVerifier from the zip file containing certificates. If the
  // path to the zip file doesn't exist, falls back to use the public key.
  // Returns a tuple with the created PayloadVerifier and if we should perform
  // the verification.
  std::pair<std::unique_ptr<PayloadVerifier>, bool> CreatePayloadVerifier();

  // After install_plan_ is filled with partition names and sizes, initialize
  // metadata of partitions and map necessary devices before opening devices.
  // Also see comment for the static PreparePartitionsForUpdate().
  bool PreparePartitionsForUpdate(uint64_t* required_size);

  // Check if current manifest contains timestamp errors.
  // Return:
  // - kSuccess if update is valid.
  // - kPayloadTimestampError if downgrade is detected
  // - kDownloadManifestParseError if |new_version| has an incorrect format
  // - Other error values if the source of error is known, or kError for
  //   a generic error on the device.
  ErrorCode CheckTimestampError() const;

  // Check if partition `part_name` is a dynamic partition.
  bool IsDynamicPartition(const std::string& part_name, uint32_t slot);

  // Update Engine preference store.
  PrefsInterface* prefs_;

  // BootControl and Hardware interface references.
  BootControlInterface* boot_control_;
  HardwareInterface* hardware_;

  // The DownloadActionDelegate instance monitoring the DownloadAction, or a
  // nullptr if not used.
  DownloadActionDelegate* download_delegate_;

  // Install Plan based on Omaha Response.
  InstallPlan* install_plan_;

  // Pointer to the current payload in install_plan_.payloads.
  InstallPlan::Payload* payload_{nullptr};

  PayloadMetadata payload_metadata_;

  // Parsed manifest. Set after enough bytes to parse the manifest were
  // downloaded.
  DeltaArchiveManifest manifest_;
  bool manifest_parsed_{false};
  bool manifest_valid_{false};
  uint64_t metadata_size_{0};
  uint32_t metadata_signature_size_{0};
  uint64_t major_payload_version_{0};

  // Accumulated number of operations per partition. The i-th element is the
  // sum of the number of operations for all the partitions from 0 to i
  // inclusive. Valid when |manifest_valid_| is true.
  std::vector<size_t> acc_num_operations_;

  // The total operations in a payload. Valid when |manifest_valid_| is true,
  // otherwise 0.
  size_t num_total_operations_{0};

  // The list of partitions to update as found in the manifest major
  // version 2. When parsing an older manifest format, the information is
  // converted over to this format instead.
  std::vector<PartitionUpdate> partitions_;

  // Index in the list of partitions (|partitions_| member) of the current
  // partition being processed.
  size_t current_partition_{0};

  // Index of the next operation to perform in the manifest. The index is
  // linear on the total number of operation on the manifest.
  size_t next_operation_num_{0};

  // A buffer used for accumulating downloaded data. Initially, it stores the
  // payload metadata; once that's downloaded and parsed, it stores data for
  // the next update operation.
  brillo::Blob buffer_;
  // Offset of buffer_ in the binary blobs section of the update.
  uint64_t buffer_offset_{0};

  // Last |next_operation_num_| value updated as part of the progress update.
  uint64_t last_updated_operation_num_{std::numeric_limits<uint64_t>::max()};

  // The block size (parsed from the manifest).
  uint32_t block_size_{0};

  // Calculates the whole payload file hash, including headers and signatures.
  HashCalculator payload_hash_calculator_;

  // Calculates the hash of the portion of the payload signed by the payload
  // signature. This hash skips the metadata signature portion, located after
  // the metadata and doesn't include the payload signature itself.
  HashCalculator signed_hash_calculator_;

  // Signatures message blob extracted directly from the payload.
  std::string signatures_message_data_;

  // The public key to be used. Provided as a member so that tests can
  // override with test keys.
  std::string public_key_path_{constants::kUpdatePayloadPublicKeyPath};

  // The path to the zip file with X509 certificates.
  std::string update_certificates_path_{constants::kUpdateCertificatesPath};

  // The number of bytes received so far, used for progress tracking.
  size_t total_bytes_received_{0};

  // An overall progress counter, which should reflect both download progress
  // and the ratio of applied operations. Range is 0-100.
  unsigned overall_progress_{0};

  // The last progress chunk recorded.
  unsigned last_progress_chunk_{0};

  // If |true|, the update is user initiated (vs. periodic update checks).
  bool interactive_{false};

  // The timeout after which we should force emitting a progress log
  // (constant), and the actual point in time for the next forced log to be
  // emitted.
  const base::TimeDelta forced_progress_log_wait_{
      base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)};
  base::TimeTicks forced_progress_log_time_;

  // The frequency that we should write an update checkpoint (constant), and
  // the point in time at which the next checkpoint should be written.
  const base::TimeDelta update_checkpoint_wait_{
      base::TimeDelta::FromSeconds(kCheckpointFrequencySeconds)};
  base::TimeTicks update_checkpoint_time_;

  std::unique_ptr<PartitionWriter> partition_writer_;

  DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
};

}  // namespace chromeos_update_engine

#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_DELTA_PERFORMER_H_