meta-gbmc-staging: entity-manager: gracefully handle read-only filesystem

When the filesystem is mounted as read-only (due to eMMC/flash errors),
any filesystem modification operations (remove, create_directory, copy)
in std::filesystem throw exceptions, which crash the entity-manager daemon.

This patch updates these calls to use the std::error_code overloads,
preventing uncaught filesystem exceptions and allowing the daemon to
continue running (or fail gracefully) rather than entering a crash loop.

no fusion2 link
Google-Bug-Id: 516166428
Tested: Compiled locally
Change-Id: I36efe6a780ed1bb05cb8d2135f4d5b8b246ea69b
Signed-off-by: Tom Tung <tomtung@google.com>
diff --git a/recipes-phosphor/configuration/entity-manager/0001-entity-manager-gracefully-handle-read-only-filesystem.patch b/recipes-phosphor/configuration/entity-manager/0001-entity-manager-gracefully-handle-read-only-filesystem.patch
new file mode 100644
index 0000000..184ebbc
--- /dev/null
+++ b/recipes-phosphor/configuration/entity-manager/0001-entity-manager-gracefully-handle-read-only-filesystem.patch
@@ -0,0 +1,82 @@
+From d4be8cc697e3328e1697119312b9087c33bba6ff Mon Sep 17 00:00:00 2001
+From: Tom Tung <tomtung@google.com>
+Date: Sun, 24 May 2026 09:12:12 +0000
+Subject: [PATCH] entity-manager: gracefully handle read-only filesystem
+
+When the filesystem is mounted as read-only (due to eMMC/flash errors),
+any filesystem modification operations (remove, create_directory, copy)
+in std::filesystem throw exceptions, which crash the entity-manager daemon.
+
+This patch updates these calls to use the std::error_code overloads,
+preventing uncaught filesystem exceptions and allowing the daemon to
+continue running (or fail gracefully) rather than entering a crash loop.
+Critical failures are logged with the standard error code description.
+
+Google-Bug-Id: 516166428
+Signed-off-by: Tom Tung <tomtung@google.com>
+---
+
+Index: git/src/entity_manager.cpp
+===================================================================
+--- git.orig/src/entity_manager.cpp	2026-05-25 02:30:03.121779182 +0000
++++ git/src/entity_manager.cpp	2026-05-25 02:35:44.863908292 +0000
+@@ -148,7 +148,12 @@
+ // writes output files to persist data
+ bool writeJsonFiles(const nlohmann::json& systemConfiguration)
+ {
+-    std::filesystem::create_directory(configurationOutDir);
++    std::error_code ec;
++    std::filesystem::create_directory(configurationOutDir, ec);
++    if (ec)
++    {
++        std::cerr << "Failed to create configuration output directory: " << ec.message() << "\n";
++    }
+     std::ofstream output(currentConfiguration);
+     if (!output.good())
+     {
+@@ -1465,10 +1470,27 @@
+         if (std::filesystem::is_regular_file(currentConfiguration))
+         {
+             // this file could just be deleted, but it's nice for debug
+-            std::filesystem::create_directory(tempConfigDir);
+-            std::filesystem::remove(lastConfiguration);
+-            std::filesystem::copy(currentConfiguration, lastConfiguration);
+-            std::filesystem::remove(currentConfiguration);
++            std::error_code ec;
++            std::filesystem::create_directory(tempConfigDir, ec);
++            if (ec)
++            {
++                std::cerr << "Failed to create backup directory: " << ec.message() << "\n";
++            }
++            std::filesystem::remove(lastConfiguration, ec);
++            if (ec)
++            {
++                std::cerr << "Failed to remove old backup config: " << ec.message() << "\n";
++            }
++            std::filesystem::copy(currentConfiguration, lastConfiguration, ec);
++            if (ec)
++            {
++                std::cerr << "Failed to copy config to backup: " << ec.message() << "\n";
++            }
++            std::filesystem::remove(currentConfiguration, ec);
++            if (ec)
++            {
++                std::cerr << "Failed to remove current configuration: " << ec.message() << "\n";
++            }
+ 
+             std::ifstream jsonStream(lastConfiguration);
+             if (jsonStream.good())
+@@ -1494,7 +1516,12 @@
+     {
+         // not an error, just logging at this level to make it in the journal
+         std::cerr << "Clearing previous configuration\n";
+-        std::filesystem::remove(currentConfiguration);
++        std::error_code ec;
++        std::filesystem::remove(currentConfiguration, ec);
++        if (ec)
++        {
++            std::cerr << "Failed to clear previous configuration: " << ec.message() << "\n";
++        }
+     }
+ 
+     // some boards only show up after power is on, we want to not say they are
diff --git a/recipes-phosphor/configuration/entity-manager_%.bbappend b/recipes-phosphor/configuration/entity-manager_%.bbappend
index 9339dbf..ec86bbb 100644
--- a/recipes-phosphor/configuration/entity-manager_%.bbappend
+++ b/recipes-phosphor/configuration/entity-manager_%.bbappend
@@ -25,6 +25,7 @@
     file://0005-fru_device-fix-I2C-functionality-checking-error.patch \
     file://0001-Add-SkipDbusBindUnbind-property-to-sensor-EM-configs-in-EM.patch \
     file://0001-entity-manager-Fix-FOUND-probe-evaluation-for-templa.patch \
+    file://0001-entity-manager-gracefully-handle-read-only-filesystem.patch \
 "
 
 DEPENDS:append:gbmc = " stdplus"