Refactor of cgpt tool for 32->64 autoupdate work.

This check-in splits the cgpt into two layers. The top layer (cmd_* files) does
the command-line parsing and the bottom layer (cgpt_* files) does the actual
cgpt work.

This is done so that the bottom layer can be reused for the monolithic
C++ post-installer code that will be done in subsequent checkins.

BUG=chromium-os:25374
TEST=Tested with existing cgpt unit tests as well as running the cgpt commands manually.

Change-Id: I69a31eb3e867a1430cac9a694581331368aa7bb4
Reviewed-on: https://gerrit.chromium.org/gerrit/14940
Reviewed-by: Jay Srinivasan <jaysri@chromium.org>
Tested-by: Jay Srinivasan <jaysri@chromium.org>
Commit-Ready: Jay Srinivasan <jaysri@chromium.org>
diff --git a/cgpt/cgpt_prioritize.c b/cgpt/cgpt_prioritize.c
new file mode 100644
index 0000000..b28e84c
--- /dev/null
+++ b/cgpt/cgpt_prioritize.c
@@ -0,0 +1,217 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#include <string.h>
+
+#include "cgptlib_internal.h"
+#include "cgpt_params.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// We need a sorted list of priority groups, where each element in the list
+// contains an unordered list of GPT partition numbers.
+
+#define MAX_GROUPS 17                   // 0-15, plus one "higher"
+
+typedef struct {
+  int priority;                         // priority of this group
+  int num_parts;                        // number of partitions in this group
+  uint32_t *part;                       // array of partitions in this group
+} group_t;
+
+typedef struct {
+  int max_parts;                       // max number of partitions in any group
+  int num_groups;                      // number of non-empty groups
+  group_t group[MAX_GROUPS];           // array of groups
+} group_list_t;
+
+
+static group_list_t *NewGroupList(int max_p) {
+  int i;
+  group_list_t *gl = (group_list_t *)malloc(sizeof(group_list_t));
+  require(gl);
+  gl->max_parts = max_p;
+  gl->num_groups = 0;
+  // reserve space for the maximum number of partitions in every group
+  for (i=0; i<MAX_GROUPS; i++) {
+    gl->group[i].priority = -1;
+    gl->group[i].num_parts = 0;
+    gl->group[i].part = (uint32_t *)malloc(sizeof(uint32_t) * max_p);
+    require(gl->group[i].part);
+  }
+
+  return gl;
+}
+
+static void FreeGroups(group_list_t *gl) {
+  int i;
+  for (i=0; i<MAX_GROUPS; i++)
+    free(gl->group[i].part);
+  free(gl);
+}
+
+static void AddToGroup(group_list_t *gl, int priority, int partition) {
+  int i;
+  // See if I've already got a group with this priority
+  for (i=0; i<gl->num_groups; i++)
+    if (gl->group[i].priority == priority)
+      break;
+  if (i == gl->num_groups) {
+    // no, add a group
+    require(i < MAX_GROUPS);
+    gl->num_groups++;
+    gl->group[i].priority = priority;
+  }
+  // add the partition to it
+  int j = gl->group[i].num_parts;
+  gl->group[i].part[j] = partition;
+  gl->group[i].num_parts++;
+}
+
+static void ChangeGroup(group_list_t *gl, int old_priority, int new_priority) {
+  int i;
+  for (i=0; i<gl->num_groups; i++)
+    if (gl->group[i].priority == old_priority) {
+      gl->group[i].priority = new_priority;
+      break;
+    }
+}
+
+static void SortGroups(group_list_t *gl) {
+  int i, j;
+  group_t tmp;
+
+  // straight insertion sort is fast enough
+  for (i=1; i<gl->num_groups; i++) {
+    tmp = gl->group[i];
+    for (j=i; j && (gl->group[j-1].priority < tmp.priority); j--)
+      gl->group[j] = gl->group[j-1];
+    gl->group[j] = tmp;
+  }
+}
+
+int cgpt_prioritize(CgptPrioritizeParams *params) {
+  struct drive drive;
+
+  int priority;
+
+  int gpt_retval;
+  GptEntry *entry;
+  uint32_t index;
+  uint32_t max_part;
+  int num_kernels;
+  int i,j;
+  group_list_t *groups;
+
+  if (params == NULL)
+    return CGPT_FAILED;
+
+  if (CGPT_OK != DriveOpen(params->driveName, &drive))
+    return CGPT_FAILED;
+
+  if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
+    Error("GptSanityCheck() returned %d: %s\n",
+          gpt_retval, GptError(gpt_retval));
+    return CGPT_FAILED;
+  }
+
+  max_part = GetNumberOfEntries(&drive.gpt);
+
+  if (params->set_partition) {
+    if (params->set_partition < 1 || params->set_partition > max_part) {
+      Error("invalid partition number: %d (must be between 1 and %d\n",
+            params->set_partition, max_part);
+      goto bad;
+    }
+    index = params->set_partition - 1;
+    // it must be a kernel
+    entry = GetEntry(&drive.gpt, PRIMARY, index);
+    if (!GuidEqual(&entry->type, &guid_chromeos_kernel)) {
+      Error("partition %d is not a ChromeOS kernel\n", params->set_partition);
+      goto bad;
+    }
+  }
+
+  // How many kernel partitions do I have?
+  num_kernels = 0;
+  for (i = 0; i < max_part; i++) {
+    entry = GetEntry(&drive.gpt, PRIMARY, i);
+    if (GuidEqual(&entry->type, &guid_chromeos_kernel))
+      num_kernels++;
+  }
+
+  if (num_kernels) {
+    // Determine the current priority groups
+    groups = NewGroupList(num_kernels);
+    for (i = 0; i < max_part; i++) {
+      entry = GetEntry(&drive.gpt, PRIMARY, i);
+      if (!GuidEqual(&entry->type, &guid_chromeos_kernel))
+        continue;
+
+      priority = GetPriority(&drive.gpt, PRIMARY, i);
+
+      // Is this partition special?
+      if (params->set_partition && (i+1 == params->set_partition)) {
+        params->orig_priority = priority;  // remember the original priority
+        if (params->set_friends)
+          AddToGroup(groups, priority, i); // we'll move them all later
+        else
+          AddToGroup(groups, 99, i);       // move only this one
+      } else {
+        AddToGroup(groups, priority, i);   // just remember
+      }
+    }
+
+    // If we're including friends, then change the original group priority
+    if (params->set_partition && params->set_friends) {
+      ChangeGroup(groups, params->orig_priority, 99);
+    }
+
+    // Sorting gives the new order. Now we just need to reassign the
+    // priorities.
+    SortGroups(groups);
+
+    // We'll never lower anything to zero, so if the last group is priority zero
+    // we can ignore it.
+    i = groups->num_groups;
+    if (groups->group[i-1].priority == 0)
+      groups->num_groups--;
+
+    // Where do we start?
+    if (params->max_priority)
+      priority = params->max_priority;
+    else
+      priority = groups->num_groups > 15 ? 15 : groups->num_groups;
+
+    // Figure out what the new values should be
+    for (i=0; i<groups->num_groups; i++) {
+      groups->group[i].priority = priority;
+      if (priority > 1)
+        priority--;
+    }
+
+    // Now apply the ranking to the GPT
+    for (i=0; i<groups->num_groups; i++)
+      for (j=0; j<groups->group[i].num_parts; j++)
+        SetPriority(&drive.gpt, PRIMARY,
+                    groups->group[i].part[j], groups->group[i].priority);
+
+    FreeGroups(groups);
+  }
+
+  // Write it all out
+  RepairEntries(&drive.gpt, MASK_PRIMARY);
+  RepairHeader(&drive.gpt, MASK_PRIMARY);
+
+  drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
+                         GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
+  UpdateCrc(&drive.gpt);
+
+  return DriveClose(&drive, 1);
+
+bad:
+  (void) DriveClose(&drive, 0);
+  return CGPT_FAILED;
+}