blob: 9dd3fa4d2332949615be4ca977af0b8b3c70157b [file] [log] [blame]
Amin Hassani924183b2017-09-27 14:50:59 -07001//
2// Copyright (C) 2017 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/deflate_utils.h"
18
19#include <algorithm>
20#include <string>
21#include <utility>
22
23#include <base/files/file_util.h>
24#include <base/logging.h>
25#include <base/strings/string_util.h>
26
27#include "update_engine/common/utils.h"
28#include "update_engine/payload_generator/delta_diff_generator.h"
29#include "update_engine/payload_generator/extent_ranges.h"
30#include "update_engine/payload_generator/extent_utils.h"
31#include "update_engine/payload_generator/squashfs_filesystem.h"
32#include "update_engine/update_metadata.pb.h"
33
34using std::string;
35using std::vector;
36
37namespace chromeos_update_engine {
38namespace deflate_utils {
39namespace {
40
41// The minimum size for a squashfs image to be processed.
42const uint64_t kMinimumSquashfsImageSize = 1 * 1024 * 1024; // bytes
43
44// TODO(*): Optimize this so we don't have to read all extents into memory in
45// case it is large.
46bool CopyExtentsToFile(const string& in_path,
47 const vector<Extent> extents,
48 const string& out_path,
49 size_t block_size) {
50 brillo::Blob data(BlocksInExtents(extents) * block_size);
51 TEST_AND_RETURN_FALSE(
52 utils::ReadExtents(in_path, extents, &data, data.size(), block_size));
53 TEST_AND_RETURN_FALSE(
54 utils::WriteFile(out_path.c_str(), data.data(), data.size()));
55 return true;
56}
57
58bool IsSquashfsImage(const string& part_path,
59 const FilesystemInterface::File& file) {
60 // Only check for files with img postfix.
61 if (base::EndsWith(file.name, ".img", base::CompareCase::SENSITIVE) &&
62 BlocksInExtents(file.extents) >= kMinimumSquashfsImageSize / kBlockSize) {
63 brillo::Blob super_block;
64 TEST_AND_RETURN_FALSE(
65 utils::ReadFileChunk(part_path,
66 file.extents[0].start_block() * kBlockSize,
67 100,
68 &super_block));
69 return SquashfsFilesystem::IsSquashfsImage(super_block);
70 }
71 return false;
72}
73
74// Realigns subfiles |files| of a splitted file |file| into its correct
75// positions. This can be used for squashfs, zip, apk, etc.
76bool RealignSplittedFiles(const FilesystemInterface::File& file,
77 vector<FilesystemInterface::File>* files) {
78 // We have to shift all the Extents in |files|, based on the Extents of the
79 // |file| itself.
80 size_t num_blocks = 0;
81 for (auto& in_file : *files) { // We need to modify so no constant.
82 TEST_AND_RETURN_FALSE(
83 ShiftExtentsOverExtents(file.extents, &in_file.extents));
84 in_file.name = file.name + "/" + in_file.name;
85 num_blocks += BlocksInExtents(in_file.extents);
86 }
87
88 // Check that all files in |in_files| cover the entire image.
89 TEST_AND_RETURN_FALSE(BlocksInExtents(file.extents) == num_blocks);
90 return true;
91}
92
93} // namespace
94
95bool ShiftExtentsOverExtents(const vector<Extent>& base_extents,
96 vector<Extent>* over_extents) {
97 if (BlocksInExtents(base_extents) < BlocksInExtents(*over_extents)) {
98 LOG(ERROR) << "over_extents have more blocks than base_extents! Invalid!";
99 return false;
100 }
101 for (size_t idx = 0; idx < over_extents->size(); idx++) {
102 auto over_ext = &over_extents->at(idx);
103 auto gap_blocks = base_extents[0].start_block();
104 auto last_end_block = base_extents[0].start_block();
105 for (auto base_ext : base_extents) { // We need to modify |base_ext|, so we
106 // use copy.
107 gap_blocks += base_ext.start_block() - last_end_block;
108 last_end_block = base_ext.start_block() + base_ext.num_blocks();
109 base_ext.set_start_block(base_ext.start_block() - gap_blocks);
110 if (over_ext->start_block() >= base_ext.start_block() &&
111 over_ext->start_block() <
112 base_ext.start_block() + base_ext.num_blocks()) {
113 if (over_ext->start_block() + over_ext->num_blocks() <=
114 base_ext.start_block() + base_ext.num_blocks()) {
115 // |over_ext| is inside |base_ext|, increase its start block.
116 over_ext->set_start_block(over_ext->start_block() + gap_blocks);
117 } else {
118 // |over_ext| spills over this |base_ext|, split it into two.
119 auto new_blocks = base_ext.start_block() + base_ext.num_blocks() -
120 over_ext->start_block();
121 vector<Extent> new_extents = {
122 ExtentForRange(gap_blocks + over_ext->start_block(), new_blocks),
123 ExtentForRange(over_ext->start_block() + new_blocks,
124 over_ext->num_blocks() - new_blocks)};
125 *over_ext = new_extents[0];
126 over_extents->insert(std::next(over_extents->begin(), idx + 1),
127 new_extents[1]);
128 }
129 break; // We processed |over_ext|, so break the loop;
130 }
131 }
132 }
133 return true;
134}
135
136bool PreprocessParitionFiles(const PartitionConfig& part,
137 vector<FilesystemInterface::File>* result_files) {
138 // Get the file system files.
139 vector<FilesystemInterface::File> tmp_files;
140 part.fs_interface->GetFiles(&tmp_files);
141 result_files->reserve(tmp_files.size());
142
143 for (const auto& file : tmp_files) {
144 if (IsSquashfsImage(part.path, file)) {
145 // Read the image into a file.
146 base::FilePath path;
147 TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&path));
148 ScopedPathUnlinker old_unlinker(path.value());
149 TEST_AND_RETURN_FALSE(
150 CopyExtentsToFile(part.path, file.extents, path.value(), kBlockSize));
151 // Test if it is actually a Squashfs file.
152 auto sqfs = SquashfsFilesystem::CreateFromFile(path.value());
153 if (sqfs) {
154 // It is an squashfs file. Get its files to replace with itself.
155 vector<FilesystemInterface::File> files;
156 sqfs->GetFiles(&files);
157
158 // Replace squashfs file with its files only if |files| has at
159 // least two files.
160 if (files.size() > 1) {
161 TEST_AND_RETURN_FALSE(RealignSplittedFiles(file, &files));
162 result_files->insert(result_files->end(), files.begin(), files.end());
163 continue;
164 }
165 } else {
166 LOG(WARNING) << "We thought file: " << file.name
167 << " was a Squashfs file, but it was not.";
168 }
169 }
170 // TODO(ahassani): Process other types of files like apk, zip, etc.
171 result_files->push_back(file);
172 }
173
174 return true;
175}
176
177} // namespace deflate_utils
178} // namespace chromeos_update_engine