Read/write window property defines FFS partition file location.

The BMC loads the PNOR partitions in a read-only volume.

So after that there are following two cases:

Read: Tries to open the file in read only mode from
the partition(READONLY/READWRITE/PRESERVED), Partition
Table tells the partition type depends on the offset.
if file is not there in the mapped partition
then tries to open the file from the read only partition.

Write: Tries to open the file in write mode from the
partition(READWRITE/PRESERVED), if file is not there
in the mapped partition then mailbox daemon will copy
the requested partition to the read-write volume to
make changes to it.

Change-Id: Ic0ef882380b56536ac55feae3ec563de95fdd4a6
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/pnor_partition.cpp b/pnor_partition.cpp
new file mode 100644
index 0000000..3f4fd02
--- /dev/null
+++ b/pnor_partition.cpp
@@ -0,0 +1,187 @@
+#include "pnor_partition.hpp"
+#include "config.h"
+#include "mboxd_flash.h"
+#include "mboxd_pnor_partition_table.h"
+#include "xyz/openbmc_project/Common/error.hpp"
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "common.h"
+
+#include <string>
+#include <exception>
+#include <stdexcept>
+#include <iostream>
+
+namespace openpower
+{
+namespace virtual_pnor
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace std::string_literals;
+
+ReturnCode Request::open(const std::string& path,
+                         int mode)
+{
+    if (mode == O_RDWR &&
+        partition->data.user.data[1] & PARTITION_READONLY)
+    {
+        MSG_ERR("Can't open the RO partition for write");
+        return ReturnCode::PARTITION_READ_ONLY;
+    }
+
+    fs::path partitionFilePath = path;
+
+    if (!fs::exists(partitionFilePath))
+    {
+        return ReturnCode::FILE_NOT_FOUND;
+    }
+
+    auto descriptor = ::open(partitionFilePath.c_str(), mode);
+    if (descriptor < 0)
+    {
+        return ReturnCode::FILE_OPEN_FAILURE;
+    }
+
+    fd.set(descriptor);
+    descriptor = -1;
+
+    return ReturnCode::SUCCESS;
+}
+
+std::string Request::getPartitionFilePath(struct mbox_context* context,
+                                          uint32_t offset)
+{
+    partition = vpnor_get_partition(context, offset);
+    if (!partition)
+    {
+        MSG_ERR("Couldn't get the partition info for offset[0x%.8x]",offset);
+        elog<InternalFailure>();
+    }
+
+    fs::path partitionFilePath;
+
+    switch (partition->data.user.data[1] &
+            (PARTITION_PRESERVED | PARTITION_READONLY))
+    {
+        case PARTITION_PRESERVED:
+            partitionFilePath = context->paths.prsv_loc;
+            break;
+
+        case PARTITION_READONLY:
+            partitionFilePath = context->paths.ro_loc;
+            break;
+
+        default:
+            partitionFilePath = context->paths.rw_loc;
+    }
+    partitionFilePath /= partition->data.name;
+    return partitionFilePath.string();
+}
+
+const pnor_partition* RORequest::getPartitionInfo(struct mbox_context* context,
+                                                  uint32_t offset)
+{
+    std::string path = getPartitionFilePath(context, offset);
+
+    ReturnCode rc = Request::open(path, O_RDONLY);
+    if (rc == ReturnCode::SUCCESS)
+    {
+        return partition;
+    }
+    // not interested in any other error except FILE_NOT_FOUND
+    if (rc != ReturnCode::FILE_NOT_FOUND)
+    {
+        elog<InternalFailure>();
+    }
+
+    // if the offset lies in read only partition then throw error.
+    if (partition->data.user.data[1] & PARTITION_READONLY)
+    {
+        MSG_ERR("Can't open the partition file");
+        elog<InternalFailure>();
+    }
+
+    MSG_DBG("Couldn't open the file[%s]", path.c_str());
+    // we don't get the file in the respective partition(RW/PSRV)
+    // try to open it from RO location.
+
+    fs::path partitionFilePath = context->paths.ro_loc;
+    partitionFilePath /= partition->data.name;
+
+    rc = Request::open(path, O_RDONLY);
+    if (rc != ReturnCode::SUCCESS)
+    {
+        elog<InternalFailure>();
+    }
+
+    return partition;
+
+}
+
+const pnor_partition* RWRequest::getPartitionInfo(struct mbox_context* context,
+                                                  uint32_t offset)
+{
+    std::string path = getPartitionFilePath(context, offset);
+
+    ReturnCode rc = Request::open(path, O_RDWR);
+    if (rc == ReturnCode::SUCCESS)
+    {
+        return partition;
+    }
+    // not interested in any other error except FILE_NOT_FOUND
+    if (rc != ReturnCode::FILE_NOT_FOUND)
+    {
+        elog<InternalFailure>();
+    }
+
+    // if the file is not available in the respective(RW/PSRV) partition
+    // then copy the file from RO to the respective(RW/PSRV) partition
+    // and open it for writing.
+
+    fs::path fromPath = context->paths.ro_loc;
+    fromPath /= partition->data.name;
+    if (!fs::exists(fromPath))
+    {
+        MSG_ERR("Couldn't find the file[%s]",fromPath.c_str());
+        elog<InternalFailure>();
+    }
+    //copy the file from ro to respective partition
+    fs::path toPath = context->paths.rw_loc;
+
+    if (partition->data.user.data[1] & PARTITION_PRESERVED)
+    {
+        toPath = context->paths.prsv_loc;
+    }
+
+    toPath /= partition->data.name;
+
+    MSG_DBG("Didn't find the file in the desired partition so copying[%s]\n",
+            toPath.c_str());
+
+    if (fs::copy_file(fromPath, toPath))
+    {
+        MSG_DBG("File copied from[%s] to [%s]\n",
+                fromPath.c_str(), toPath.c_str());
+    }
+
+    rc  = Request::open(toPath.c_str(), O_RDWR);
+
+    if (rc != ReturnCode::SUCCESS)
+    {
+        elog<InternalFailure>();
+    }
+
+    return partition;
+}
+
+}// namespace virtual_pnor
+}// namespace openpower