1 /*
2  * Copyright (C) 2014 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 <ctype.h>
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fnmatch.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/mount.h>
25 #include <unistd.h>
26 
27 #include <algorithm>
28 #include <array>
29 #include <utility>
30 #include <vector>
31 
32 #include <android-base/file.h>
33 #include <android-base/parseint.h>
34 #include <android-base/properties.h>
35 #include <android-base/stringprintf.h>
36 #include <android-base/strings.h>
37 #include <libgsi/libgsi.h>
38 
39 #include "fs_mgr_priv.h"
40 
41 using android::base::EndsWith;
42 using android::base::ParseByteCount;
43 using android::base::ParseInt;
44 using android::base::ReadFileToString;
45 using android::base::Readlink;
46 using android::base::Split;
47 using android::base::StartsWith;
48 
49 namespace android {
50 namespace fs_mgr {
51 namespace {
52 
53 constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
54 
55 struct FlagList {
56     const char *name;
57     uint64_t flag;
58 };
59 
60 FlagList kMountFlagsList[] = {
61         {"noatime", MS_NOATIME},
62         {"noexec", MS_NOEXEC},
63         {"nosuid", MS_NOSUID},
64         {"nodev", MS_NODEV},
65         {"nodiratime", MS_NODIRATIME},
66         {"ro", MS_RDONLY},
67         {"rw", 0},
68         {"sync", MS_SYNCHRONOUS},
69         {"remount", MS_REMOUNT},
70         {"bind", MS_BIND},
71         {"rec", MS_REC},
72         {"unbindable", MS_UNBINDABLE},
73         {"private", MS_PRIVATE},
74         {"slave", MS_SLAVE},
75         {"shared", MS_SHARED},
76         {"defaults", 0},
77 };
78 
CalculateZramSize(int percentage)79 off64_t CalculateZramSize(int percentage) {
80     off64_t total;
81 
82     total  = sysconf(_SC_PHYS_PAGES);
83     total *= percentage;
84     total /= 100;
85 
86     total *= sysconf(_SC_PAGESIZE);
87 
88     return total;
89 }
90 
91 // Fills 'dt_value' with the underlying device tree value string without the trailing '\0'.
92 // Returns true if 'dt_value' has a valid string, 'false' otherwise.
ReadDtFile(const std::string & file_name,std::string * dt_value)93 bool ReadDtFile(const std::string& file_name, std::string* dt_value) {
94     if (android::base::ReadFileToString(file_name, dt_value)) {
95         if (!dt_value->empty()) {
96             // Trim the trailing '\0' out, otherwise the comparison will produce false-negatives.
97             dt_value->resize(dt_value->size() - 1);
98             return true;
99         }
100     }
101 
102     return false;
103 }
104 
ParseFileEncryption(const std::string & arg,FstabEntry * entry)105 void ParseFileEncryption(const std::string& arg, FstabEntry* entry) {
106     entry->fs_mgr_flags.file_encryption = true;
107     entry->encryption_options = arg;
108 }
109 
SetMountFlag(const std::string & flag,FstabEntry * entry)110 bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
111     for (const auto& [name, value] : kMountFlagsList) {
112         if (flag == name) {
113             entry->flags |= value;
114             return true;
115         }
116     }
117     return false;
118 }
119 
ParseMountFlags(const std::string & flags,FstabEntry * entry)120 void ParseMountFlags(const std::string& flags, FstabEntry* entry) {
121     std::string fs_options;
122     for (const auto& flag : Split(flags, ",")) {
123         if (!SetMountFlag(flag, entry)) {
124             // Unknown flag, so it must be a filesystem specific option.
125             if (!fs_options.empty()) {
126                 fs_options.append(",");  // appends a comma if not the first
127             }
128             fs_options.append(flag);
129 
130             if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
131                 const auto arg = flag.substr(equal_sign + 1);
132                 if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
133                     if (!ParseInt(arg, &entry->reserved_size)) {
134                         LWARNING << "Warning: reserve_root= flag malformed: " << arg;
135                     } else {
136                         entry->reserved_size <<= 12;
137                     }
138                 } else if (StartsWith(flag, "lowerdir=")) {
139                     entry->lowerdir = std::move(arg);
140                 }
141             }
142         }
143     }
144     entry->fs_options = std::move(fs_options);
145 }
146 
ParseFsMgrFlags(const std::string & flags,FstabEntry * entry)147 void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
148     for (const auto& flag : Split(flags, ",")) {
149         if (flag.empty() || flag == "defaults") continue;
150         std::string arg;
151         if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
152             arg = flag.substr(equal_sign + 1);
153         }
154 
155         // First handle flags that simply set a boolean.
156 #define CheckFlag(flag_name, value)       \
157     if (flag == flag_name) {              \
158         entry->fs_mgr_flags.value = true; \
159         continue;                         \
160     }
161 
162         CheckFlag("wait", wait);
163         CheckFlag("check", check);
164         CheckFlag("nonremovable", nonremovable);
165         CheckFlag("recoveryonly", recovery_only);
166         CheckFlag("noemulatedsd", no_emulated_sd);
167         CheckFlag("notrim", no_trim);
168         CheckFlag("verify", verify);
169         CheckFlag("formattable", formattable);
170         CheckFlag("slotselect", slot_select);
171         CheckFlag("latemount", late_mount);
172         CheckFlag("nofail", no_fail);
173         CheckFlag("verifyatboot", verify_at_boot);
174         CheckFlag("quota", quota);
175         CheckFlag("avb", avb);
176         CheckFlag("logical", logical);
177         CheckFlag("checkpoint=block", checkpoint_blk);
178         CheckFlag("checkpoint=fs", checkpoint_fs);
179         CheckFlag("first_stage_mount", first_stage_mount);
180         CheckFlag("slotselect_other", slot_select_other);
181         CheckFlag("fsverity", fs_verity);
182         CheckFlag("metadata_csum", ext_meta_csum);
183         CheckFlag("fscompress", fs_compress);
184 
185 #undef CheckFlag
186 
187         // Then handle flags that take an argument.
188         if (StartsWith(flag, "encryptable=")) {
189             // The encryptable flag is followed by an = and the  location of the keys.
190             entry->fs_mgr_flags.crypt = true;
191             entry->key_loc = arg;
192         } else if (StartsWith(flag, "voldmanaged=")) {
193             // The voldmanaged flag is followed by an = and the label, a colon and the partition
194             // number or the word "auto", e.g. voldmanaged=sdcard:3
195             entry->fs_mgr_flags.vold_managed = true;
196             auto parts = Split(arg, ":");
197             if (parts.size() != 2) {
198                 LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
199                 continue;
200             }
201 
202             entry->label = std::move(parts[0]);
203             if (parts[1] == "auto") {
204                 entry->partnum = -1;
205             } else {
206                 if (!ParseInt(parts[1], &entry->partnum)) {
207                     entry->partnum = -1;
208                     LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
209                     continue;
210                 }
211             }
212         } else if (StartsWith(flag, "length=")) {
213             // The length flag is followed by an = and the size of the partition.
214             if (!ParseInt(arg, &entry->length)) {
215                 LWARNING << "Warning: length= flag malformed: " << arg;
216             }
217         } else if (StartsWith(flag, "swapprio=")) {
218             if (!ParseInt(arg, &entry->swap_prio)) {
219                 LWARNING << "Warning: swapprio= flag malformed: " << arg;
220             }
221         } else if (StartsWith(flag, "zramsize=")) {
222             if (!arg.empty() && arg.back() == '%') {
223                 arg.pop_back();
224                 int val;
225                 if (ParseInt(arg, &val, 0, 100)) {
226                     entry->zram_size = CalculateZramSize(val);
227                 } else {
228                     LWARNING << "Warning: zramsize= flag malformed: " << arg;
229                 }
230             } else {
231                 if (!ParseInt(arg, &entry->zram_size)) {
232                     LWARNING << "Warning: zramsize= flag malformed: " << arg;
233                 }
234             }
235         } else if (StartsWith(flag, "forceencrypt=")) {
236             // The forceencrypt flag is followed by an = and the location of the keys.
237             entry->fs_mgr_flags.force_crypt = true;
238             entry->key_loc = arg;
239         } else if (StartsWith(flag, "fileencryption=")) {
240             ParseFileEncryption(arg, entry);
241         } else if (StartsWith(flag, "forcefdeorfbe=")) {
242             // The forcefdeorfbe flag is followed by an = and the location of the keys.  Get it and
243             // return it.
244             entry->fs_mgr_flags.force_fde_or_fbe = true;
245             entry->key_loc = arg;
246             entry->encryption_options = "aes-256-xts:aes-256-cts";
247         } else if (StartsWith(flag, "max_comp_streams=")) {
248             if (!ParseInt(arg, &entry->max_comp_streams)) {
249                 LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
250             }
251         } else if (StartsWith(flag, "reservedsize=")) {
252             // The reserved flag is followed by an = and the reserved size of the partition.
253             uint64_t size;
254             if (!ParseByteCount(arg, &size)) {
255                 LWARNING << "Warning: reservedsize= flag malformed: " << arg;
256             } else {
257                 entry->reserved_size = static_cast<off64_t>(size);
258             }
259         } else if (StartsWith(flag, "readahead_size_kb=")) {
260             int val;
261             if (ParseInt(arg, &val, 0, 16 * 1024)) {
262                 entry->readahead_size_kb = val;
263             } else {
264                 LWARNING << "Warning: readahead_size_kb= flag malformed (0 ~ 16MB): " << arg;
265             }
266         } else if (StartsWith(flag, "eraseblk=")) {
267             // The erase block size flag is followed by an = and the flash erase block size. Get it,
268             // check that it is a power of 2 and at least 4096, and return it.
269             off64_t val;
270             if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
271                 LWARNING << "Warning: eraseblk= flag malformed: " << arg;
272             } else {
273                 entry->erase_blk_size = val;
274             }
275         } else if (StartsWith(flag, "logicalblk=")) {
276             // The logical block size flag is followed by an = and the flash logical block size. Get
277             // it, check that it is a power of 2 and at least 4096, and return it.
278             off64_t val;
279             if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
280                 LWARNING << "Warning: logicalblk= flag malformed: " << arg;
281             } else {
282                 entry->logical_blk_size = val;
283             }
284         } else if (StartsWith(flag, "avb_keys=")) {  // must before the following "avb"
285             entry->avb_keys = arg;
286         } else if (StartsWith(flag, "avb")) {
287             entry->fs_mgr_flags.avb = true;
288             entry->vbmeta_partition = arg;
289         } else if (StartsWith(flag, "keydirectory=")) {
290             // The metadata flag is followed by an = and the directory for the keys.
291             entry->metadata_key_dir = arg;
292         } else if (StartsWith(flag, "metadata_encryption=")) {
293             // Specify the cipher and flags to use for metadata encryption
294             entry->metadata_encryption = arg;
295         } else if (StartsWith(flag, "sysfs_path=")) {
296             // The path to trigger device gc by idle-maint of vold.
297             entry->sysfs_path = arg;
298         } else if (StartsWith(flag, "zram_backingdev_size=")) {
299             if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
300                 LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
301             }
302         } else {
303             LWARNING << "Warning: unknown flag: " << flag;
304         }
305     }
306 }
307 
InitAndroidDtDir()308 std::string InitAndroidDtDir() {
309     std::string android_dt_dir;
310     // The platform may specify a custom Android DT path in kernel cmdline
311     if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
312         !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
313         // Fall back to the standard procfs-based path
314         android_dt_dir = kDefaultAndroidDtDir;
315     }
316     return android_dt_dir;
317 }
318 
IsDtFstabCompatible()319 bool IsDtFstabCompatible() {
320     std::string dt_value;
321     std::string file_name = get_android_dt_dir() + "/fstab/compatible";
322 
323     if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
324         // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
325         std::string status_value;
326         std::string status_file_name = get_android_dt_dir() + "/fstab/status";
327         return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
328                status_value == "okay";
329     }
330 
331     return false;
332 }
333 
ReadFstabFromDt()334 std::string ReadFstabFromDt() {
335     if (!is_dt_compatible() || !IsDtFstabCompatible()) {
336         return {};
337     }
338 
339     std::string fstabdir_name = get_android_dt_dir() + "/fstab";
340     std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
341     if (!fstabdir) return {};
342 
343     dirent* dp;
344     // Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
345     std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
346     while ((dp = readdir(fstabdir.get())) != NULL) {
347         // skip over name, compatible and .
348         if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
349 
350         // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
351         std::vector<std::string> fstab_entry;
352         std::string file_name;
353         std::string value;
354         // skip a partition entry if the status property is present and not set to ok
355         file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
356         if (ReadDtFile(file_name, &value)) {
357             if (value != "okay" && value != "ok") {
358                 LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
359                 continue;
360             }
361         }
362 
363         file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
364         if (!ReadDtFile(file_name, &value)) {
365             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
366             return {};
367         }
368         fstab_entry.push_back(value);
369 
370         std::string mount_point;
371         file_name =
372             android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
373         if (ReadDtFile(file_name, &value)) {
374             LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
375             mount_point = value;
376         } else {
377             mount_point = android::base::StringPrintf("/%s", dp->d_name);
378         }
379         fstab_entry.push_back(mount_point);
380 
381         file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
382         if (!ReadDtFile(file_name, &value)) {
383             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
384             return {};
385         }
386         fstab_entry.push_back(value);
387 
388         file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
389         if (!ReadDtFile(file_name, &value)) {
390             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
391             return {};
392         }
393         fstab_entry.push_back(value);
394 
395         file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
396         if (!ReadDtFile(file_name, &value)) {
397             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
398             return {};
399         }
400         fstab_entry.push_back(value);
401         // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.
402         fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
403     }
404 
405     // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.
406     std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
407               [](const auto& a, const auto& b) { return a.first < b.first; });
408 
409     std::string fstab_result;
410     for (const auto& [_, dt_entry] : fstab_dt_entries) {
411         fstab_result += dt_entry + "\n";
412     }
413     return fstab_result;
414 }
415 
416 // Return the path to the fstab file.  There may be multiple fstab files; the
417 // one that is returned will be the first that exists of fstab.<fstab_suffix>,
418 // fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
419 // in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
420 // the first stage ramdisk during early boot.  Previously, the first stage
421 // ramdisk's copy of the fstab had to be located in the root directory, but now
422 // the system/etc directory is supported too and is the preferred location.
GetFstabPath()423 std::string GetFstabPath() {
424     for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
425         std::string suffix;
426 
427         if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
428 
429         for (const char* prefix : {// late-boot/post-boot locations
430                                    "/odm/etc/fstab.", "/vendor/etc/fstab.",
431                                    // early boot locations
432                                    "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
433                                    "/fstab.", "/first_stage_ramdisk/fstab."}) {
434             std::string fstab_path = prefix + suffix;
435             if (access(fstab_path.c_str(), F_OK) == 0) {
436                 return fstab_path;
437             }
438         }
439     }
440 
441     return "";
442 }
443 
ReadFstabFile(FILE * fstab_file,bool proc_mounts,Fstab * fstab_out)444 bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
445     ssize_t len;
446     size_t alloc_len = 0;
447     char *line = NULL;
448     const char *delim = " \t";
449     char *save_ptr, *p;
450     Fstab fstab;
451 
452     while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
453         /* if the last character is a newline, shorten the string by 1 byte */
454         if (line[len - 1] == '\n') {
455             line[len - 1] = '\0';
456         }
457 
458         /* Skip any leading whitespace */
459         p = line;
460         while (isspace(*p)) {
461             p++;
462         }
463         /* ignore comments or empty lines */
464         if (*p == '#' || *p == '\0')
465             continue;
466 
467         FstabEntry entry;
468 
469         if (!(p = strtok_r(line, delim, &save_ptr))) {
470             LERROR << "Error parsing mount source";
471             goto err;
472         }
473         entry.blk_device = p;
474 
475         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
476             LERROR << "Error parsing mount_point";
477             goto err;
478         }
479         entry.mount_point = p;
480 
481         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
482             LERROR << "Error parsing fs_type";
483             goto err;
484         }
485         entry.fs_type = p;
486 
487         if (!(p = strtok_r(NULL, delim, &save_ptr))) {
488             LERROR << "Error parsing mount_flags";
489             goto err;
490         }
491 
492         ParseMountFlags(p, &entry);
493 
494         // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
495         if (proc_mounts) {
496             p += strlen(p);
497         } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
498             LERROR << "Error parsing fs_mgr_options";
499             goto err;
500         }
501 
502         ParseFsMgrFlags(p, &entry);
503 
504         if (entry.fs_mgr_flags.logical) {
505             entry.logical_partition_name = entry.blk_device;
506         }
507 
508         fstab.emplace_back(std::move(entry));
509     }
510 
511     if (fstab.empty()) {
512         LERROR << "No entries found in fstab";
513         goto err;
514     }
515 
516     /* If an A/B partition, modify block device to be the real block device */
517     if (!fs_mgr_update_for_slotselect(&fstab)) {
518         LERROR << "Error updating for slotselect";
519         goto err;
520     }
521     free(line);
522     *fstab_out = std::move(fstab);
523     return true;
524 
525 err:
526     free(line);
527     return false;
528 }
529 
530 /* Extracts <device>s from the by-name symlinks specified in a fstab:
531  *   /dev/block/<type>/<device>/by-name/<partition>
532  *
533  * <type> can be: platform, pci or vbd.
534  *
535  * For example, given the following entries in the input fstab:
536  *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
537  *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
538  * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
539  */
ExtraBootDevices(const Fstab & fstab)540 std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
541     std::set<std::string> boot_devices;
542 
543     for (const auto& entry : fstab) {
544         std::string blk_device = entry.blk_device;
545         // Skips blk_device that doesn't conform to the format.
546         if (!android::base::StartsWith(blk_device, "/dev/block") ||
547             android::base::StartsWith(blk_device, "/dev/block/by-name") ||
548             android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
549             continue;
550         }
551         // Skips non-by_name blk_device.
552         // /dev/block/<type>/<device>/by-name/<partition>
553         //                           ^ slash_by_name
554         auto slash_by_name = blk_device.find("/by-name");
555         if (slash_by_name == std::string::npos) continue;
556         blk_device.erase(slash_by_name);  // erases /by-name/<partition>
557 
558         // Erases /dev/block/, now we have <type>/<device>
559         blk_device.erase(0, std::string("/dev/block/").size());
560 
561         // <type>/<device>
562         //       ^ first_slash
563         auto first_slash = blk_device.find('/');
564         if (first_slash == std::string::npos) continue;
565 
566         auto boot_device = blk_device.substr(first_slash + 1);
567         if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
568     }
569 
570     return boot_devices;
571 }
572 
BuildDsuUserdataFstabEntry()573 FstabEntry BuildDsuUserdataFstabEntry() {
574     constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
575 
576     FstabEntry userdata = {
577             .blk_device = "userdata_gsi",
578             .mount_point = "/data",
579             .fs_type = "ext4",
580             .flags = kFlags,
581             .reserved_size = 128 * 1024 * 1024,
582     };
583     userdata.fs_mgr_flags.wait = true;
584     userdata.fs_mgr_flags.check = true;
585     userdata.fs_mgr_flags.logical = true;
586     userdata.fs_mgr_flags.quota = true;
587     userdata.fs_mgr_flags.late_mount = true;
588     userdata.fs_mgr_flags.formattable = true;
589     return userdata;
590 }
591 
EraseFstabEntry(Fstab * fstab,const std::string & mount_point)592 bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
593     auto iter = std::remove_if(fstab->begin(), fstab->end(),
594                                [&](const auto& entry) { return entry.mount_point == mount_point; });
595     if (iter != fstab->end()) {
596         fstab->erase(iter, fstab->end());
597         return true;
598     }
599     return false;
600 }
601 
602 }  // namespace
603 
TransformFstabForDsu(Fstab * fstab,const std::string & dsu_slot,const std::vector<std::string> & dsu_partitions)604 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
605                           const std::vector<std::string>& dsu_partitions) {
606     static constexpr char kDsuKeysDir[] = "/avb";
607     // Convert userdata
608     // Inherit fstab properties for userdata.
609     FstabEntry userdata;
610     if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
611         userdata = *entry;
612         userdata.blk_device = android::gsi::kDsuUserdata;
613         userdata.fs_mgr_flags.logical = true;
614         userdata.fs_mgr_flags.formattable = true;
615         if (!userdata.metadata_key_dir.empty()) {
616             userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
617         }
618     } else {
619         userdata = BuildDsuUserdataFstabEntry();
620     }
621 
622     if (EraseFstabEntry(fstab, "/data")) {
623         fstab->emplace_back(userdata);
624     }
625 
626     // Convert others
627     for (auto&& partition : dsu_partitions) {
628         if (!EndsWith(partition, gsi::kDsuPostfix)) {
629             continue;
630         }
631         // userdata has been handled
632         if (partition == android::gsi::kDsuUserdata) {
633             continue;
634         }
635         // scratch is handled by fs_mgr_overlayfs
636         if (partition == android::gsi::kDsuScratch) {
637             continue;
638         }
639         // dsu_partition_name = corresponding_partition_name + kDsuPostfix
640         // e.g.
641         //    system_gsi for system
642         //    product_gsi for product
643         //    vendor_gsi for vendor
644         std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
645         std::string mount_point = "/" + lp_name;
646         std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
647         if (entries.empty()) {
648             FstabEntry entry = {
649                     .blk_device = partition,
650                     // .logical_partition_name is required to look up AVB Hashtree descriptors.
651                     .logical_partition_name = "system",
652                     .mount_point = mount_point,
653                     .fs_type = "ext4",
654                     .flags = MS_RDONLY,
655                     .fs_options = "barrier=1",
656                     .avb_keys = kDsuKeysDir,
657             };
658             entry.fs_mgr_flags.wait = true;
659             entry.fs_mgr_flags.logical = true;
660             entry.fs_mgr_flags.first_stage_mount = true;
661             fstab->emplace_back(entry);
662         } else {
663             // If the corresponding partition exists, transform all its Fstab
664             // by pointing .blk_device to the DSU partition.
665             for (auto&& entry : entries) {
666                 entry->blk_device = partition;
667                 // AVB keys for DSU should always be under kDsuKeysDir.
668                 entry->avb_keys = kDsuKeysDir;
669             }
670             // Make sure the ext4 is included to support GSI.
671             auto partition_ext4 =
672                     std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
673                         return entry.mount_point == mount_point && entry.fs_type == "ext4";
674                     });
675             if (partition_ext4 == fstab->end()) {
676                 auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
677                 new_entry.fs_type = "ext4";
678                 fstab->emplace_back(new_entry);
679             }
680         }
681     }
682 }
683 
EnableMandatoryFlags(Fstab * fstab)684 void EnableMandatoryFlags(Fstab* fstab) {
685     // Devices launched in R and after should enable fs_verity on userdata. The flag causes tune2fs
686     // to enable the feature. A better alternative would be to enable on mkfs at the beginning.
687     if (android::base::GetIntProperty("ro.product.first_api_level", 0) >= 30) {
688         std::vector<FstabEntry*> data_entries = GetEntriesForMountPoint(fstab, "/data");
689         for (auto&& entry : data_entries) {
690             // Besides ext4, f2fs is also supported. But the image is already created with verity
691             // turned on when it was first introduced.
692             if (entry->fs_type == "ext4") {
693                 entry->fs_mgr_flags.fs_verity = true;
694             }
695         }
696     }
697 }
698 
ReadFstabFromFile(const std::string & path,Fstab * fstab_out)699 bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
700     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
701     if (!fstab_file) {
702         PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
703         return false;
704     }
705 
706     bool is_proc_mounts = path == "/proc/mounts";
707 
708     Fstab fstab;
709     if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
710         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
711         return false;
712     }
713     if (!is_proc_mounts) {
714         if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
715             // This is expected to fail if host is android Q, since Q doesn't
716             // support DSU slotting. The DSU "active" indicator file would be
717             // non-existent or empty if DSU is enabled within the guest system.
718             // In that case, just use the default slot name "dsu".
719             std::string dsu_slot;
720             if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
721                 PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
722                 return false;
723             }
724             if (dsu_slot.empty()) {
725                 dsu_slot = "dsu";
726                 LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
727             }
728             // This file is non-existent on Q vendor.
729             std::string lp_names;
730             if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
731                 PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
732                 return false;
733             }
734             TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
735         } else if (errno != ENOENT) {
736             PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
737             return false;
738         }
739     }
740 
741     SkipMountingPartitions(&fstab, false /* verbose */);
742     EnableMandatoryFlags(&fstab);
743 
744     *fstab_out = std::move(fstab);
745     return true;
746 }
747 
748 // Returns fstab entries parsed from the device tree if they exist
ReadFstabFromDt(Fstab * fstab,bool verbose)749 bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
750     std::string fstab_buf = ReadFstabFromDt();
751     if (fstab_buf.empty()) {
752         if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
753         return false;
754     }
755 
756     std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
757         fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
758                  fstab_buf.length(), "r"), fclose);
759     if (!fstab_file) {
760         if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
761         return false;
762     }
763 
764     if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
765         if (verbose) {
766             LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
767                    << fstab_buf;
768         }
769         return false;
770     }
771 
772     SkipMountingPartitions(fstab, verbose);
773 
774     return true;
775 }
776 
777 #ifdef NO_SKIP_MOUNT
SkipMountingPartitions(Fstab *,bool)778 bool SkipMountingPartitions(Fstab*, bool) {
779     return true;
780 }
781 #else
782 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
783 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
784 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
785 // only common files for all targets can be put into system partition. It is under
786 // /system/system_ext because GSI is a single system.img that includes the contents of system_ext
787 // partition and product partition under /system/system_ext and /system/product, respectively.
SkipMountingPartitions(Fstab * fstab,bool verbose)788 bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
789     static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
790 
791     std::string skip_config;
792     auto save_errno = errno;
793     if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
794         errno = save_errno;  // missing file is expected
795         return true;
796     }
797 
798     std::vector<std::string> skip_mount_patterns;
799     for (const auto& line : Split(skip_config, "\n")) {
800         if (line.empty() || StartsWith(line, "#")) {
801             continue;
802         }
803         skip_mount_patterns.push_back(line);
804     }
805 
806     // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry
807     // would be partitioned to the second group.
808     auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool {
809         for (const auto& pattern : skip_mount_patterns) {
810             if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) {
811                 return false;
812             }
813         }
814         return true;
815     };
816     auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch);
817     if (verbose) {
818         for (auto it = remove_from; it != fstab->end(); ++it) {
819             LINFO << "Skip mounting mountpoint: " << it->mount_point;
820         }
821     }
822     fstab->erase(remove_from, fstab->end());
823     return true;
824 }
825 #endif
826 
827 // Loads the fstab file and combines with fstab entries passed in from device tree.
ReadDefaultFstab(Fstab * fstab)828 bool ReadDefaultFstab(Fstab* fstab) {
829     fstab->clear();
830     ReadFstabFromDt(fstab, false /* verbose */);
831 
832     std::string default_fstab_path;
833     // Use different fstab paths for normal boot and recovery boot, respectively
834     if (access("/system/bin/recovery", F_OK) == 0) {
835         default_fstab_path = "/etc/recovery.fstab";
836     } else {  // normal boot
837         default_fstab_path = GetFstabPath();
838     }
839 
840     Fstab default_fstab;
841     if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
842         for (auto&& entry : default_fstab) {
843             fstab->emplace_back(std::move(entry));
844         }
845     } else {
846         LINFO << __FUNCTION__ << "(): failed to find device default fstab";
847     }
848 
849     return !fstab->empty();
850 }
851 
GetEntryForMountPoint(Fstab * fstab,const std::string & path)852 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
853     if (fstab == nullptr) {
854         return nullptr;
855     }
856 
857     for (auto& entry : *fstab) {
858         if (entry.mount_point == path) {
859             return &entry;
860         }
861     }
862 
863     return nullptr;
864 }
865 
GetEntriesForMountPoint(Fstab * fstab,const std::string & path)866 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
867     std::vector<FstabEntry*> entries;
868     if (fstab == nullptr) {
869         return entries;
870     }
871 
872     for (auto& entry : *fstab) {
873         if (entry.mount_point == path) {
874             entries.emplace_back(&entry);
875         }
876     }
877 
878     return entries;
879 }
880 
GetBootDevices()881 std::set<std::string> GetBootDevices() {
882     // First check bootconfig, then kernel commandline, then the device tree
883     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
884     std::string value;
885     if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
886         fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
887         std::set<std::string> boot_devices;
888         // remove quotes and split by spaces
889         auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
890         for (std::string_view device : boot_device_strings) {
891             // trim the trailing comma, keep the rest.
892             base::ConsumeSuffix(&device, ",");
893             boot_devices.emplace(device);
894         }
895         return boot_devices;
896     }
897 
898     if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
899         ReadDtFile(dt_file_name, &value)) {
900         auto boot_devices = Split(value, ",");
901         return std::set<std::string>(boot_devices.begin(), boot_devices.end());
902     }
903 
904     std::string cmdline;
905     if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
906         std::set<std::string> boot_devices;
907         const std::string cmdline_key = "androidboot.boot_device";
908         for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
909             if (key == cmdline_key) {
910                 boot_devices.emplace(value);
911             }
912         }
913         if (!boot_devices.empty()) {
914             return boot_devices;
915         }
916     }
917 
918     // Fallback to extract boot devices from fstab.
919     Fstab fstab;
920     if (!ReadDefaultFstab(&fstab)) {
921         return {};
922     }
923 
924     return ExtraBootDevices(fstab);
925 }
926 
GetVerityDeviceName(const FstabEntry & entry)927 std::string GetVerityDeviceName(const FstabEntry& entry) {
928     std::string base_device;
929     if (entry.mount_point == "/") {
930         // When using system-as-root, the device name is fixed as "vroot".
931         if (entry.fs_mgr_flags.avb) {
932             return "vroot";
933         }
934         base_device = "system";
935     } else {
936         base_device = android::base::Basename(entry.mount_point);
937     }
938     return base_device + "-verity";
939 }
940 
941 }  // namespace fs_mgr
942 }  // namespace android
943 
944 // FIXME: The same logic is duplicated in system/core/init/
get_android_dt_dir()945 const std::string& get_android_dt_dir() {
946     // Set once and saves time for subsequent calls to this function
947     static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
948     return kAndroidDtDir;
949 }
950 
is_dt_compatible()951 bool is_dt_compatible() {
952     std::string file_name = get_android_dt_dir() + "/compatible";
953     std::string dt_value;
954     if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
955         if (dt_value == "android,firmware") {
956             return true;
957         }
958     }
959 
960     return false;
961 }
962