Add Ability for FileManager to Create Permanent Files
Atomically create permanent files in the file system.
Only using non-throwing APIs, even if the remove fails, its not too big of a deal.
#tlbmc
#bmc-bloom
PiperOrigin-RevId: 823707810
Change-Id: Ie4d3c284dfc45f95e7210aa62e5fd1e6a1b38e6a
diff --git a/tlbmc/redfish/routes/action_managers/file_manager.cc b/tlbmc/redfish/routes/action_managers/file_manager.cc
index 8ed29c3..6efd2e4 100644
--- a/tlbmc/redfish/routes/action_managers/file_manager.cc
+++ b/tlbmc/redfish/routes/action_managers/file_manager.cc
@@ -6,14 +6,19 @@
#include <unistd.h>
#include <cstddef>
+#include <filesystem> // NOLINT
+#include <fstream>
#include <string>
#include <string_view>
+#include <system_error> // NOLINT
#include <utility>
#include <vector>
#include "absl/cleanup/cleanup.h"
+#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
+#include "absl/strings/str_cat.h"
namespace milotic_tlbmc {
@@ -73,4 +78,44 @@
return StagedTempFile::Create(content, temp_name_template);
}
+absl::Status FileManager::WriteToFile(std::string_view content,
+ std::string_view path) {
+ // Create the parent directory if it doesn't exist.
+ std::filesystem::path parent_dir = std::filesystem::path(path).parent_path();
+ std::error_code create_parent_dir_ec;
+ // We don't care if the directory already exists.
+ if (std::filesystem::create_directories(parent_dir, create_parent_dir_ec);
+ create_parent_dir_ec) {
+ return absl::InternalError(
+ absl::StrCat("Failed to create parent directory for file: ", path,
+ " with error: ", create_parent_dir_ec.message()));
+ }
+
+ std::string temp_path = absl::StrCat(path, ".tmp");
+ std::ofstream file(temp_path);
+ if (!file.is_open()) {
+ return absl::InternalError(
+ absl::StrCat("Failed creating staging file: ", temp_path));
+ }
+ file << content;
+ file.close();
+
+ // Atomically create the file by renaming the temporary file to the final name
+ std::error_code rename_ec;
+ if (std::filesystem::rename(temp_path, path, rename_ec); rename_ec) {
+ std::error_code remove_ec;
+ std::filesystem::remove(temp_path, remove_ec);
+ // Not too big of a deal if it doesn't clean up the temp file.
+ if (remove_ec) {
+ LOG(WARNING) << "Failed to delete temp file: " << temp_path
+ << " with error: " << remove_ec.message();
+ }
+ return absl::InternalError(
+ absl::StrCat("Failed to create the final file: ", path,
+ " with error: ", rename_ec.message()));
+ }
+
+ return absl::OkStatus();
+}
+
} // namespace milotic_tlbmc
diff --git a/tlbmc/redfish/routes/action_managers/file_manager.h b/tlbmc/redfish/routes/action_managers/file_manager.h
index aae3523..171eca4 100644
--- a/tlbmc/redfish/routes/action_managers/file_manager.h
+++ b/tlbmc/redfish/routes/action_managers/file_manager.h
@@ -4,6 +4,7 @@
#include <string>
#include <string_view>
+#include "absl/status/status.h"
#include "absl/status/statusor.h"
namespace milotic_tlbmc {
@@ -30,6 +31,10 @@
// Create temporary file which is deleted as soon as the object is destroyed.
static absl::StatusOr<StagedTempFile> CreateTempFile(
std::string_view content, std::string_view temp_name_template);
+
+ // Create a permanent file
+ static absl::Status WriteToFile(std::string_view content,
+ std::string_view path);
};
} // namespace milotic_tlbmc