cgpt_add.c revision 7d401c5cd58ec422e239b28c334fc2e94778f565
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
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);
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(&params->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(&params->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.
68static 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, &params->unique_guid, sizeof(Guid));
80  } else if (GuidIsZero(&entry->type)) {
81    if (!uuid_generator) {
82      Error("Unable to generate new GUID. uuid_generator not set.\n");
83      return -1;
84    }
85    (*uuid_generator)((uint8_t *)&entry->unique);
86  }
87  if (params->set_type)
88    memcpy(&entry->type, &params->type_guid, sizeof(Guid));
89  if (params->label) {
90    if (CGPT_OK != UTF8ToUTF16((uint8_t *)params->label, entry->name,
91                               sizeof(entry->name) / sizeof(entry->name[0]))) {
92      Error("The label cannot be converted to UTF16.\n");
93      return -1;
94    }
95  }
96  return 0;
97}
98
99static int MtdSetEntryAttributes(struct drive *drive,
100                                 uint32_t index,
101                                 CgptAddParams *params) {
102  MtdDiskPartition *entry;
103
104  entry = MtdGetEntry(&drive->mtd, PRIMARY, index);
105  if (params->set_begin)
106    entry->starting_lba = params->begin;
107  if (params->set_size)
108    entry->ending_lba = entry->starting_lba + params->size - 1;
109  if (params->set_type)
110    MtdSetEntryType(entry, LookupMtdTypeForGuid(&params->type_guid));
111
112  return 0;
113}
114
115// This is an internal helper function which assumes no NULL args are passed.
116// It sets the given attribute values for a single entry at the given index.
117static int SetEntryAttributes(struct drive *drive,
118                              uint32_t index,
119                              CgptAddParams *params) {
120  if (params->set_raw) {
121    SetRaw(drive, PRIMARY, index, params->raw_value);
122  } else {
123    if (params->set_successful)
124      SetSuccessful(drive, PRIMARY, index, params->successful);
125    if (params->set_tries)
126      SetTries(drive, PRIMARY, index, params->tries);
127    if (params->set_priority)
128      SetPriority(drive, PRIMARY, index, params->priority);
129  }
130
131  // New partitions must specify type, begin, and size.
132  if (IsUnused(drive, PRIMARY, index)) {
133    if (!params->set_begin || !params->set_size || !params->set_type) {
134      Error("-t, -b, and -s options are required for new partitions\n");
135      return -1;
136    }
137    if (GuidIsZero(&params->type_guid)) {
138      Error("New partitions must have a type other than \"unused\"\n");
139      return -1;
140    }
141  }
142
143  return 0;
144}
145
146static int CgptCheckAddValidity(struct drive *drive) {
147  if (drive->is_mtd) {
148    if (drive->mtd.primary.crc32 != MtdHeaderCrc(&drive->mtd.primary)) {
149      Error("MTD header CRC is invalid\n");
150      return -1;
151    }
152  } else {
153    int gpt_retval;
154    if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
155      Error("GptSanityCheck() returned %d: %s\n",
156            gpt_retval, GptError(gpt_retval));
157      return -1;
158    }
159
160    if (((drive->gpt.valid_headers & MASK_BOTH) != MASK_BOTH) ||
161        ((drive->gpt.valid_entries & MASK_BOTH) != MASK_BOTH)) {
162      Error("one of the GPT header/entries is invalid.\n"
163            "please run 'cgpt repair' before adding anything.\n");
164      return -1;
165    }
166  }
167  return 0;
168}
169
170static int CgptGetUnusedPartition(struct drive *drive, uint32_t *index,
171                                  CgptAddParams *params) {
172  uint32_t i;
173  uint32_t max_part = GetNumberOfEntries(drive);
174  if (params->partition) {
175    if (params->partition > max_part) {
176      Error("invalid partition number: %d\n", params->partition);
177      return -1;
178    }
179    *index = params->partition - 1;
180    return 0;
181  } else {
182    // Find next empty partition.
183    for (i = 0; i < max_part; i++) {
184      if (IsUnused(drive, PRIMARY, i)) {
185        params->partition = i + 1;
186        *index = i;
187        return 0;
188      }
189    }
190    Error("no unused partitions available\n");
191    return -1;
192  }
193}
194
195int CgptSetAttributes(CgptAddParams *params) {
196  struct drive drive;
197
198  if (params == NULL)
199    return CGPT_FAILED;
200
201  if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
202    return CGPT_FAILED;
203
204  if (CgptCheckAddValidity(&drive)) {
205    goto bad;
206  }
207
208  if (params->partition == 0 ||
209      params->partition >= GetNumberOfEntries(&drive)) {
210    Error("invalid partition number: %d\n", params->partition);
211    goto bad;
212  }
213
214  SetEntryAttributes(&drive, params->partition - 1, params);
215
216  UpdateAllEntries(&drive);
217
218  // Write it all out.
219  return DriveClose(&drive, 1);
220
221bad:
222  DriveClose(&drive, 0);
223  return CGPT_FAILED;
224}
225
226// This method gets the partition details such as the attributes, the
227// guids of the partitions, etc. Input is the partition number or the
228// unique id of the partition. Output is populated in the respective
229// fields of params.
230int CgptGetPartitionDetails(CgptAddParams *params) {
231  struct drive drive;
232  int result = CGPT_FAILED;
233  int index;
234
235  if (params == NULL)
236    return CGPT_FAILED;
237
238  if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
239    return CGPT_FAILED;
240
241  if (CgptCheckAddValidity(&drive)) {
242    goto bad;
243  }
244
245  int max_part = GetNumberOfEntries(&drive);
246  if (params->partition > 0) {
247    if (params->partition >= max_part) {
248      Error("invalid partition number: %d\n", params->partition);
249      goto bad;
250    }
251  } else {
252    if (!params->set_unique) {
253      Error("either partition or unique_id must be specified\n");
254      goto bad;
255    }
256    if (drive.is_mtd) {
257      Error("MTD partitions cannot be specified by unique_id\n");
258      goto bad;
259    }
260    for (index = 0; index < max_part; index++) {
261      GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
262      if (GuidEqual(&entry->unique, &params->unique_guid)) {
263        params->partition = index + 1;
264        break;
265      }
266    }
267    if (index >= max_part) {
268      Error("no partitions with the given unique id available\n");
269      goto bad;
270    }
271  }
272  index = params->partition - 1;
273
274  if(drive.is_mtd) {
275    MtdDiskPartition *entry = MtdGetEntry(&drive.mtd, PRIMARY, index);
276    uint64_t start_lba, end_lba;
277    const Guid *guid = LookupGuidForMtdType(MtdGetEntryType(entry));
278    memcpy(&params->type_guid, guid, sizeof(params->type_guid));
279    memset(&params->unique_guid, 0, sizeof(params->unique_guid));
280    start_lba = entry->starting_lba;
281    end_lba = entry->ending_lba;
282    params->begin = start_lba;
283    params->size = end_lba - start_lba + 1;
284    params->raw_value = entry->flags;
285  } else {
286    // GPT-specific code
287    GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
288    params->begin = entry->starting_lba;
289    params->size =  entry->ending_lba - entry->starting_lba + 1;
290    memcpy(&params->type_guid, &entry->type, sizeof(Guid));
291    memcpy(&params->unique_guid, &entry->unique, sizeof(Guid));
292    params->raw_value = entry->attrs.fields.gpt_att;
293  }
294
295  params->successful = GetSuccessful(&drive, PRIMARY, index);
296  params->tries = GetTries(&drive, PRIMARY, index);
297  params->priority = GetPriority(&drive, PRIMARY, index);
298  result = CGPT_OK;
299
300bad:
301  DriveClose(&drive, 0);
302  return result;
303}
304
305int GptAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
306  GptEntry *entry, backup;
307  int rv;
308
309  entry = GetEntry(&drive->gpt, PRIMARY, index);
310  memcpy(&backup, entry, sizeof(backup));
311
312  if (SetEntryAttributes(drive, index, params) ||
313      GptSetEntryAttributes(drive, index, params)) {
314    memcpy(entry, &backup, sizeof(*entry));
315    return -1;
316  }
317
318  UpdateAllEntries(drive);
319
320  rv = CheckEntries((GptEntry*)drive->gpt.primary_entries,
321                    (GptHeader*)drive->gpt.primary_header);
322
323  if (0 != rv) {
324    // If the modified entry is illegal, recover it and return error.
325    memcpy(entry, &backup, sizeof(*entry));
326    Error("%s\n", GptErrorText(rv));
327    Error(DumpCgptAddParams(params));
328    return -1;
329  }
330
331  return 0;
332}
333
334int MtdAdd(struct drive *drive, CgptAddParams *params, uint32_t index) {
335  MtdDiskPartition *entry, backup;
336  entry = MtdGetEntry(&drive->mtd, PRIMARY, index);
337  memcpy(&backup, entry, sizeof(backup));
338
339  if (SetEntryAttributes(drive, index, params) ||
340      MtdSetEntryAttributes(drive, index, params)) {
341    memcpy(entry, &backup, sizeof(*entry));
342    return -1;
343  }
344
345  UpdateAllEntries(drive);
346
347  return 0;
348}
349
350int CgptAdd(CgptAddParams *params) {
351  struct drive drive;
352  uint32_t index;
353
354  if (params == NULL)
355    return CGPT_FAILED;
356
357  if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR))
358    return CGPT_FAILED;
359
360  if (CgptCheckAddValidity(&drive)) {
361    goto bad;
362  }
363
364  if (CgptGetUnusedPartition(&drive, &index, params)) {
365    goto bad;
366  }
367
368  if (drive.is_mtd) {
369    if (MtdAdd(&drive, params, index))
370      goto bad;
371  } else {
372    if (GptAdd(&drive, params, index))
373      goto bad;
374  }
375
376  // Write it all out.
377  return DriveClose(&drive, 1);
378
379bad:
380  DriveClose(&drive, 0);
381  return CGPT_FAILED;
382}
383