Adds /dev/block/by-name/<partition> symlinks

During uevent processing, some "by-name" symlinks will be created.
    /dev/block/<type>/<device>/by-name/<partition>

<type> can be: platform, pci or vbd.
<device> might be: soc.0/f9824900.sdhci, soc.0/f9824900.sdhci, etc.
<partition> might be: system, vendor, system_a, system_b, etc.

e.g., on a non-A/B device:
    /dev/block/platform/soc.0/f9824900.sdhci/by-name/system
    /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor

On a A/B device:
    /dev/block/platform/soc/1da4000.ufshc/by-name/system_a
    /dev/block/platform/soc/1da4000.ufshc/by-name/system_b
    /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a
    /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_b

However, those symlinks are "device-specific".

This change adds the "generic" symlinks in ueventd, in addition to
the existing symlinks, when the possible "boot devices" are specified
in device tree. e.g.,

    &firmware_android {
	compatible = "android,firmware";
	boot_devices ="soc/1da4000.ufshc,soc.0/f9824900.sdhci";
    }

The following symlinks will then be created on the aforementioned non-A/B
and A/B devices, respectively.

    /dev/block/by-name/system
    /dev/block/by-name/vendor

    /dev/block/by-name/system_a
    /dev/block/by-name/system_b
    /dev/block/by-name/vendor_a
    /dev/block/by-name/vendor_b

Note that both <type> and <device> are skipped in the newly create symlinks.
It assumes there is no more than one devices with the same <partition>,
which is the assumption of current first stage mount flow.

Finally, when 'boot_devices' in DT is absent, it fallbacks to extract
'boot_devices' from fstab settings. e.g., using 'soc/1da4000.ufshc',
'soc.0/f9824900.sdhci' for a fstab with the following content:

   /dev/block/platform/soc/1da4000.ufshc/by-name/system
   /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor

Bug: 78613232
Test: adb shell ls /dev/block/by-name
Change-Id: Iec920b5a72409b6a2bdbeeb290f0a3acd2046b5d
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 472ab59..af4d6c1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -686,6 +686,49 @@
     return a;
 }
 
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ *   /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+    std::set<std::string> boot_devices;
+
+    for (int i = 0; i < fstab.num_entries; i++) {
+        std::string blk_device(fstab.recs[i].blk_device);
+        // Skips blk_device that doesn't conform to the format.
+        if (!android::base::StartsWith(blk_device, "/dev/block") ||
+            android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+            android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+            continue;
+        }
+        // Skips non-by_name blk_device.
+        // /dev/block/<type>/<device>/by-name/<partition>
+        //                           ^ slash_by_name
+        auto slash_by_name = blk_device.find("/by-name");
+        if (slash_by_name == std::string::npos) continue;
+        blk_device.erase(slash_by_name);  // erases /by-name/<partition>
+
+        // Erases /dev/block/, now we have <type>/<device>
+        blk_device.erase(0, std::string("/dev/block/").size());
+
+        // <type>/<device>
+        //       ^ first_slash
+        auto first_slash = blk_device.find('/');
+        if (first_slash == std::string::npos) continue;
+
+        auto boot_device = blk_device.substr(first_slash + 1);
+        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+    }
+
+    return boot_devices;
+}
+
 struct fstab *fs_mgr_read_fstab(const char *fstab_path)
 {
     FILE *fstab_file;
@@ -863,6 +906,23 @@
     return nullptr;
 }
 
+std::set<std::string> fs_mgr_get_boot_devices() {
+    // boot_devices can be specified in device tree.
+    std::string dt_value;
+    std::string file_name = get_android_dt_dir() + "/boot_devices";
+    if (read_dt_file(file_name, &dt_value)) {
+        auto boot_devices = android::base::Split(dt_value, ",");
+        return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+    }
+
+    // Fallback to extract boot devices from fstab.
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    if (fstab) return extract_boot_devices(*fstab);
+
+    return {};
+}
+
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
 {
     return fstab->fs_mgr_flags & MF_VOLDMANAGED;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 04ccfc5..d232cca 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -22,6 +22,7 @@
 #include <stdint.h>
 #include <stdio.h>
 
+#include <set>
 #include <string>
 
 /*
@@ -89,5 +90,6 @@
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
+std::set<std::string> fs_mgr_get_boot_devices();
 
 #endif /* __CORE_FS_TAB_H */
diff --git a/init/devices.cpp b/init/devices.cpp
index 688ad61..ada1e28 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -329,6 +329,10 @@
                          << partition_name_sanitized << "'";
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
+        // Adds symlink: /dev/block/by-name/<partition_name>.
+        if (boot_devices_.find(device) != boot_devices_.end()) {
+            links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
+        }
     }
 
     auto last_slash = uevent.path.rfind('/');
@@ -346,8 +350,14 @@
                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
             }
 
-            if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
-                PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+            if (symlink(devpath.c_str(), link.c_str())) {
+                if (errno != EEXIST) {
+                    PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+                } else if (std::string link_path;
+                           Readlink(link, &link_path) && link_path != devpath) {
+                    PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
+                                << ", which already links to: " << link_path;
+                }
             }
         }
     }
@@ -411,16 +421,18 @@
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
-                             std::vector<Subsystem> subsystems, bool skip_restorecon)
+                             std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
+                             bool skip_restorecon)
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
       subsystems_(std::move(subsystems)),
+      boot_devices_(std::move(boot_devices)),
       skip_restorecon_(skip_restorecon),
       sysfs_mount_point_("/sys") {}
 
 DeviceHandler::DeviceHandler()
     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
-                    std::vector<Subsystem>{}, false) {}
+                    std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
 
 }  // namespace init
 }  // namespace android
diff --git a/init/devices.h b/init/devices.h
index 1f8f1e8..f9035da 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <algorithm>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -103,8 +104,8 @@
 
     DeviceHandler();
     DeviceHandler(std::vector<Permissions> dev_permissions,
-                  std::vector<SysfsPermissions> sysfs_permissions,
-                  std::vector<Subsystem> subsystems, bool skip_restorecon);
+                  std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
+                  std::set<std::string> boot_devices, bool skip_restorecon);
     ~DeviceHandler(){};
 
     void HandleDeviceEvent(const Uevent& uevent);
@@ -125,6 +126,7 @@
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
     std::vector<Subsystem> subsystems_;
+    std::set<std::string> boot_devices_;
     bool skip_restorecon_;
     std::string sysfs_mount_point_;
 };
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 28df696..34de640 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -77,7 +77,7 @@
     std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
-    DeviceHandler device_handler_;
+    std::unique_ptr<DeviceHandler> device_handler_;
     UeventListener uevent_listener_;
 };
 
@@ -147,6 +147,11 @@
     if (IsDmLinearEnabled()) {
         dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
     }
+
+    auto boot_devices = fs_mgr_get_boot_devices();
+    device_handler_ =
+        std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+                                        std::vector<Subsystem>{}, std::move(boot_devices), false);
 }
 
 std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -205,7 +210,7 @@
         bool found = false;
         auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
             if (uevent.path == dm_path) {
-                device_handler_.HandleDeviceEvent(uevent);
+                device_handler_->HandleDeviceEvent(uevent);
                 found = true;
                 return ListenerAction::kStop;
             }
@@ -262,7 +267,7 @@
     if (iter != required_devices_partition_names_.end()) {
         LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
         required_devices_partition_names_.erase(iter);
-        device_handler_.HandleDeviceEvent(uevent);
+        device_handler_->HandleDeviceEvent(uevent);
         if (required_devices_partition_names_.empty()) {
             return ListenerAction::kStop;
         } else {
@@ -299,7 +304,7 @@
     auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
         if (uevent.device_name == device_name) {
             LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
-            device_handler_.HandleDeviceEvent(uevent);
+            device_handler_->HandleDeviceEvent(uevent);
             found = true;
             return ListenerAction::kStop;
         }
@@ -469,7 +474,7 @@
         // is not empty. e.g.,
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
         //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
-        std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
+        std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
         if (!links.empty()) {
             auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
             if (!inserted) {
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 1435d82..a284203 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -30,6 +30,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <fstab/fstab.h>
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
@@ -242,8 +243,9 @@
     std::string hardware = android::base::GetProperty("ro.hardware", "");
     parser.ParseConfig("/ueventd." + hardware + ".rc");
 
+    auto boot_devices = fs_mgr_get_boot_devices();
     return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
-                         std::move(subsystems), true);
+                         std::move(subsystems), std::move(boot_devices), true);
 }
 
 int ueventd_main(int argc, char** argv) {