payload_generation_config.cc revision 0497d05764dcae38e7a7419a2f25eff043172850
1//
2// Copyright (C) 2015 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/payload_generator/payload_generation_config.h"
18
19#include <base/logging.h>
20
21#include "update_engine/common/utils.h"
22#include "update_engine/payload_consumer/delta_performer.h"
23#include "update_engine/payload_generator/delta_diff_generator.h"
24#include "update_engine/payload_generator/delta_diff_utils.h"
25#include "update_engine/payload_generator/ext2_filesystem.h"
26#include "update_engine/payload_generator/raw_filesystem.h"
27
28namespace chromeos_update_engine {
29
30bool PostInstallConfig::IsEmpty() const {
31  return run == false && path.empty() && filesystem_type.empty();
32}
33
34bool PartitionConfig::ValidateExists() const {
35  TEST_AND_RETURN_FALSE(!path.empty());
36  TEST_AND_RETURN_FALSE(utils::FileExists(path.c_str()));
37  TEST_AND_RETURN_FALSE(size > 0);
38  // The requested size is within the limits of the file.
39  TEST_AND_RETURN_FALSE(static_cast<off_t>(size) <=
40                        utils::FileSize(path.c_str()));
41  return true;
42}
43
44bool PartitionConfig::OpenFilesystem() {
45  if (path.empty())
46    return true;
47  fs_interface.reset();
48  if (diff_utils::IsExtFilesystem(path)) {
49    fs_interface = Ext2Filesystem::CreateFromFile(path);
50    // TODO(deymo): The delta generator algorithm doesn't support a block size
51    // different than 4 KiB. Remove this check once that's fixed. b/26972455
52    if (fs_interface)
53      TEST_AND_RETURN_FALSE(fs_interface->GetBlockSize() == kBlockSize);
54  }
55
56  if (!fs_interface) {
57    // Fall back to a RAW filesystem.
58    TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
59    fs_interface = RawFilesystem::Create(
60      "<" + name + "-partition>",
61      kBlockSize,
62      size / kBlockSize);
63  }
64  return true;
65}
66
67bool ImageConfig::ValidateIsEmpty() const {
68  TEST_AND_RETURN_FALSE(ImageInfoIsEmpty());
69  return partitions.empty();
70}
71
72bool ImageConfig::LoadImageSize() {
73  for (PartitionConfig& part : partitions) {
74    if (part.path.empty())
75      continue;
76    part.size = utils::FileSize(part.path);
77  }
78  return true;
79}
80
81bool ImageConfig::LoadPostInstallConfig(const brillo::KeyValueStore& store) {
82  bool found_postinstall = false;
83  for (PartitionConfig& part : partitions) {
84    bool run_postinstall;
85    if (!store.GetBoolean("RUN_POSTINSTALL_" + part.name, &run_postinstall) ||
86        !run_postinstall)
87      continue;
88    found_postinstall = true;
89    part.postinstall.run = true;
90    store.GetString("POSTINSTALL_PATH_" + part.name, &part.postinstall.path);
91    store.GetString("FILESYSTEM_TYPE_" + part.name,
92                    &part.postinstall.filesystem_type);
93  }
94  if (!found_postinstall) {
95    LOG(ERROR) << "No valid postinstall config found.";
96    return false;
97  }
98  return true;
99}
100
101bool ImageConfig::ImageInfoIsEmpty() const {
102  return image_info.board().empty()
103    && image_info.key().empty()
104    && image_info.channel().empty()
105    && image_info.version().empty()
106    && image_info.build_channel().empty()
107    && image_info.build_version().empty();
108}
109
110PayloadVersion::PayloadVersion(uint64_t major_version, uint32_t minor_version) {
111  major = major_version;
112  minor = minor_version;
113}
114
115bool PayloadVersion::Validate() const {
116  TEST_AND_RETURN_FALSE(major == kChromeOSMajorPayloadVersion ||
117                        major == kBrilloMajorPayloadVersion);
118  TEST_AND_RETURN_FALSE(minor == kFullPayloadMinorVersion ||
119                        minor == kInPlaceMinorPayloadVersion ||
120                        minor == kSourceMinorPayloadVersion ||
121                        minor == kOpSrcHashMinorPayloadVersion ||
122                        minor == kImgdiffMinorPayloadVersion);
123  return true;
124}
125
126bool PayloadVersion::OperationAllowed(InstallOperation_Type operation) const {
127  switch (operation) {
128    // Full operations:
129    case InstallOperation::REPLACE:
130    case InstallOperation::REPLACE_BZ:
131      // These operations were included in the original payload format.
132      return true;
133
134    case InstallOperation::REPLACE_XZ:
135      // These operations are included in the major version used in Brillo, but
136      // can also be used with minor version 3 or newer.
137      return major == kBrilloMajorPayloadVersion ||
138             minor >= kOpSrcHashMinorPayloadVersion;
139
140    case InstallOperation::ZERO:
141    case InstallOperation::DISCARD:
142      // The implementation of these operations had a bug in earlier versions
143      // that prevents them from being used in any payload. We will enable
144      // them for delta payloads for now.
145      return minor >= kImgdiffMinorPayloadVersion;
146
147    // Delta operations:
148    case InstallOperation::MOVE:
149    case InstallOperation::BSDIFF:
150      // MOVE and BSDIFF were replaced by SOURCE_COPY and SOURCE_BSDIFF and
151      // should not be used in newer delta versions, since the idempotent checks
152      // were removed.
153      return minor == kInPlaceMinorPayloadVersion;
154
155    case InstallOperation::SOURCE_COPY:
156    case InstallOperation::SOURCE_BSDIFF:
157      return minor >= kSourceMinorPayloadVersion;
158
159    case InstallOperation::IMGDIFF:
160      return minor >= kImgdiffMinorPayloadVersion && imgdiff_allowed;
161  }
162  return false;
163}
164
165bool PayloadVersion::IsDelta() const {
166  return minor != kFullPayloadMinorVersion;
167}
168
169bool PayloadVersion::InplaceUpdate() const {
170  return minor == kInPlaceMinorPayloadVersion;
171}
172
173bool PayloadGenerationConfig::Validate() const {
174  TEST_AND_RETURN_FALSE(version.Validate());
175  TEST_AND_RETURN_FALSE(version.IsDelta() == is_delta);
176  if (is_delta) {
177    for (const PartitionConfig& part : source.partitions) {
178      if (!part.path.empty()) {
179        TEST_AND_RETURN_FALSE(part.ValidateExists());
180        TEST_AND_RETURN_FALSE(part.size % block_size == 0);
181      }
182      // Source partition should not have postinstall.
183      TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
184    }
185
186    // If new_image_info is present, old_image_info must be present.
187    TEST_AND_RETURN_FALSE(source.ImageInfoIsEmpty() ==
188                          target.ImageInfoIsEmpty());
189  } else {
190    // All the "source" image fields must be empty for full payloads.
191    TEST_AND_RETURN_FALSE(source.ValidateIsEmpty());
192  }
193
194  // In all cases, the target image must exists.
195  for (const PartitionConfig& part : target.partitions) {
196    TEST_AND_RETURN_FALSE(part.ValidateExists());
197    TEST_AND_RETURN_FALSE(part.size % block_size == 0);
198    if (version.minor == kInPlaceMinorPayloadVersion &&
199        part.name == kLegacyPartitionNameRoot)
200      TEST_AND_RETURN_FALSE(rootfs_partition_size >= part.size);
201    if (version.major == kChromeOSMajorPayloadVersion)
202      TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
203  }
204
205  TEST_AND_RETURN_FALSE(hard_chunk_size == -1 ||
206                        hard_chunk_size % block_size == 0);
207  TEST_AND_RETURN_FALSE(soft_chunk_size % block_size == 0);
208
209  TEST_AND_RETURN_FALSE(rootfs_partition_size % block_size == 0);
210
211  return true;
212}
213
214}  // namespace chromeos_update_engine
215