Bill Richardson | 3430b32 | 2010-11-29 14:24:51 -0800 | [diff] [blame] | 1 | // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "cgpt.h" |
| 6 | |
| 7 | #include <getopt.h> |
| 8 | #include <stdio.h> |
| 9 | #include <stdlib.h> |
| 10 | #include <string.h> |
| 11 | #include <uuid/uuid.h> |
| 12 | |
| 13 | #include "cgptlib_internal.h" |
| 14 | |
| 15 | static void Usage(void) |
| 16 | { |
| 17 | printf("\nUsage: %s prioritize [OPTIONS] DRIVE\n\n" |
| 18 | "Reorder the priority of all active ChromeOS Kernel partitions.\n\n" |
| 19 | "Options:\n" |
| 20 | " -P NUM Highest priority to use in the new ordering. The\n" |
| 21 | " other partitions will be ranked in decreasing\n" |
| 22 | " priority while preserving their original order.\n" |
| 23 | " If necessary the lowest ranks will be coalesced.\n" |
| 24 | " No active kernels will be lowered to priority 0.\n" |
| 25 | " -i NUM Specify the partition to make the highest in the new\n" |
| 26 | " order.\n" |
| 27 | " -f Friends of the given partition (those with the same\n" |
| 28 | " starting priority) are also updated to the new\n" |
| 29 | " highest priority.\n" |
| 30 | "\n" |
| 31 | "With no options this will set the lowest active kernel to\n" |
| 32 | "priority 1 while maintaining the original order.\n" |
| 33 | "\n", progname); |
| 34 | } |
| 35 | |
| 36 | ////////////////////////////////////////////////////////////////////////////// |
| 37 | // I want a sorted list of priority groups, where each element in the list |
| 38 | // contains an unordered list of GPT partition numbers. This is a stupid |
| 39 | // implementation, but our needs are simple and don't justify the time or space |
| 40 | // it would take to write a "better" one. |
| 41 | #define MAX_GROUPS 17 // 0-15, plus one "higher" |
| 42 | |
| 43 | typedef struct { |
| 44 | int priority; // priority of this group |
| 45 | int num_parts; // number of partitions in this group |
| 46 | uint32_t *part; // array of partitions in this group |
| 47 | } group_t; |
| 48 | |
| 49 | typedef struct { |
| 50 | int max_parts; // max number of partitions in any group |
| 51 | int num_groups; // number of non-empty groups |
| 52 | group_t group[MAX_GROUPS]; // array of groups |
| 53 | } group_list_t; |
| 54 | |
| 55 | |
| 56 | static group_list_t *NewGroupList(int max_p) { |
| 57 | int i; |
| 58 | group_list_t *gl = (group_list_t *)malloc(sizeof(group_list_t)); |
| 59 | require(gl); |
| 60 | gl->max_parts = max_p; |
| 61 | gl->num_groups = 0; |
| 62 | // reserve space for the maximum number of partitions in every group |
| 63 | for (i=0; i<MAX_GROUPS; i++) { |
| 64 | gl->group[i].priority = -1; |
| 65 | gl->group[i].num_parts = 0; |
| 66 | gl->group[i].part = (uint32_t *)malloc(sizeof(uint32_t) * max_p); |
| 67 | require(gl->group[i].part); |
| 68 | } |
| 69 | |
| 70 | return gl; |
| 71 | } |
| 72 | |
| 73 | static void FreeGroups(group_list_t *gl) { |
| 74 | int i; |
| 75 | for (i=0; i<MAX_GROUPS; i++) |
| 76 | free(gl->group[i].part); |
| 77 | free(gl); |
| 78 | } |
| 79 | |
| 80 | static void AddToGroup(group_list_t *gl, int priority, int partition) { |
| 81 | int i; |
| 82 | // See if I've already got a group with this priority |
| 83 | for (i=0; i<gl->num_groups; i++) |
| 84 | if (gl->group[i].priority == priority) |
| 85 | break; |
| 86 | if (i == gl->num_groups) { |
| 87 | // no, add a group |
| 88 | require(i < MAX_GROUPS); |
| 89 | gl->num_groups++; |
| 90 | gl->group[i].priority = priority; |
| 91 | } |
| 92 | // add the partition to it |
| 93 | int j = gl->group[i].num_parts; |
| 94 | gl->group[i].part[j] = partition; |
| 95 | gl->group[i].num_parts++; |
| 96 | } |
| 97 | |
| 98 | static void ChangeGroup(group_list_t *gl, int old_priority, int new_priority) { |
| 99 | int i; |
| 100 | for (i=0; i<gl->num_groups; i++) |
| 101 | if (gl->group[i].priority == old_priority) { |
| 102 | gl->group[i].priority = new_priority; |
| 103 | break; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | static void SortGroups(group_list_t *gl) { |
| 108 | int i, j; |
| 109 | group_t tmp; |
| 110 | |
| 111 | // straight insertion sort is fast enough |
| 112 | for (i=1; i<gl->num_groups; i++) { |
| 113 | tmp = gl->group[i]; |
| 114 | for (j=i; j && (gl->group[j-1].priority < tmp.priority); j--) |
| 115 | gl->group[j] = gl->group[j-1]; |
| 116 | gl->group[j] = tmp; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | |
| 121 | ////////////////////////////////////////////////////////////////////////////// |
| 122 | |
| 123 | int cmd_prioritize(int argc, char *argv[]) { |
| 124 | struct drive drive; |
| 125 | uint32_t set_partition = 0; |
| 126 | int set_friends = 0; |
| 127 | int max_priority = 0; |
| 128 | int priority; |
| 129 | int orig_priority = 0; |
| 130 | int gpt_retval; |
| 131 | GptEntry *entry; |
| 132 | uint32_t index; |
| 133 | uint32_t max_part; |
| 134 | int num_kernels; |
| 135 | int i,j; |
| 136 | group_list_t *groups; |
| 137 | |
| 138 | int c; |
| 139 | int errorcnt = 0; |
| 140 | char *e = 0; |
| 141 | |
| 142 | opterr = 0; // quiet, you |
| 143 | while ((c=getopt(argc, argv, ":hi:fP:")) != -1) |
| 144 | { |
| 145 | switch (c) |
| 146 | { |
| 147 | case 'i': |
| 148 | set_partition = (uint32_t)strtoul(optarg, &e, 0); |
| 149 | if (!*optarg || (e && *e)) |
| 150 | { |
| 151 | Error("invalid argument to -%c: \"%s\"\n", c, optarg); |
| 152 | errorcnt++; |
| 153 | } |
| 154 | break; |
| 155 | case 'f': |
| 156 | set_friends = 1; |
| 157 | break; |
| 158 | case 'P': |
| 159 | max_priority = (int)strtol(optarg, &e, 0); |
| 160 | if (!*optarg || (e && *e)) |
| 161 | { |
| 162 | Error("invalid argument to -%c: \"%s\"\n", c, optarg); |
| 163 | errorcnt++; |
| 164 | } |
| 165 | if (max_priority < 1 || max_priority > 15) { |
| 166 | Error("value for -%c must be between 1 and 15\n", c); |
| 167 | errorcnt++; |
| 168 | } |
| 169 | break; |
| 170 | |
| 171 | case 'h': |
| 172 | Usage(); |
| 173 | return CGPT_OK; |
| 174 | case '?': |
| 175 | Error("unrecognized option: -%c\n", optopt); |
| 176 | errorcnt++; |
| 177 | break; |
| 178 | case ':': |
| 179 | Error("missing argument to -%c\n", optopt); |
| 180 | errorcnt++; |
| 181 | break; |
| 182 | default: |
| 183 | errorcnt++; |
| 184 | break; |
| 185 | } |
| 186 | } |
| 187 | if (errorcnt) |
| 188 | { |
| 189 | Usage(); |
| 190 | return CGPT_FAILED; |
| 191 | } |
| 192 | |
| 193 | if (set_friends && !set_partition) { |
| 194 | Error("the -f option is only useful with the -i option\n"); |
| 195 | Usage(); |
| 196 | return CGPT_FAILED; |
| 197 | } |
| 198 | |
| 199 | if (optind >= argc) { |
| 200 | Error("missing drive argument\n"); |
| 201 | return CGPT_FAILED; |
| 202 | } |
| 203 | |
| 204 | if (CGPT_OK != DriveOpen(argv[optind], &drive)) |
| 205 | return CGPT_FAILED; |
| 206 | |
| 207 | if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) { |
| 208 | Error("GptSanityCheck() returned %d: %s\n", |
| 209 | gpt_retval, GptError(gpt_retval)); |
| 210 | return CGPT_FAILED; |
| 211 | } |
| 212 | |
| 213 | max_part = GetNumberOfEntries(&drive.gpt); |
| 214 | |
| 215 | if (set_partition) { |
| 216 | if (set_partition < 1 || set_partition > max_part) { |
| 217 | Error("invalid partition number: %d (must be between 1 and %d\n", |
| 218 | set_partition, max_part); |
| 219 | goto bad; |
| 220 | } |
| 221 | index = set_partition - 1; |
| 222 | // it must be a kernel |
| 223 | entry = GetEntry(&drive.gpt, PRIMARY, index); |
| 224 | if (!GuidEqual(&entry->type, &guid_chromeos_kernel)) { |
| 225 | Error("partition %d is not a ChromeOS kernel\n", set_partition); |
| 226 | goto bad; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | // How many kernel partitions do I have? |
| 231 | num_kernels = 0; |
| 232 | for (i = 0; i < max_part; i++) { |
| 233 | entry = GetEntry(&drive.gpt, PRIMARY, i); |
| 234 | if (GuidEqual(&entry->type, &guid_chromeos_kernel)) |
| 235 | num_kernels++; |
| 236 | } |
| 237 | |
| 238 | if (!num_kernels) |
| 239 | // nothing to do, so don't |
| 240 | goto good; |
| 241 | |
| 242 | // Determine the current priority groups |
| 243 | groups = NewGroupList(num_kernels); |
| 244 | for (i = 0; i < max_part; i++) { |
| 245 | entry = GetEntry(&drive.gpt, PRIMARY, i); |
| 246 | if (!GuidEqual(&entry->type, &guid_chromeos_kernel)) |
| 247 | continue; |
| 248 | |
| 249 | priority = GetPriority(&drive.gpt, PRIMARY, i); |
| 250 | |
| 251 | // Is this partition special? |
| 252 | if (set_partition && (i+1 == set_partition)) { |
| 253 | orig_priority = priority; // remember the original priority |
| 254 | if (set_friends) |
| 255 | AddToGroup(groups, priority, i); // we'll move them all later |
| 256 | else |
| 257 | AddToGroup(groups, 99, i); // move only this one |
| 258 | } else { |
| 259 | AddToGroup(groups, priority, i); // just remember |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | // If we're including friends, then change the original group priority |
| 264 | if (set_partition && set_friends) { |
| 265 | ChangeGroup(groups, orig_priority, 99); |
| 266 | } |
| 267 | |
| 268 | // Sorting gives the new order. Now we just need to reassign the |
| 269 | // priorities. |
| 270 | SortGroups(groups); |
| 271 | |
| 272 | // We'll never lower anything to zero, so if the last group is priority zero |
| 273 | // we can ignore it. |
| 274 | i = groups->num_groups; |
| 275 | if (groups->group[i-1].priority == 0) |
| 276 | groups->num_groups--; |
| 277 | |
| 278 | // Where do we start? |
| 279 | if (max_priority) |
| 280 | priority = max_priority; |
| 281 | else |
| 282 | priority = groups->num_groups > 15 ? 15 : groups->num_groups; |
| 283 | |
| 284 | // Figure out what the new values should be |
| 285 | for (i=0; i<groups->num_groups; i++) { |
| 286 | groups->group[i].priority = priority; |
| 287 | if (priority > 1) |
| 288 | priority--; |
| 289 | } |
| 290 | |
| 291 | // Now apply the ranking to the GPT |
| 292 | for (i=0; i<groups->num_groups; i++) |
| 293 | for (j=0; j<groups->group[i].num_parts; j++) |
| 294 | SetPriority(&drive.gpt, PRIMARY, |
| 295 | groups->group[i].part[j], groups->group[i].priority); |
| 296 | |
| 297 | FreeGroups(groups); |
| 298 | |
| 299 | |
| 300 | // Write it all out |
| 301 | good: |
| 302 | RepairEntries(&drive.gpt, MASK_PRIMARY); |
| 303 | RepairHeader(&drive.gpt, MASK_PRIMARY); |
| 304 | |
| 305 | drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
| 306 | GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2); |
| 307 | UpdateCrc(&drive.gpt); |
| 308 | |
| 309 | return DriveClose(&drive, 1); |
| 310 | |
| 311 | bad: |
| 312 | (void) DriveClose(&drive, 0); |
| 313 | return CGPT_FAILED; |
| 314 | } |