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