diff options
Diffstat (limited to 'cgpt/cgpt_prioritize.c')
-rw-r--r-- | cgpt/cgpt_prioritize.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/cgpt/cgpt_prioritize.c b/cgpt/cgpt_prioritize.c new file mode 100644 index 00000000..b28e84c1 --- /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; +} |