• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
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 //
16 
17 #include "update_engine/payload_generator/full_update_generator.h"
18 
19 #include <fcntl.h>
20 
21 #include <algorithm>
22 
23 #include <base/format_macros.h>
24 #include <android-base/stringprintf.h>
25 #include <base/synchronization/lock.h>
26 #include <base/threading/simple_thread.h>
27 #include <brillo/secure_blob.h>
28 
29 #include "update_engine/common/utils.h"
30 #include "update_engine/payload_generator/delta_diff_utils.h"
31 
32 using std::vector;
33 
34 namespace chromeos_update_engine {
35 
36 namespace {
37 
38 const size_t kDefaultFullChunkSize = 1024 * 1024;  // 1 MiB
39 
40 // This class encapsulates a full update chunk processing thread work. The
41 // processor reads a chunk of data from the input file descriptor and compresses
42 // it. The processor will destroy itself when the work is done.
43 class ChunkProcessor : public base::DelegateSimpleThread::Delegate {
44  public:
45   // Read a chunk of |size| bytes from |fd| starting at offset |offset|.
ChunkProcessor(const PayloadVersion & version,int fd,off_t offset,size_t size,BlobFileWriter * blob_file,AnnotatedOperation * aop)46   ChunkProcessor(const PayloadVersion& version,
47                  int fd,
48                  off_t offset,
49                  size_t size,
50                  BlobFileWriter* blob_file,
51                  AnnotatedOperation* aop)
52       : version_(version),
53         fd_(fd),
54         offset_(offset),
55         size_(size),
56         blob_file_(blob_file),
57         aop_(aop) {}
58   // We use a default move constructor since all the data members are POD types.
59   ChunkProcessor(ChunkProcessor&&) = default;
60   ~ChunkProcessor() override = default;
61 
62   // Overrides DelegateSimpleThread::Delegate.
63   // Run() handles the read from |fd| in a thread-safe way, and stores the
64   // new operation to generate the region starting at |offset| of size |size|
65   // in the output operation |aop|. The associated blob data is stored in
66   // |blob_fd| and |blob_file_size| is updated.
67   void Run() override;
68 
69  private:
70   bool ProcessChunk();
71 
72   // Work parameters.
73   const PayloadVersion& version_;
74   int fd_;
75   off_t offset_;
76   size_t size_;
77   BlobFileWriter* blob_file_;
78   AnnotatedOperation* aop_;
79 
80   DISALLOW_COPY_AND_ASSIGN(ChunkProcessor);
81 };
82 
Run()83 void ChunkProcessor::Run() {
84   if (!ProcessChunk()) {
85     LOG(ERROR) << "Error processing region at " << offset_ << " of size "
86                << size_;
87   }
88 }
89 
ProcessChunk()90 bool ChunkProcessor::ProcessChunk() {
91   brillo::Blob buffer_in_(size_);
92   brillo::Blob op_blob;
93   ssize_t bytes_read = -1;
94   TEST_AND_RETURN_FALSE(utils::PReadAll(
95       fd_, buffer_in_.data(), buffer_in_.size(), offset_, &bytes_read));
96   TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(size_));
97 
98   InstallOperation::Type op_type{};
99   TEST_AND_RETURN_FALSE(diff_utils::GenerateBestFullOperation(
100       buffer_in_, version_, &op_blob, &op_type));
101 
102   aop_->op.set_type(op_type);
103   TEST_AND_RETURN_FALSE(aop_->SetOperationBlob(op_blob, blob_file_));
104   return true;
105 }
106 
107 }  // namespace
108 
GenerateOperations(const PayloadGenerationConfig & config,const PartitionConfig & old_part,const PartitionConfig & new_part,BlobFileWriter * blob_file,vector<AnnotatedOperation> * aops)109 bool FullUpdateGenerator::GenerateOperations(
110     const PayloadGenerationConfig& config,
111     const PartitionConfig& old_part,
112     const PartitionConfig& new_part,
113     BlobFileWriter* blob_file,
114     vector<AnnotatedOperation>* aops) {
115   TEST_AND_RETURN_FALSE(new_part.ValidateExists());
116 
117   // FullUpdateGenerator requires a positive chunk_size, otherwise there will
118   // be only one operation with the whole partition which should not be allowed.
119   // For performance reasons, we force a small default hard limit of 1 MiB. This
120   // limit can be changed in the config, and we will use the smaller of the two
121   // soft/hard limits.
122   size_t full_chunk_size{};
123   if (config.hard_chunk_size >= 0) {
124     full_chunk_size = std::min(static_cast<size_t>(config.hard_chunk_size),
125                                config.soft_chunk_size);
126   } else {
127     full_chunk_size = std::min(kDefaultFullChunkSize, config.soft_chunk_size);
128     LOG(INFO) << "No chunk_size provided, using the default chunk_size for the "
129               << "full operations: " << full_chunk_size << " bytes.";
130   }
131   TEST_AND_RETURN_FALSE(full_chunk_size > 0);
132   TEST_AND_RETURN_FALSE(full_chunk_size % config.block_size == 0);
133 
134   size_t chunk_blocks = full_chunk_size / config.block_size;
135   size_t max_threads = diff_utils::GetMaxThreads();
136   LOG(INFO) << "Compressing partition " << new_part.name << " from "
137             << new_part.path << " splitting in chunks of " << chunk_blocks
138             << " blocks (" << config.block_size << " bytes each) using "
139             << max_threads << " threads";
140 
141   int in_fd = open(new_part.path.c_str(), O_RDONLY, 0);
142   TEST_AND_RETURN_FALSE(in_fd >= 0);
143   ScopedFdCloser in_fd_closer(&in_fd);
144 
145   // We potentially have all the ChunkProcessors in memory but only
146   // |max_threads| will actually hold a block in memory while we process.
147   size_t partition_blocks = new_part.size / config.block_size;
148   size_t num_chunks = utils::DivRoundUp(partition_blocks, chunk_blocks);
149   aops->resize(num_chunks);
150   vector<ChunkProcessor> chunk_processors;
151   chunk_processors.reserve(num_chunks);
152   blob_file->IncTotalBlobs(num_chunks);
153 
154   for (size_t i = 0; i < num_chunks; ++i) {
155     size_t start_block = i * chunk_blocks;
156     // The last chunk could be smaller.
157     size_t num_blocks =
158         std::min(chunk_blocks, partition_blocks - i * chunk_blocks);
159 
160     // Preset all the static information about the operations. The
161     // ChunkProcessor will set the rest.
162     AnnotatedOperation* aop = aops->data() + i;
163     aop->name = android::base::StringPrintf(
164         "<%s-operation-%" PRIuS ">", new_part.name.c_str(), i);
165     Extent* dst_extent = aop->op.add_dst_extents();
166     dst_extent->set_start_block(start_block);
167     dst_extent->set_num_blocks(num_blocks);
168 
169     chunk_processors.emplace_back(
170         config.version,
171         in_fd,
172         static_cast<off_t>(start_block) * config.block_size,
173         num_blocks * config.block_size,
174         blob_file,
175         aop);
176   }
177 
178   // Thread pool used for worker threads.
179   base::DelegateSimpleThreadPool thread_pool("full-update-generator",
180                                              max_threads);
181   thread_pool.Start();
182   for (ChunkProcessor& processor : chunk_processors)
183     thread_pool.AddWork(&processor);
184   thread_pool.JoinAll();
185 
186   // All the operations must have a type set at this point. Otherwise, a
187   // ChunkProcessor failed to complete.
188   for (const AnnotatedOperation& aop : *aops) {
189     if (!aop.op.has_type())
190       return false;
191   }
192   return true;
193 }
194 
195 }  // namespace chromeos_update_engine
196