// 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 #include #include #include #include #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; igroup[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; igroup[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; inum_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; inum_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; inum_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; inum_groups; i++) { groups->group[i].priority = priority; if (priority > 1) priority--; } // Now apply the ranking to the GPT for (i=0; inum_groups; i++) for (j=0; jgroup[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; }