payload_generation_config.cc revision 7d2800522c9cbc134c13dc920119d5eb1aa2acb9
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/mapfile_filesystem.h"
27#include "update_engine/payload_generator/raw_filesystem.h"
28
29namespace chromeos_update_engine {
30
31bool PostInstallConfig::IsEmpty() const {
32  return !run && path.empty() && filesystem_type.empty() && !optional;
33}
34
35bool PartitionConfig::ValidateExists() const {
36  TEST_AND_RETURN_FALSE(!path.empty());
37  TEST_AND_RETURN_FALSE(utils::FileExists(path.c_str()));
38  TEST_AND_RETURN_FALSE(size > 0);
39  // The requested size is within the limits of the file.
40  TEST_AND_RETURN_FALSE(static_cast<off_t>(size) <=
41                        utils::FileSize(path.c_str()));
42  return true;
43}
44
45bool PartitionConfig::OpenFilesystem() {
46  if (path.empty())
47    return true;
48  fs_interface.reset();
49  if (diff_utils::IsExtFilesystem(path)) {
50    fs_interface = Ext2Filesystem::CreateFromFile(path);
51    // TODO(deymo): The delta generator algorithm doesn't support a block size
52    // different than 4 KiB. Remove this check once that's fixed. b/26972455
53    if (fs_interface) {
54      TEST_AND_RETURN_FALSE(fs_interface->GetBlockSize() == kBlockSize);
55      return true;
56    }
57  }
58
59  if (!mapfile_path.empty()) {
60    fs_interface = MapfileFilesystem::CreateFromFile(path, mapfile_path);
61    if (fs_interface) {
62      TEST_AND_RETURN_FALSE(fs_interface->GetBlockSize() == kBlockSize);
63      return true;
64    }
65  }
66
67  // Fall back to a RAW filesystem.
68  TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
69  fs_interface = RawFilesystem::Create(
70      "<" + name + "-partition>", kBlockSize, size / kBlockSize);
71  return true;
72}
73
74bool ImageConfig::ValidateIsEmpty() const {
75  TEST_AND_RETURN_FALSE(ImageInfoIsEmpty());
76  return partitions.empty();
77}
78
79bool ImageConfig::LoadImageSize() {
80  for (PartitionConfig& part : partitions) {
81    if (part.path.empty())
82      continue;
83    part.size = utils::FileSize(part.path);
84  }
85  return true;
86}
87
88bool ImageConfig::LoadPostInstallConfig(const brillo::KeyValueStore& store) {
89  bool found_postinstall = false;
90  for (PartitionConfig& part : partitions) {
91    bool run_postinstall;
92    if (!store.GetBoolean("RUN_POSTINSTALL_" + part.name, &run_postinstall) ||
93        !run_postinstall)
94      continue;
95    found_postinstall = true;
96    part.postinstall.run = true;
97    store.GetString("POSTINSTALL_PATH_" + part.name, &part.postinstall.path);
98    store.GetString("FILESYSTEM_TYPE_" + part.name,
99                    &part.postinstall.filesystem_type);
100    store.GetBoolean("POSTINSTALL_OPTIONAL_" + part.name,
101                     &part.postinstall.optional);
102  }
103  if (!found_postinstall) {
104    LOG(ERROR) << "No valid postinstall config found.";
105    return false;
106  }
107  return true;
108}
109
110bool ImageConfig::ImageInfoIsEmpty() const {
111  return image_info.board().empty()
112    && image_info.key().empty()
113    && image_info.channel().empty()
114    && image_info.version().empty()
115    && image_info.build_channel().empty()
116    && image_info.build_version().empty();
117}
118
119PayloadVersion::PayloadVersion(uint64_t major_version, uint32_t minor_version) {
120  major = major_version;
121  minor = minor_version;
122}
123
124bool PayloadVersion::Validate() const {
125  TEST_AND_RETURN_FALSE(major == kChromeOSMajorPayloadVersion ||
126                        major == kBrilloMajorPayloadVersion);
127  TEST_AND_RETURN_FALSE(minor == kFullPayloadMinorVersion ||
128                        minor == kInPlaceMinorPayloadVersion ||
129                        minor == kSourceMinorPayloadVersion ||
130                        minor == kOpSrcHashMinorPayloadVersion ||
131                        minor == kPuffdiffMinorPayloadVersion);
132  return true;
133}
134
135bool PayloadVersion::OperationAllowed(InstallOperation_Type operation) const {
136  switch (operation) {
137    // Full operations:
138    case InstallOperation::REPLACE:
139    case InstallOperation::REPLACE_BZ:
140      // These operations were included in the original payload format.
141      return true;
142
143    case InstallOperation::REPLACE_XZ:
144      // These operations are included in the major version used in Brillo, but
145      // can also be used with minor version 3 or newer.
146      return major == kBrilloMajorPayloadVersion ||
147             minor >= kOpSrcHashMinorPayloadVersion;
148
149    case InstallOperation::ZERO:
150    case InstallOperation::DISCARD:
151      // The implementation of these operations had a bug in earlier versions
152      // that prevents them from being used in any payload. We will enable
153      // them for delta payloads for now.
154      return minor >= kPuffdiffMinorPayloadVersion;
155
156    // Delta operations:
157    case InstallOperation::MOVE:
158    case InstallOperation::BSDIFF:
159      // MOVE and BSDIFF were replaced by SOURCE_COPY and SOURCE_BSDIFF and
160      // should not be used in newer delta versions, since the idempotent checks
161      // were removed.
162      return minor == kInPlaceMinorPayloadVersion;
163
164    case InstallOperation::SOURCE_COPY:
165    case InstallOperation::SOURCE_BSDIFF:
166      return minor >= kSourceMinorPayloadVersion;
167
168    case InstallOperation::BROTLI_BSDIFF:
169      return minor >= kPuffdiffMinorPayloadVersion;
170    // TODO(*) Revert the disablement of puffdiff after we fix b/72815313.
171    case InstallOperation::PUFFDIFF:
172      return false;
173  }
174  return false;
175}
176
177bool PayloadVersion::IsDelta() const {
178  return minor != kFullPayloadMinorVersion;
179}
180
181bool PayloadVersion::InplaceUpdate() const {
182  return minor == kInPlaceMinorPayloadVersion;
183}
184
185bool PayloadGenerationConfig::Validate() const {
186  TEST_AND_RETURN_FALSE(version.Validate());
187  TEST_AND_RETURN_FALSE(version.IsDelta() == is_delta);
188  if (is_delta) {
189    for (const PartitionConfig& part : source.partitions) {
190      if (!part.path.empty()) {
191        TEST_AND_RETURN_FALSE(part.ValidateExists());
192        TEST_AND_RETURN_FALSE(part.size % block_size == 0);
193      }
194      // Source partition should not have postinstall.
195      TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
196    }
197
198    // If new_image_info is present, old_image_info must be present.
199    TEST_AND_RETURN_FALSE(source.ImageInfoIsEmpty() ==
200                          target.ImageInfoIsEmpty());
201  } else {
202    // All the "source" image fields must be empty for full payloads.
203    TEST_AND_RETURN_FALSE(source.ValidateIsEmpty());
204  }
205
206  // In all cases, the target image must exists.
207  for (const PartitionConfig& part : target.partitions) {
208    TEST_AND_RETURN_FALSE(part.ValidateExists());
209    TEST_AND_RETURN_FALSE(part.size % block_size == 0);
210    if (version.minor == kInPlaceMinorPayloadVersion &&
211        part.name == kLegacyPartitionNameRoot)
212      TEST_AND_RETURN_FALSE(rootfs_partition_size >= part.size);
213    if (version.major == kChromeOSMajorPayloadVersion)
214      TEST_AND_RETURN_FALSE(part.postinstall.IsEmpty());
215  }
216
217  TEST_AND_RETURN_FALSE(hard_chunk_size == -1 ||
218                        hard_chunk_size % block_size == 0);
219  TEST_AND_RETURN_FALSE(soft_chunk_size % block_size == 0);
220
221  TEST_AND_RETURN_FALSE(rootfs_partition_size % block_size == 0);
222
223  return true;
224}
225
226}  // namespace chromeos_update_engine
227