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
DumpCgptAddParams(const CgptAddParams * params)15 static 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.
GptSetEntryAttributes(struct drive * drive,uint32_t index,CgptAddParams * params)68 static 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
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.
SetEntryAttributes(struct drive * drive,uint32_t index,CgptAddParams * params)100 static int SetEntryAttributes(struct drive *drive,
101 uint32_t index,
102 CgptAddParams *params) {
103 if (params->set_raw) {
104 SetRaw(drive, PRIMARY, index, params->raw_value);
105 } else {
106 if (params->set_successful)
107 SetSuccessful(drive, PRIMARY, index, params->successful);
108 if (params->set_tries)
109 SetTries(drive, PRIMARY, index, params->tries);
110 if (params->set_priority)
111 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(¶ms->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
CgptCheckAddValidity(struct drive * drive)129 static int CgptCheckAddValidity(struct drive *drive) {
130 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;
135 }
136
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
144 return 0;
145 }
146
CgptGetUnusedPartition(struct drive * drive,uint32_t * index,CgptAddParams * params)147 static 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;
169 }
170 }
171
CgptSetAttributes(CgptAddParams * params)172 int CgptSetAttributes(CgptAddParams *params) {
173 struct drive drive;
174
175 if (params == NULL)
176 return CGPT_FAILED;
177
178 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
179 params->drive_size))
180 return CGPT_FAILED;
181
182 if (CgptCheckAddValidity(&drive)) {
183 goto bad;
184 }
185
186 if (params->partition == 0 ||
187 params->partition >= GetNumberOfEntries(&drive)) {
188 Error("invalid partition number: %d\n", params->partition);
189 goto bad;
190 }
191
192 SetEntryAttributes(&drive, params->partition - 1, params);
193
194 UpdateAllEntries(&drive);
195
196 // Write it all out.
197 return DriveClose(&drive, 1);
198
199 bad:
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.
CgptGetPartitionDetails(CgptAddParams * params)208 int CgptGetPartitionDetails(CgptAddParams *params) {
209 struct drive drive;
210 int result = CGPT_FAILED;
211 int index;
212
213 if (params == NULL)
214 return CGPT_FAILED;
215
216 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
217 params->drive_size))
218 return CGPT_FAILED;
219
220 if (CgptCheckAddValidity(&drive)) {
221 goto bad;
222 }
223
224 int max_part = GetNumberOfEntries(&drive);
225 if (params->partition > 0) {
226 if (params->partition >= max_part) {
227 Error("invalid partition number: %d\n", params->partition);
228 goto bad;
229 }
230 } else {
231 if (!params->set_unique) {
232 Error("either partition or unique_id must be specified\n");
233 goto bad;
234 }
235 for (index = 0; index < max_part; index++) {
236 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
237 if (GuidEqual(&entry->unique, ¶ms->unique_guid)) {
238 params->partition = index + 1;
239 break;
240 }
241 }
242 if (index >= max_part) {
243 Error("no partitions with the given unique id available\n");
244 goto bad;
245 }
246 }
247 index = params->partition - 1;
248
249 // 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(¶ms->type_guid, &entry->type, sizeof(Guid));
254 memcpy(¶ms->unique_guid, &entry->unique, sizeof(Guid));
255 params->raw_value = entry->attrs.fields.gpt_att;
256
257 params->successful = GetSuccessful(&drive, PRIMARY, index);
258 params->tries = GetTries(&drive, PRIMARY, index);
259 params->priority = GetPriority(&drive, PRIMARY, index);
260 result = CGPT_OK;
261
262 bad:
263 DriveClose(&drive, 0);
264 return result;
265 }
266
GptAdd(struct drive * drive,CgptAddParams * params,uint32_t index)267 static int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
268 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
CgptAdd(CgptAddParams * params)296 int CgptAdd(CgptAddParams *params) {
297 struct drive drive;
298 uint32_t index;
299
300 if (params == NULL)
301 return CGPT_FAILED;
302
303 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
304 params->drive_size))
305 return CGPT_FAILED;
306
307 if (CgptCheckAddValidity(&drive)) {
308 goto bad;
309 }
310
311 if (CgptGetUnusedPartition(&drive, &index, params)) {
312 goto bad;
313 }
314
315 if (GptAdd(&drive, params, index))
316 goto bad;
317
318 // Write it all out.
319 return DriveClose(&drive, 1);
320
321 bad:
322 DriveClose(&drive, 0);
323 return CGPT_FAILED;
324 }
325