serialize: Store priority in directory instead of file

Currently the priority value is stored in <persist_dir>/<version_id>.

There is a need to persist the version purpose as well, because it
can be BMC or System. To achieve this, we can either store the priority
and purpose on different files under a single directory, or store both
values in the current single file.

Proposing to implement the former because cereal is not designed to
append data to a serialized file, therefore it'd be necessary to store
all the values every time a file is saved, and would make it confusing
having to somehow pass the priority and purpose values when updating
the priority. This would get more complicated as more files were added
to be persisted. Also the purpose only needs to be updated once, and
doesn't change, so makes sense to store different values that are stored
at different times in different files.

Proposed change:
- Store the values under <persiste_dir>/<version_id>/<property_name>.
- Change the persist directory to /var/lib/phosphor-bmc-code-mgmt/
  instead of /var/lib/obmc/phosphor-bmc-code-mgmt/. This allows for this
  change from file to directory to be backward compatible because the
  current code fails to do a remove if the version_id is a directory.
  This would also make the path consistent with the general guidelines
  (see https://lists.ozlabs.org/pipermail/openbmc/2019-November/019388.html)
  as the /var/lib/obmc/ directory is being deprecated eventually.
- Change the function names to specify they apply to priority values.

Tested: Verified the properties were stored in a directory, and updates
        and downgrades handled the values correctly, since if the value
        is not found in a file/directory, there's a backup value in U-Boot.

Change-Id: I244334af51721385c748fe06d2e3d029ede1d138
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/configure.ac b/configure.ac
index 2da97ad..7f52361 100755
--- a/configure.ac
+++ b/configure.ac
@@ -107,7 +107,7 @@
     [The name of the BMC table of contents file])
 AC_DEFINE(ALT_RWFS, "/media/alt/var/persist",
     [The path of the alt rwfs overlay])
-AC_DEFINE(PERSIST_DIR, "/var/lib/obmc/phosphor-bmc-code-mgmt/",
+AC_DEFINE(PERSIST_DIR, "/var/lib/phosphor-bmc-code-mgmt/",
     [The dir where activation data is stored in files])
 AC_DEFINE(SYSTEMD_BUSNAME, "org.freedesktop.systemd1",
     [The systemd busname])
diff --git a/item_updater.cpp b/item_updater.cpp
index 975c6e9..7fc9fa3 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -235,7 +235,7 @@
             if (activationState == server::Activation::Activations::Active)
             {
                 uint8_t priority = std::numeric_limits<uint8_t>::max();
-                if (!restoreFromFile(id, priority))
+                if (!restorePriority(id, priority))
                 {
                     if (isVersionFunctional)
                     {
@@ -298,7 +298,7 @@
 
         // Delete ReadOnly partitions if it's not active
         removeReadOnlyPartition(entryId);
-        removeFile(entryId);
+        removePersistDataDirectory(entryId);
 
         // Removing entry in versions map
         this->versions.erase(entryId);
@@ -307,7 +307,7 @@
     {
         // Delete ReadOnly partitions even if we can't find the version
         removeReadOnlyPartition(entryId);
-        removeFile(entryId);
+        removePersistDataDirectory(entryId);
 
         log<level::ERR>("Error: Failed to find version in item updater "
                         "versions map. Unable to remove.",
@@ -381,7 +381,7 @@
 
 void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
 {
-    storeToFile(versionId, value);
+    storePriority(versionId, value);
     helper.setEntry(versionId, value);
 }
 
diff --git a/serialize.cpp b/serialize.cpp
index b61233c..483cd7a 100644
--- a/serialize.cpp
+++ b/serialize.cpp
@@ -5,6 +5,7 @@
 #include <cereal/archives/json.hpp>
 #include <experimental/filesystem>
 #include <fstream>
+#include <phosphor-logging/log.hpp>
 #include <sdbusplus/server.hpp>
 
 namespace phosphor
@@ -14,36 +15,47 @@
 namespace updater
 {
 
+using namespace phosphor::logging;
 namespace fs = std::experimental::filesystem;
 
-void storeToFile(std::string versionId, uint8_t priority)
+const std::string priorityName = "priority";
+
+void storePriority(const std::string& versionId, uint8_t priority)
 {
-    if (!fs::is_directory(PERSIST_DIR))
+    auto path = fs::path(PERSIST_DIR) / versionId;
+    if (!fs::is_directory(path))
     {
-        fs::create_directories(PERSIST_DIR);
+        if (fs::exists(path))
+        {
+            // Delete if it's a non-directory file
+            log<level::WARNING>("Removing non-directory file",
+                                entry("PATH=%s", path.c_str()));
+            fs::remove_all(path);
+        }
+        fs::create_directories(path);
     }
-    std::string path = PERSIST_DIR + versionId;
+    path = path / priorityName;
 
     std::ofstream os(path.c_str());
     cereal::JSONOutputArchive oarchive(os);
-    oarchive(cereal::make_nvp("priority", priority));
+    oarchive(cereal::make_nvp(priorityName, priority));
 }
 
-bool restoreFromFile(std::string versionId, uint8_t& priority)
+bool restorePriority(const std::string& versionId, uint8_t& priority)
 {
-    std::string path = PERSIST_DIR + versionId;
+    auto path = fs::path(PERSIST_DIR) / versionId / priorityName;
     if (fs::exists(path))
     {
         std::ifstream is(path.c_str(), std::ios::in);
         try
         {
             cereal::JSONInputArchive iarchive(is);
-            iarchive(cereal::make_nvp("priority", priority));
+            iarchive(cereal::make_nvp(priorityName, priority));
             return true;
         }
         catch (cereal::Exception& e)
         {
-            fs::remove(path);
+            fs::remove_all(path);
         }
     }
 
@@ -88,12 +100,12 @@
     return false;
 }
 
-void removeFile(std::string versionId)
+void removePersistDataDirectory(const std::string& versionId)
 {
-    std::string path = PERSIST_DIR + versionId;
+    auto path = fs::path(PERSIST_DIR) / versionId;
     if (fs::exists(path))
     {
-        fs::remove(path);
+        fs::remove_all(path);
     }
 }
 
diff --git a/serialize.hpp b/serialize.hpp
index adaf018..9a9df09 100644
--- a/serialize.hpp
+++ b/serialize.hpp
@@ -13,23 +13,23 @@
 
 namespace fs = std::experimental::filesystem;
 
-/** @brief Serialization function - stores activation information to file
+/** @brief Serialization function - stores priority information to file
  *  @param[in] versionId - The version for which to store information.
  *  @param[in] priority - RedundancyPriority value for that version.
  **/
-void storeToFile(std::string versionId, uint8_t priority);
+void storePriority(const std::string& versionId, uint8_t priority);
 
-/** @brief Serialization function - restores activation information from file
+/** @brief Serialization function - restores priority information from file
  *  @param[in] versionId - The version for which to retrieve information.
  *  @param[in] priority - RedundancyPriority reference for that version.
  *  @return true if restore was successful, false if not
  **/
-bool restoreFromFile(std::string versionId, uint8_t& priority);
+bool restorePriority(const std::string& versionId, uint8_t& priority);
 
-/** @brief Removes the serial file for a given version.
+/** @brief Removes the serial directory for a given version.
  *  @param[in] versionId - The version for which to remove a file, if it exists.
  **/
-void removeFile(std::string versionId);
+void removePersistDataDirectory(const std::string& versionId);
 
 } // namespace updater
 } // namespace software