xref: /aosp_15_r20/external/vboot_reference/cgpt/cgpt_add.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
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(&params->type_guid, tmp, sizeof(tmp));
26     fprintf(stderr, "-t %s ", tmp);
27   }
28   if (params->set_unique) {
29     GuidToStr(&params->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, &params->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, &params->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(&params->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, &params->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(&params->type_guid, &entry->type, sizeof(Guid));
241   memcpy(&params->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