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