Determine functional versions from mount directory

In order to support the same version on the primary and backup flash
locations, the version string is not enough to determine which version
is functional. Therefore add a functional suffix to the mount directory
of the functional and use that to set the Version as functional.

Tested: The mount directories indicate which version is functional,
associations look correct.

- static:
root@romulus:~# ls -l /run/media/rofs-79139bc0-functional/etc/
lrwxrwxrwx    1 root     root            15 Jan 22 20:11 os-release ->
/etc/os-release

root@romulus:~# busctl call xyz.openbmc_project.ObjectMapper
/xyz/openbmc_project/software/functional org.freedesktop.DBus.Properties
Get ss xyz.openbmc_project.Association endpoints
v as 1 "/xyz/openbmc_project/software/79139bc0"

- ubi
root@witherspoon:~# df
/dev/ubiblock0_0         18816     18816         0 100% /media/rofs-cfb85943-functional
/dev/ubiblock4_0         18816     18816         0 100% /media/rofs-26085328

- mmc:
Verified functional association is correct, the rofs directories are
still unmounted after the bmc updater starts.

Change-Id: I8114a86b36ca1c6b1634b01fcce3cef0a2369eca
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/item_updater.cpp b/item_updater.cpp
index 94de753..9309a3f 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -172,8 +172,8 @@
         return;
     }
 
-    // Read os-release from /etc/ to get the functional BMC version
-    auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
+    // Functional images are mounted as rofs-<location>-functional
+    constexpr auto functionalSuffix = "-functional";
 
     // Read os-release from folders under /media/ to get
     // BMC Software Versions.
@@ -229,6 +229,14 @@
 
             // The flash location is part of the mount name: rofs-<location>
             auto flashId = iter.path().native().substr(BMC_RO_PREFIX_LEN);
+            auto functional = false;
+            if (iter.path().native().find(functionalSuffix) !=
+                std::string::npos)
+            {
+                // Set functional to true and remove the functional suffix
+                functional = true;
+                flashId.erase(flashId.length() - strlen(functionalSuffix));
+            }
 
             auto purpose = server::Version::VersionPurpose::BMC;
             restorePurpose(flashId, purpose);
@@ -241,7 +249,7 @@
 
             // Create functional association if this is the functional
             // version
-            if (version.compare(functionalVersion) == 0)
+            if (functional)
             {
                 createFunctionalAssociation(path);
             }
@@ -267,8 +275,11 @@
             auto versionPtr = std::make_unique<VersionClass>(
                 bus, path, version, purpose, extendedVersion, flashId,
                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
-            auto isVersionFunctional = versionPtr->isFunctional();
-            if (!isVersionFunctional)
+            if (functional)
+            {
+                versionPtr->setFunctional(true);
+            }
+            else
             {
                 versionPtr->deleteObject =
                     std::make_unique<phosphor::software::manager::Delete>(
@@ -288,7 +299,7 @@
                 uint8_t priority = std::numeric_limits<uint8_t>::max();
                 if (!restorePriority(flashId, priority))
                 {
-                    if (isVersionFunctional)
+                    if (functional)
                     {
                         priority = 0;
                     }
@@ -308,20 +319,21 @@
     }
 
     // If there are no bmc versions mounted under MEDIA_DIR, then read the
-    // /etc/os-release and create rofs-<versionId> under MEDIA_DIR, then call
-    // again processBMCImage() to create the D-Bus interface for it.
+    // /etc/os-release and create rofs-<versionId>-functional under MEDIA_DIR,
+    // then call again processBMCImage() to create the D-Bus interface for it.
     if (activations.size() == 0)
     {
         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
         auto id = phosphor::software::manager::Version::getId(version);
-        auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
+        auto versionFileDir = BMC_ROFS_PREFIX + id + functionalSuffix + "/etc/";
         try
         {
             if (!fs::is_directory(versionFileDir))
             {
                 fs::create_directories(versionFileDir);
             }
-            auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
+            auto versionFilePath =
+                BMC_ROFS_PREFIX + id + functionalSuffix + OS_RELEASE_FILE;
             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
             ItemUpdater::processBMCImage();
         }
diff --git a/mmc/obmc-flash-mmc-mount.service.in b/mmc/obmc-flash-mmc-mount.service.in
index 8b21537..36316fd 100644
--- a/mmc/obmc-flash-mmc-mount.service.in
+++ b/mmc/obmc-flash-mmc-mount.service.in
@@ -5,11 +5,8 @@
 [Service]
 Type=oneshot
 RemainAfterExit=no
-ExecStart=/bin/mkdir -p @MEDIA_DIR@/rofs-a
-ExecStart=/bin/mkdir -p @MEDIA_DIR@/rofs-b
+ExecStart=/usr/bin/obmc-flash-bmc mmc-mount @MEDIA_DIR@
 ExecStart=/bin/mkdir -p @MEDIA_DIR@/hostfw
-ExecStart=-/bin/sh -c '/bin/mount PARTLABEL=rofs-a @MEDIA_DIR@/rofs-a -t ext4 -o ro || /bin/rmdir @MEDIA_DIR@/rofs-a'
-ExecStart=-/bin/sh -c '/bin/mount PARTLABEL=rofs-b @MEDIA_DIR@/rofs-b -t ext4 -o ro || /bin/rmdir @MEDIA_DIR@/rofs-b'
 ExecStart=-/bin/mount PARTLABEL=hostfw @MEDIA_DIR@/hostfw -t ext4
 
 [Install]
diff --git a/mmc/obmc-flash-mmc-umount.service.in b/mmc/obmc-flash-mmc-umount.service.in
index 535377c..b0d4831 100644
--- a/mmc/obmc-flash-mmc-umount.service.in
+++ b/mmc/obmc-flash-mmc-umount.service.in
@@ -7,6 +7,8 @@
 RemainAfterExit=no
 ExecStart=-/bin/umount @MEDIA_DIR@/rofs-a
 ExecStart=-/bin/umount @MEDIA_DIR@/rofs-b
+ExecStart=-/bin/umount @MEDIA_DIR@/rofs-a-functional
+ExecStart=-/bin/umount @MEDIA_DIR@/rofs-b-functional
 
 [Install]
 WantedBy=xyz.openbmc_project.Software.BMC.Updater.service
diff --git a/obmc-flash-bmc b/obmc-flash-bmc
index aa9773f..30c3fcd 100644
--- a/obmc-flash-bmc
+++ b/obmc-flash-bmc
@@ -264,6 +264,9 @@
   altbmcmtd="$(findmtd "alt-bmc")"
   mtds="${bmcmtd: -1}","${altbmcmtd: -1}"
 
+  rootubi="$(findrootubi)"
+  rootname="$(findname "${rootubi}")"
+
   IFS=',' read -r -a mtds <<< "$mtds"
   mtds=($(echo "${mtds[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
   for mtd in ${mtds[@]}; do
@@ -279,7 +282,11 @@
       name="$(echo -e "${name}" | tr -d '[:space:]')"
 
       if [[ ${name} == rofs-* ]]; then
-        mountdir="/media/${name}"
+        if [[ "${name}" == "${rootname}" ]]; then
+          mountdir="/media/${name}-functional"
+        else
+          mountdir="/media/${name}"
+        fi
 
         if [ ! -d ${mountdir} ]; then
           mkdir -p "${mountdir}"
@@ -515,6 +522,20 @@
   fi
 }
 
+mmc_mount() {
+  primaryId="$(mmc_get_primary_label)"
+  secondaryId="$(mmc_get_secondary_label)"
+
+  primaryDir="${mediaDir}/rofs-${primaryId}-functional"
+  secondaryDir="${mediaDir}/rofs-${secondaryId}"
+
+  mkdir -p "${primaryDir}"
+  mkdir -p "${secondaryDir}"
+
+  mount PARTLABEL=rofs-${primaryId} "${primaryDir}" -t ext4 -o ro || rmdir "${primaryDir}"
+  mount PARTLABEL=rofs-${secondaryId} "${secondaryDir}" -t ext4 -o ro || rmdir "${secondaryDir}"
+}
+
 mmc_update() {
   # Update u-boot if needed
   bootPartition="mmcblk0boot0"
@@ -658,6 +679,10 @@
     imgpath="$3"
     mmc_update
     ;;
+  mmc-mount)
+    mediaDir="$2"
+    mmc_mount
+    ;;
   mmc-remove)
     flashid="$2"
     mmc_remove
diff --git a/version.cpp b/version.cpp
index 871b9f5..11d4201 100644
--- a/version.cpp
+++ b/version.cpp
@@ -202,11 +202,6 @@
     return version;
 }
 
-bool Version::isFunctional()
-{
-    return versionStr == getBMCVersion(OS_RELEASE_FILE);
-}
-
 void Delete::delete_()
 {
     if (parent.eraseCallback)
diff --git a/version.hpp b/version.hpp
index 8a68cb5..73cc137 100644
--- a/version.hpp
+++ b/version.hpp
@@ -143,12 +143,22 @@
      */
     static std::string getBMCVersion(const std::string& releaseFilePath);
 
-    /* @brief Check if this version matches the currently running version
+    /* @brief Check if this version is functional.
      *
-     * @return - Returns true if this version matches the currently running
-     *           version.
+     * @return - Returns the functional value.
      */
-    bool isFunctional();
+    bool isFunctional() const
+    {
+        return functional;
+    }
+
+    /** @brief Set the functional value.
+     * @param[in] value - True or False
+     */
+    void setFunctional(bool value)
+    {
+        functional = value;
+    }
 
     /** @brief Persistent Delete D-Bus object */
     std::unique_ptr<Delete> deleteObject;
@@ -159,6 +169,9 @@
   private:
     /** @brief This Version's version string */
     const std::string versionStr;
+
+    /** @brief If this version is the functional one */
+    bool functional = false;
 };
 
 } // namespace manager