cgpt_add.c revision 7d401c5cd58ec422e239b28c334fc2e94778f565
1// Copyright (c) 2012 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 <string.h> 6 7#define _STUB_IMPLEMENTATION_ 8 9#include "cgpt.h" 10#include "cgpt_params.h" 11#include "cgptlib_internal.h" 12#include "utility.h" 13#include "vboot_host.h" 14 15static const char* DumpCgptAddParams(const CgptAddParams *params) { 16 static char buf[256]; 17 char tmp[64]; 18 19 buf[0] = 0; 20 snprintf(tmp, sizeof(tmp), "-i %d ", params->partition); 21 StrnAppend(buf, tmp, sizeof(buf)); 22 if (params->label) { 23 snprintf(tmp, sizeof(tmp), "-l %s ", params->label); 24 StrnAppend(buf, tmp, sizeof(buf)); 25 } 26 if (params->set_begin) { 27 snprintf(tmp, sizeof(tmp), "-b %llu ", (unsigned long long)params->begin); 28 StrnAppend(buf, tmp, sizeof(buf)); 29 } 30 if (params->set_size) { 31 snprintf(tmp, sizeof(tmp), "-s %llu ", (unsigned long long)params->size); 32 StrnAppend(buf, tmp, sizeof(buf)); 33 } 34 if (params->set_type) { 35 GuidToStr(¶ms->type_guid, tmp, sizeof(tmp)); 36 StrnAppend(buf, "-t ", sizeof(buf)); 37 StrnAppend(buf, tmp, sizeof(buf)); 38 StrnAppend(buf, " ", sizeof(buf)); 39 } 40 if (params->set_unique) { 41 GuidToStr(¶ms->unique_guid, tmp, sizeof(tmp)); 42 StrnAppend(buf, "-u ", sizeof(buf)); 43 StrnAppend(buf, tmp, sizeof(buf)); 44 StrnAppend(buf, " ", sizeof(buf)); 45 } 46 if (params->set_successful) { 47 snprintf(tmp, sizeof(tmp), "-S %d ", params->successful); 48 StrnAppend(buf, tmp, sizeof(buf)); 49 } 50 if (params->set_tries) { 51 snprintf(tmp, sizeof(tmp), "-T %d ", params->tries); 52 StrnAppend(buf, tmp, sizeof(buf)); 53 } 54 if (params->set_priority) { 55 snprintf(tmp, sizeof(tmp), "-P %d ", params->priority); 56 StrnAppend(buf, tmp, sizeof(buf)); 57 } 58 if (params->set_raw) { 59 snprintf(tmp, sizeof(tmp), "-A 0x%x ", params->raw_value); 60 StrnAppend(buf, tmp, sizeof(buf)); 61 } 62 63 StrnAppend(buf, "\n", sizeof(buf)); 64 return buf; 65} 66 67// This is the implementation-specific helper function. 68static int GptSetEntryAttributes(struct drive *drive, 69 uint32_t index, 70 CgptAddParams *params) { 71 GptEntry *entry; 72 73 entry = GetEntry(&drive->gpt, PRIMARY, index); 74 if (params->set_begin) 75 entry->starting_lba = params->begin; 76 if (params->set_size) 77 entry->ending_lba = entry->starting_lba + params->size - 1; 78 if (params->set_unique) { 79 memcpy(&entry->unique, ¶ms->unique_guid, sizeof(Guid)); 80 } else if (GuidIsZero(&entry->type)) { 81 if (!uuid_generator) { 82 Error("Unable to generate new GUID. uuid_generator not set.\n"); 83 return -1; 84 } 85 (*uuid_generator)((uint8_t *)&entry->unique); 86 } 87 if (params->set_type) 88 memcpy(&entry->type, ¶ms->type_guid, sizeof(Guid)); 89 if (params->label) { 90 if (CGPT_OK != UTF8ToUTF16((uint8_t *)params->label, entry->name, 91 sizeof(entry->name) / sizeof(entry->name[0]))) { 92 Error("The label cannot be converted to UTF16.\n"); 93 return -1; 94 } 95 } 96 return 0; 97} 98 99static int MtdSetEntryAttributes(struct drive *drive, 100 uint32_t index, 101 CgptAddParams *params) { 102 MtdDiskPartition *entry; 103 104 entry = MtdGetEntry(&drive->mtd, PRIMARY, index); 105 if (params->set_begin) 106 entry->starting_lba = params->begin; 107 if (params->set_size) 108 entry->ending_lba = entry->starting_lba + params->size - 1; 109 if (params->set_type) 110 MtdSetEntryType(entry, LookupMtdTypeForGuid(¶ms->type_guid)); 111 112 return 0; 113} 114 115// This is an internal helper function which assumes no NULL args are passed. 116// It sets the given attribute values for a single entry at the given index. 117static int SetEntryAttributes(struct drive *drive, 118 uint32_t index, 119 CgptAddParams *params) { 120 if (params->set_raw) { 121 SetRaw(drive, PRIMARY, index, params->raw_value); 122 } else { 123 if (params->set_successful) 124 SetSuccessful(drive, PRIMARY, index, params->successful); 125 if (params->set_tries) 126 SetTries(drive, PRIMARY, index, params->tries); 127 if (params->set_priority) 128 SetPriority(drive, PRIMARY, index, params->priority); 129 } 130 131 // New partitions must specify type, begin, and size. 132 if (IsUnused(drive, PRIMARY, index)) { 133 if (!params->set_begin || !params->set_size || !params->set_type) { 134 Error("-t, -b, and -s options are required for new partitions\n"); 135 return -1; 136 } 137 if (GuidIsZero(¶ms->type_guid)) { 138 Error("New partitions must have a type other than \"unused\"\n"); 139 return -1; 140 } 141 } 142 143 return 0; 144} 145 146static int CgptCheckAddValidity(struct drive *drive) { 147 if (drive->is_mtd) { 148 if (drive->mtd.primary.crc32 != MtdHeaderCrc(&drive->mtd.primary)) { 149 Error("MTD header CRC is invalid\n"); 150 return -1; 151 } 152 } else { 153 int gpt_retval; 154 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) { 155 Error("GptSanityCheck() returned %d: %s\n", 156 gpt_retval, GptError(gpt_retval)); 157 return -1; 158 } 159 160 if (((drive->gpt.valid_headers & MASK_BOTH) != MASK_BOTH) || 161 ((drive->gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) { 162 Error("one of the GPT header/entries is invalid.\n" 163 "please run 'cgpt repair' before adding anything.\n"); 164 return -1; 165 } 166 } 167 return 0; 168} 169 170static int CgptGetUnusedPartition(struct drive *drive, uint32_t *index, 171 CgptAddParams *params) { 172 uint32_t i; 173 uint32_t max_part = GetNumberOfEntries(drive); 174 if (params->partition) { 175 if (params->partition > max_part) { 176 Error("invalid partition number: %d\n", params->partition); 177 return -1; 178 } 179 *index = params->partition - 1; 180 return 0; 181 } else { 182 // Find next empty partition. 183 for (i = 0; i < max_part; i++) { 184 if (IsUnused(drive, PRIMARY, i)) { 185 params->partition = i + 1; 186 *index = i; 187 return 0; 188 } 189 } 190 Error("no unused partitions available\n"); 191 return -1; 192 } 193} 194 195int CgptSetAttributes(CgptAddParams *params) { 196 struct drive drive; 197 198 if (params == NULL) 199 return CGPT_FAILED; 200 201 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) 202 return CGPT_FAILED; 203 204 if (CgptCheckAddValidity(&drive)) { 205 goto bad; 206 } 207 208 if (params->partition == 0 || 209 params->partition >= GetNumberOfEntries(&drive)) { 210 Error("invalid partition number: %d\n", params->partition); 211 goto bad; 212 } 213 214 SetEntryAttributes(&drive, params->partition - 1, params); 215 216 UpdateAllEntries(&drive); 217 218 // Write it all out. 219 return DriveClose(&drive, 1); 220 221bad: 222 DriveClose(&drive, 0); 223 return CGPT_FAILED; 224} 225 226// This method gets the partition details such as the attributes, the 227// guids of the partitions, etc. Input is the partition number or the 228// unique id of the partition. Output is populated in the respective 229// fields of params. 230int CgptGetPartitionDetails(CgptAddParams *params) { 231 struct drive drive; 232 int result = CGPT_FAILED; 233 int index; 234 235 if (params == NULL) 236 return CGPT_FAILED; 237 238 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) 239 return CGPT_FAILED; 240 241 if (CgptCheckAddValidity(&drive)) { 242 goto bad; 243 } 244 245 int max_part = GetNumberOfEntries(&drive); 246 if (params->partition > 0) { 247 if (params->partition >= max_part) { 248 Error("invalid partition number: %d\n", params->partition); 249 goto bad; 250 } 251 } else { 252 if (!params->set_unique) { 253 Error("either partition or unique_id must be specified\n"); 254 goto bad; 255 } 256 if (drive.is_mtd) { 257 Error("MTD partitions cannot be specified by unique_id\n"); 258 goto bad; 259 } 260 for (index = 0; index < max_part; index++) { 261 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index); 262 if (GuidEqual(&entry->unique, ¶ms->unique_guid)) { 263 params->partition = index + 1; 264 break; 265 } 266 } 267 if (index >= max_part) { 268 Error("no partitions with the given unique id available\n"); 269 goto bad; 270 } 271 } 272 index = params->partition - 1; 273 274 if(drive.is_mtd) { 275 MtdDiskPartition *entry = MtdGetEntry(&drive.mtd, PRIMARY, index); 276 uint64_t start_lba, end_lba; 277 const Guid *guid = LookupGuidForMtdType(MtdGetEntryType(entry)); 278 memcpy(¶ms->type_guid, guid, sizeof(params->type_guid)); 279 memset(¶ms->unique_guid, 0, sizeof(params->unique_guid)); 280 start_lba = entry->starting_lba; 281 end_lba = entry->ending_lba; 282 params->begin = start_lba; 283 params->size = end_lba - start_lba + 1; 284 params->raw_value = entry->flags; 285 } else { 286 // GPT-specific code 287 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index); 288 params->begin = entry->starting_lba; 289 params->size = entry->ending_lba - entry->starting_lba + 1; 290 memcpy(¶ms->type_guid, &entry->type, sizeof(Guid)); 291 memcpy(¶ms->unique_guid, &entry->unique, sizeof(Guid)); 292 params->raw_value = entry->attrs.fields.gpt_att; 293 } 294 295 params->successful = GetSuccessful(&drive, PRIMARY, index); 296 params->tries = GetTries(&drive, PRIMARY, index); 297 params->priority = GetPriority(&drive, PRIMARY, index); 298 result = CGPT_OK; 299 300bad: 301 DriveClose(&drive, 0); 302 return result; 303} 304 305int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) { 306 GptEntry *entry, backup; 307 int rv; 308 309 entry = GetEntry(&drive->gpt, PRIMARY, index); 310 memcpy(&backup, entry, sizeof(backup)); 311 312 if (SetEntryAttributes(drive, index, params) || 313 GptSetEntryAttributes(drive, index, params)) { 314 memcpy(entry, &backup, sizeof(*entry)); 315 return -1; 316 } 317 318 UpdateAllEntries(drive); 319 320 rv = CheckEntries((GptEntry*)drive->gpt.primary_entries, 321 (GptHeader*)drive->gpt.primary_header); 322 323 if (0 != rv) { 324 // If the modified entry is illegal, recover it and return error. 325 memcpy(entry, &backup, sizeof(*entry)); 326 Error("%s\n", GptErrorText(rv)); 327 Error(DumpCgptAddParams(params)); 328 return -1; 329 } 330 331 return 0; 332} 333 334int MtdAdd(struct drive *drive, CgptAddParams *params, uint32_t index) { 335 MtdDiskPartition *entry, backup; 336 entry = MtdGetEntry(&drive->mtd, PRIMARY, index); 337 memcpy(&backup, entry, sizeof(backup)); 338 339 if (SetEntryAttributes(drive, index, params) || 340 MtdSetEntryAttributes(drive, index, params)) { 341 memcpy(entry, &backup, sizeof(*entry)); 342 return -1; 343 } 344 345 UpdateAllEntries(drive); 346 347 return 0; 348} 349 350int CgptAdd(CgptAddParams *params) { 351 struct drive drive; 352 uint32_t index; 353 354 if (params == NULL) 355 return CGPT_FAILED; 356 357 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) 358 return CGPT_FAILED; 359 360 if (CgptCheckAddValidity(&drive)) { 361 goto bad; 362 } 363 364 if (CgptGetUnusedPartition(&drive, &index, params)) { 365 goto bad; 366 } 367 368 if (drive.is_mtd) { 369 if (MtdAdd(&drive, params, index)) 370 goto bad; 371 } else { 372 if (GptAdd(&drive, params, index)) 373 goto bad; 374 } 375 376 // Write it all out. 377 return DriveClose(&drive, 1); 378 379bad: 380 DriveClose(&drive, 0); 381 return CGPT_FAILED; 382} 383