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