OLD | NEW |
(Empty) | |
| 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 * Update GPT attribute bits. |
| 6 */ |
| 7 #include <getopt.h> |
| 8 #include <stdio.h> |
| 9 #include <stdlib.h> |
| 10 #include "cgpt.h" |
| 11 #include "cgptlib_internal.h" |
| 12 #include "utility.h" |
| 13 |
| 14 static struct number_range |
| 15 range_127_0 = {127, 0}; |
| 16 |
| 17 /* Integers to store parsed argument. */ |
| 18 static int help, partition, begin_lba, size_lba; |
| 19 static char type[128], unique[128], name[128]; |
| 20 |
| 21 /* The structure for getopt_long(). When you add/delete any line, please refine |
| 22 * attribute_comments[] and third parameter of getopt_long() too. */ |
| 23 static struct option adm_options[] = { |
| 24 {.name = "help", .has_arg = no_argument, .flag = 0, .val = 'h'}, |
| 25 {.name = "partition", .has_arg = required_argument, .flag = 0, .val = 'i'}, |
| 26 #if 0//FIXME |
| 27 {.name = "bad", .has_arg = required_argument, .flag = 0, .val = 'b'}, |
| 28 {.name = "successful", .has_arg = required_argument, .flag = 0, .val = 's'}, |
| 29 {.name = "tries", .has_arg = required_argument, .flag = 0, .val = 't'}, |
| 30 {.name = "priority", .has_arg = required_argument, .flag = 0, .val = 'p'}, |
| 31 #endif |
| 32 {.name = "type", .has_arg = required_argument, .flag = 0, .val = 't'}, |
| 33 {.name = "unique", .has_arg = required_argument, .flag = 0, .val = 'u'}, |
| 34 {.name = "begin", .has_arg = required_argument, .flag = 0, .val = 'b'}, |
| 35 {.name = "size", .has_arg = required_argument, .flag = 0, .val = 's'}, |
| 36 {.name = "name", .has_arg = required_argument, .flag = 0, .val = 'n'}, |
| 37 { /* last element, which should be zero. */ } |
| 38 }; |
| 39 |
| 40 /* Extra information than struct option, please update this structure if you |
| 41 * add/remove any line in attribute_options[]. */ |
| 42 static struct option_details adm_options_details[] = { |
| 43 /* help */ |
| 44 { .comment = "print this help", |
| 45 .validator = AssignTrue, |
| 46 .valid_range = 0, |
| 47 .parsed = &help}, |
| 48 /* partition */ |
| 49 { .comment = "partition number (MUST HAVE)", |
| 50 .validator = InNumberRange, |
| 51 .valid_range = &range_127_0, |
| 52 .parsed = &partition}, |
| 53 #if 0//FIXME |
| 54 /* bad */ |
| 55 { .comment = "mark partition bad", |
| 56 .validator = InNumberRange, |
| 57 .valid_range = &range_1_0, |
| 58 .parsed = &bad}, |
| 59 /* successful */ |
| 60 { .comment = "mark partition successful", |
| 61 .validator = InNumberRange, |
| 62 .valid_range = &range_1_0, |
| 63 .parsed = &successful}, |
| 64 /* tries */ |
| 65 { .comment = "tries", |
| 66 .validator = InNumberRange, |
| 67 .valid_range = &range_15_0, |
| 68 .parsed = &tries}, |
| 69 /* priority */ |
| 70 { .comment = "priority to boot", |
| 71 .validator = InNumberRange, |
| 72 .valid_range = &range_15_0, |
| 73 .parsed = &priority}, |
| 74 #endif |
| 75 /* type */ |
| 76 { .comment = "Partition Type (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)", |
| 77 .validator = CopyString, |
| 78 .valid_range = (void*)sizeof(type), |
| 79 .parsed = &type}, |
| 80 /* uuid */ |
| 81 { .comment = "Partition UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)", |
| 82 .validator = CopyString, |
| 83 .valid_range = (void*)sizeof(unique), |
| 84 .parsed = &unique}, |
| 85 /* start */ |
| 86 { .comment = "starting LBA", |
| 87 .validator = InNumberRange, |
| 88 .valid_range = 0, |
| 89 .parsed = &begin_lba}, |
| 90 /* end */ |
| 91 { .comment = "ending LBA", |
| 92 .validator = InNumberRange, |
| 93 .valid_range = 0, |
| 94 .parsed = &size_lba}, |
| 95 /* name */ |
| 96 { .comment = "Partition name", |
| 97 .validator = CopyString, |
| 98 .valid_range = (void*)sizeof(name), |
| 99 .parsed = &name}, |
| 100 { /* last element, which should be zero. */ } |
| 101 }; |
| 102 |
| 103 void AdmHelp() { |
| 104 printf("\nUsage: %s {add|delete|modify} [OPTIONS] device_name\n\n", progname); |
| 105 ShowOptions(adm_options, adm_options_details, ARRAY_COUNT(adm_options)); |
| 106 PrintTypes(); |
| 107 printf("\n"); |
| 108 } |
| 109 |
| 110 enum { |
| 111 ADD, |
| 112 DELETE, |
| 113 MODIFY, |
| 114 } command; |
| 115 |
| 116 /* Parses all options (and validates them), then opens the drive and sets |
| 117 * corresponding bits in GPT entry. */ |
| 118 int CgptAdm(int argc, char *argv[]) { |
| 119 struct drive drive; |
| 120 char *cmd; |
| 121 GptEntry *entry; |
| 122 Guid type_guid, unique_guid; |
| 123 int dirty = 0; |
| 124 |
| 125 /* I know this is NOT the perfect place to put code to make options[] and |
| 126 * details[] are synced. But this is the best place we have right now since C |
| 127 * preprocessor doesn't know sizeof() for #if directive. */ |
| 128 assert(ARRAY_COUNT(adm_options) == |
| 129 ARRAY_COUNT(adm_options_details)); |
| 130 |
| 131 cmd = argv[optind - 1]; |
| 132 if (!strcmp("add", cmd)) command = ADD; |
| 133 else if (!strcmp("delete", cmd)) command = DELETE; |
| 134 else if (!strcmp("modify", cmd)) command = MODIFY; |
| 135 |
| 136 #if 0//FIXME |
| 137 help = partition = bad = successful = tries = priority = |
| 138 #endif |
| 139 help = partition = begin_lba = size_lba = NOT_INITED; |
| 140 type[0] = '\0'; |
| 141 unique[0] = '\0'; |
| 142 name[0] = '\0'; |
| 143 |
| 144 if (CGPT_OK != HandleOptions(argc, argv, |
| 145 "hi:t:u:b:s:n:", |
| 146 ARRAY_COUNT(adm_options), |
| 147 adm_options, |
| 148 adm_options_details)) |
| 149 return CGPT_FAILED; |
| 150 if (help != NOT_INITED) { |
| 151 AdmHelp(); |
| 152 return CGPT_FAILED; |
| 153 } |
| 154 |
| 155 if (CGPT_OK != OpenDriveInLastArgument(argc, argv, &drive)) |
| 156 return CGPT_FAILED; |
| 157 |
| 158 if (CheckValid(&drive) != CGPT_OK) goto error_close; |
| 159 |
| 160 if (partition == NOT_INITED) { |
| 161 printf("[ERROR] Please provide partition number with --partition or -i.\n"); |
| 162 goto error_close; |
| 163 } |
| 164 |
| 165 entry = GetEntry(&drive.gpt, PRIMARY, partition); |
| 166 /* check before really doing something. */ |
| 167 switch (command) { |
| 168 case ADD: |
| 169 if (NonZeroGuid(&entry->type)) { |
| 170 printf("[ERROR] partition %d is not free, use '%s modify' instead.\n", |
| 171 partition, progname); |
| 172 goto error_close; |
| 173 } |
| 174 if (type[0] == '\0') { |
| 175 printf("* You must give a type with '--type' or '-t'.\n"); |
| 176 PrintTypes(); |
| 177 goto error_close; |
| 178 } |
| 179 if (begin_lba == NOT_INITED) { |
| 180 printf("* You didn't give the begin LBA, use '--begin' to specify.\n"); |
| 181 goto error_close; |
| 182 } |
| 183 if (size_lba == NOT_INITED) { |
| 184 printf("* You didn't give size, use '--size' to specify.\n"); |
| 185 goto error_close; |
| 186 } |
| 187 break; |
| 188 case DELETE: |
| 189 if (!NonZeroGuid(&entry->type)) { |
| 190 printf("[ERROR] partition %d is free already.\n", partition); |
| 191 goto error_close; |
| 192 } |
| 193 break; |
| 194 case MODIFY: |
| 195 if (!NonZeroGuid(&entry->type)) { |
| 196 printf("[ERROR] partition %d is free, use '%s add' first.\n", |
| 197 partition, progname); |
| 198 goto error_close; |
| 199 } |
| 200 break; |
| 201 } |
| 202 |
| 203 #if 0 //FIXME |
| 204 if (bad != NOT_INITED) |
| 205 SetBad(&drive.gpt, PRIMARY, partition, bad); |
| 206 if (successful != NOT_INITED) |
| 207 SetSuccessful(&drive.gpt, PRIMARY, partition, successful); |
| 208 if (tries != NOT_INITED) |
| 209 SetTries(&drive.gpt, PRIMARY, partition, tries); |
| 210 if (priority != NOT_INITED) |
| 211 SetPriority(&drive.gpt, PRIMARY, partition, priority); |
| 212 #endif |
| 213 if (type[0]) { |
| 214 if (CGPT_OK != SupportedType(type, &type_guid) && |
| 215 CGPT_OK != StrToGuid(type, &type_guid)) { |
| 216 printf("[ERROR] You didn't give a valid type [%s]\n", type); |
| 217 goto error_close; |
| 218 } |
| 219 Memcpy(&entry->type, &type_guid, sizeof(Guid)); |
| 220 ++dirty; |
| 221 } |
| 222 if (unique[0]) { |
| 223 if (CGPT_OK != StrToGuid(unique, &unique_guid)) { |
| 224 printf("[ERROR] You didn't give a valid UUID [%s]\n", unique); |
| 225 goto error_close; |
| 226 } |
| 227 Memcpy(&entry->unique, &unique_guid, sizeof(Guid)); |
| 228 ++dirty; |
| 229 } |
| 230 if (begin_lba != NOT_INITED) { |
| 231 entry->starting_lba = begin_lba; |
| 232 ++dirty; |
| 233 } |
| 234 if (size_lba != NOT_INITED) { |
| 235 entry->ending_lba = entry->starting_lba + size_lba - 1; |
| 236 ++dirty; |
| 237 } |
| 238 if (name[0]) { |
| 239 UTF8ToUTF16((uint8_t*)name, entry->name); |
| 240 ++dirty; |
| 241 } |
| 242 |
| 243 if (command == DELETE) { |
| 244 Guid unused = GPT_ENT_TYPE_UNUSED; |
| 245 Memcpy(&entry->type, &unused, sizeof(Guid)); |
| 246 } |
| 247 |
| 248 if (dirty) { |
| 249 uint32_t valid_entries; |
| 250 |
| 251 valid_entries = drive.gpt.valid_entries; |
| 252 if ((valid_entries != CheckValidEntries(&drive.gpt)) || |
| 253 (valid_entries != CheckOverlappedPartition(&drive.gpt))) { |
| 254 printf("\n[ERROR] Your change makes GPT invalid (or worse). " |
| 255 "Please check your arguments.\n\n"); |
| 256 drive.gpt.modified = 0; /* DriveClose() won't update hard drive. */ |
| 257 goto error_close; |
| 258 } |
| 259 |
| 260 /* Claims primary is good, then secondary will be overwritten. */ |
| 261 RepairEntries(&drive.gpt, MASK_PRIMARY); |
| 262 RepairHeader(&drive.gpt, MASK_PRIMARY); |
| 263 |
| 264 /* Forces headers and entries are modified so that CRC32 will be |
| 265 * re-calculated and headers and entries will be updated to drive. */ |
| 266 drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
| 267 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2); |
| 268 UpdateCrc(&drive.gpt); |
| 269 } |
| 270 DriveClose(&drive); |
| 271 return CGPT_OK; |
| 272 |
| 273 error_close: |
| 274 DriveClose(&drive); |
| 275 return CGPT_FAILED; |
| 276 } |
OLD | NEW |