blob: d6b3cabddb4a0244fce69c0eb2ed14bfe8110ade [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
98// This is an internal helper function which assumes no NULL args are passed.
99// It sets the given attribute values for a single entry at the given index.
100static int SetEntryAttributes(struct drive *drive,
101 uint32_t index,
102 CgptAddParams *params) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800103 if (params->set_raw) {
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700104 SetRaw(drive, PRIMARY, index, params->raw_value);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800105 } else {
106 if (params->set_successful)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700107 SetSuccessful(drive, PRIMARY, index, params->successful);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800108 if (params->set_tries)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700109 SetTries(drive, PRIMARY, index, params->tries);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800110 if (params->set_priority)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700111 SetPriority(drive, PRIMARY, index, params->priority);
112 }
113
114 // New partitions must specify type, begin, and size.
115 if (IsUnused(drive, PRIMARY, index)) {
116 if (!params->set_begin || !params->set_size || !params->set_type) {
117 Error("-t, -b, and -s options are required for new partitions\n");
118 return -1;
119 }
120 if (GuidIsZero(&params->type_guid)) {
121 Error("New partitions must have a type other than \"unused\"\n");
122 return -1;
123 }
124 }
125
126 return 0;
127}
128
129static int CgptCheckAddValidity(struct drive *drive) {
Nam T. Nguyen8577b532014-11-25 13:26:53 -0800130 int gpt_retval;
131 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
132 Error("GptSanityCheck() returned %d: %s\n",
133 gpt_retval, GptError(gpt_retval));
134 return -1;
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700135 }
Nam T. Nguyen8577b532014-11-25 13:26:53 -0800136
137 if (((drive->gpt.valid_headers & MASK_BOTH) != MASK_BOTH) ||
138 ((drive->gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) {
139 Error("one of the GPT header/entries is invalid.\n"
140 "please run 'cgpt repair' before adding anything.\n");
141 return -1;
142 }
143
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700144 return 0;
145}
146
147static int CgptGetUnusedPartition(struct drive *drive, uint32_t *index,
148 CgptAddParams *params) {
149 uint32_t i;
150 uint32_t max_part = GetNumberOfEntries(drive);
151 if (params->partition) {
152 if (params->partition > max_part) {
153 Error("invalid partition number: %d\n", params->partition);
154 return -1;
155 }
156 *index = params->partition - 1;
157 return 0;
158 } else {
159 // Find next empty partition.
160 for (i = 0; i < max_part; i++) {
161 if (IsUnused(drive, PRIMARY, i)) {
162 params->partition = i + 1;
163 *index = i;
164 return 0;
165 }
166 }
167 Error("no unused partitions available\n");
168 return -1;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800169 }
170}
Jay Srinivasana0581432012-01-26 21:50:05 -0800171
Bill Richardson3f806a22013-03-20 15:02:34 -0700172int CgptSetAttributes(CgptAddParams *params) {
Jay Srinivasana0581432012-01-26 21:50:05 -0800173 struct drive drive;
174
Jay Srinivasana0581432012-01-26 21:50:05 -0800175 if (params == NULL)
176 return CGPT_FAILED;
177
Nam T. Nguyenab899592014-11-13 19:30:46 -0800178 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
179 params->drive_size))
Jay Srinivasana0581432012-01-26 21:50:05 -0800180 return CGPT_FAILED;
181
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700182 if (CgptCheckAddValidity(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800183 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800184 }
185
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700186 if (params->partition == 0 ||
187 params->partition >= GetNumberOfEntries(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800188 Error("invalid partition number: %d\n", params->partition);
189 goto bad;
190 }
191
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700192 SetEntryAttributes(&drive, params->partition - 1, params);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800193
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700194 UpdateAllEntries(&drive);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800195
196 // Write it all out.
197 return DriveClose(&drive, 1);
198
199bad:
200 DriveClose(&drive, 0);
201 return CGPT_FAILED;
202}
203
204// This method gets the partition details such as the attributes, the
205// guids of the partitions, etc. Input is the partition number or the
206// unique id of the partition. Output is populated in the respective
207// fields of params.
Bill Richardson3f806a22013-03-20 15:02:34 -0700208int CgptGetPartitionDetails(CgptAddParams *params) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800209 struct drive drive;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800210 int result = CGPT_FAILED;
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700211 int index;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800212
213 if (params == NULL)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700214 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800215
Nam T. Nguyenab899592014-11-13 19:30:46 -0800216 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
217 params->drive_size))
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700218 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800219
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700220 if (CgptCheckAddValidity(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800221 goto bad;
222 }
223
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700224 int max_part = GetNumberOfEntries(&drive);
225 if (params->partition > 0) {
226 if (params->partition >= max_part) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800227 Error("invalid partition number: %d\n", params->partition);
228 goto bad;
229 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800230 } else {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800231 if (!params->set_unique) {
232 Error("either partition or unique_id must be specified\n");
233 goto bad;
234 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800235 for (index = 0; index < max_part; index++) {
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700236 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800237 if (GuidEqual(&entry->unique, &params->unique_guid)) {
238 params->partition = index + 1;
239 break;
240 }
241 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800242 if (index >= max_part) {
243 Error("no partitions with the given unique id available\n");
244 goto bad;
245 }
246 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700247 index = params->partition - 1;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800248
Nam T. Nguyen8577b532014-11-25 13:26:53 -0800249 // GPT-specific code
250 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
251 params->begin = entry->starting_lba;
252 params->size = entry->ending_lba - entry->starting_lba + 1;
253 memcpy(&params->type_guid, &entry->type, sizeof(Guid));
254 memcpy(&params->unique_guid, &entry->unique, sizeof(Guid));
255 params->raw_value = entry->attrs.fields.gpt_att;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800256
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700257 params->successful = GetSuccessful(&drive, PRIMARY, index);
258 params->tries = GetTries(&drive, PRIMARY, index);
259 params->priority = GetPriority(&drive, PRIMARY, index);
Jay Srinivasan250549d2012-02-16 17:40:45 -0800260 result = CGPT_OK;
261
262bad:
263 DriveClose(&drive, 0);
264 return result;
265}
266
Bill Richardson18e03702014-06-23 17:48:33 -0700267static int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
Albert Chaulk7d401c52013-04-02 14:31:17 -0700268 GptEntry *entry, backup;
269 int rv;
270
271 entry = GetEntry(&drive->gpt, PRIMARY, index);
272 memcpy(&backup, entry, sizeof(backup));
273
274 if (SetEntryAttributes(drive, index, params) ||
275 GptSetEntryAttributes(drive, index, params)) {
276 memcpy(entry, &backup, sizeof(*entry));
277 return -1;
278 }
279
280 UpdateAllEntries(drive);
281
282 rv = CheckEntries((GptEntry*)drive->gpt.primary_entries,
283 (GptHeader*)drive->gpt.primary_header);
284
285 if (0 != rv) {
286 // If the modified entry is illegal, recover it and return error.
287 memcpy(entry, &backup, sizeof(*entry));
288 Error("%s\n", GptErrorText(rv));
289 Error(DumpCgptAddParams(params));
290 return -1;
291 }
292
293 return 0;
294}
295
Bill Richardson3f806a22013-03-20 15:02:34 -0700296int CgptAdd(CgptAddParams *params) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800297 struct drive drive;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800298 uint32_t index;
299
300 if (params == NULL)
Jay Srinivasana0581432012-01-26 21:50:05 -0800301 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800302
Nam T. Nguyenab899592014-11-13 19:30:46 -0800303 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
304 params->drive_size))
Jay Srinivasan250549d2012-02-16 17:40:45 -0800305 return CGPT_FAILED;
306
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700307 if (CgptCheckAddValidity(&drive)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800308 goto bad;
309 }
310
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700311 if (CgptGetUnusedPartition(&drive, &index, params)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800312 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800313 }
314
Nam T. Nguyen8577b532014-11-25 13:26:53 -0800315 if (GptAdd(&drive, params, index))
316 goto bad;
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800317
Jay Srinivasan250549d2012-02-16 17:40:45 -0800318 // Write it all out.
Jay Srinivasana0581432012-01-26 21:50:05 -0800319 return DriveClose(&drive, 1);
320
321bad:
Jay Srinivasan250549d2012-02-16 17:40:45 -0800322 DriveClose(&drive, 0);
Jay Srinivasana0581432012-01-26 21:50:05 -0800323 return CGPT_FAILED;
324}