xref: /aosp_15_r20/system/core/fs_mgr/libsnapshot/scratch_super.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 // Copyright (C) 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <dirent.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <linux/fs.h>
19 #include <selinux/selinux.h>
20 #include <stdlib.h>
21 #include <sys/mount.h>
22 #include <sys/stat.h>
23 #include <sys/statvfs.h>
24 #include <sys/types.h>
25 #include <sys/vfs.h>
26 #include <unistd.h>
27 
28 #include <android-base/file.h>
29 #include <android-base/logging.h>
30 #include <android-base/macros.h>
31 #include <android-base/properties.h>
32 #include <android-base/scopeguard.h>
33 #include <android-base/strings.h>
34 #include <android-base/unique_fd.h>
35 #include <ext4_utils/ext4_utils.h>
36 
37 #include <libsnapshot/snapshot.h>
38 
39 #include <fs_mgr.h>
40 #include <fs_mgr_dm_linear.h>
41 #include <fstab/fstab.h>
42 #include <liblp/builder.h>
43 #include <storage_literals/storage_literals.h>
44 #include <algorithm>
45 #include <filesystem>
46 #include <memory>
47 #include <optional>
48 #include <string>
49 #include <vector>
50 
51 #include "device_info.h"
52 #include "scratch_super.h"
53 
54 using namespace std::literals;
55 using namespace android::dm;
56 using namespace android::fs_mgr;
57 using namespace android::storage_literals;
58 
59 namespace android {
60 namespace snapshot {
61 
UmountScratch()62 static bool UmountScratch() {
63     auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
64     std::error_code ec;
65 
66     if (std::filesystem::remove_all(ota_dir, ec) == static_cast<std::uintmax_t>(-1)) {
67         LOG(ERROR) << "Failed to remove OTA directory: " << ec.message();
68         return false;
69     }
70 
71     if (umount(kOtaMetadataMount) != 0) {
72         PLOG(ERROR) << "UmountScratch failed";
73         return false;
74     }
75 
76     LOG(INFO) << "umount scratch_super success";
77     return true;
78 }
79 
CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo * info)80 bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info) {
81     if (!UmountScratch()) {
82         return false;
83     }
84 
85     std::unique_ptr<MetadataBuilder> builder;
86     const auto partition_name = android::base::Basename(kOtaMetadataMount);
87     const std::vector<int> slots = {0, 1};
88 
89     if (info == nullptr) {
90         info = new android::snapshot::DeviceInfo();
91     }
92 
93     std::string super_device;
94     if (info->IsTestDevice()) {
95         super_device = "super";
96     } else {
97         super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
98     }
99     const auto& opener = info->GetPartitionOpener();
100     std::string slot_suffix = info->GetSlotSuffix();
101     // Walk both the slots and clean up metadata related to scratch space from
102     // both the slots.
103     for (auto slot : slots) {
104         std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
105         if (!builder) {
106             return false;
107         }
108 
109         if (builder->FindPartition(partition_name) != nullptr) {
110             builder->RemovePartition(partition_name);
111             auto metadata = builder->Export();
112             if (!metadata) {
113                 return false;
114             }
115             if (!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(),
116                                       slot)) {
117                 LOG(ERROR) << "UpdatePartitionTable failed for slot: " << slot;
118                 return false;
119             }
120             if (DestroyLogicalPartition(partition_name)) {
121                 LOG(INFO) << "CleanupScratchOtaMetadata success for slot: " << slot;
122             }
123         }
124     }
125 
126     return true;
127 }
128 
SetupOTADirs()129 static bool SetupOTADirs() {
130     if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
131         PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
132         return false;
133     }
134     const auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
135     if (mkdir(ota_dir.c_str(), 0755) != 0 && errno != EEXIST) {
136         PLOG(ERROR) << "mkdir " << ota_dir;
137         return false;
138     }
139 
140     const auto snapshot_dir = ota_dir + "/" + "snapshots";
141     if (mkdir(snapshot_dir.c_str(), 0755) != 0 && errno != EEXIST) {
142         PLOG(ERROR) << "mkdir " << snapshot_dir;
143         return false;
144     }
145     if (setfscreatecon(nullptr)) {
146         PLOG(ERROR) << "setfscreatecon null";
147         return false;
148     }
149     return true;
150 }
151 
MountScratch(const std::string & device_path)152 static bool MountScratch(const std::string& device_path) {
153     if (access(device_path.c_str(), R_OK | W_OK)) {
154         LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
155         return false;
156     }
157 
158     std::string filesystem_candidate;
159     if (fs_mgr_is_ext4(device_path)) {
160         filesystem_candidate = "ext4";
161     } else {
162         LOG(ERROR) << "Scratch partition is not ext4";
163         return false;
164     }
165     if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
166         PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
167         return false;
168     }
169     if (mkdir(kOtaMetadataMount, 0755) && (errno != EEXIST)) {
170         PLOG(ERROR) << "create " << kOtaMetadataMount;
171         return false;
172     }
173 
174     android::fs_mgr::FstabEntry entry;
175     entry.blk_device = device_path;
176     entry.mount_point = kOtaMetadataMount;
177     entry.flags = MS_NOATIME;
178     entry.flags |= MS_SYNCHRONOUS;
179     entry.fs_options = "nodiscard";
180     fs_mgr_set_blk_ro(device_path, false);
181     entry.fs_mgr_flags.check = true;
182 
183     bool mounted = false;
184     entry.fs_type = filesystem_candidate.c_str();
185     if (fs_mgr_do_mount_one(entry) == 0) {
186         mounted = true;
187     }
188 
189     if (setfscreatecon(nullptr)) {
190         PLOG(ERROR) << "setfscreatecon null";
191         return false;
192     }
193     if (!mounted) {
194         rmdir(kOtaMetadataMount);
195         return false;
196     }
197 
198     return true;
199 }
200 
MakeScratchFilesystem(const std::string & scratch_device)201 static bool MakeScratchFilesystem(const std::string& scratch_device) {
202     std::string fs_type;
203     std::string command;
204     if (!access(kMkExt4, X_OK)) {
205         fs_type = "ext4";
206         command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kOtaMetadataMount;
207     } else {
208         LOG(ERROR) << "No supported mkfs command or filesystem driver available, supported "
209                       "filesystems "
210                       "are: f2fs, ext4";
211         return false;
212     }
213     command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
214     fs_mgr_set_blk_ro(scratch_device, false);
215     auto ret = system(command.c_str());
216     if (ret) {
217         LOG(ERROR) << "make " << fs_type << " filesystem on " << scratch_device
218                    << " return=" << ret;
219         return false;
220     }
221     return true;
222 }
223 
CreateDynamicScratch(const ISnapshotManager::IDeviceInfo * info,std::string * scratch_device)224 static bool CreateDynamicScratch(const ISnapshotManager::IDeviceInfo* info,
225                                  std::string* scratch_device) {
226     const auto partition_name = android::base::Basename(kOtaMetadataMount);
227     auto& dm = DeviceMapper::Instance();
228     if (info == nullptr) {
229         info = new android::snapshot::DeviceInfo();
230     }
231 
232     std::string super_device;
233     if (info->IsTestDevice()) {
234         super_device = "super";
235     } else {
236         super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
237     }
238 
239     bool partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
240     if (partition_exists) {
241         LOG(ERROR) << "Partition already exists: " << partition_name;
242         return false;
243     }
244 
245     const auto& opener = info->GetPartitionOpener();
246     std::string slot_suffix = info->GetSlotSuffix();
247     int slot = SlotNumberForSlotSuffix(slot_suffix);
248     std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
249 
250     if (!builder) {
251         LOG(ERROR) << "open " << super_device << " failed";
252         return false;
253     }
254 
255     auto partition = builder->FindPartition(partition_name);
256     partition_exists = partition != nullptr;
257     if (partition_exists) {
258         LOG(ERROR) << "Partition exists in super metadata";
259         return false;
260     }
261 
262     partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
263     if (!partition) {
264         LOG(ERROR) << "AddPartition failed " << partition_name;
265         return false;
266     }
267 
268     auto free_space = builder->AllocatableSpace() - builder->UsedSpace();
269     if (free_space < kOtaMetadataPartitionSize) {
270         LOG(ERROR) << "No space in super partition. Free space: " << free_space
271                    << " Requested space: " << kOtaMetadataPartitionSize;
272         return false;
273     }
274 
275     LOG(INFO) << "CreateDynamicScratch: free_space: " << free_space
276               << " scratch_size: " << kOtaMetadataPartitionSize << " slot_number: " << slot;
277 
278     if (!builder->ResizePartition(partition, kOtaMetadataPartitionSize)) {
279         LOG(ERROR) << "ResizePartition failed: " << partition_name << " free_space: " << free_space
280                    << " scratch_size: " << kOtaMetadataPartitionSize;
281         return false;
282     }
283 
284     auto metadata = builder->Export();
285     CreateLogicalPartitionParams params;
286 
287     if (!metadata ||
288         !UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(), slot)) {
289         LOG(ERROR) << "UpdatePartitionTable failed: " << partition_name;
290         return false;
291     }
292     params = {
293             .block_device = super_device,
294             .metadata_slot = slot,
295             .partition_name = partition_name,
296             .force_writable = true,
297             .timeout_ms = 10s,
298             .partition_opener = &info->GetPartitionOpener(),
299     };
300 
301     if (!CreateLogicalPartition(params, scratch_device)) {
302         LOG(ERROR) << "CreateLogicalPartition failed";
303         return false;
304     }
305 
306     LOG(INFO) << "Scratch device created successfully: " << *scratch_device << " slot: " << slot;
307     return true;
308 }
309 
IsScratchOtaMetadataOnSuper()310 bool IsScratchOtaMetadataOnSuper() {
311     auto partition_name = android::base::Basename(kOtaMetadataMount);
312     auto source_slot = fs_mgr_get_slot_suffix();
313     auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
314 
315     const auto super_device =
316             kPhysicalDevice + fs_mgr_get_super_partition_name(!source_slot_number);
317 
318     auto metadata = android::fs_mgr::ReadMetadata(super_device, !source_slot_number);
319     if (!metadata) {
320         return false;
321     }
322     auto partition = android::fs_mgr::FindPartition(*metadata.get(), partition_name);
323     if (!partition) {
324         return false;
325     }
326 
327     auto& dm = DeviceMapper::Instance();
328     if (dm.GetState(partition_name) == DmDeviceState::ACTIVE) {
329         LOG(INFO) << "Partition: " << partition_name << " is active";
330         return true;
331     }
332 
333     CreateLogicalPartitionParams params = {
334             .block_device = super_device,
335             .metadata = metadata.get(),
336             .partition = partition,
337     };
338 
339     std::string scratch_path;
340     if (!CreateLogicalPartition(params, &scratch_path)) {
341         LOG(ERROR) << "Could not create logical partition: " << partition_name;
342         return false;
343     }
344     LOG(INFO) << "Scratch device: " << scratch_path << " created successfully";
345 
346     return true;
347 }
348 
GetScratchOtaMetadataPartition()349 std::string GetScratchOtaMetadataPartition() {
350     std::string device;
351     auto& dm = DeviceMapper::Instance();
352     auto partition_name = android::base::Basename(kOtaMetadataMount);
353 
354     bool invalid_partition = (dm.GetState(partition_name) == DmDeviceState::INVALID);
355     if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &device)) {
356         return device;
357     }
358     return "";
359 }
360 
ScratchAlreadyMounted(const std::string & mount_point)361 static bool ScratchAlreadyMounted(const std::string& mount_point) {
362     android::fs_mgr::Fstab fstab;
363     if (!ReadFstabFromProcMounts(&fstab)) {
364         return false;
365     }
366     for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
367         if (entry->fs_type == "ext4") {
368             return true;
369         }
370     }
371     return false;
372 }
373 
MapScratchOtaMetadataPartition(const std::string & scratch_device)374 std::string MapScratchOtaMetadataPartition(const std::string& scratch_device) {
375     if (!ScratchAlreadyMounted(kOtaMetadataMount)) {
376         if (!MountScratch(scratch_device)) {
377             return "";
378         }
379     }
380 
381     auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
382     if (access(ota_dir.c_str(), F_OK) != 0) {
383         return "";
384     }
385     return ota_dir;
386 }
387 
388 // Entry point to create a scratch device on super partition
389 // This will create a 1MB space in super. The space will be
390 // from the current active slot. Ext4 filesystem will be created
391 // on this scratch device and all the OTA related directories
392 // will be created.
CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo * info)393 bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info) {
394     std::string scratch_device;
395 
396     if (!CreateDynamicScratch(info, &scratch_device)) {
397         LOG(ERROR) << "CreateDynamicScratch failed";
398         return false;
399     }
400     if (!MakeScratchFilesystem(scratch_device)) {
401         LOG(ERROR) << "MakeScratchFilesystem failed";
402         return false;
403     }
404     if (!MountScratch(scratch_device)) {
405         LOG(ERROR) << "MountScratch failed";
406         return false;
407     }
408     if (!SetupOTADirs()) {
409         LOG(ERROR) << "SetupOTADirs failed";
410         return false;
411     }
412     return true;
413 }
414 
415 }  // namespace snapshot
416 }  // namespace android
417