blob: 357df5016917c8a0dacf1ba18a9d09b6d657f1bb [file] [log] [blame]
David Andersonb2988ab2019-04-16 17:14:09 -07001/*
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
Howard Chen4663de62019-11-05 20:46:20 +080017#include "partition_installer.h"
David Andersonb2988ab2019-04-16 17:14:09 -070018
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>
David Anderson8bdf6252019-06-11 16:43:24 -070024#include <ext4_utils/ext4_utils.h>
David Andersonb2988ab2019-04-16 17:14:09 -070025#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
33namespace android {
34namespace gsi {
35
36using namespace std::literals;
37using namespace android::dm;
David Anderson9ca77282019-07-15 23:56:13 +000038using namespace android::fiemap;
David Andersonb2988ab2019-04-16 17:14:09 -070039using namespace android::fs_mgr;
40using 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
44static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
David Andersonb2988ab2019-04-16 17:14:09 -070045
Howard Chen4663de62019-11-05 20:46:20 +080046PartitionInstaller::PartitionInstaller(GsiService* service, const std::string& install_dir,
Howard Chenee5c2b12019-11-08 11:57:47 +080047 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_);
David Andersonb2988ab2019-04-16 17:14:09 -070056}
57
Howard Chen4663de62019-11-05 20:46:20 +080058PartitionInstaller::~PartitionInstaller() {
59 Finish();
David Andersonb2988ab2019-04-16 17:14:09 -070060 if (!succeeded_) {
61 // Close open handles before we remove files.
David Anderson64b53fb2019-07-01 19:05:35 -070062 system_device_ = nullptr;
63 PostInstallCleanup(images_.get());
David Andersonb2988ab2019-04-16 17:14:09 -070064 }
Howard Chen5676d962019-08-05 16:21:00 +080065 if (IsAshmemMapped()) {
66 UnmapAshmem();
67 }
David Andersonb2988ab2019-04-16 17:14:09 -070068}
69
Howard Chen4663de62019-11-05 20:46:20 +080070void PartitionInstaller::PostInstallCleanup() {
Howard Chenee5c2b12019-11-08 11:57:47 +080071 auto manager = ImageManager::Open(MetadataDir(active_dsu_), install_dir_);
David Anderson64b53fb2019-07-01 19:05:35 -070072 if (!manager) {
73 LOG(ERROR) << "Could not open image manager";
74 return;
David Andersonb2988ab2019-04-16 17:14:09 -070075 }
David Anderson64b53fb2019-07-01 19:05:35 -070076 return PostInstallCleanup(manager.get());
77}
78
Howard Chen4663de62019-11-05 20:46:20 +080079void PartitionInstaller::PostInstallCleanup(ImageManager* manager) {
Howard Chen18109b12019-08-13 17:00:44 +080080 std::string file = GetBackingFile(name_);
81 if (manager->IsImageMapped(file)) {
82 LOG(ERROR) << "unmap " << file;
83 manager->UnmapImageDevice(file);
David Andersonb2988ab2019-04-16 17:14:09 -070084 }
Howard Chen4663de62019-11-05 20:46:20 +080085 manager->DeleteBackingImage(file);
David Andersonb2988ab2019-04-16 17:14:09 -070086}
87
Howard Chen4663de62019-11-05 20:46:20 +080088int PartitionInstaller::StartInstall() {
David Andersonb2988ab2019-04-16 17:14:09 -070089 if (int status = PerformSanityChecks()) {
90 return status;
91 }
Howard Chen18109b12019-08-13 17:00:44 +080092 if (int status = Preallocate()) {
David Andersonb2988ab2019-04-16 17:14:09 -070093 return status;
94 }
Howard Chen18109b12019-08-13 17:00:44 +080095 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 }
David Andersonb2988ab2019-04-16 17:14:09 -0700106
Howard Chen18109b12019-08-13 17:00:44 +0800107 // Clear the progress indicator.
108 service_->UpdateProgress(IGsiService::STATUS_NO_OPERATION, 0);
David Andersonb2988ab2019-04-16 17:14:09 -0700109 }
David Andersonb2988ab2019-04-16 17:14:09 -0700110 return IGsiService::INSTALL_OK;
111}
112
Howard Chen4663de62019-11-05 20:46:20 +0800113int PartitionInstaller::PerformSanityChecks() {
David Anderson64b53fb2019-07-01 19:05:35 -0700114 if (!images_) {
115 LOG(ERROR) << "unable to create image manager";
116 return IGsiService::INSTALL_ERROR_GENERIC;
117 }
Howard Chen18109b12019-08-13 17:00:44 +0800118 if (size_ < 0) {
119 LOG(ERROR) << "image size " << size_ << " is negative";
David Andersonb2988ab2019-04-16 17:14:09 -0700120 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.
Paul Trautrime59f49c2019-05-21 14:43:54 +0900135 uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
David Andersonb2988ab2019-04-16 17:14:09 -0700136 uint64_t fs_size = sb.f_blocks * sb.f_frsize;
Howard Chen18109b12019-08-13 17:00:44 +0800137 if (free_space <= (size_)) {
David Andersonb2988ab2019-04-16 17:14:09 -0700138 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
Howard Chen4663de62019-11-05 20:46:20 +0800152int PartitionInstaller::Preallocate() {
Howard Chen18109b12019-08-13 17:00:44 +0800153 std::string file = GetBackingFile(name_);
Howard Chen4663de62019-11-05 20:46:20 +0800154 if (!images_->UnmapImageIfExists(file)) {
155 LOG(ERROR) << "failed to UnmapImageIfExists " << file;
156 return IGsiService::INSTALL_ERROR_GENERIC;
David Andersonb2988ab2019-04-16 17:14:09 -0700157 }
Howard Chen4663de62019-11-05 20:46:20 +0800158 // 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;
Howard Chen18109b12019-08-13 17:00:44 +0800163 return IGsiService::INSTALL_ERROR_GENERIC;
164 }
David Andersonb2988ab2019-04-16 17:14:09 -0700165 }
Howard Chen4663de62019-11-05 20:46:20 +0800166 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 }
David Andersonb2988ab2019-04-16 17:14:09 -0700171 service_->UpdateProgress(IGsiService::STATUS_COMPLETE, 0);
172 return IGsiService::INSTALL_OK;
173}
174
Howard Chen4663de62019-11-05 20:46:20 +0800175bool PartitionInstaller::CreateImage(const std::string& name, uint64_t size) {
David Anderson64b53fb2019-07-01 19:05:35 -0700176 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 };
David Anderson1fdec262019-07-24 14:03:49 -0700181 int flags = ImageManager::CREATE_IMAGE_DEFAULT;
Howard Chen18109b12019-08-13 17:00:44 +0800182 if (readOnly_) {
David Anderson1fdec262019-07-24 14:03:49 -0700183 flags |= ImageManager::CREATE_IMAGE_READONLY;
184 }
185 return images_->CreateBackingImage(name, size, flags, std::move(progress));
David Andersonb2988ab2019-04-16 17:14:09 -0700186}
187
Howard Chen4663de62019-11-05 20:46:20 +0800188std::unique_ptr<MappedDevice> PartitionInstaller::OpenPartition(const std::string& name) {
Howard Chen73595fe2019-11-05 14:53:22 +0800189 return MappedDevice::Open(images_.get(), 10s, name);
David Andersonb2988ab2019-04-16 17:14:09 -0700190}
191
Howard Chen4663de62019-11-05 20:46:20 +0800192bool PartitionInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) {
Howard Chen18109b12019-08-13 17:00:44 +0800193 service_->StartAsyncOperation("write " + name_, size_);
David Andersonb2988ab2019-04-16 17:14:09 -0700194
195 if (bytes < 0) {
196 LOG(ERROR) << "chunk size " << bytes << " is negative";
197 return false;
198 }
199
David Anderson64b53fb2019-07-01 19:05:35 -0700200 static const size_t kBlockSize = 4096;
201 auto buffer = std::make_unique<char[]>(kBlockSize);
David Andersonb2988ab2019-04-16 17:14:09 -0700202
203 int progress = -1;
204 uint64_t remaining = bytes;
205 while (remaining) {
David Anderson64b53fb2019-07-01 19:05:35 -0700206 size_t max_to_read = std::min(static_cast<uint64_t>(kBlockSize), remaining);
David Andersonb2988ab2019-04-16 17:14:09 -0700207 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.
Howard Chen18109b12019-08-13 17:00:44 +0800224 int new_progress = ((size_ - remaining) * 1000) / size_;
David Andersonb2988ab2019-04-16 17:14:09 -0700225 if (new_progress != progress) {
Howard Chen18109b12019-08-13 17:00:44 +0800226 service_->UpdateProgress(IGsiService::STATUS_WORKING, size_ - remaining);
David Andersonb2988ab2019-04-16 17:14:09 -0700227 }
228 }
229
Howard Chen18109b12019-08-13 17:00:44 +0800230 service_->UpdateProgress(IGsiService::STATUS_COMPLETE, size_);
David Andersonb2988ab2019-04-16 17:14:09 -0700231 return true;
232}
233
Howard Chen4663de62019-11-05 20:46:20 +0800234bool PartitionInstaller::IsFinishedWriting() {
Howard Chen18109b12019-08-13 17:00:44 +0800235 return gsi_bytes_written_ == size_;
Howard Chen5676d962019-08-05 16:21:00 +0800236}
237
Howard Chen4663de62019-11-05 20:46:20 +0800238bool PartitionInstaller::IsAshmemMapped() {
Howard Chen5676d962019-08-05 16:21:00 +0800239 return ashmem_data_ != MAP_FAILED;
240}
241
Howard Chen4663de62019-11-05 20:46:20 +0800242bool PartitionInstaller::CommitGsiChunk(const void* data, size_t bytes) {
Howard Chen18109b12019-08-13 17:00:44 +0800243 if (static_cast<uint64_t>(bytes) > size_ - gsi_bytes_written_) {
David Andersonb2988ab2019-04-16 17:14:09 -0700244 // We cannot write past the end of the image file.
Howard Chen18109b12019-08-13 17:00:44 +0800245 LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << size_
David Andersonb2988ab2019-04-16 17:14:09 -0700246 << " expected, " << gsi_bytes_written_ << " written)";
247 return false;
248 }
249 if (service_->should_abort()) {
250 return false;
251 }
David Anderson64b53fb2019-07-01 19:05:35 -0700252 if (!android::base::WriteFully(system_device_->fd(), data, bytes)) {
David Andersonb2988ab2019-04-16 17:14:09 -0700253 PLOG(ERROR) << "write failed";
254 return false;
255 }
256 gsi_bytes_written_ += bytes;
257 return true;
258}
259
Yo Chiang53bed1c2020-01-01 16:25:19 +0800260int PartitionInstaller::GetPartitionFd() {
261 return system_device_->fd();
262}
263
Howard Chen4663de62019-11-05 20:46:20 +0800264bool PartitionInstaller::MapAshmem(int fd, size_t size) {
Howard Chen5676d962019-08-05 16:21:00 +0800265 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
Howard Chen4663de62019-11-05 20:46:20 +0800270void PartitionInstaller::UnmapAshmem() {
Howard Chen5676d962019-08-05 16:21:00 +0800271 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
Howard Chen4663de62019-11-05 20:46:20 +0800279bool PartitionInstaller::CommitGsiChunk(size_t bytes) {
Howard Chen5676d962019-08-05 16:21:00 +0800280 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
Howard Chen4663de62019-11-05 20:46:20 +0800291const std::string PartitionInstaller::GetBackingFile(std::string name) {
Howard Chen18109b12019-08-13 17:00:44 +0800292 return name + "_gsi";
293}
294
Howard Chen4663de62019-11-05 20:46:20 +0800295bool PartitionInstaller::Format() {
Howard Chen18109b12019-08-13 17:00:44 +0800296 auto file = GetBackingFile(name_);
297 auto device = OpenPartition(file);
David Anderson64b53fb2019-07-01 19:05:35 -0700298 if (!device) {
David Andersonb2988ab2019-04-16 17:14:09 -0700299 return false;
300 }
301
302 // libcutils checks the first 4K, no matter the block size.
303 std::string zeroes(4096, 0);
David Anderson64b53fb2019-07-01 19:05:35 -0700304 if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
Howard Chen18109b12019-08-13 17:00:44 +0800305 PLOG(ERROR) << "write " << file;
David Andersonb2988ab2019-04-16 17:14:09 -0700306 return false;
307 }
308 return true;
309}
310
Howard Chen4663de62019-11-05 20:46:20 +0800311int PartitionInstaller::Finish() {
Howard Chenee5c2b12019-11-08 11:57:47 +0800312 if (readOnly_ && gsi_bytes_written_ != size_) {
David Andersonb2988ab2019-04-16 17:14:09 -0700313 // We cannot boot if the image is incomplete.
Howard Chen18109b12019-08-13 17:00:44 +0800314 LOG(ERROR) << "image incomplete; expected " << size_ << " bytes, waiting for "
315 << (size_ - gsi_bytes_written_) << " bytes";
David Andersonb2988ab2019-04-16 17:14:09 -0700316 return IGsiService::INSTALL_ERROR_GENERIC;
317 }
Howard Chenee5c2b12019-11-08 11:57:47 +0800318 if (system_device_ != nullptr && fsync(system_device_->fd())) {
Howard Chen18109b12019-08-13 17:00:44 +0800319 PLOG(ERROR) << "fsync failed for " << name_ << "_gsi";
David Andersonb2988ab2019-04-16 17:14:09 -0700320 return IGsiService::INSTALL_ERROR_GENERIC;
321 }
David Anderson64b53fb2019-07-01 19:05:35 -0700322 system_device_ = {};
David Andersonb2988ab2019-04-16 17:14:09 -0700323
324 // If files moved (are no longer pinned), the metadata file will be invalid.
David Anderson64b53fb2019-07-01 19:05:35 -0700325 // This check can be removed once b/133967059 is fixed.
326 if (!images_->Validate()) {
327 return IGsiService::INSTALL_ERROR_GENERIC;
David Andersonb2988ab2019-04-16 17:14:09 -0700328 }
329
David Andersonb2988ab2019-04-16 17:14:09 -0700330 succeeded_ = true;
331 return IGsiService::INSTALL_OK;
332}
333
Howard Chenee5c2b12019-11-08 11:57:47 +0800334int 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);
Howard Chen73595fe2019-11-05 14:53:22 +0800337 // The device object has to be destroyed before the image object
338 auto device = MappedDevice::Open(image.get(), 10s, name);
David Anderson64b53fb2019-07-01 19:05:35 -0700339 if (!device) {
David Anderson8bdf6252019-06-11 16:43:24 -0700340 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);
David Anderson64b53fb2019-07-01 19:05:35 -0700348 uint64_t erase_size = std::min(kEraseSize, get_block_device_size(device->fd()));
David Anderson8bdf6252019-06-11 16:43:24 -0700349 for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
David Anderson64b53fb2019-07-01 19:05:35 -0700350 if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) {
Howard Chen423129b2020-03-03 13:28:37 +0800351 PLOG(ERROR) << "write " << name;
David Anderson8bdf6252019-06-11 16:43:24 -0700352 return IGsiService::INSTALL_ERROR_GENERIC;
353 }
354 }
David Andersonb2988ab2019-04-16 17:14:09 -0700355 return IGsiService::INSTALL_OK;
356}
357
358} // namespace gsi
359} // namespace android