blob: b87fe4b4ae839520032a558bce9d7c665991a183 [file] [log] [blame]
Jay Srinivasana0581432012-01-26 21:50:05 -08001// 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
Jay Srinivasana0581432012-01-26 21:50:05 -08005#include <string.h>
6
Bill Richardson5fed2a62013-03-04 15:11:38 -08007#define _STUB_IMPLEMENTATION_
Bill Richardson0c3ba242013-03-29 11:09:30 -07008
9#include "cgpt.h"
10#include "cgpt_params.h"
11#include "cgptlib_internal.h"
Bill Richardson5fed2a62013-03-04 15:11:38 -080012#include "utility.h"
Bill Richardson0c3ba242013-03-29 11:09:30 -070013#include "vboot_host.h"
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080014
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);
Bill Richardson5fed2a62013-03-04 15:11:38 -080021 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080022 if (params->label) {
23 snprintf(tmp, sizeof(tmp), "-l %s ", params->label);
Bill Richardson5fed2a62013-03-04 15:11:38 -080024 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080025 }
26 if (params->set_begin) {
27 snprintf(tmp, sizeof(tmp), "-b %llu ", (unsigned long long)params->begin);
Bill Richardson5fed2a62013-03-04 15:11:38 -080028 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080029 }
30 if (params->set_size) {
31 snprintf(tmp, sizeof(tmp), "-s %llu ", (unsigned long long)params->size);
Bill Richardson5fed2a62013-03-04 15:11:38 -080032 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080033 }
34 if (params->set_type) {
35 GuidToStr(&params->type_guid, tmp, sizeof(tmp));
Bill Richardson5fed2a62013-03-04 15:11:38 -080036 StrnAppend(buf, "-t ", sizeof(buf));
37 StrnAppend(buf, tmp, sizeof(buf));
38 StrnAppend(buf, " ", sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080039 }
40 if (params->set_unique) {
41 GuidToStr(&params->unique_guid, tmp, sizeof(tmp));
Bill Richardson5fed2a62013-03-04 15:11:38 -080042 StrnAppend(buf, "-u ", sizeof(buf));
43 StrnAppend(buf, tmp, sizeof(buf));
44 StrnAppend(buf, " ", sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080045 }
46 if (params->set_successful) {
47 snprintf(tmp, sizeof(tmp), "-S %d ", params->successful);
Bill Richardson5fed2a62013-03-04 15:11:38 -080048 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080049 }
50 if (params->set_tries) {
51 snprintf(tmp, sizeof(tmp), "-T %d ", params->tries);
Bill Richardson5fed2a62013-03-04 15:11:38 -080052 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080053 }
54 if (params->set_priority) {
55 snprintf(tmp, sizeof(tmp), "-P %d ", params->priority);
Bill Richardson5fed2a62013-03-04 15:11:38 -080056 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080057 }
58 if (params->set_raw) {
59 snprintf(tmp, sizeof(tmp), "-A 0x%x ", params->raw_value);
Bill Richardson5fed2a62013-03-04 15:11:38 -080060 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080061 }
62
Bill Richardson5fed2a62013-03-04 15:11:38 -080063 StrnAppend(buf, "\n", sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080064 return buf;
65}
66
Albert Chaulkfa6b35c2013-03-26 13:43:02 -070067// This is the implementation-specific helper function.
68static int GptSetEntryAttributes(struct drive *drive,
Jay Srinivasan250549d2012-02-16 17:40:45 -080069 uint32_t index,
70 CgptAddParams *params) {
Albert Chaulkfa6b35c2013-03-26 13:43:02 -070071 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, &params->unique_guid, sizeof(Guid));
80 } else if (GuidIsZero(&entry->type)) {
Bill Richardson4cb54972014-06-20 14:33:00 -070081 if (CGPT_OK != GenerateGuid(&entry->unique)) {
82 Error("Unable to generate new GUID.\n");
83 return -1;
Albert Chaulkfa6b35c2013-03-26 13:43:02 -070084 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -070085 }
86 if (params->set_type)
87 memcpy(&entry->type, &params->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
Albert Chaulk7d401c52013-04-02 14:31:17 -070098static int MtdSetEntryAttributes(struct drive *drive,
99 uint32_t index,
100 CgptAddParams *params) {
101 MtdDiskPartition *entry;
102
103 entry = MtdGetEntry(&drive->mtd, PRIMARY, index);
Albert Chaulk494646d2013-07-19 12:56:38 -0700104 if (params->set_begin) {
105 uint64_t start = params->begin * drive->mtd.sector_bytes;
106 memcpy(&entry->starting_offset, &start, sizeof(params->begin));
107 }
Albert Chaulk289b6042013-06-25 11:30:46 -0700108 if (params->set_size) {
109 uint64_t start;
110 uint64_t end;
111 MtdGetPartitionSize(entry, &start, NULL, NULL);
Albert Chaulk494646d2013-07-19 12:56:38 -0700112 end = start + params->size * drive->mtd.sector_bytes - 1;
Albert Chaulk289b6042013-06-25 11:30:46 -0700113 memcpy(&entry->ending_offset, &end, sizeof(end));
114 }
Albert Chaulk7d401c52013-04-02 14:31:17 -0700115 if (params->set_type)
116 MtdSetEntryType(entry, LookupMtdTypeForGuid(&params->type_guid));
Albert Chaulk32fd6de2013-07-25 11:32:57 -0700117 if (params->label) {
118 strncpy(entry->label, params->label, sizeof(entry->label));
119 }
Albert Chaulk7d401c52013-04-02 14:31:17 -0700120
121 return 0;
122}
123
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700124// 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) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800129 if (params->set_raw) {
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700130 SetRaw(drive, PRIMARY, index, params->raw_value);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800131 } else {
132 if (params->set_successful)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700133 SetSuccessful(drive, PRIMARY, index, params->successful);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800134 if (params->set_tries)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700135 SetTries(drive, PRIMARY, index, params->tries);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800136 if (params->set_priority)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700137 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(&params->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) {
Albert Chaulk7d401c52013-04-02 14:31:17 -0700156 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 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700168
Albert Chaulk7d401c52013-04-02 14:31:17 -0700169 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 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700175 }
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;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800201 }
202}
Jay Srinivasana0581432012-01-26 21:50:05 -0800203
Bill Richardson3f806a22013-03-20 15:02:34 -0700204int CgptSetAttributes(CgptAddParams *params) {
Jay Srinivasana0581432012-01-26 21:50:05 -0800205 struct drive drive;
206
Jay Srinivasana0581432012-01-26 21:50:05 -0800207 if (params == NULL)
208 return CGPT_FAILED;
209
Bill Richardson23429d32012-04-30 11:33:13 -0700210 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
Jay Srinivasana0581432012-01-26 21:50:05 -0800211 return CGPT_FAILED;
212
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700213 if (CgptCheckAddValidity(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800214 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800215 }
216
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700217 if (params->partition == 0 ||
218 params->partition >= GetNumberOfEntries(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800219 Error("invalid partition number: %d\n", params->partition);
220 goto bad;
221 }
222
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700223 SetEntryAttributes(&drive, params->partition - 1, params);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800224
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700225 UpdateAllEntries(&drive);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800226
227 // Write it all out.
228 return DriveClose(&drive, 1);
229
230bad:
231 DriveClose(&drive, 0);
232 return CGPT_FAILED;
233}
234
235// This method gets the partition details such as the attributes, the
236// guids of the partitions, etc. Input is the partition number or the
237// unique id of the partition. Output is populated in the respective
238// fields of params.
Bill Richardson3f806a22013-03-20 15:02:34 -0700239int CgptGetPartitionDetails(CgptAddParams *params) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800240 struct drive drive;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800241 int result = CGPT_FAILED;
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700242 int index;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800243
244 if (params == NULL)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700245 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800246
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700247 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
248 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800249
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700250 if (CgptCheckAddValidity(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800251 goto bad;
252 }
253
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700254 int max_part = GetNumberOfEntries(&drive);
255 if (params->partition > 0) {
256 if (params->partition >= max_part) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800257 Error("invalid partition number: %d\n", params->partition);
258 goto bad;
259 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800260 } else {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800261 if (!params->set_unique) {
262 Error("either partition or unique_id must be specified\n");
263 goto bad;
264 }
Albert Chaulk7d401c52013-04-02 14:31:17 -0700265 if (drive.is_mtd) {
266 Error("MTD partitions cannot be specified by unique_id\n");
267 goto bad;
268 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800269 for (index = 0; index < max_part; index++) {
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700270 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800271 if (GuidEqual(&entry->unique, &params->unique_guid)) {
272 params->partition = index + 1;
273 break;
274 }
275 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800276 if (index >= max_part) {
277 Error("no partitions with the given unique id available\n");
278 goto bad;
279 }
280 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700281 index = params->partition - 1;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800282
Albert Chaulk7d401c52013-04-02 14:31:17 -0700283 if(drive.is_mtd) {
284 MtdDiskPartition *entry = MtdGetEntry(&drive.mtd, PRIMARY, index);
Albert Chaulk7d401c52013-04-02 14:31:17 -0700285 const Guid *guid = LookupGuidForMtdType(MtdGetEntryType(entry));
286 memcpy(&params->type_guid, guid, sizeof(params->type_guid));
287 memset(&params->unique_guid, 0, sizeof(params->unique_guid));
Albert Chaulk289b6042013-06-25 11:30:46 -0700288 MtdGetPartitionSizeInSectors(entry, &params->begin, NULL, &params->size);
Albert Chaulk7d401c52013-04-02 14:31:17 -0700289 params->raw_value = entry->flags;
290 } else {
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700291 // GPT-specific code
292 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
293 params->begin = entry->starting_lba;
294 params->size = entry->ending_lba - entry->starting_lba + 1;
295 memcpy(&params->type_guid, &entry->type, sizeof(Guid));
296 memcpy(&params->unique_guid, &entry->unique, sizeof(Guid));
297 params->raw_value = entry->attrs.fields.gpt_att;
298 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800299
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700300 params->successful = GetSuccessful(&drive, PRIMARY, index);
301 params->tries = GetTries(&drive, PRIMARY, index);
302 params->priority = GetPriority(&drive, PRIMARY, index);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800303 result = CGPT_OK;
304
305bad:
306 DriveClose(&drive, 0);
307 return result;
308}
309
Albert Chaulk7d401c52013-04-02 14:31:17 -0700310int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
311 GptEntry *entry, backup;
312 int rv;
313
314 entry = GetEntry(&drive->gpt, PRIMARY, index);
315 memcpy(&backup, entry, sizeof(backup));
316
317 if (SetEntryAttributes(drive, index, params) ||
318 GptSetEntryAttributes(drive, index, params)) {
319 memcpy(entry, &backup, sizeof(*entry));
320 return -1;
321 }
322
323 UpdateAllEntries(drive);
324
325 rv = CheckEntries((GptEntry*)drive->gpt.primary_entries,
326 (GptHeader*)drive->gpt.primary_header);
327
328 if (0 != rv) {
329 // If the modified entry is illegal, recover it and return error.
330 memcpy(entry, &backup, sizeof(*entry));
331 Error("%s\n", GptErrorText(rv));
332 Error(DumpCgptAddParams(params));
333 return -1;
334 }
335
336 return 0;
337}
338
339int MtdAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
340 MtdDiskPartition *entry, backup;
341 entry = MtdGetEntry(&drive->mtd, PRIMARY, index);
342 memcpy(&backup, entry, sizeof(backup));
343
344 if (SetEntryAttributes(drive, index, params) ||
345 MtdSetEntryAttributes(drive, index, params)) {
346 memcpy(entry, &backup, sizeof(*entry));
347 return -1;
348 }
349
350 UpdateAllEntries(drive);
351
352 return 0;
353}
354
Bill Richardson3f806a22013-03-20 15:02:34 -0700355int CgptAdd(CgptAddParams *params) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800356 struct drive drive;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800357 uint32_t index;
358
359 if (params == NULL)
Jay Srinivasana0581432012-01-26 21:50:05 -0800360 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800361
Bill Richardson23429d32012-04-30 11:33:13 -0700362 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
Jay Srinivasan250549d2012-02-16 17:40:45 -0800363 return CGPT_FAILED;
364
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700365 if (CgptCheckAddValidity(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800366 goto bad;
367 }
368
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700369 if (CgptGetUnusedPartition(&drive, &index, params)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800370 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800371 }
372
Albert Chaulk7d401c52013-04-02 14:31:17 -0700373 if (drive.is_mtd) {
374 if (MtdAdd(&drive, params, index))
375 goto bad;
376 } else {
377 if (GptAdd(&drive, params, index))
378 goto bad;
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800379 }
380
Jay Srinivasan250549d2012-02-16 17:40:45 -0800381 // Write it all out.
Jay Srinivasana0581432012-01-26 21:50:05 -0800382 return DriveClose(&drive, 1);
383
384bad:
Jay Srinivasan250549d2012-02-16 17:40:45 -0800385 DriveClose(&drive, 0);
Jay Srinivasana0581432012-01-26 21:50:05 -0800386 return CGPT_FAILED;
387}