1 /* Copyright 2012 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <stdio.h>
7 #include <string.h>
8
9 #include "cgpt.h"
10 #include "cgptlib_internal.h"
11 #include "cgpt_params.h"
12 #include "vboot_host.h"
13
PrintCgptAddParams(const CgptAddParams * params)14 static void PrintCgptAddParams(const CgptAddParams *params) {
15 char tmp[64];
16
17 fprintf(stderr, "-i %d ", params->partition);
18 if (params->label)
19 fprintf(stderr, "-l %s ", params->label);
20 if (params->set_begin)
21 fprintf(stderr, "-b %llu ", (unsigned long long)params->begin);
22 if (params->set_size)
23 fprintf(stderr, "-s %llu ", (unsigned long long)params->size);
24 if (params->set_type) {
25 GuidToStr(¶ms->type_guid, tmp, sizeof(tmp));
26 fprintf(stderr, "-t %s ", tmp);
27 }
28 if (params->set_unique) {
29 GuidToStr(¶ms->unique_guid, tmp, sizeof(tmp));
30 fprintf(stderr, "-u %s ", tmp);
31 }
32 if (params->set_error_counter)
33 fprintf(stderr, "-E %d ", params->error_counter);
34 if (params->set_successful)
35 fprintf(stderr, "-S %d ", params->successful);
36 if (params->set_tries)
37 fprintf(stderr, "-T %d ", params->tries);
38 if (params->set_priority)
39 fprintf(stderr, "-P %d ", params->priority);
40 if (params->set_required)
41 fprintf(stderr, "-R %d ", params->required);
42 if (params->set_legacy_boot)
43 fprintf(stderr, "-B %d ", params->legacy_boot);
44 if (params->set_raw)
45 fprintf(stderr, "-A %#x ", params->raw_value);
46
47 fprintf(stderr, "\n");
48 }
49
50 // This is the implementation-specific helper function.
GptSetEntryAttributes(struct drive * drive,uint32_t index,CgptAddParams * params)51 static int GptSetEntryAttributes(struct drive *drive,
52 uint32_t index,
53 CgptAddParams *params) {
54 GptEntry *entry;
55
56 entry = GetEntry(&drive->gpt, PRIMARY, index);
57 if (params->set_begin)
58 entry->starting_lba = params->begin;
59 if (params->set_size)
60 entry->ending_lba = entry->starting_lba + params->size - 1;
61 if (params->set_unique) {
62 memcpy(&entry->unique, ¶ms->unique_guid, sizeof(Guid));
63 } else if (GuidIsZero(&entry->type)) {
64 if (CGPT_OK != GenerateGuid(&entry->unique)) {
65 Error("Unable to generate new GUID.\n");
66 return -1;
67 }
68 }
69 if (params->set_type)
70 memcpy(&entry->type, ¶ms->type_guid, sizeof(Guid));
71 if (params->label) {
72 if (CGPT_OK != UTF8ToUTF16((const uint8_t *)params->label, entry->name,
73 sizeof(entry->name) / sizeof(entry->name[0]))) {
74 Error("The label cannot be converted to UTF16.\n");
75 return -1;
76 }
77 }
78 return 0;
79 }
80
81 // This is an internal helper function which assumes no NULL args are passed.
82 // It sets the given attribute values for a single entry at the given index.
SetEntryAttributes(struct drive * drive,uint32_t index,CgptAddParams * params)83 static int SetEntryAttributes(struct drive *drive,
84 uint32_t index,
85 CgptAddParams *params) {
86 if (params->set_raw) {
87 SetRaw(drive, PRIMARY, index, params->raw_value);
88 } else {
89 if (params->set_error_counter)
90 SetErrorCounter(drive, PRIMARY, index, params->error_counter);
91 if (params->set_successful)
92 SetSuccessful(drive, PRIMARY, index, params->successful);
93 if (params->set_tries)
94 SetTries(drive, PRIMARY, index, params->tries);
95 if (params->set_priority)
96 SetPriority(drive, PRIMARY, index, params->priority);
97 if (params->set_legacy_boot)
98 SetLegacyBoot(drive, PRIMARY, index, params->legacy_boot);
99 if (params->set_required)
100 SetRequired(drive, PRIMARY, index, params->required);
101 }
102
103 // New partitions must specify type, begin, and size.
104 if (IsUnused(drive, PRIMARY, index)) {
105 if (!params->set_begin || !params->set_size || !params->set_type) {
106 Error("-t, -b, and -s options are required for new partitions\n");
107 return -1;
108 }
109 if (GuidIsZero(¶ms->type_guid)) {
110 Error("New partitions must have a type other than \"unused\"\n");
111 return -1;
112 }
113 }
114
115 return 0;
116 }
117
CgptCheckAddValidity(struct drive * drive)118 static int CgptCheckAddValidity(struct drive *drive) {
119 int gpt_retval;
120 if (GPT_SUCCESS != (gpt_retval = GptValidityCheck(&drive->gpt))) {
121 Error("GptValidityCheck() returned %d: %s\n",
122 gpt_retval, GptError(gpt_retval));
123 return -1;
124 }
125
126 if (CGPT_OK != CheckValid(drive)) {
127 Error("please run 'cgpt repair' before adding anything.\n");
128 return -1;
129 }
130
131 return 0;
132 }
133
CgptGetUnusedPartition(struct drive * drive,uint32_t * index,CgptAddParams * params)134 static int CgptGetUnusedPartition(struct drive *drive, uint32_t *index,
135 CgptAddParams *params) {
136 uint32_t i;
137 uint32_t max_part = GetNumberOfEntries(drive);
138 if (params->partition) {
139 if (params->partition > max_part) {
140 Error("invalid partition number: %d\n", params->partition);
141 return -1;
142 }
143 *index = params->partition - 1;
144 return 0;
145 } else {
146 // Find next empty partition.
147 for (i = 0; i < max_part; i++) {
148 if (IsUnused(drive, PRIMARY, i)) {
149 params->partition = i + 1;
150 *index = i;
151 return 0;
152 }
153 }
154 Error("no unused partitions available\n");
155 return -1;
156 }
157 }
158
CgptSetAttributes(CgptAddParams * params)159 int CgptSetAttributes(CgptAddParams *params) {
160 struct drive drive;
161
162 if (params == NULL)
163 return CGPT_FAILED;
164
165 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
166 params->drive_size))
167 return CGPT_FAILED;
168
169 if (CgptCheckAddValidity(&drive)) {
170 goto bad;
171 }
172
173 if (params->partition == 0 ||
174 params->partition >= GetNumberOfEntries(&drive)) {
175 Error("invalid partition number: %d\n", params->partition);
176 goto bad;
177 }
178
179 SetEntryAttributes(&drive, params->partition - 1, params);
180
181 UpdateAllEntries(&drive);
182
183 // Write it all out.
184 return DriveClose(&drive, 1);
185
186 bad:
187 DriveClose(&drive, 0);
188 return CGPT_FAILED;
189 }
190
191 // This method gets the partition details such as the attributes, the
192 // guids of the partitions, etc. Input is the partition number or the
193 // unique id of the partition. Output is populated in the respective
194 // fields of params.
CgptGetPartitionDetails(CgptAddParams * params)195 int CgptGetPartitionDetails(CgptAddParams *params) {
196 struct drive drive;
197 int result = CGPT_FAILED;
198 int index;
199
200 if (params == NULL)
201 return CGPT_FAILED;
202
203 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY,
204 params->drive_size))
205 return CGPT_FAILED;
206
207 if (CgptCheckAddValidity(&drive)) {
208 goto bad;
209 }
210
211 int max_part = GetNumberOfEntries(&drive);
212 if (params->partition > 0) {
213 if (params->partition >= max_part) {
214 Error("invalid partition number: %d\n", params->partition);
215 goto bad;
216 }
217 } else {
218 if (!params->set_unique) {
219 Error("either partition or unique_id must be specified\n");
220 goto bad;
221 }
222 for (index = 0; index < max_part; index++) {
223 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
224 if (GuidEqual(&entry->unique, ¶ms->unique_guid)) {
225 params->partition = index + 1;
226 break;
227 }
228 }
229 if (index >= max_part) {
230 Error("no partitions with the given unique id available\n");
231 goto bad;
232 }
233 }
234 index = params->partition - 1;
235
236 // GPT-specific code
237 GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
238 params->begin = entry->starting_lba;
239 params->size = entry->ending_lba - entry->starting_lba + 1;
240 memcpy(¶ms->type_guid, &entry->type, sizeof(Guid));
241 memcpy(¶ms->unique_guid, &entry->unique, sizeof(Guid));
242 params->raw_value = entry->attrs.fields.gpt_att;
243
244 params->error_counter = GetErrorCounter(&drive, PRIMARY, index);
245 params->successful = GetSuccessful(&drive, PRIMARY, index);
246 params->tries = GetTries(&drive, PRIMARY, index);
247 params->priority = GetPriority(&drive, PRIMARY, index);
248 result = CGPT_OK;
249
250 bad:
251 DriveClose(&drive, 0);
252 return result;
253 }
254
GptAdd(struct drive * drive,CgptAddParams * params,uint32_t index)255 static int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
256 GptEntry *entry, backup;
257 int rv;
258
259 entry = GetEntry(&drive->gpt, PRIMARY, index);
260 memcpy(&backup, entry, sizeof(backup));
261
262 if (SetEntryAttributes(drive, index, params) ||
263 GptSetEntryAttributes(drive, index, params)) {
264 memcpy(entry, &backup, sizeof(*entry));
265 return -1;
266 }
267
268 UpdateAllEntries(drive);
269
270 rv = CheckEntries((GptEntry*)drive->gpt.primary_entries,
271 (GptHeader*)drive->gpt.primary_header);
272
273 if (0 != rv) {
274 // If the modified entry is illegal, recover it and return error.
275 memcpy(entry, &backup, sizeof(*entry));
276 Error("%s\n", GptErrorText(rv));
277 Error("");
278 PrintCgptAddParams(params);
279 return -1;
280 }
281
282 return 0;
283 }
284
CgptAdd(CgptAddParams * params)285 int CgptAdd(CgptAddParams *params) {
286 struct drive drive;
287 uint32_t index;
288
289 if (params == NULL)
290 return CGPT_FAILED;
291
292 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
293 params->drive_size))
294 return CGPT_FAILED;
295
296 if (CgptCheckAddValidity(&drive)) {
297 goto bad;
298 }
299
300 if (CgptGetUnusedPartition(&drive, &index, params)) {
301 goto bad;
302 }
303
304 if (GptAdd(&drive, params, index))
305 goto bad;
306
307 // Write it all out.
308 return DriveClose(&drive, 1);
309
310 bad:
311 DriveClose(&drive, 0);
312 return CGPT_FAILED;
313 }
314