gptfdisk: Provide sgdisk_read for direct reads of the partition table

This allows volume manager to read the partition table via a simple
function call instead of doing a popen() and parsing the output.

Change-Id: I1d8ec6d5c03dc62d2b6b498efe02b41b1b11be46
diff --git a/sgdisk.cc b/sgdisk.cc
index 7c26b60..cd65ae6 100644
--- a/sgdisk.cc
+++ b/sgdisk.cc
@@ -20,25 +20,22 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include "sgdisk.h"
+
 using namespace std;
 
 #define MAX_OPTIONS 50
 
-/*
- * Dump partition details in a machine readable format:
- *
- * DISK [mbr|gpt] [guid]
- * PART [n] [type] [guid]
- */
-static int android_dump(char* device) {
+int sgdisk_read(const char* device, sgdisk_partition_table& ptbl,
+                vector<sgdisk_partition>& partitions) {
     BasicMBRData mbrData;
     GPTData gptData;
     GPTPart partData;
     int numParts = 0;
-    stringstream res;
 
     /* Silence noisy underlying library */
-    int stdout = dup(STDOUT_FILENO);
+    int stdout_fd = dup(STDOUT_FILENO);
+    int stderr_fd = dup(STDERR_FILENO);
     int silence = open("/dev/null", 0);
     dup2(silence, STDOUT_FILENO);
     dup2(silence, STDERR_FILENO);
@@ -50,11 +47,16 @@
 
     switch (mbrData.GetValidity()) {
     case mbr:
-        res << "DISK mbr" << endl;
-        for (int i = 0; i < MAX_MBR_PARTS; i++) {
+        ptbl.type = MBR;
+        ptbl.guid.clear();
+        for (size_t i = 0; i < MAX_MBR_PARTS; i++) {
             if (mbrData.GetLength(i) > 0) {
-                res << "PART " << (i + 1) << " " << hex
-                        << (int) mbrData.GetType(i) << dec << endl;
+                char typebuf[2+8+1];
+                sprintf(typebuf, "%x", (unsigned int)mbrData.GetType(i));
+                sgdisk_partition part;
+                part.num = i + 1;
+                part.type = typebuf;
+                partitions.push_back(part);
             }
         }
         break;
@@ -66,14 +68,17 @@
             return 9;
         }
 
-        res << "DISK gpt " << gptData.GetDiskGUID() << endl;
-        numParts = gptData.GetNumParts();
-        for (int i = 0; i < numParts; i++) {
+        ptbl.type = GPT;
+        ptbl.guid = gptData.GetDiskGUID().AsString();
+        for (size_t i = 0; i < gptData.GetNumParts(); i++) {
             partData = gptData[i];
             if (partData.GetFirstLBA() > 0) {
-                res << "PART " << (i + 1) << " " << partData.GetType() << " "
-                        << partData.GetUniqueGUID() << " "
-                        << partData.GetDescription() << endl;
+                sgdisk_partition part;
+                part.num = i + 1;
+                part.type = partData.GetType().AsString();
+                part.guid = partData.GetUniqueGUID().AsString();
+                part.name = partData.GetDescription();
+                partitions.push_back(part);
             }
         }
         break;
@@ -82,12 +87,50 @@
         return 10;
     }
 
-    /* Write our actual output */
-    string resString = res.str();
-    write(stdout, resString.c_str(), resString.length());
+    fflush(stdout);
+    fflush(stderr);
+    dup2(stdout_fd, STDOUT_FILENO);
+    dup2(stderr_fd, STDERR_FILENO);
+    close(silence);
+
     return 0;
 }
 
+/*
+ * Dump partition details in a machine readable format:
+ *
+ * DISK [mbr|gpt] [guid]
+ * PART [n] [type] [guid]
+ */
+static int android_dump(const char* device) {
+    sgdisk_partition_table ptbl;
+    vector<sgdisk_partition> partitions;
+    int rc = sgdisk_read(device, ptbl, partitions);
+    if (rc == 0) {
+        stringstream res;
+        switch (ptbl.type) {
+        case MBR:
+            res << "DISK mbr" << endl;
+            for (auto& part : partitions) {
+                res << "PART " << part.num << " " << part.type << endl;
+            }
+            break;
+        case GPT:
+            res << "DISK gpt " << ptbl.guid << endl;
+            for (auto& part : partitions) {
+                res << "PART " << part.num << " " << part.type << " "
+                    << part.guid << " " << part.name << endl;
+            }
+            break;
+        default:
+            return 10;
+        }
+        string partStr = res.str();
+        write(STDOUT_FILENO, partStr.c_str(), partStr.length());
+    }
+    return rc;
+}
+
 extern "C" int main(int argc, char *argv[]) {
     for (int i = 0; i < argc; i++) {
         if (!strcmp("--android-dump", argv[i])) {
diff --git a/sgdisk.h b/sgdisk.h
new file mode 100644
index 0000000..d98ee2d
--- /dev/null
+++ b/sgdisk.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Lineage Android Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SGDISK
+#define __SGDISK
+
+#include <string>
+#include <vector>
+
+enum ptbl_type {
+    MBR,
+    GPT
+};
+
+struct sgdisk_partition_table {
+    ptbl_type   type;
+    std::string guid;
+};
+
+struct sgdisk_partition {
+    int         num;
+    std::string type;
+    std::string guid;
+    std::string name;
+};
+
+int sgdisk_read(const char* device, sgdisk_partition_table& ptbl,
+                std::vector<sgdisk_partition>& partitions);
+
+#endif