216 lines
6.0 KiB
C
216 lines
6.0 KiB
C
|
/* 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 <string.h>
|
||
|
|
||
|
#include "cgpt.h"
|
||
|
#include "cgptlib_internal.h"
|
||
|
#include "vboot_host.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 CgptPrioritize(CgptPrioritizeParams *params) {
|
||
|
struct drive drive;
|
||
|
|
||
|
int priority;
|
||
|
|
||
|
int gpt_retval;
|
||
|
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->drive_name, &drive, O_RDWR,
|
||
|
params->drive_size))
|
||
|
return CGPT_FAILED;
|
||
|
|
||
|
if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
|
||
|
Error("GptSanityCheck() returned %d: %s\n",
|
||
|
gpt_retval, GptError(gpt_retval));
|
||
|
goto bad;
|
||
|
}
|
||
|
|
||
|
if (CGPT_OK != CheckValid(&drive)) {
|
||
|
Error("please run 'cgpt repair' before reordering the priority.\n");
|
||
|
(void) DriveClose(&drive, 0);
|
||
|
return CGPT_OK;
|
||
|
}
|
||
|
|
||
|
max_part = GetNumberOfEntries(&drive);
|
||
|
|
||
|
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
|
||
|
if (!IsKernel(&drive, PRIMARY, index)) {
|
||
|
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++) {
|
||
|
if (IsKernel(&drive, PRIMARY, i))
|
||
|
num_kernels++;
|
||
|
}
|
||
|
|
||
|
if (num_kernels) {
|
||
|
// Determine the current priority groups
|
||
|
groups = NewGroupList(num_kernels);
|
||
|
for (i = 0; i < max_part; i++) {
|
||
|
if (!IsKernel(&drive, PRIMARY, i))
|
||
|
continue;
|
||
|
|
||
|
priority = GetPriority(&drive, 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, PRIMARY,
|
||
|
groups->group[i].part[j], groups->group[i].priority);
|
||
|
|
||
|
FreeGroups(groups);
|
||
|
}
|
||
|
|
||
|
// Write it all out
|
||
|
UpdateAllEntries(&drive);
|
||
|
|
||
|
return DriveClose(&drive, 1);
|
||
|
|
||
|
bad:
|
||
|
(void) DriveClose(&drive, 0);
|
||
|
return CGPT_FAILED;
|
||
|
}
|