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