blob: eaf0b73e8719ee96e5658f6c6d0e5c216cf8802a [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
Jay Srinivasan250549d2012-02-16 17:40:45 -080067// This is an internal helper function which assumes no NULL args are passed.
68// It sets the given attribute values for a single entry at the given index.
69static void set_entry_attributes(struct drive drive,
70 GptEntry *entry,
71 uint32_t index,
72 CgptAddParams *params) {
73 if (params->set_raw) {
74 entry->attrs.fields.gpt_att = params->raw_value;
75 } else {
76 if (params->set_successful)
77 SetSuccessful(&drive.gpt, PRIMARY, index, params->successful);
78 if (params->set_tries)
79 SetTries(&drive.gpt, PRIMARY, index, params->tries);
80 if (params->set_priority)
81 SetPriority(&drive.gpt, PRIMARY, index, params->priority);
82 }
83}
Jay Srinivasana0581432012-01-26 21:50:05 -080084
Jay Srinivasan250549d2012-02-16 17:40:45 -080085// Set the attributes such as is_successful, num_tries_left, priority, etc.
86// from the given values in params.
Bill Richardson3f806a22013-03-20 15:02:34 -070087int CgptSetAttributes(CgptAddParams *params) {
Jay Srinivasana0581432012-01-26 21:50:05 -080088 struct drive drive;
89
90 int gpt_retval;
91 GptEntry *entry;
92 uint32_t index;
93
94 if (params == NULL)
95 return CGPT_FAILED;
96
Bill Richardson23429d32012-04-30 11:33:13 -070097 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
Jay Srinivasana0581432012-01-26 21:50:05 -080098 return CGPT_FAILED;
99
100 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
101 Error("GptSanityCheck() returned %d: %s\n",
102 gpt_retval, GptError(gpt_retval));
Jay Srinivasan250549d2012-02-16 17:40:45 -0800103 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800104 }
105
106 if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) ||
107 ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) {
108 Error("one of the GPT header/entries is invalid.\n"
109 "please run 'cgpt repair' before adding anything.\n");
Jay Srinivasan250549d2012-02-16 17:40:45 -0800110 goto bad;
111 }
112
113 if (params->partition == 0) {
114 Error("invalid partition number: %d\n", params->partition);
115 goto bad;
116 }
117
118 uint32_t max_part = GetNumberOfEntries(&drive.gpt);
119 if (params->partition > max_part) {
120 Error("invalid partition number: %d\n", params->partition);
121 goto bad;
122 }
123
124 index = params->partition - 1;
125 entry = GetEntry(&drive.gpt, PRIMARY, index);
126
127 set_entry_attributes(drive, entry, index, params);
128
129 RepairEntries(&drive.gpt, MASK_PRIMARY);
130 RepairHeader(&drive.gpt, MASK_PRIMARY);
131
132 drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
133 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
134 UpdateCrc(&drive.gpt);
135
136 // Write it all out.
137 return DriveClose(&drive, 1);
138
139bad:
140 DriveClose(&drive, 0);
141 return CGPT_FAILED;
142}
143
144// This method gets the partition details such as the attributes, the
145// guids of the partitions, etc. Input is the partition number or the
146// unique id of the partition. Output is populated in the respective
147// fields of params.
Bill Richardson3f806a22013-03-20 15:02:34 -0700148int CgptGetPartitionDetails(CgptAddParams *params) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800149 struct drive drive;
150
151 int gpt_retval;
152 GptEntry *entry;
153 uint32_t index;
154 int result = CGPT_FAILED;
155
156 if (params == NULL)
157 return result;
158
Bill Richardson23429d32012-04-30 11:33:13 -0700159 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800160 Error("Unable to open drive: %s\n", params->drive_name);
161 return result;
162 }
163
164 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
165 Error("GptSanityCheck() returned %d: %s\n",
166 gpt_retval, GptError(gpt_retval));
167 goto bad;
168 }
169
170 if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) ||
171 ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) {
172 Error("one of the GPT header/entries is invalid.\n"
173 "please run 'cgpt repair' before adding anything.\n");
174 goto bad;
175 }
176
177 uint32_t max_part = GetNumberOfEntries(&drive.gpt);
178
179 if (params->partition) {
180 if (params->partition > max_part) {
181 Error("invalid partition number: %d\n", params->partition);
182 goto bad;
183 }
184
185 // A valid partition number has been specified, so get the entry directly.
186 index = params->partition - 1;
187 entry = GetEntry(&drive.gpt, PRIMARY, index);
188 } else {
189 // Partition number is not specified, try looking up by the unique id.
190 if (!params->set_unique) {
191 Error("either partition or unique_id must be specified\n");
192 goto bad;
193 }
194
195 // A unique id is specified. find the entry that matches it.
196 for (index = 0; index < max_part; index++) {
197 entry = GetEntry(&drive.gpt, PRIMARY, index);
198 if (GuidEqual(&entry->unique, &params->unique_guid)) {
199 params->partition = index + 1;
200 break;
201 }
202 }
203
204 if (index >= max_part) {
205 Error("no partitions with the given unique id available\n");
206 goto bad;
207 }
208 }
209
210 // At this point, irrespective of whether a partition number is specified
211 // or a unique id is specified, we have valid non-null values for all these:
212 // index, entry, params->partition.
213
214 params->begin = entry->starting_lba;
215 params->size = entry->ending_lba - entry->starting_lba + 1;
216 memcpy(&params->type_guid, &entry->type, sizeof(Guid));
217 memcpy(&params->unique_guid, &entry->unique, sizeof(Guid));
218
219 params->raw_value = entry->attrs.fields.gpt_att;
220 params->successful = GetSuccessful(&drive.gpt, PRIMARY, index);
221 params->tries = GetTries(&drive.gpt, PRIMARY, index);
222 params->priority = GetPriority(&drive.gpt, PRIMARY, index);
223 result = CGPT_OK;
224
225bad:
226 DriveClose(&drive, 0);
227 return result;
228}
229
230
Bill Richardson3f806a22013-03-20 15:02:34 -0700231int CgptAdd(CgptAddParams *params) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800232 struct drive drive;
233
234 int gpt_retval;
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800235 GptEntry *entry, backup;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800236 uint32_t index;
Vadim Bendebury65d3c272012-09-24 17:41:18 -0700237 int rv;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800238
239 if (params == NULL)
Jay Srinivasana0581432012-01-26 21:50:05 -0800240 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800241
Bill Richardson23429d32012-04-30 11:33:13 -0700242 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
Jay Srinivasan250549d2012-02-16 17:40:45 -0800243 return CGPT_FAILED;
244
245 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
246 Error("GptSanityCheck() returned %d: %s\n",
247 gpt_retval, GptError(gpt_retval));
248 goto bad;
249 }
250
251 if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) ||
252 ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) {
253 Error("one of the GPT header/entries is invalid.\n"
254 "please run 'cgpt repair' before adding anything.\n");
255 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800256 }
257
258 uint32_t max_part = GetNumberOfEntries(&drive.gpt);
259 if (params->partition) {
260 if (params->partition > max_part) {
261 Error("invalid partition number: %d\n", params->partition);
262 goto bad;
263 }
264 index = params->partition - 1;
265 entry = GetEntry(&drive.gpt, PRIMARY, index);
266 } else {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800267 // Find next empty partition.
Jay Srinivasana0581432012-01-26 21:50:05 -0800268 for (index = 0; index < max_part; index++) {
269 entry = GetEntry(&drive.gpt, PRIMARY, index);
Bill Richardson3f806a22013-03-20 15:02:34 -0700270 if (GuidIsZero(&entry->type)) {
Jay Srinivasana0581432012-01-26 21:50:05 -0800271 params->partition = index + 1;
272 break;
273 }
274 }
275 if (index >= max_part) {
276 Error("no unused partitions available\n");
277 goto bad;
278 }
279 }
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800280 memcpy(&backup, entry, sizeof(backup));
Jay Srinivasana0581432012-01-26 21:50:05 -0800281
282 // New partitions must specify type, begin, and size.
Bill Richardson3f806a22013-03-20 15:02:34 -0700283 if (GuidIsZero(&entry->type)) {
Jay Srinivasana0581432012-01-26 21:50:05 -0800284 if (!params->set_begin || !params->set_size || !params->set_type) {
285 Error("-t, -b, and -s options are required for new partitions\n");
286 goto bad;
287 }
Bill Richardson3f806a22013-03-20 15:02:34 -0700288 if (GuidIsZero(&params->type_guid)) {
Jay Srinivasana0581432012-01-26 21:50:05 -0800289 Error("New partitions must have a type other than \"unused\"\n");
290 goto bad;
291 }
292 if (!params->set_unique)
Jay Srinivasan5fac7572012-02-23 10:59:01 -0800293 if (!uuid_generator) {
294 Error("Unable to generate new GUID. uuid_generator not set.\n");
295 goto bad;
296 }
297 (*uuid_generator)((uint8_t *)&entry->unique);
Jay Srinivasana0581432012-01-26 21:50:05 -0800298 }
299
300 if (params->set_begin)
301 entry->starting_lba = params->begin;
302 if (params->set_size)
Bill Richardsonda77e692012-08-24 17:52:01 -0700303 entry->ending_lba = entry->starting_lba + params->size - 1;
Jay Srinivasana0581432012-01-26 21:50:05 -0800304 if (params->set_type)
305 memcpy(&entry->type, &params->type_guid, sizeof(Guid));
306 if (params->set_unique)
307 memcpy(&entry->unique, &params->unique_guid, sizeof(Guid));
308 if (params->label) {
309 if (CGPT_OK != UTF8ToUTF16((uint8_t *)params->label, entry->name,
310 sizeof(entry->name) / sizeof(entry->name[0]))) {
311 Error("The label cannot be converted to UTF16.\n");
312 goto bad;
313 }
314 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800315
316 set_entry_attributes(drive, entry, index, params);
Jay Srinivasana0581432012-01-26 21:50:05 -0800317
318 RepairEntries(&drive.gpt, MASK_PRIMARY);
319 RepairHeader(&drive.gpt, MASK_PRIMARY);
320
321 drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
322 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
323 UpdateCrc(&drive.gpt);
324
Vadim Bendebury65d3c272012-09-24 17:41:18 -0700325 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.
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800330 memcpy(entry, &backup, sizeof(*entry));
Vadim Bendebury65d3c272012-09-24 17:41:18 -0700331 Error("%s\n", GptErrorText(rv));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800332 Error(DumpCgptAddParams(params));
333 goto bad;
334 }
335
Jay Srinivasan250549d2012-02-16 17:40:45 -0800336 // Write it all out.
Jay Srinivasana0581432012-01-26 21:50:05 -0800337 return DriveClose(&drive, 1);
338
339bad:
Jay Srinivasan250549d2012-02-16 17:40:45 -0800340 DriveClose(&drive, 0);
Jay Srinivasana0581432012-01-26 21:50:05 -0800341 return CGPT_FAILED;
342}