blob: 9a966bacfe23f485a08e3f2f2d446b6ec4633f93 [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
5#include "cgpt.h"
6
7#include <string.h>
8
9#include "cgptlib_internal.h"
10#include "cgpt_params.h"
Bill Richardson5fed2a62013-03-04 15:11:38 -080011#define _STUB_IMPLEMENTATION_
12#include "utility.h"
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080013
14static const char* DumpCgptAddParams(const CgptAddParams *params) {
15 static char buf[256];
16 char tmp[64];
17
18 buf[0] = 0;
19 snprintf(tmp, sizeof(tmp), "-i %d ", params->partition);
Bill Richardson5fed2a62013-03-04 15:11:38 -080020 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080021 if (params->label) {
22 snprintf(tmp, sizeof(tmp), "-l %s ", params->label);
Bill Richardson5fed2a62013-03-04 15:11:38 -080023 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080024 }
25 if (params->set_begin) {
26 snprintf(tmp, sizeof(tmp), "-b %llu ", (unsigned long long)params->begin);
Bill Richardson5fed2a62013-03-04 15:11:38 -080027 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080028 }
29 if (params->set_size) {
30 snprintf(tmp, sizeof(tmp), "-s %llu ", (unsigned long long)params->size);
Bill Richardson5fed2a62013-03-04 15:11:38 -080031 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080032 }
33 if (params->set_type) {
34 GuidToStr(&params->type_guid, tmp, sizeof(tmp));
Bill Richardson5fed2a62013-03-04 15:11:38 -080035 StrnAppend(buf, "-t ", sizeof(buf));
36 StrnAppend(buf, tmp, sizeof(buf));
37 StrnAppend(buf, " ", sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080038 }
39 if (params->set_unique) {
40 GuidToStr(&params->unique_guid, tmp, sizeof(tmp));
Bill Richardson5fed2a62013-03-04 15:11:38 -080041 StrnAppend(buf, "-u ", sizeof(buf));
42 StrnAppend(buf, tmp, sizeof(buf));
43 StrnAppend(buf, " ", sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080044 }
45 if (params->set_successful) {
46 snprintf(tmp, sizeof(tmp), "-S %d ", params->successful);
Bill Richardson5fed2a62013-03-04 15:11:38 -080047 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080048 }
49 if (params->set_tries) {
50 snprintf(tmp, sizeof(tmp), "-T %d ", params->tries);
Bill Richardson5fed2a62013-03-04 15:11:38 -080051 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080052 }
53 if (params->set_priority) {
54 snprintf(tmp, sizeof(tmp), "-P %d ", params->priority);
Bill Richardson5fed2a62013-03-04 15:11:38 -080055 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080056 }
57 if (params->set_raw) {
58 snprintf(tmp, sizeof(tmp), "-A 0x%x ", params->raw_value);
Bill Richardson5fed2a62013-03-04 15:11:38 -080059 StrnAppend(buf, tmp, sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080060 }
61
Bill Richardson5fed2a62013-03-04 15:11:38 -080062 StrnAppend(buf, "\n", sizeof(buf));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +080063 return buf;
64}
65
Jay Srinivasan250549d2012-02-16 17:40:45 -080066// This is an internal helper function which assumes no NULL args are passed.
67// It sets the given attribute values for a single entry at the given index.
68static void set_entry_attributes(struct drive drive,
69 GptEntry *entry,
70 uint32_t index,
71 CgptAddParams *params) {
72 if (params->set_raw) {
73 entry->attrs.fields.gpt_att = params->raw_value;
74 } else {
75 if (params->set_successful)
76 SetSuccessful(&drive.gpt, PRIMARY, index, params->successful);
77 if (params->set_tries)
78 SetTries(&drive.gpt, PRIMARY, index, params->tries);
79 if (params->set_priority)
80 SetPriority(&drive.gpt, PRIMARY, index, params->priority);
81 }
82}
Jay Srinivasana0581432012-01-26 21:50:05 -080083
Jay Srinivasan250549d2012-02-16 17:40:45 -080084// Set the attributes such as is_successful, num_tries_left, priority, etc.
85// from the given values in params.
86int cgpt_set_attributes(CgptAddParams *params) {
Jay Srinivasana0581432012-01-26 21:50:05 -080087 struct drive drive;
88
89 int gpt_retval;
90 GptEntry *entry;
91 uint32_t index;
92
93 if (params == NULL)
94 return CGPT_FAILED;
95
Bill Richardson23429d32012-04-30 11:33:13 -070096 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
Jay Srinivasana0581432012-01-26 21:50:05 -080097 return CGPT_FAILED;
98
99 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
100 Error("GptSanityCheck() returned %d: %s\n",
101 gpt_retval, GptError(gpt_retval));
Jay Srinivasan250549d2012-02-16 17:40:45 -0800102 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800103 }
104
105 if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) ||
106 ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) {
107 Error("one of the GPT header/entries is invalid.\n"
108 "please run 'cgpt repair' before adding anything.\n");
Jay Srinivasan250549d2012-02-16 17:40:45 -0800109 goto bad;
110 }
111
112 if (params->partition == 0) {
113 Error("invalid partition number: %d\n", params->partition);
114 goto bad;
115 }
116
117 uint32_t max_part = GetNumberOfEntries(&drive.gpt);
118 if (params->partition > max_part) {
119 Error("invalid partition number: %d\n", params->partition);
120 goto bad;
121 }
122
123 index = params->partition - 1;
124 entry = GetEntry(&drive.gpt, PRIMARY, index);
125
126 set_entry_attributes(drive, entry, index, params);
127
128 RepairEntries(&drive.gpt, MASK_PRIMARY);
129 RepairHeader(&drive.gpt, MASK_PRIMARY);
130
131 drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
132 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
133 UpdateCrc(&drive.gpt);
134
135 // Write it all out.
136 return DriveClose(&drive, 1);
137
138bad:
139 DriveClose(&drive, 0);
140 return CGPT_FAILED;
141}
142
143// This method gets the partition details such as the attributes, the
144// guids of the partitions, etc. Input is the partition number or the
145// unique id of the partition. Output is populated in the respective
146// fields of params.
147int cgpt_get_partition_details(CgptAddParams *params) {
148 struct drive drive;
149
150 int gpt_retval;
151 GptEntry *entry;
152 uint32_t index;
153 int result = CGPT_FAILED;
154
155 if (params == NULL)
156 return result;
157
Bill Richardson23429d32012-04-30 11:33:13 -0700158 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR)) {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800159 Error("Unable to open drive: %s\n", params->drive_name);
160 return result;
161 }
162
163 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
164 Error("GptSanityCheck() returned %d: %s\n",
165 gpt_retval, GptError(gpt_retval));
166 goto bad;
167 }
168
169 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 goto bad;
174 }
175
176 uint32_t max_part = GetNumberOfEntries(&drive.gpt);
177
178 if (params->partition) {
179 if (params->partition > max_part) {
180 Error("invalid partition number: %d\n", params->partition);
181 goto bad;
182 }
183
184 // A valid partition number has been specified, so get the entry directly.
185 index = params->partition - 1;
186 entry = GetEntry(&drive.gpt, PRIMARY, index);
187 } else {
188 // Partition number is not specified, try looking up by the unique id.
189 if (!params->set_unique) {
190 Error("either partition or unique_id must be specified\n");
191 goto bad;
192 }
193
194 // A unique id is specified. find the entry that matches it.
195 for (index = 0; index < max_part; index++) {
196 entry = GetEntry(&drive.gpt, PRIMARY, index);
197 if (GuidEqual(&entry->unique, &params->unique_guid)) {
198 params->partition = index + 1;
199 break;
200 }
201 }
202
203 if (index >= max_part) {
204 Error("no partitions with the given unique id available\n");
205 goto bad;
206 }
207 }
208
209 // At this point, irrespective of whether a partition number is specified
210 // or a unique id is specified, we have valid non-null values for all these:
211 // index, entry, params->partition.
212
213 params->begin = entry->starting_lba;
214 params->size = entry->ending_lba - entry->starting_lba + 1;
215 memcpy(&params->type_guid, &entry->type, sizeof(Guid));
216 memcpy(&params->unique_guid, &entry->unique, sizeof(Guid));
217
218 params->raw_value = entry->attrs.fields.gpt_att;
219 params->successful = GetSuccessful(&drive.gpt, PRIMARY, index);
220 params->tries = GetTries(&drive.gpt, PRIMARY, index);
221 params->priority = GetPriority(&drive.gpt, PRIMARY, index);
222 result = CGPT_OK;
223
224bad:
225 DriveClose(&drive, 0);
226 return result;
227}
228
229
230int cgpt_add(CgptAddParams *params) {
231 struct drive drive;
232
233 int gpt_retval;
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800234 GptEntry *entry, backup;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800235 uint32_t index;
Vadim Bendebury65d3c272012-09-24 17:41:18 -0700236 int rv;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800237
238 if (params == NULL)
Jay Srinivasana0581432012-01-26 21:50:05 -0800239 return CGPT_FAILED;
Jay Srinivasan250549d2012-02-16 17:40:45 -0800240
Bill Richardson23429d32012-04-30 11:33:13 -0700241 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
Jay Srinivasan250549d2012-02-16 17:40:45 -0800242 return CGPT_FAILED;
243
244 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
245 Error("GptSanityCheck() returned %d: %s\n",
246 gpt_retval, GptError(gpt_retval));
247 goto bad;
248 }
249
250 if (((drive.gpt.valid_headers & MASK_BOTH) != MASK_BOTH) ||
251 ((drive.gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) {
252 Error("one of the GPT header/entries is invalid.\n"
253 "please run 'cgpt repair' before adding anything.\n");
254 goto bad;
Jay Srinivasana0581432012-01-26 21:50:05 -0800255 }
256
257 uint32_t max_part = GetNumberOfEntries(&drive.gpt);
258 if (params->partition) {
259 if (params->partition > max_part) {
260 Error("invalid partition number: %d\n", params->partition);
261 goto bad;
262 }
263 index = params->partition - 1;
264 entry = GetEntry(&drive.gpt, PRIMARY, index);
265 } else {
Jay Srinivasan250549d2012-02-16 17:40:45 -0800266 // Find next empty partition.
Jay Srinivasana0581432012-01-26 21:50:05 -0800267 for (index = 0; index < max_part; index++) {
268 entry = GetEntry(&drive.gpt, PRIMARY, index);
269 if (IsZero(&entry->type)) {
270 params->partition = index + 1;
271 break;
272 }
273 }
274 if (index >= max_part) {
275 Error("no unused partitions available\n");
276 goto bad;
277 }
278 }
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800279 memcpy(&backup, entry, sizeof(backup));
Jay Srinivasana0581432012-01-26 21:50:05 -0800280
281 // New partitions must specify type, begin, and size.
282 if (IsZero(&entry->type)) {
283 if (!params->set_begin || !params->set_size || !params->set_type) {
284 Error("-t, -b, and -s options are required for new partitions\n");
285 goto bad;
286 }
287 if (IsZero(&params->type_guid)) {
288 Error("New partitions must have a type other than \"unused\"\n");
289 goto bad;
290 }
291 if (!params->set_unique)
Jay Srinivasan5fac7572012-02-23 10:59:01 -0800292 if (!uuid_generator) {
293 Error("Unable to generate new GUID. uuid_generator not set.\n");
294 goto bad;
295 }
296 (*uuid_generator)((uint8_t *)&entry->unique);
Jay Srinivasana0581432012-01-26 21:50:05 -0800297 }
298
299 if (params->set_begin)
300 entry->starting_lba = params->begin;
301 if (params->set_size)
Bill Richardsonda77e692012-08-24 17:52:01 -0700302 entry->ending_lba = entry->starting_lba + params->size - 1;
Jay Srinivasana0581432012-01-26 21:50:05 -0800303 if (params->set_type)
304 memcpy(&entry->type, &params->type_guid, sizeof(Guid));
305 if (params->set_unique)
306 memcpy(&entry->unique, &params->unique_guid, sizeof(Guid));
307 if (params->label) {
308 if (CGPT_OK != UTF8ToUTF16((uint8_t *)params->label, entry->name,
309 sizeof(entry->name) / sizeof(entry->name[0]))) {
310 Error("The label cannot be converted to UTF16.\n");
311 goto bad;
312 }
313 }
Jay Srinivasan250549d2012-02-16 17:40:45 -0800314
315 set_entry_attributes(drive, entry, index, params);
Jay Srinivasana0581432012-01-26 21:50:05 -0800316
317 RepairEntries(&drive.gpt, MASK_PRIMARY);
318 RepairHeader(&drive.gpt, MASK_PRIMARY);
319
320 drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
321 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
322 UpdateCrc(&drive.gpt);
323
Vadim Bendebury65d3c272012-09-24 17:41:18 -0700324 rv = CheckEntries((GptEntry*)drive.gpt.primary_entries,
325 (GptHeader*)drive.gpt.primary_header);
326
327 if (0 != rv) {
328 // If the modified entry is illegal, recover it and return error.
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800329 memcpy(entry, &backup, sizeof(*entry));
Vadim Bendebury65d3c272012-09-24 17:41:18 -0700330 Error("%s\n", GptErrorText(rv));
Louis Yung-Chieh Lo66b47ba2012-06-22 14:44:02 +0800331 Error(DumpCgptAddParams(params));
332 goto bad;
333 }
334
Jay Srinivasan250549d2012-02-16 17:40:45 -0800335 // Write it all out.
Jay Srinivasana0581432012-01-26 21:50:05 -0800336 return DriveClose(&drive, 1);
337
338bad:
Jay Srinivasan250549d2012-02-16 17:40:45 -0800339 DriveClose(&drive, 0);
Jay Srinivasana0581432012-01-26 21:50:05 -0800340 return CGPT_FAILED;
341}