Index: cgpt/cmd_prioritize.c |
diff --git a/cgpt/cmd_prioritize.c b/cgpt/cmd_prioritize.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3f345689baa8dd92a45548837aba3b84a1e8d22e |
--- /dev/null |
+++ b/cgpt/cmd_prioritize.c |
@@ -0,0 +1,314 @@ |
+// Copyright (c) 2010 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 <getopt.h> |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <string.h> |
+#include <uuid/uuid.h> |
+ |
+#include "cgptlib_internal.h" |
+ |
+static void Usage(void) |
+{ |
+ printf("\nUsage: %s prioritize [OPTIONS] DRIVE\n\n" |
+ "Reorder the priority of all active ChromeOS Kernel partitions.\n\n" |
+ "Options:\n" |
+ " -P NUM Highest priority to use in the new ordering. The\n" |
+ " other partitions will be ranked in decreasing\n" |
+ " priority while preserving their original order.\n" |
+ " If necessary the lowest ranks will be coalesced.\n" |
+ " No active kernels will be lowered to priority 0.\n" |
+ " -i NUM Specify the partition to make the highest in the new\n" |
+ " order.\n" |
+ " -f Friends of the given partition (those with the same\n" |
+ " starting priority) are also updated to the new\n" |
+ " highest priority.\n" |
+ "\n" |
+ "With no options this will set the lowest active kernel to\n" |
+ "priority 1 while maintaining the original order.\n" |
+ "\n", progname); |
+} |
+ |
+////////////////////////////////////////////////////////////////////////////// |
+// I want a sorted list of priority groups, where each element in the list |
+// contains an unordered list of GPT partition numbers. This is a stupid |
+// implementation, but our needs are simple and don't justify the time or space |
+// it would take to write a "better" one. |
+#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 cmd_prioritize(int argc, char *argv[]) { |
+ struct drive drive; |
+ uint32_t set_partition = 0; |
+ int set_friends = 0; |
+ int max_priority = 0; |
+ int priority; |
+ int orig_priority = 0; |
+ int gpt_retval; |
+ GptEntry *entry; |
+ uint32_t index; |
+ uint32_t max_part; |
+ int num_kernels; |
+ int i,j; |
+ group_list_t *groups; |
+ |
+ int c; |
+ int errorcnt = 0; |
+ char *e = 0; |
+ |
+ opterr = 0; // quiet, you |
+ while ((c=getopt(argc, argv, ":hi:fP:")) != -1) |
+ { |
+ switch (c) |
+ { |
+ case 'i': |
+ set_partition = (uint32_t)strtoul(optarg, &e, 0); |
+ if (!*optarg || (e && *e)) |
+ { |
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg); |
+ errorcnt++; |
+ } |
+ break; |
+ case 'f': |
+ set_friends = 1; |
+ break; |
+ case 'P': |
+ max_priority = (int)strtol(optarg, &e, 0); |
+ if (!*optarg || (e && *e)) |
+ { |
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg); |
+ errorcnt++; |
+ } |
+ if (max_priority < 1 || max_priority > 15) { |
+ Error("value for -%c must be between 1 and 15\n", c); |
+ errorcnt++; |
+ } |
+ break; |
+ |
+ case 'h': |
+ Usage(); |
+ return CGPT_OK; |
+ case '?': |
+ Error("unrecognized option: -%c\n", optopt); |
+ errorcnt++; |
+ break; |
+ case ':': |
+ Error("missing argument to -%c\n", optopt); |
+ errorcnt++; |
+ break; |
+ default: |
+ errorcnt++; |
+ break; |
+ } |
+ } |
+ if (errorcnt) |
+ { |
+ Usage(); |
+ return CGPT_FAILED; |
+ } |
+ |
+ if (set_friends && !set_partition) { |
+ Error("the -f option is only useful with the -i option\n"); |
+ Usage(); |
+ return CGPT_FAILED; |
+ } |
+ |
+ if (optind >= argc) { |
+ Error("missing drive argument\n"); |
+ return CGPT_FAILED; |
+ } |
+ |
+ if (CGPT_OK != DriveOpen(argv[optind], &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 (set_partition) { |
+ if (set_partition < 1 || set_partition > max_part) { |
+ Error("invalid partition number: %d (must be between 1 and %d\n", |
+ set_partition, max_part); |
+ goto bad; |
+ } |
+ index = 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", 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) |
+ // nothing to do, so don't |
+ goto good; |
+ |
+ // 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 (set_partition && (i+1 == set_partition)) { |
+ orig_priority = priority; // remember the original priority |
+ if (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 (set_partition && set_friends) { |
+ ChangeGroup(groups, 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 (max_priority) |
+ priority = 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 |
+good: |
+ 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; |
+} |