vpnor: partition: Only attempt read() for in-bounds accesses

Attempting to read after an lseek() past the end of the file leads to an
error and we bail out. The error propagates up the callstack and causes
errors on the host side, where really we should just be returning an
erased window.

Change-Id: I06cdda4f7f6aa4d66e133de95cd642e04418bbe0
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/vpnor/partition.cpp b/vpnor/partition.cpp
index 4547d64..90156f7 100644
--- a/vpnor/partition.cpp
+++ b/vpnor/partition.cpp
@@ -133,37 +133,42 @@
 
     size_t fileSize = fs::file_size(path);
 
-    int fd = ::open(path.c_str(), O_RDONLY);
-    if (fd == -1)
+    size_t access_len = 0;
+    if (offset < fileSize)
     {
-        MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(),
-                errno);
-        throw std::system_error(errno, std::system_category());
-    }
+        int fd = ::open(path.c_str(), O_RDONLY);
+        if (fd == -1)
+        {
+            MSG_ERR("Failed to open backing file at '%s': %d\n", path.c_str(),
+                    errno);
+            throw std::system_error(errno, std::system_category());
+        }
 
-    int rc = lseek(fd, offset, SEEK_SET);
-    if (rc < 0)
-    {
-        MSG_ERR("Failed to seek to %zu in %s (%zu bytes): %d\n", offset,
-                path.c_str(), fileSize, errno);
-        throw std::system_error(errno, std::system_category());
-    }
+        int rc = lseek(fd, offset, SEEK_SET);
+        if (rc < 0)
+        {
+            MSG_ERR("Failed to seek to %zu in %s (%zu bytes): %d\n", offset,
+                    path.c_str(), fileSize, errno);
+            throw std::system_error(errno, std::system_category());
+        }
 
-    auto access_len = std::min(len, fileSize - offset);
-    rc = fd_process_all(::read, fd, dst, access_len);
-    if (rc < 0)
-    {
-        MSG_ERR("Requested %zu bytes but failed to read %zu from %s (%zu) at "
+        access_len = std::min(len, fileSize - offset);
+        rc = fd_process_all(::read, fd, dst, access_len);
+        if (rc < 0)
+        {
+            MSG_ERR(
+                "Requested %zu bytes but failed to read %zu from %s (%zu) at "
                 "%zu: %d\n",
                 len, access_len, path.c_str(), fileSize, offset, errno);
-        throw std::system_error(errno, std::system_category());
+            throw std::system_error(errno, std::system_category());
+        }
+
+        close(fd);
     }
 
     /* Set any remaining buffer space to the erased state */
     memset((char*)dst + access_len, 0xff, len - access_len);
 
-    close(fd);
-
     return len;
 }