blob: 56a3ca82e1ba9a982ed9acb8772684bbfdd0ce73 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
2// Copyright (C) 2012 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//
Darin Petkov7a22d792010-11-08 14:10:00 -080016
Alex Deymo161c4a12014-05-16 15:56:21 -070017#include "update_engine/payload_generator/full_update_generator.h"
Darin Petkov7a22d792010-11-08 14:10:00 -080018
Darin Petkov7a22d792010-11-08 14:10:00 -080019#include <fcntl.h>
Alex Deymo161c4a12014-05-16 15:56:21 -070020#include <inttypes.h>
Darin Petkov7a22d792010-11-08 14:10:00 -080021
Alex Vakulenko072359c2014-07-18 11:41:07 -070022#include <algorithm>
23#include <deque>
Alex Deymobc91a272014-05-20 16:45:33 -070024#include <memory>
Darin Petkov7a22d792010-11-08 14:10:00 -080025
Alex Deymode942f32015-03-13 11:57:15 -070026#include <base/format_macros.h>
Alex Deymof1cbe172015-03-05 15:58:37 -080027#include <base/strings/string_util.h>
Alex Deymo7fad7b72015-07-21 22:22:47 -070028#include <base/strings/stringprintf.h>
29#include <base/synchronization/lock.h>
30#include <base/threading/simple_thread.h>
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070031#include <brillo/secure_blob.h>
Darin Petkov7a22d792010-11-08 14:10:00 -080032
Alex Deymo39910dc2015-11-09 17:04:30 -080033#include "update_engine/common/utils.h"
Alex Deymo0bc26112015-10-19 20:54:57 -070034#include "update_engine/payload_generator/bzip.h"
Darin Petkov7a22d792010-11-08 14:10:00 -080035
Darin Petkov7a22d792010-11-08 14:10:00 -080036using std::vector;
37
38namespace chromeos_update_engine {
39
40namespace {
41
Alex Deymode942f32015-03-13 11:57:15 -070042const size_t kDefaultFullChunkSize = 1024 * 1024; // 1 MiB
43
Alex Deymo7fad7b72015-07-21 22:22:47 -070044// This class encapsulates a full update chunk processing thread work. The
45// processor reads a chunk of data from the input file descriptor and compresses
46// it. The processor will destroy itself when the work is done.
47class ChunkProcessor : public base::DelegateSimpleThread::Delegate {
48 public:
49 // Read a chunk of |size| bytes from |fd| starting at offset |offset|.
50 ChunkProcessor(int fd, off_t offset, size_t size,
51 BlobFileWriter* blob_file, AnnotatedOperation* aop)
52 : fd_(fd),
53 offset_(offset),
54 size_(size),
55 blob_file_(blob_file),
56 aop_(aop) {}
57 // We use a default move constructor since all the data members are POD types.
58 ChunkProcessor(ChunkProcessor&&) = default;
59 ~ChunkProcessor() override = default;
60
61 // Overrides DelegateSimpleThread::Delegate.
62 // Run() handles the read from |fd| in a thread-safe way, and stores the
63 // new operation to generate the region starting at |offset| of size |size|
64 // in the output operation |aop|. The associated blob data is stored in
65 // |blob_fd| and |blob_file_size| is updated.
66 void Run() override;
67
68 private:
69 bool ProcessChunk();
70
Alex Deymo7fad7b72015-07-21 22:22:47 -070071 // Work parameters.
Darin Petkov7a22d792010-11-08 14:10:00 -080072 int fd_;
73 off_t offset_;
Alex Deymo7fad7b72015-07-21 22:22:47 -070074 size_t size_;
75 BlobFileWriter* blob_file_;
76 AnnotatedOperation* aop_;
Darin Petkov7a22d792010-11-08 14:10:00 -080077
78 DISALLOW_COPY_AND_ASSIGN(ChunkProcessor);
79};
80
Alex Deymo7fad7b72015-07-21 22:22:47 -070081void ChunkProcessor::Run() {
82 if (!ProcessChunk()) {
83 LOG(ERROR) << "Error processing region at " << offset_ << " of size "
84 << size_;
Darin Petkov7a22d792010-11-08 14:10:00 -080085 }
Darin Petkov7a22d792010-11-08 14:10:00 -080086}
87
Alex Deymo7fad7b72015-07-21 22:22:47 -070088bool ChunkProcessor::ProcessChunk() {
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070089 brillo::Blob buffer_in_(size_);
90 brillo::Blob op_blob;
Darin Petkov7a22d792010-11-08 14:10:00 -080091 ssize_t bytes_read = -1;
92 TEST_AND_RETURN_FALSE(utils::PReadAll(fd_,
93 buffer_in_.data(),
94 buffer_in_.size(),
95 offset_,
96 &bytes_read));
Alex Deymo7fad7b72015-07-21 22:22:47 -070097 TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(size_));
98 TEST_AND_RETURN_FALSE(BzipCompress(buffer_in_, &op_blob));
99
Alex Deymoa12ee112015-08-12 22:19:32 -0700100 InstallOperation_Type op_type = InstallOperation::REPLACE_BZ;
Alex Deymo7fad7b72015-07-21 22:22:47 -0700101
102 if (op_blob.size() >= buffer_in_.size()) {
103 // A REPLACE is cheaper than a REPLACE_BZ in this case.
104 op_blob = std::move(buffer_in_);
Alex Deymoa12ee112015-08-12 22:19:32 -0700105 op_type = InstallOperation::REPLACE;
Alex Deymo7fad7b72015-07-21 22:22:47 -0700106 }
107
Sen Jiang5456c192015-08-19 15:01:16 -0700108 TEST_AND_RETURN_FALSE(aop_->SetOperationBlob(&op_blob, blob_file_));
Alex Deymo7fad7b72015-07-21 22:22:47 -0700109 aop_->op.set_type(op_type);
Darin Petkov7a22d792010-11-08 14:10:00 -0800110 return true;
111}
112
113} // namespace
114
Alex Deymo477aec22015-03-24 23:40:48 -0700115bool FullUpdateGenerator::GenerateOperations(
Alex Deymof1cbe172015-03-05 15:58:37 -0800116 const PayloadGenerationConfig& config,
Sen Jiangebdf17d2015-08-19 11:53:27 -0700117 const PartitionConfig& old_part,
118 const PartitionConfig& new_part,
Sen Jiang8cc502d2015-08-10 10:04:54 -0700119 BlobFileWriter* blob_file,
Sen Jiangebdf17d2015-08-19 11:53:27 -0700120 vector<AnnotatedOperation>* aops) {
121 TEST_AND_RETURN_FALSE(new_part.ValidateExists());
Alex Deymode942f32015-03-13 11:57:15 -0700122
Alex Deymo477aec22015-03-24 23:40:48 -0700123 // FullUpdateGenerator requires a positive chunk_size, otherwise there will
Alex Deymof1cbe172015-03-05 15:58:37 -0800124 // be only one operation with the whole partition which should not be allowed.
Alex Deymo2d3b2d62015-07-17 17:34:36 -0700125 // For performance reasons, we force a small default hard limit of 1 MiB. This
126 // limit can be changed in the config, and we will use the smaller of the two
127 // soft/hard limits.
128 size_t full_chunk_size;
129 if (config.hard_chunk_size >= 0) {
130 full_chunk_size = std::min(static_cast<size_t>(config.hard_chunk_size),
131 config.soft_chunk_size);
Alex Deymode942f32015-03-13 11:57:15 -0700132 } else {
Alex Deymo2d3b2d62015-07-17 17:34:36 -0700133 full_chunk_size = std::min(kDefaultFullChunkSize, config.soft_chunk_size);
Alex Deymode942f32015-03-13 11:57:15 -0700134 LOG(INFO) << "No chunk_size provided, using the default chunk_size for the "
135 << "full operations: " << full_chunk_size << " bytes.";
136 }
137 TEST_AND_RETURN_FALSE(full_chunk_size > 0);
138 TEST_AND_RETURN_FALSE(full_chunk_size % config.block_size == 0);
Darin Petkov7a22d792010-11-08 14:10:00 -0800139
Sen Jiangebdf17d2015-08-19 11:53:27 -0700140 size_t chunk_blocks = full_chunk_size / config.block_size;
Alex Deymof329b932014-10-30 01:37:48 -0700141 size_t max_threads = std::max(sysconf(_SC_NPROCESSORS_ONLN), 4L);
Sen Jiang625406c2015-09-16 16:35:23 -0700142 LOG(INFO) << "Compressing partition " << new_part.name
Alex Deymo7fad7b72015-07-21 22:22:47 -0700143 << " from " << new_part.path << " splitting in chunks of "
Sen Jiangebdf17d2015-08-19 11:53:27 -0700144 << chunk_blocks << " blocks (" << config.block_size
Alex Deymo7fad7b72015-07-21 22:22:47 -0700145 << " bytes each) using " << max_threads << " threads";
Darin Petkov7a22d792010-11-08 14:10:00 -0800146
Alex Deymo7fad7b72015-07-21 22:22:47 -0700147 int in_fd = open(new_part.path.c_str(), O_RDONLY, 0);
148 TEST_AND_RETURN_FALSE(in_fd >= 0);
149 ScopedFdCloser in_fd_closer(&in_fd);
Darin Petkov7a22d792010-11-08 14:10:00 -0800150
Alex Deymo7fad7b72015-07-21 22:22:47 -0700151 // We potentially have all the ChunkProcessors in memory but only
152 // |max_threads| will actually hold a block in memory while we process.
Sen Jiangebdf17d2015-08-19 11:53:27 -0700153 size_t partition_blocks = new_part.size / config.block_size;
Alex Deymo7fad7b72015-07-21 22:22:47 -0700154 size_t num_chunks = (partition_blocks + chunk_blocks - 1) / chunk_blocks;
155 aops->resize(num_chunks);
156 vector<ChunkProcessor> chunk_processors;
157 chunk_processors.reserve(num_chunks);
Sen Jiang8cc502d2015-08-10 10:04:54 -0700158 blob_file->SetTotalBlobs(num_chunks);
Darin Petkov7a22d792010-11-08 14:10:00 -0800159
Alex Deymo7fad7b72015-07-21 22:22:47 -0700160 for (size_t i = 0; i < num_chunks; ++i) {
161 size_t start_block = i * chunk_blocks;
162 // The last chunk could be smaller.
163 size_t num_blocks = std::min(chunk_blocks,
164 partition_blocks - i * chunk_blocks);
Darin Petkov7a22d792010-11-08 14:10:00 -0800165
Alex Deymo7fad7b72015-07-21 22:22:47 -0700166 // Preset all the static information about the operations. The
167 // ChunkProcessor will set the rest.
168 AnnotatedOperation* aop = aops->data() + i;
169 aop->name = base::StringPrintf("<%s-operation-%" PRIuS ">",
Sen Jiang625406c2015-09-16 16:35:23 -0700170 new_part.name.c_str(), i);
Alex Deymo7fad7b72015-07-21 22:22:47 -0700171 Extent* dst_extent = aop->op.add_dst_extents();
172 dst_extent->set_start_block(start_block);
173 dst_extent->set_num_blocks(num_blocks);
Darin Petkov7a22d792010-11-08 14:10:00 -0800174
Alex Deymo7fad7b72015-07-21 22:22:47 -0700175 chunk_processors.emplace_back(
176 in_fd,
Sen Jiangebdf17d2015-08-19 11:53:27 -0700177 static_cast<off_t>(start_block) * config.block_size,
178 num_blocks * config.block_size,
Sen Jiang8cc502d2015-08-10 10:04:54 -0700179 blob_file,
Alex Deymo7fad7b72015-07-21 22:22:47 -0700180 aop);
Darin Petkov7a22d792010-11-08 14:10:00 -0800181 }
182
Alex Deymo7fad7b72015-07-21 22:22:47 -0700183 // Thread pool used for worker threads.
184 base::DelegateSimpleThreadPool thread_pool("full-update-generator",
185 max_threads);
186 thread_pool.Start();
187 for (ChunkProcessor& processor : chunk_processors)
188 thread_pool.AddWork(&processor);
189 thread_pool.JoinAll();
Sen Jiangaf1575f2015-11-09 13:34:16 -0800190
191 // All the work done, disable logging.
192 blob_file->SetTotalBlobs(0);
193
Alex Deymo7fad7b72015-07-21 22:22:47 -0700194 // All the operations must have a type set at this point. Otherwise, a
195 // ChunkProcessor failed to complete.
196 for (const AnnotatedOperation& aop : *aops) {
197 if (!aop.op.has_type())
198 return false;
199 }
Darin Petkov7a22d792010-11-08 14:10:00 -0800200 return true;
201}
202
203} // namespace chromeos_update_engine