summaryrefslogtreecommitdiff
path: root/cgpt/cgpt_create.c
blob: 77980bfc78beaa566e293d3b17829454dacfc103 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


#include <string.h>

#include "cgpt.h"
#include "cgptlib_internal.h"
#include "vboot_host.h"

static void AllocAndClear(uint8_t **buf, uint64_t size) {
  if (*buf) {
    memset(*buf, 0, size);
  } else {
    *buf = calloc(1, size);
    if (!*buf) {
      Error("Cannot allocate %" PRIu64 " bytes.\n", size);
      abort();
    }
  }
}

static int GptCreate(struct drive *drive, CgptCreateParams *params) {
  // Do not replace any existing IGNOREME GPT headers.
  if (!memcmp(((GptHeader*)drive->gpt.primary_header)->signature,
              GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) {
    drive->gpt.ignored |= MASK_PRIMARY;
    Warning("Primary GPT was marked ignored, will not overwrite.\n");
  }

  if (!memcmp(((GptHeader*)drive->gpt.secondary_header)->signature,
              GPT_HEADER_SIGNATURE_IGNORED, GPT_HEADER_SIGNATURE_SIZE)) {
    drive->gpt.ignored |= MASK_SECONDARY;
    Warning("Secondary GPT was marked ignored, will not overwrite.\n");
  }

  // Allocate and/or erase the data.
  // We cannot assume the GPT headers or entry arrays have been allocated
  // by GptLoad() because those fields might have failed validation checks.
  AllocAndClear(&drive->gpt.primary_header,
                drive->gpt.sector_bytes * GPT_HEADER_SECTORS);
  AllocAndClear(&drive->gpt.secondary_header,
                drive->gpt.sector_bytes * GPT_HEADER_SECTORS);

  drive->gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
                         GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);

  // Initialize a blank set
  if (!params->zap) {
    GptHeader *h = (GptHeader *)drive->gpt.primary_header;
    memcpy(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE);
    h->revision = GPT_HEADER_REVISION;
    h->size = sizeof(GptHeader);
    h->my_lba = GPT_PMBR_SECTORS;  /* The second sector on drive. */
    h->alternate_lba = drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS;
    if (CGPT_OK != GenerateGuid(&h->disk_uuid)) {
      Error("Unable to generate new GUID.\n");
      return -1;
    }

    /* Calculate number of entries */
    h->size_of_entry = sizeof(GptEntry);
    h->number_of_entries = MAX_NUMBER_OF_ENTRIES;
    if (drive->gpt.flags & GPT_FLAG_EXTERNAL) {
      // We might have smaller space for the GPT table. Scale accordingly.
      //
      // +------+------------+---------------+-----+--------------+-----------+
      // | PMBR | Prim. Head | Prim. Entries | ... | Sec. Entries | Sec. Head |
      // +------+------------+---------------+-----+--------------+-----------+
      //
      // Half the size of gpt_drive_sectors must be big enough to hold PMBR +
      // GPT Header + Entries Table, though the secondary structures do not
      // contain PMBR.
      size_t required_headers_size =
          (GPT_PMBR_SECTORS + GPT_HEADER_SECTORS) * drive->gpt.sector_bytes;
      size_t min_entries_size = MIN_NUMBER_OF_ENTRIES * h->size_of_entry;
      size_t required_min_size = required_headers_size + min_entries_size;
      size_t half_size =
          (drive->gpt.gpt_drive_sectors / 2) * drive->gpt.sector_bytes;
      if (half_size < required_min_size) {
        Error("Not enough space to store GPT structures. Required %zu bytes.\n",
              required_min_size * 2);
        return -1;
      }
      size_t max_entries =
          (half_size - required_headers_size) / h->size_of_entry;
      if (h->number_of_entries > max_entries) {
        h->number_of_entries = max_entries;
      }
    }

    /* Then use number of entries to calculate entries_lba. */
    h->entries_lba = h->my_lba + GPT_HEADER_SECTORS;
    if (!(drive->gpt.flags & GPT_FLAG_EXTERNAL)) {
      h->entries_lba += params->padding;
      h->first_usable_lba = h->entries_lba + CalculateEntriesSectors(h,
                                               drive->gpt.sector_bytes);
      h->last_usable_lba =
        (drive->gpt.streaming_drive_sectors - GPT_HEADER_SECTORS -
          CalculateEntriesSectors(h, drive->gpt.sector_bytes) - 1);
    } else {
      h->first_usable_lba = params->padding;
      h->last_usable_lba = (drive->gpt.streaming_drive_sectors - 1);
    }

    size_t entries_size = h->number_of_entries * h->size_of_entry;
    AllocAndClear(&drive->gpt.primary_entries, entries_size);
    AllocAndClear(&drive->gpt.secondary_entries, entries_size);

    // Copy to secondary
    RepairHeader(&drive->gpt, MASK_PRIMARY);

    UpdateCrc(&drive->gpt);
  }

  return 0;
}

int CgptCreate(CgptCreateParams *params) {
  struct drive drive;

  if (params == NULL)
    return CGPT_FAILED;

  if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDWR,
                           params->drive_size))
    return CGPT_FAILED;

  if (GptCreate(&drive, params))
    goto bad;

  // Write it all out
  return DriveClose(&drive, 1);

bad:

  DriveClose(&drive, 0);
  return CGPT_FAILED;
}