1 /*
2 * Copyright (C) 2019 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 "partition_installer.h"
18
19 #include <sys/statvfs.h>
20
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <android-base/unique_fd.h>
24 #include <ext4_utils/ext4_utils.h>
25 #include <fs_mgr_dm_linear.h>
26 #include <libdm/dm.h>
27 #include <libgsi/libgsi.h>
28
29 #include "file_paths.h"
30 #include "gsi_service.h"
31 #include "libgsi_private.h"
32
33 namespace android {
34 namespace gsi {
35
36 using namespace std::literals;
37 using namespace android::dm;
38 using namespace android::fiemap;
39 using namespace android::fs_mgr;
40 using android::base::unique_fd;
41
42 // The default size of userdata.img for GSI.
43 // We are looking for /data to have atleast 40% free space
44 static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
45
PartitionInstaller(GsiService * service,const std::string & install_dir,const std::string & name,const std::string & active_dsu,int64_t size,bool read_only)46 PartitionInstaller::PartitionInstaller(GsiService* service, const std::string& install_dir,
47 const std::string& name, const std::string& active_dsu,
48 int64_t size, bool read_only)
49 : service_(service),
50 install_dir_(install_dir),
51 name_(name),
52 active_dsu_(active_dsu),
53 size_(size),
54 readOnly_(read_only) {
55 images_ = ImageManager::Open(MetadataDir(active_dsu), install_dir_);
56 }
57
~PartitionInstaller()58 PartitionInstaller::~PartitionInstaller() {
59 Finish();
60 if (!succeeded_) {
61 // Close open handles before we remove files.
62 system_device_ = nullptr;
63 PostInstallCleanup(images_.get());
64 }
65 if (IsAshmemMapped()) {
66 UnmapAshmem();
67 }
68 }
69
PostInstallCleanup()70 void PartitionInstaller::PostInstallCleanup() {
71 auto manager = ImageManager::Open(MetadataDir(active_dsu_), install_dir_);
72 if (!manager) {
73 LOG(ERROR) << "Could not open image manager";
74 return;
75 }
76 return PostInstallCleanup(manager.get());
77 }
78
PostInstallCleanup(ImageManager * manager)79 void PartitionInstaller::PostInstallCleanup(ImageManager* manager) {
80 std::string file = GetBackingFile(name_);
81 if (manager->IsImageMapped(file)) {
82 LOG(ERROR) << "unmap " << file;
83 manager->UnmapImageDevice(file);
84 }
85 manager->DeleteBackingImage(file);
86 }
87
StartInstall()88 int PartitionInstaller::StartInstall() {
89 if (int status = PerformSanityChecks()) {
90 return status;
91 }
92 if (int status = Preallocate()) {
93 return status;
94 }
95 if (!readOnly_) {
96 if (!Format()) {
97 return IGsiService::INSTALL_ERROR_GENERIC;
98 }
99 succeeded_ = true;
100 } else {
101 // Map ${name}_gsi so we can write to it.
102 system_device_ = OpenPartition(GetBackingFile(name_));
103 if (!system_device_) {
104 return IGsiService::INSTALL_ERROR_GENERIC;
105 }
106
107 // Clear the progress indicator.
108 service_->UpdateProgress(IGsiService::STATUS_NO_OPERATION, 0);
109 }
110 return IGsiService::INSTALL_OK;
111 }
112
PerformSanityChecks()113 int PartitionInstaller::PerformSanityChecks() {
114 if (!images_) {
115 LOG(ERROR) << "unable to create image manager";
116 return IGsiService::INSTALL_ERROR_GENERIC;
117 }
118 if (size_ < 0) {
119 LOG(ERROR) << "image size " << size_ << " is negative";
120 return IGsiService::INSTALL_ERROR_GENERIC;
121 }
122 if (android::gsi::IsGsiRunning()) {
123 LOG(ERROR) << "cannot install gsi inside a live gsi";
124 return IGsiService::INSTALL_ERROR_GENERIC;
125 }
126
127 struct statvfs sb;
128 if (statvfs(install_dir_.c_str(), &sb)) {
129 PLOG(ERROR) << "failed to read file system stats";
130 return IGsiService::INSTALL_ERROR_GENERIC;
131 }
132
133 // This is the same as android::vold::GetFreebytes() but we also
134 // need the total file system size so we open code it here.
135 uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
136 uint64_t fs_size = sb.f_blocks * sb.f_frsize;
137 if (free_space <= (size_)) {
138 LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
139 return IGsiService::INSTALL_ERROR_NO_SPACE;
140 }
141 // We are asking for 40% of the /data to be empty.
142 // TODO: may be not hard code it like this
143 double free_space_percent = ((1.0 * free_space) / fs_size) * 100;
144 if (free_space_percent < kMinimumFreeSpaceThreshold) {
145 LOG(ERROR) << "free space " << static_cast<uint64_t>(free_space_percent)
146 << "% is below the minimum threshold of " << kMinimumFreeSpaceThreshold << "%";
147 return IGsiService::INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
148 }
149 return IGsiService::INSTALL_OK;
150 }
151
Preallocate()152 int PartitionInstaller::Preallocate() {
153 std::string file = GetBackingFile(name_);
154 if (!images_->UnmapImageIfExists(file)) {
155 LOG(ERROR) << "failed to UnmapImageIfExists " << file;
156 return IGsiService::INSTALL_ERROR_GENERIC;
157 }
158 // always delete the old one when it presents in case there might a partition
159 // with same name but different size.
160 if (images_->BackingImageExists(file)) {
161 if (!images_->DeleteBackingImage(file)) {
162 LOG(ERROR) << "failed to DeleteBackingImage " << file;
163 return IGsiService::INSTALL_ERROR_GENERIC;
164 }
165 }
166 service_->StartAsyncOperation("create " + name_, size_);
167 if (!CreateImage(file, size_)) {
168 LOG(ERROR) << "Could not create userdata image";
169 return IGsiService::INSTALL_ERROR_GENERIC;
170 }
171 service_->UpdateProgress(IGsiService::STATUS_COMPLETE, 0);
172 return IGsiService::INSTALL_OK;
173 }
174
CreateImage(const std::string & name,uint64_t size)175 bool PartitionInstaller::CreateImage(const std::string& name, uint64_t size) {
176 auto progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
177 service_->UpdateProgress(IGsiService::STATUS_WORKING, bytes);
178 if (service_->should_abort()) return false;
179 return true;
180 };
181 int flags = ImageManager::CREATE_IMAGE_DEFAULT;
182 if (readOnly_) {
183 flags |= ImageManager::CREATE_IMAGE_READONLY;
184 }
185 return images_->CreateBackingImage(name, size, flags, std::move(progress));
186 }
187
OpenPartition(const std::string & name)188 std::unique_ptr<MappedDevice> PartitionInstaller::OpenPartition(const std::string& name) {
189 return MappedDevice::Open(images_.get(), 10s, name);
190 }
191
CommitGsiChunk(int stream_fd,int64_t bytes)192 bool PartitionInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) {
193 service_->StartAsyncOperation("write " + name_, size_);
194
195 if (bytes < 0) {
196 LOG(ERROR) << "chunk size " << bytes << " is negative";
197 return false;
198 }
199
200 static const size_t kBlockSize = 4096;
201 auto buffer = std::make_unique<char[]>(kBlockSize);
202
203 int progress = -1;
204 uint64_t remaining = bytes;
205 while (remaining) {
206 size_t max_to_read = std::min(static_cast<uint64_t>(kBlockSize), remaining);
207 ssize_t rv = TEMP_FAILURE_RETRY(read(stream_fd, buffer.get(), max_to_read));
208 if (rv < 0) {
209 PLOG(ERROR) << "read gsi chunk";
210 return false;
211 }
212 if (rv == 0) {
213 LOG(ERROR) << "no bytes left in stream";
214 return false;
215 }
216 if (!CommitGsiChunk(buffer.get(), rv)) {
217 return false;
218 }
219 CHECK(static_cast<uint64_t>(rv) <= remaining);
220 remaining -= rv;
221
222 // Only update the progress when the % (or permille, in this case)
223 // significantly changes.
224 int new_progress = ((size_ - remaining) * 1000) / size_;
225 if (new_progress != progress) {
226 service_->UpdateProgress(IGsiService::STATUS_WORKING, size_ - remaining);
227 }
228 }
229
230 service_->UpdateProgress(IGsiService::STATUS_COMPLETE, size_);
231 return true;
232 }
233
IsFinishedWriting()234 bool PartitionInstaller::IsFinishedWriting() {
235 return gsi_bytes_written_ == size_;
236 }
237
IsAshmemMapped()238 bool PartitionInstaller::IsAshmemMapped() {
239 return ashmem_data_ != MAP_FAILED;
240 }
241
CommitGsiChunk(const void * data,size_t bytes)242 bool PartitionInstaller::CommitGsiChunk(const void* data, size_t bytes) {
243 if (static_cast<uint64_t>(bytes) > size_ - gsi_bytes_written_) {
244 // We cannot write past the end of the image file.
245 LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << size_
246 << " expected, " << gsi_bytes_written_ << " written)";
247 return false;
248 }
249 if (service_->should_abort()) {
250 return false;
251 }
252 if (!android::base::WriteFully(system_device_->fd(), data, bytes)) {
253 PLOG(ERROR) << "write failed";
254 return false;
255 }
256 gsi_bytes_written_ += bytes;
257 return true;
258 }
259
GetPartitionFd()260 int PartitionInstaller::GetPartitionFd() {
261 return system_device_->fd();
262 }
263
MapAshmem(int fd,size_t size)264 bool PartitionInstaller::MapAshmem(int fd, size_t size) {
265 ashmem_size_ = size;
266 ashmem_data_ = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
267 return ashmem_data_ != MAP_FAILED;
268 }
269
UnmapAshmem()270 void PartitionInstaller::UnmapAshmem() {
271 if (munmap(ashmem_data_, ashmem_size_) != 0) {
272 PLOG(ERROR) << "cannot munmap";
273 return;
274 }
275 ashmem_data_ = MAP_FAILED;
276 ashmem_size_ = -1;
277 }
278
CommitGsiChunk(size_t bytes)279 bool PartitionInstaller::CommitGsiChunk(size_t bytes) {
280 if (!IsAshmemMapped()) {
281 PLOG(ERROR) << "ashmem is not mapped";
282 return false;
283 }
284 bool success = CommitGsiChunk(ashmem_data_, bytes);
285 if (success && IsFinishedWriting()) {
286 UnmapAshmem();
287 }
288 return success;
289 }
290
GetBackingFile(std::string name)291 const std::string PartitionInstaller::GetBackingFile(std::string name) {
292 return name + "_gsi";
293 }
294
Format()295 bool PartitionInstaller::Format() {
296 auto file = GetBackingFile(name_);
297 auto device = OpenPartition(file);
298 if (!device) {
299 return false;
300 }
301
302 // libcutils checks the first 4K, no matter the block size.
303 std::string zeroes(4096, 0);
304 if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
305 PLOG(ERROR) << "write " << file;
306 return false;
307 }
308 return true;
309 }
310
Finish()311 int PartitionInstaller::Finish() {
312 if (readOnly_ && gsi_bytes_written_ != size_) {
313 // We cannot boot if the image is incomplete.
314 LOG(ERROR) << "image incomplete; expected " << size_ << " bytes, waiting for "
315 << (size_ - gsi_bytes_written_) << " bytes";
316 return IGsiService::INSTALL_ERROR_GENERIC;
317 }
318 if (system_device_ != nullptr && fsync(system_device_->fd())) {
319 PLOG(ERROR) << "fsync failed for " << name_ << "_gsi";
320 return IGsiService::INSTALL_ERROR_GENERIC;
321 }
322 system_device_ = {};
323
324 // If files moved (are no longer pinned), the metadata file will be invalid.
325 // This check can be removed once b/133967059 is fixed.
326 if (!images_->Validate()) {
327 return IGsiService::INSTALL_ERROR_GENERIC;
328 }
329
330 succeeded_ = true;
331 return IGsiService::INSTALL_OK;
332 }
333
WipeWritable(const std::string & active_dsu,const std::string & install_dir,const std::string & name)334 int PartitionInstaller::WipeWritable(const std::string& active_dsu, const std::string& install_dir,
335 const std::string& name) {
336 auto image = ImageManager::Open(MetadataDir(active_dsu), install_dir);
337 // The device object has to be destroyed before the image object
338 auto device = MappedDevice::Open(image.get(), 10s, name);
339 if (!device) {
340 return IGsiService::INSTALL_ERROR_GENERIC;
341 }
342
343 // Wipe the first 1MiB of the device, ensuring both the first block and
344 // the superblock are destroyed.
345 static constexpr uint64_t kEraseSize = 1024 * 1024;
346
347 std::string zeroes(4096, 0);
348 uint64_t erase_size = std::min(kEraseSize, get_block_device_size(device->fd()));
349 for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
350 if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
351 PLOG(ERROR) << "write " << name;
352 return IGsiService::INSTALL_ERROR_GENERIC;
353 }
354 }
355 return IGsiService::INSTALL_OK;
356 }
357
358 } // namespace gsi
359 } // namespace android
360