]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/gpt.c
fstrim: add -v to the systemd service
[thirdparty/util-linux.git] / libfdisk / src / gpt.c
CommitLineData
5dbff4c0 1/*
766d5156
DB
2 * Copyright (C) 2007 Karel Zak <kzak@redhat.com>
3 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
4 *
5 * GUID Partition Table (GPT) support. Based on UEFI Specs 2.3.1
6 * Chapter 5: GUID Partition Table (GPT) Disk Layout (Jun 27th, 2012).
7 * Some ideas and inspiration from GNU parted and gptfdisk.
5dbff4c0 8 */
5dbff4c0
KZ
9#include <stdio.h>
10#include <string.h>
11#include <stdlib.h>
12#include <inttypes.h>
13#include <sys/stat.h>
5dbff4c0
KZ
14#include <sys/utsname.h>
15#include <sys/types.h>
16#include <fcntl.h>
17#include <unistd.h>
18#include <errno.h>
766d5156
DB
19#include <ctype.h>
20#include <uuid.h>
5dbff4c0 21
62d50bbe
KZ
22#include "fdiskP.h"
23
766d5156 24#include "nls.h"
766d5156 25#include "crc32.h"
810f986b 26#include "blkdev.h"
9eca9d0d 27#include "bitops.h"
766d5156 28#include "strutils.h"
19613111 29#include "all-io.h"
3457d90e 30#include "pt-mbr.h"
766d5156 31
0077e7cd
KZ
32/**
33 * SECTION: gpt
705854f3
KZ
34 * @title: UEFI GPT
35 * @short_description: specific functionality
0077e7cd
KZ
36 */
37
766d5156
DB
38#define GPT_HEADER_SIGNATURE 0x5452415020494645LL /* EFI PART */
39#define GPT_HEADER_REVISION_V1_02 0x00010200
40#define GPT_HEADER_REVISION_V1_00 0x00010000
41#define GPT_HEADER_REVISION_V0_99 0x00009900
e9bf0935 42#define GPT_HEADER_MINSZ 92 /* bytes */
766d5156
DB
43
44#define GPT_PMBR_LBA 0
45#define GPT_MBR_PROTECTIVE 1
46#define GPT_MBR_HYBRID 2
47
0a7cdf80 48#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001ULL
766d5156
DB
49
50#define EFI_PMBR_OSTYPE 0xEE
51#define MSDOS_MBR_SIGNATURE 0xAA55
e39966c6 52#define GPT_PART_NAME_LEN (72 / sizeof(uint16_t))
74760053 53#define GPT_NPARTITIONS FDISK_GPT_NPARTITIONS_DEFAULT
766d5156
DB
54
55/* Globally unique identifier */
56struct gpt_guid {
57 uint32_t time_low;
58 uint16_t time_mid;
59 uint16_t time_hi_and_version;
60 uint8_t clock_seq_hi;
61 uint8_t clock_seq_low;
62 uint8_t node[6];
63};
64
65
66/* only checking that the GUID is 0 is enough to verify an empty partition. */
67#define GPT_UNUSED_ENTRY_GUID \
68 ((struct gpt_guid) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
69 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
70
71/* Linux native partition type */
c0d14b09 72#define GPT_DEFAULT_ENTRY_TYPE "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
766d5156
DB
73
74/*
75 * Attribute bits
76 */
01086b80
KZ
77enum {
78 /* UEFI specific */
79 GPT_ATTRBIT_REQ = 0,
80 GPT_ATTRBIT_NOBLOCK = 1,
81 GPT_ATTRBIT_LEGACY = 2,
82
83 /* GUID specific (range 48..64)*/
84 GPT_ATTRBIT_GUID_FIRST = 48,
85 GPT_ATTRBIT_GUID_COUNT = 16
86};
c83f772e 87
d1b7bfe5
SR
88#define GPT_ATTRSTR_REQ "RequiredPartition"
89#define GPT_ATTRSTR_REQ_TYPO "RequiredPartiton"
01086b80
KZ
90#define GPT_ATTRSTR_NOBLOCK "NoBlockIOProtocol"
91#define GPT_ATTRSTR_LEGACY "LegacyBIOSBootable"
c83f772e 92
766d5156
DB
93/* The GPT Partition entry array contains an array of GPT entries. */
94struct gpt_entry {
d45fa25d
KZ
95 struct gpt_guid type; /* purpose and type of the partition */
96 struct gpt_guid partition_guid;
766d5156
DB
97 uint64_t lba_start;
98 uint64_t lba_end;
01086b80 99 uint64_t attrs;
d45fa25d 100 uint16_t name[GPT_PART_NAME_LEN];
766d5156
DB
101} __attribute__ ((packed));
102
103/* GPT header */
104struct gpt_header {
105 uint64_t signature; /* header identification */
106 uint32_t revision; /* header version */
107 uint32_t size; /* in bytes */
108 uint32_t crc32; /* header CRC checksum */
109 uint32_t reserved1; /* must be 0 */
9ed38607 110 uint64_t my_lba; /* LBA of block that contains this struct (LBA 1) */
766d5156
DB
111 uint64_t alternative_lba; /* backup GPT header */
112 uint64_t first_usable_lba; /* first usable logical block for partitions */
113 uint64_t last_usable_lba; /* last usable logical block for partitions */
3f731001 114 struct gpt_guid disk_guid; /* unique disk identifier */
9ed38607 115 uint64_t partition_entry_lba; /* LBA of start of partition entries array */
766d5156
DB
116 uint32_t npartition_entries; /* total partition entries - normally 128 */
117 uint32_t sizeof_partition_entry; /* bytes for each GUID pt */
118 uint32_t partition_entry_array_crc32; /* partition CRC checksum */
9ed38607 119 uint8_t reserved2[512 - 92]; /* must all be 0 */
766d5156
DB
120} __attribute__ ((packed));
121
122struct gpt_record {
123 uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */
124 uint8_t start_head; /* unused by EFI, pt start in CHS */
125 uint8_t start_sector; /* unused by EFI, pt start in CHS */
126 uint8_t start_track;
127 uint8_t os_type; /* EFI and legacy non-EFI OS types */
128 uint8_t end_head; /* unused by EFI, pt end in CHS */
129 uint8_t end_sector; /* unused by EFI, pt end in CHS */
130 uint8_t end_track; /* unused by EFI, pt end in CHS */
131 uint32_t starting_lba; /* used by EFI - start addr of the on disk pt */
132 uint32_t size_in_lba; /* used by EFI - size of pt in LBA */
133} __attribute__ ((packed));
134
135/* Protected MBR and legacy MBR share same structure */
136struct gpt_legacy_mbr {
137 uint8_t boot_code[440];
138 uint32_t unique_mbr_signature;
139 uint16_t unknown;
140 struct gpt_record partition_record[4];
141 uint16_t signature;
142} __attribute__ ((packed));
143
144/*
145 * Here be dragons!
146 * See: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
147 */
148#define DEF_GUID(_u, _n) \
149 { \
150 .typestr = (_u), \
151 .name = (_n), \
152 }
153
5aa8d13b
KZ
154/* Probably the most complete list of the GUIDs are at:
155 * https://wikipedia.org/wiki/GUID_Partition_Table
156 */
766d5156
DB
157static struct fdisk_parttype gpt_parttypes[] =
158{
159 /* Generic OS */
160 DEF_GUID("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", N_("EFI System")),
161
162 DEF_GUID("024DEE41-33E7-11D3-9D69-0008C781F39F", N_("MBR partition scheme")),
ae488940
KZ
163 DEF_GUID("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", N_("Intel Fast Flash")),
164
766d5156 165 /* Hah!IdontneedEFI */
5a1b4999 166 DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot")),
766d5156 167
5aa8d13b
KZ
168 /* NIH syndrome */
169 DEF_GUID("F4019732-066E-4E12-8273-346C5641494F", N_("Sony boot partition")),
170 DEF_GUID("BFBFAFE7-A34F-448A-9A5B-6213EB736C22", N_("Lenovo boot partition")),
171
172 /* PowerPC reference platform boot partition */
173 DEF_GUID("9E1A2D38-C612-4316-AA26-8B49521E5A8B", N_("PowerPC PReP boot")),
174
175 /* Open Network Install Environment */
176 DEF_GUID("7412F7D5-A156-4B13-81DC-867174929325", N_("ONIE boot")),
177 DEF_GUID("D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", N_("ONIE config")),
178
766d5156
DB
179 /* Windows */
180 DEF_GUID("E3C9E316-0B5C-4DB8-817D-F92DF00215AE", N_("Microsoft reserved")),
181 DEF_GUID("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", N_("Microsoft basic data")),
182 DEF_GUID("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", N_("Microsoft LDM metadata")),
183 DEF_GUID("AF9B60A0-1431-4F62-BC68-3311714A69AD", N_("Microsoft LDM data")),
0d0d12ad 184 DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery environment")),
766d5156 185 DEF_GUID("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", N_("IBM General Parallel Fs")),
210d4595 186 DEF_GUID("E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", N_("Microsoft Storage Spaces")),
766d5156
DB
187
188 /* HP-UX */
5a1b4999
KZ
189 DEF_GUID("75894C1E-3AEB-11D3-B7C1-7B03A0000000", N_("HP-UX data")),
190 DEF_GUID("E2A1E728-32E3-11D6-A682-7B03A0000000", N_("HP-UX service")),
766d5156 191
5a1b4999
KZ
192 /* Linux (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec) */
193 DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")),
766d5156 194 DEF_GUID("0FC63DAF-8483-4772-8E79-3D69D8477DE4", N_("Linux filesystem")),
5a1b4999
KZ
195 DEF_GUID("3B8F8425-20E0-4F3B-907F-1A25A76F98E8", N_("Linux server data")),
196 DEF_GUID("44479540-F297-41B2-9AF7-D131D5F0458A", N_("Linux root (x86)")),
4b7248c0 197 DEF_GUID("69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", N_("Linux root (ARM)")),
5a1b4999 198 DEF_GUID("4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", N_("Linux root (x86-64)")),
4b7248c0 199 DEF_GUID("B921B045-1DF0-41C3-AF44-4C6F280D3FAE", N_("Linux root (ARM-64)")),
f136260a 200 DEF_GUID("993D8D3D-F80E-4225-855A-9DAF8ED7EA97", N_("Linux root (IA-64)")),
5a1b4999
KZ
201 DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")),
202 DEF_GUID("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", N_("Linux home")),
766d5156 203 DEF_GUID("A19D880F-05FC-4D3B-A006-743F0F84911E", N_("Linux RAID")),
5a1b4999 204 DEF_GUID("BC13C2FF-59E6-4262-A352-B275FD6F7172", N_("Linux extended boot")),
766d5156 205 DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")),
5aa8d13b
KZ
206 /* ... too crazy, ignore for now:
207 DEF_GUID("7FFEC5C9-2D00-49B7-8941-3EA10A5586B7", N_("Linux plain dm-crypt")),
208 DEF_GUID("CA7D7CCB-63ED-4C53-861C-1742536059CC", N_("Linux LUKS")),
209 */
766d5156 210
e9bf0935 211 /* FreeBSD */
766d5156
DB
212 DEF_GUID("516E7CB4-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD data")),
213 DEF_GUID("83BD6B9D-7F41-11DC-BE0B-001560B84F0F", N_("FreeBSD boot")),
214 DEF_GUID("516E7CB5-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD swap")),
215 DEF_GUID("516E7CB6-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD UFS")),
216 DEF_GUID("516E7CBA-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD ZFS")),
217 DEF_GUID("516E7CB8-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD Vinum")),
218
219 /* Apple OSX */
220 DEF_GUID("48465300-0000-11AA-AA11-00306543ECAC", N_("Apple HFS/HFS+")),
221 DEF_GUID("55465300-0000-11AA-AA11-00306543ECAC", N_("Apple UFS")),
222 DEF_GUID("52414944-0000-11AA-AA11-00306543ECAC", N_("Apple RAID")),
223 DEF_GUID("52414944-5F4F-11AA-AA11-00306543ECAC", N_("Apple RAID offline")),
224 DEF_GUID("426F6F74-0000-11AA-AA11-00306543ECAC", N_("Apple boot")),
225 DEF_GUID("4C616265-6C00-11AA-AA11-00306543ECAC", N_("Apple label")),
226 DEF_GUID("5265636F-7665-11AA-AA11-00306543ECAC", N_("Apple TV recovery")),
227 DEF_GUID("53746F72-6167-11AA-AA11-00306543ECAC", N_("Apple Core storage")),
228
229 /* Solaris */
230 DEF_GUID("6A82CB45-1DD2-11B2-99A6-080020736631", N_("Solaris boot")),
231 DEF_GUID("6A85CF4D-1DD2-11B2-99A6-080020736631", N_("Solaris root")),
232 /* same as Apple ZFS */
233 DEF_GUID("6A898CC3-1DD2-11B2-99A6-080020736631", N_("Solaris /usr & Apple ZFS")),
234 DEF_GUID("6A87C46F-1DD2-11B2-99A6-080020736631", N_("Solaris swap")),
235 DEF_GUID("6A8B642B-1DD2-11B2-99A6-080020736631", N_("Solaris backup")),
236 DEF_GUID("6A8EF2E9-1DD2-11B2-99A6-080020736631", N_("Solaris /var")),
237 DEF_GUID("6A90BA39-1DD2-11B2-99A6-080020736631", N_("Solaris /home")),
238 DEF_GUID("6A9283A5-1DD2-11B2-99A6-080020736631", N_("Solaris alternate sector")),
239 DEF_GUID("6A945A3B-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 1")),
240 DEF_GUID("6A9630D1-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 2")),
241 DEF_GUID("6A980767-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 3")),
242 DEF_GUID("6A96237F-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 4")),
243 DEF_GUID("6A8D2AC7-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 5")),
244
245 /* NetBSD */
246 DEF_GUID("49F48D32-B10E-11DC-B99B-0019D1879648", N_("NetBSD swap")),
247 DEF_GUID("49F48D5A-B10E-11DC-B99B-0019D1879648", N_("NetBSD FFS")),
248 DEF_GUID("49F48D82-B10E-11DC-B99B-0019D1879648", N_("NetBSD LFS")),
249 DEF_GUID("2DB519C4-B10E-11DC-B99B-0019D1879648", N_("NetBSD concatenated")),
250 DEF_GUID("2DB519EC-B10E-11DC-B99B-0019D1879648", N_("NetBSD encrypted")),
251 DEF_GUID("49F48DAA-B10E-11DC-B99B-0019D1879648", N_("NetBSD RAID")),
252
253 /* ChromeOS */
254 DEF_GUID("FE3A2A5D-4F32-41A7-B725-ACCC3285A309", N_("ChromeOS kernel")),
255 DEF_GUID("3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", N_("ChromeOS root fs")),
256 DEF_GUID("2E0A753D-9E48-43B0-8337-B15192CB1B5E", N_("ChromeOS reserved")),
257
258 /* MidnightBSD */
259 DEF_GUID("85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD data")),
260 DEF_GUID("85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD boot")),
261 DEF_GUID("85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD swap")),
5adcb6c7 262 DEF_GUID("0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD UFS")),
766d5156
DB
263 DEF_GUID("85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD ZFS")),
264 DEF_GUID("85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD Vinum")),
b8d07d9b 265
5aa8d13b
KZ
266 /* Ceph */
267 DEF_GUID("45B0969E-9B03-4F30-B4C6-B4B80CEFF106", N_("Ceph Journal")),
268 DEF_GUID("45B0969E-9B03-4F30-B4C6-5EC00CEFF106", N_("Ceph Encrypted Journal")),
269 DEF_GUID("4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D", N_("Ceph OSD")),
270 DEF_GUID("4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D", N_("Ceph crypt OSD")),
271 DEF_GUID("89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE", N_("Ceph disk in creation")),
272 DEF_GUID("89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE", N_("Ceph crypt disk in creation")),
273
274 /* OpenBSD */
275 DEF_GUID("824CC7A0-36A8-11E3-890A-952519AD3F61", N_("OpenBSD data")),
276
277 /* QNX */
278 DEF_GUID("CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1", N_("QNX6 file system")),
279
280 /* Plan 9 */
281 DEF_GUID("C91818F9-8025-47AF-89D2-F030D7000C2C", N_("Plan 9 partition"))
766d5156
DB
282};
283
d71ef5a4 284/* gpt_entry macros */
874aa9c3
KZ
285#define gpt_partition_start(_e) le64_to_cpu((_e)->lba_start)
286#define gpt_partition_end(_e) le64_to_cpu((_e)->lba_end)
287
d71ef5a4
KZ
288/*
289 * in-memory fdisk GPT stuff
290 */
291struct fdisk_gpt_label {
292 struct fdisk_label head; /* generic part */
293
294 /* gpt specific part */
295 struct gpt_header *pheader; /* primary header */
296 struct gpt_header *bheader; /* backup header */
297 struct gpt_entry *ents; /* entries (partitions) */
298};
299
300static void gpt_deinit(struct fdisk_label *lb);
301
9ffeb235 302static inline struct fdisk_gpt_label *self_label(struct fdisk_context *cxt)
d71ef5a4 303{
d71ef5a4
KZ
304 return (struct fdisk_gpt_label *) cxt->label;
305}
306
874aa9c3
KZ
307/*
308 * Returns the partition length, or 0 if end is before beginning.
309 */
310static uint64_t gpt_partition_size(const struct gpt_entry *e)
311{
312 uint64_t start = gpt_partition_start(e);
313 uint64_t end = gpt_partition_end(e);
314
315 return start > end ? 0 : end - start + 1ULL;
316}
317
c0d14b09 318/* prints UUID in the real byte order! */
88141067 319static void gpt_debug_uuid(const char *mesg, struct gpt_guid *guid)
c0d14b09
KZ
320{
321 const unsigned char *uuid = (unsigned char *) guid;
322
323 fprintf(stderr, "%s: "
324 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
325 mesg,
326 uuid[0], uuid[1], uuid[2], uuid[3],
327 uuid[4], uuid[5],
328 uuid[6], uuid[7],
329 uuid[8], uuid[9],
330 uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],uuid[15]);
331}
c0d14b09 332
766d5156
DB
333/*
334 * UUID is traditionally 16 byte big-endian array, except Intel EFI
335 * specification where the UUID is a structure of little-endian fields.
336 */
337static void swap_efi_guid(struct gpt_guid *uid)
338{
339 uid->time_low = swab32(uid->time_low);
340 uid->time_mid = swab16(uid->time_mid);
341 uid->time_hi_and_version = swab16(uid->time_hi_and_version);
5dbff4c0
KZ
342}
343
c0d14b09 344static int string_to_guid(const char *in, struct gpt_guid *guid)
766d5156 345{
4044d244
KZ
346 if (uuid_parse(in, (unsigned char *) guid)) { /* BE */
347 DBG(LABEL, ul_debug("GPT: failed to parse GUID: %s", in));
348 return -EINVAL;
349 }
c0d14b09 350 swap_efi_guid(guid); /* LE */
766d5156
DB
351 return 0;
352}
353
7f539277 354static char *guid_to_string(const struct gpt_guid *guid, char *out)
766d5156 355{
c0d14b09
KZ
356 struct gpt_guid u = *guid; /* LE */
357
358 swap_efi_guid(&u); /* BE */
359 uuid_unparse_upper((unsigned char *) &u, out);
360
46667ba4 361 return out;
766d5156
DB
362}
363
7f539277
KZ
364static struct fdisk_parttype *gpt_partition_parttype(
365 struct fdisk_context *cxt,
366 const struct gpt_entry *e)
367{
368 struct fdisk_parttype *t;
369 char str[37];
370
371 guid_to_string(&e->type, str);
a745611d 372 t = fdisk_label_get_parttype_from_string(cxt->label, str);
7f539277
KZ
373 return t ? : fdisk_new_unknown_parttype(0, str);
374}
375
b0a484a8
KZ
376static void gpt_entry_set_type(struct gpt_entry *e, struct gpt_guid *uuid)
377{
378 e->type = *uuid;
379 DBG(LABEL, gpt_debug_uuid("new type", &(e->type)));
380}
381
382static void gpt_entry_set_name(struct gpt_entry *e, char *str)
383{
384 char name[GPT_PART_NAME_LEN] = { 0 };
385 size_t i, sz = strlen(str);
386
387 if (sz) {
388 if (sz > GPT_PART_NAME_LEN)
389 sz = GPT_PART_NAME_LEN;
390 memcpy(name, str, sz);
391 }
392
393 for (i = 0; i < GPT_PART_NAME_LEN; i++)
394 e->name[i] = cpu_to_le16((uint16_t) name[i]);
395}
396
397static int gpt_entry_set_uuid(struct gpt_entry *e, char *str)
398{
399 struct gpt_guid uuid;
400 int rc;
401
402 rc = string_to_guid(str, &uuid);
403 if (rc)
404 return rc;
405
406 e->partition_guid = uuid;
407 return 0;
408}
7f539277
KZ
409
410
766d5156
DB
411static const char *gpt_get_header_revstr(struct gpt_header *header)
412{
413 if (!header)
414 goto unknown;
415
43a2b094 416 switch (le32_to_cpu(header->revision)) {
766d5156
DB
417 case GPT_HEADER_REVISION_V1_02:
418 return "1.2";
419 case GPT_HEADER_REVISION_V1_00:
420 return "1.0";
421 case GPT_HEADER_REVISION_V0_99:
422 return "0.99";
423 default:
424 goto unknown;
425 }
426
427unknown:
428 return "unknown";
429}
430
874aa9c3 431static inline int partition_unused(const struct gpt_entry *e)
766d5156 432{
d45fa25d 433 return !memcmp(&e->type, &GPT_UNUSED_ENTRY_GUID,
766d5156
DB
434 sizeof(struct gpt_guid));
435}
436
5989556a
KZ
437static char *gpt_get_header_id(struct gpt_header *header)
438{
439 char str[37];
440
441 guid_to_string(&header->disk_guid, str);
442
443 return strdup(str);
444}
445
446
3f731001
DB
447/*
448 * Builds a clean new valid protective MBR - will wipe out any existing data.
449 * Returns 0 on success, otherwise < 0 on error.
450 */
451static int gpt_mknew_pmbr(struct fdisk_context *cxt)
452{
453 struct gpt_legacy_mbr *pmbr = NULL;
7c2cfb18 454 int rc;
3f731001
DB
455
456 if (!cxt || !cxt->firstsector)
457 return -ENOSYS;
458
3457d90e
KZ
459 if (fdisk_has_protected_bootbits(cxt))
460 rc = fdisk_init_firstsector_buffer(cxt, 0, MBR_PT_BOOTBITS_SIZE);
461 else
462 rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
7c2cfb18
KZ
463 if (rc)
464 return rc;
3f731001
DB
465
466 pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
467
468 pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
469 pmbr->partition_record[0].os_type = EFI_PMBR_OSTYPE;
470 pmbr->partition_record[0].start_sector = 1;
471 pmbr->partition_record[0].end_head = 0xFE;
472 pmbr->partition_record[0].end_sector = 0xFF;
473 pmbr->partition_record[0].end_track = 0xFF;
474 pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
475 pmbr->partition_record[0].size_in_lba =
0a7cdf80 476 cpu_to_le32((uint32_t) min( cxt->total_sectors - 1ULL, 0xFFFFFFFFULL) );
3f731001
DB
477
478 return 0;
479}
480
481/* some universal differences between the headers */
482static void gpt_mknew_header_common(struct fdisk_context *cxt,
483 struct gpt_header *header, uint64_t lba)
484{
485 if (!cxt || !header)
486 return;
487
488 header->my_lba = cpu_to_le64(lba);
489
490 if (lba == GPT_PRIMARY_PARTITION_TABLE_LBA) { /* primary */
0a7cdf80
KZ
491 header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1ULL);
492 header->partition_entry_lba = cpu_to_le64(2ULL);
3f731001
DB
493 } else { /* backup */
494 uint64_t esz = le32_to_cpu(header->npartition_entries) * sizeof(struct gpt_entry);
495 uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
496
497 header->alternative_lba = cpu_to_le64(GPT_PRIMARY_PARTITION_TABLE_LBA);
0a7cdf80 498 header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1ULL - esects);
3f731001
DB
499 }
500}
501
502/*
503 * Builds a new GPT header (at sector lba) from a backup header2.
504 * If building a primary header, then backup is the secondary, and vice versa.
505 *
506 * Always pass a new (zeroized) header to build upon as we don't
507 * explicitly zero-set some values such as CRCs and reserved.
508 *
509 * Returns 0 on success, otherwise < 0 on error.
510 */
511static int gpt_mknew_header_from_bkp(struct fdisk_context *cxt,
512 struct gpt_header *header,
513 uint64_t lba,
514 struct gpt_header *header2)
515{
516 if (!cxt || !header || !header2)
517 return -ENOSYS;
518
519 header->signature = header2->signature;
520 header->revision = header2->revision;
521 header->size = header2->size;
522 header->npartition_entries = header2->npartition_entries;
523 header->sizeof_partition_entry = header2->sizeof_partition_entry;
524 header->first_usable_lba = header2->first_usable_lba;
525 header->last_usable_lba = header2->last_usable_lba;
526
527 memcpy(&header->disk_guid,
528 &header2->disk_guid, sizeof(header2->disk_guid));
529 gpt_mknew_header_common(cxt, header, lba);
530
531 return 0;
532}
533
45ddb828
KZ
534static struct gpt_header *gpt_copy_header(struct fdisk_context *cxt,
535 struct gpt_header *src)
536{
537 struct gpt_header *res;
538
539 if (!cxt || !src)
540 return NULL;
541
4bb82a45
KZ
542 assert(cxt->sector_size >= sizeof(struct gpt_header));
543
544 res = calloc(1, cxt->sector_size);
45ddb828
KZ
545 if (!res) {
546 fdisk_warn(cxt, _("failed to allocate GPT header"));
547 return NULL;
548 }
549
550 res->my_lba = src->alternative_lba;
551 res->alternative_lba = src->my_lba;
552
553 res->signature = src->signature;
554 res->revision = src->revision;
555 res->size = src->size;
556 res->npartition_entries = src->npartition_entries;
557 res->sizeof_partition_entry = src->sizeof_partition_entry;
558 res->first_usable_lba = src->first_usable_lba;
559 res->last_usable_lba = src->last_usable_lba;
560
561 memcpy(&res->disk_guid, &src->disk_guid, sizeof(src->disk_guid));
562
563
564 if (res->my_lba == GPT_PRIMARY_PARTITION_TABLE_LBA)
0a7cdf80 565 res->partition_entry_lba = cpu_to_le64(2ULL);
45ddb828
KZ
566 else {
567 uint64_t esz = le32_to_cpu(src->npartition_entries) * sizeof(struct gpt_entry);
568 uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
569
0a7cdf80 570 res->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1ULL - esects);
45ddb828
KZ
571 }
572
573 return res;
574}
575
b7c67e64
KZ
576static int get_script_u64(struct fdisk_context *cxt, uint64_t *num, const char *name)
577{
578 const char *str;
579 int pwr = 0, rc = 0;
580
581 assert(cxt);
582
583 *num = 0;
584
585 if (!cxt->script)
586 return 1;
587
588 str = fdisk_script_get_header(cxt->script, name);
589 if (!str)
590 return 1;
591
592 rc = parse_size(str, (uintmax_t *) num, &pwr);
593 if (rc < 0)
594 return rc;
595 if (pwr)
596 *num /= cxt->sector_size;
597 return 0;
598}
599
600static int count_first_last_lba(struct fdisk_context *cxt,
1240f549
KZ
601 uint64_t *first, uint64_t *last)
602{
b7c67e64
KZ
603 int rc = 0;
604 uint64_t flba, llba;
605
1240f549
KZ
606 uint64_t esz = 0;
607
608 assert(cxt);
b7c67e64
KZ
609 assert(first);
610 assert(last);
611
612 *first = *last = 0;
1240f549 613
b7c67e64 614 /* UEFI default */
1240f549 615 esz = sizeof(struct gpt_entry) * GPT_NPARTITIONS / cxt->sector_size;
0a7cdf80
KZ
616 llba = cxt->total_sectors - 2ULL - esz;
617 flba = esz + 2ULL;
1240f549 618
b7c67e64
KZ
619 /* script default */
620 if (cxt->script) {
621 rc = get_script_u64(cxt, first, "first-lba");
622 if (rc < 0)
623 return rc;
624
fdbd7bb9
RM
625 DBG(LABEL, ul_debug("FirstLBA: script=%"PRIu64", uefi=%"PRIu64", topology=%ju.",
626 *first, flba, (uintmax_t)cxt->first_lba));
b7c67e64
KZ
627
628 if (rc == 0 && (*first < flba || *first > llba)) {
629 fdisk_warnx(cxt, _("First LBA specified by script is out of range."));
630 return -ERANGE;
631 }
632
633 rc = get_script_u64(cxt, last, "last-lba");
634 if (rc < 0)
635 return rc;
636
fdbd7bb9
RM
637 DBG(LABEL, ul_debug("LastLBA: script=%"PRIu64", uefi=%"PRIu64", topology=%ju.",
638 *last, llba, (uintmax_t)cxt->last_lba));
b7c67e64
KZ
639
640 if (rc == 0 && (*last > llba || *last < flba)) {
641 fdisk_warnx(cxt, _("Last LBA specified by script is out of range."));
642 return -ERANGE;
643 }
644 }
645
646 if (!*last)
647 *last = llba;
648
649 /* default by topology */
650 if (!*first)
651 *first = flba < cxt->first_lba &&
652 cxt->first_lba < *last ? cxt->first_lba : flba;
653 return 0;
1240f549
KZ
654}
655
3f731001
DB
656/*
657 * Builds a clean new GPT header (currently under revision 1.0).
658 *
659 * Always pass a new (zeroized) header to build upon as we don't
660 * explicitly zero-set some values such as CRCs and reserved.
661 *
662 * Returns 0 on success, otherwise < 0 on error.
663 */
664static int gpt_mknew_header(struct fdisk_context *cxt,
665 struct gpt_header *header, uint64_t lba)
666{
1240f549 667 uint64_t first, last;
b7c67e64 668 int has_id = 0, rc;
3f731001
DB
669
670 if (!cxt || !header)
671 return -ENOSYS;
672
3f731001
DB
673 header->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
674 header->revision = cpu_to_le32(GPT_HEADER_REVISION_V1_00);
4bb82a45
KZ
675
676 /* According to EFI standard it's valid to count all the first
677 * sector into header size, but some tools may have a problem
9e930041 678 * to accept it, so use the header without the zeroed area.
4bb82a45
KZ
679 * This does not have any impact to CRC, etc. --kzak Jan-2015
680 */
681 header->size = cpu_to_le32(sizeof(struct gpt_header)
682 - sizeof(header->reserved2));
3f731001
DB
683
684 /*
09af3db4 685 * 128 partitions are the default. It can go beyond that, but
3f731001
DB
686 * we're creating a de facto header here, so no funny business.
687 */
688 header->npartition_entries = cpu_to_le32(GPT_NPARTITIONS);
689 header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry));
b4184690 690
b7c67e64
KZ
691 rc = count_first_last_lba(cxt, &first, &last);
692 if (rc)
693 return rc;
694
b4184690
KZ
695 header->first_usable_lba = cpu_to_le64(first);
696 header->last_usable_lba = cpu_to_le64(last);
3f731001
DB
697
698 gpt_mknew_header_common(cxt, header, lba);
3f731001 699
4b43f7c9
KZ
700 if (cxt->script) {
701 const char *id = fdisk_script_get_header(cxt->script, "label-id");
702 if (id && string_to_guid(id, &header->disk_guid) == 0)
703 has_id = 1;
704 }
705
706 if (!has_id) {
707 uuid_generate_random((unsigned char *) &header->disk_guid);
708 swap_efi_guid(&header->disk_guid);
709 }
3f731001
DB
710 return 0;
711}
712
766d5156
DB
713/*
714 * Checks if there is a valid protective MBR partition table.
715 * Returns 0 if it is invalid or failure. Otherwise, return
9e930041 716 * GPT_MBR_PROTECTIVE or GPT_MBR_HYBRID, depending on the detection.
766d5156
DB
717 */
718static int valid_pmbr(struct fdisk_context *cxt)
719{
879fadf1 720 int i, part = 0, ret = 0; /* invalid by default */
766d5156
DB
721 struct gpt_legacy_mbr *pmbr = NULL;
722
723 if (!cxt->firstsector)
724 goto done;
725
726 pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
727
f67c524e 728 if (le16_to_cpu(pmbr->signature) != MSDOS_MBR_SIGNATURE)
766d5156
DB
729 goto done;
730
766d5156 731 /* seems like a valid MBR was found, check DOS primary partitions */
f67c524e 732 for (i = 0; i < 4; i++) {
766d5156
DB
733 if (pmbr->partition_record[i].os_type == EFI_PMBR_OSTYPE) {
734 /*
735 * Ok, we at least know that there's a protective MBR,
736 * now check if there are other partition types for
737 * hybrid MBR.
738 */
879fadf1 739 part = i;
766d5156 740 ret = GPT_MBR_PROTECTIVE;
c6bf5c09 741 break;
766d5156 742 }
f67c524e 743 }
ac920fed 744
766d5156
DB
745 if (ret != GPT_MBR_PROTECTIVE)
746 goto done;
c6bf5c09
MM
747
748 /* LBA of the GPT partition header */
749 if (pmbr->partition_record[part].starting_lba !=
750 cpu_to_le32(GPT_PRIMARY_PARTITION_TABLE_LBA))
751 goto done;
752
f67c524e 753 for (i = 0 ; i < 4; i++) {
766d5156
DB
754 if ((pmbr->partition_record[i].os_type != EFI_PMBR_OSTYPE) &&
755 (pmbr->partition_record[i].os_type != 0x00))
756 ret = GPT_MBR_HYBRID;
f67c524e 757 }
766d5156
DB
758
759 /*
760 * Protective MBRs take up the lesser of the whole disk
761 * or 2 TiB (32bit LBA), ignoring the rest of the disk.
1fd10841
DB
762 * Some partitioning programs, nonetheless, choose to set
763 * the size to the maximum 32-bit limitation, disregarding
764 * the disk size.
766d5156
DB
765 *
766 * Hybrid MBRs do not necessarily comply with this.
59db52ad
KZ
767 *
768 * Consider a bad value here to be a warning to support dd-ing
769 * an image from a smaller disk to a bigger disk.
766d5156 770 */
f67c524e 771 if (ret == GPT_MBR_PROTECTIVE) {
0a7cdf80
KZ
772 uint64_t sz_lba = (uint64_t) le32_to_cpu(pmbr->partition_record[part].size_in_lba);
773 if (sz_lba != cxt->total_sectors - 1ULL && sz_lba != 0xFFFFFFFFULL) {
774 fdisk_warnx(cxt, _("GPT PMBR size mismatch (%"PRIu64" != %"PRIu64") "
59db52ad 775 "will be corrected by w(rite)."),
0a7cdf80 776 sz_lba, cxt->total_sectors - 1ULL);
1572fb3e 777 fdisk_label_set_changed(cxt->label, 1);
59db52ad 778 }
f67c524e 779 }
766d5156
DB
780done:
781 return ret;
782}
783
784static uint64_t last_lba(struct fdisk_context *cxt)
5dbff4c0 785{
5dbff4c0 786 struct stat s;
cbebd20d 787 uint64_t sectors = 0;
5dbff4c0 788
766d5156
DB
789 memset(&s, 0, sizeof(s));
790 if (fstat(cxt->dev_fd, &s) == -1) {
83df5feb 791 fdisk_warn(cxt, _("gpt: stat() failed"));
5dbff4c0
KZ
792 return 0;
793 }
766d5156 794
5dbff4c0 795 if (S_ISBLK(s.st_mode))
0a7cdf80 796 sectors = cxt->total_sectors - 1ULL;
cbebd20d
KZ
797 else if (S_ISREG(s.st_mode))
798 sectors = ((uint64_t) s.st_size /
799 (uint64_t) cxt->sector_size) - 1ULL;
800 else
83df5feb 801 fdisk_warnx(cxt, _("gpt: cannot handle files with mode %o"), s.st_mode);
cbebd20d 802
fdbd7bb9 803 DBG(LABEL, ul_debug("GPT last LBA: %"PRIu64"", sectors));
cbebd20d 804 return sectors;
5dbff4c0
KZ
805}
806
766d5156
DB
807static ssize_t read_lba(struct fdisk_context *cxt, uint64_t lba,
808 void *buffer, const size_t bytes)
5dbff4c0 809{
766d5156 810 off_t offset = lba * cxt->sector_size;
5dbff4c0 811
bbe8e6a9
KZ
812 if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
813 return -1;
b9710f1f 814 return (size_t)read(cxt->dev_fd, buffer, bytes) != bytes;
5dbff4c0
KZ
815}
816
766d5156
DB
817
818/* Returns the GPT entry array */
be5f8061 819static struct gpt_entry *gpt_read_entries(struct fdisk_context *cxt,
d71ef5a4 820 struct gpt_header *header)
5dbff4c0 821{
d71ef5a4
KZ
822 ssize_t sz;
823 struct gpt_entry *ret = NULL;
824 off_t offset;
825
826 assert(cxt);
827 assert(header);
828
829 sz = le32_to_cpu(header->npartition_entries) *
830 le32_to_cpu(header->sizeof_partition_entry);
831
46667ba4 832 ret = calloc(1, sz);
d71ef5a4
KZ
833 if (!ret)
834 return NULL;
835 offset = le64_to_cpu(header->partition_entry_lba) *
766d5156
DB
836 cxt->sector_size;
837
838 if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
d71ef5a4 839 goto fail;
766d5156 840 if (sz != read(cxt->dev_fd, ret, sz))
d71ef5a4 841 goto fail;
766d5156
DB
842
843 return ret;
d71ef5a4
KZ
844
845fail:
846 free(ret);
847 return NULL;
766d5156
DB
848}
849
7020de0b
KZ
850static inline uint32_t count_crc32(const unsigned char *buf, size_t len,
851 size_t ex_off, size_t ex_len)
766d5156 852{
7020de0b 853 return (crc32_exclude_offset(~0L, buf, len, ex_off, ex_len) ^ ~0L);
766d5156
DB
854}
855
7020de0b
KZ
856static inline uint32_t gpt_header_count_crc32(struct gpt_header *header)
857{
858 return count_crc32((unsigned char *) header, /* buffer */
859 le32_to_cpu(header->size), /* size of buffer */
860 offsetof(struct gpt_header, crc32), /* exclude */
861 sizeof(header->crc32)); /* size of excluded area */
862}
863
864static inline uint32_t gpt_entryarr_count_crc32(struct gpt_header *header, struct gpt_entry *ents)
865{
866 size_t arysz = 0;
867
868 arysz = le32_to_cpu(header->npartition_entries) *
869 le32_to_cpu(header->sizeof_partition_entry);
870
871 return count_crc32((unsigned char *) ents, arysz, 0, 0);
872}
873
874
766d5156
DB
875/*
876 * Recompute header and partition array 32bit CRC checksums.
877 * This function does not fail - if there's corruption, then it
9e930041 878 * will be reported when checksumming it again (ie: probing or verify).
766d5156 879 */
d71ef5a4 880static void gpt_recompute_crc(struct gpt_header *header, struct gpt_entry *ents)
766d5156 881{
766d5156
DB
882 if (!header)
883 return;
884
7020de0b
KZ
885 header->partition_entry_array_crc32 =
886 cpu_to_le32( gpt_entryarr_count_crc32(header, ents) );
766d5156 887
7020de0b 888 header->crc32 = cpu_to_le32( gpt_header_count_crc32(header) );
766d5156
DB
889}
890
891/*
892 * Compute the 32bit CRC checksum of the partition table header.
893 * Returns 1 if it is valid, otherwise 0.
894 */
d71ef5a4 895static int gpt_check_header_crc(struct gpt_header *header, struct gpt_entry *ents)
766d5156 896{
7020de0b
KZ
897 uint32_t orgcrc = le32_to_cpu(header->crc32),
898 crc = gpt_header_count_crc32(header);
766d5156 899
7020de0b 900 if (crc == orgcrc)
d71ef5a4
KZ
901 return 1;
902
766d5156 903 /*
7020de0b
KZ
904 * If we have checksum mismatch it may be due to stale data, like a
905 * partition being added or deleted. Recompute the CRC again and make
906 * sure this is not the case.
766d5156 907 */
d71ef5a4 908 if (ents) {
766d5156 909 gpt_recompute_crc(header, ents);
7020de0b 910 return gpt_header_count_crc32(header) == orgcrc;
d71ef5a4
KZ
911 }
912
913 return 0;
766d5156
DB
914}
915
916/*
917 * It initializes the partition entry array.
918 * Returns 1 if the checksum is valid, otherwise 0.
919 */
d71ef5a4
KZ
920static int gpt_check_entryarr_crc(struct gpt_header *header,
921 struct gpt_entry *ents)
766d5156 922{
d71ef5a4 923 if (!header || !ents)
7020de0b 924 return 0;
766d5156 925
7020de0b
KZ
926 return gpt_entryarr_count_crc32(header, ents) ==
927 le32_to_cpu(header->partition_entry_array_crc32);
766d5156
DB
928}
929
930static int gpt_check_lba_sanity(struct fdisk_context *cxt, struct gpt_header *header)
931{
932 int ret = 0;
933 uint64_t lu, fu, lastlba = last_lba(cxt);
934
935 fu = le64_to_cpu(header->first_usable_lba);
936 lu = le64_to_cpu(header->last_usable_lba);
937
938 /* check if first and last usable LBA make sense */
939 if (lu < fu) {
88141067 940 DBG(LABEL, ul_debug("error: header last LBA is before first LBA"));
766d5156 941 goto done;
5dbff4c0 942 }
766d5156
DB
943
944 /* check if first and last usable LBAs with the disk's last LBA */
945 if (fu > lastlba || lu > lastlba) {
88141067 946 DBG(LABEL, ul_debug("error: header LBAs are after the disk's last LBA"));
766d5156
DB
947 goto done;
948 }
949
950 /* the header has to be outside usable range */
951 if (fu < GPT_PRIMARY_PARTITION_TABLE_LBA &&
952 GPT_PRIMARY_PARTITION_TABLE_LBA < lu) {
88141067 953 DBG(LABEL, ul_debug("error: header outside of usable range"));
766d5156
DB
954 goto done;
955 }
956
957 ret = 1; /* sane */
958done:
959 return ret;
960}
961
962/* Check if there is a valid header signature */
963static int gpt_check_signature(struct gpt_header *header)
964{
965 return header->signature == cpu_to_le64(GPT_HEADER_SIGNATURE);
966}
967
968/*
969 * Return the specified GPT Header, or NULL upon failure/invalid.
970 * Note that all tests must pass to ensure a valid header,
971 * we do not rely on only testing the signature for a valid probe.
972 */
d71ef5a4
KZ
973static struct gpt_header *gpt_read_header(struct fdisk_context *cxt,
974 uint64_t lba,
975 struct gpt_entry **_ents)
766d5156
DB
976{
977 struct gpt_header *header = NULL;
d71ef5a4 978 struct gpt_entry *ents = NULL;
e9bf0935 979 uint32_t hsz;
766d5156
DB
980
981 if (!cxt)
982 return NULL;
983
4bb82a45
KZ
984 /* always allocate all sector, the area after GPT header
985 * has to be fill by zeros */
986 assert(cxt->sector_size >= sizeof(struct gpt_header));
987
988 header = calloc(1, cxt->sector_size);
46667ba4
KZ
989 if (!header)
990 return NULL;
766d5156 991
d71ef5a4 992 /* read and verify header */
4bb82a45 993 if (read_lba(cxt, lba, header, cxt->sector_size) != 0)
766d5156
DB
994 goto invalid;
995
996 if (!gpt_check_signature(header))
997 goto invalid;
998
9c6f3de6
KZ
999 /* make sure header size is between 92 and sector size bytes */
1000 hsz = le32_to_cpu(header->size);
1001 if (hsz < GPT_HEADER_MINSZ || hsz > cxt->sector_size)
1002 goto invalid;
1003
d71ef5a4
KZ
1004 if (!gpt_check_header_crc(header, NULL))
1005 goto invalid;
1006
1007 /* read and verify entries */
1008 ents = gpt_read_entries(cxt, header);
1009 if (!ents)
1010 goto invalid;
1011
1012 if (!gpt_check_entryarr_crc(header, ents))
766d5156
DB
1013 goto invalid;
1014
1015 if (!gpt_check_lba_sanity(cxt, header))
1016 goto invalid;
1017
1018 /* valid header must be at MyLBA */
1019 if (le64_to_cpu(header->my_lba) != lba)
1020 goto invalid;
1021
e9bf0935 1022
d71ef5a4
KZ
1023 if (_ents)
1024 *_ents = ents;
1025 else
1026 free(ents);
1027
fdbd7bb9 1028 DBG(LABEL, ul_debug("found valid GPT Header on LBA %"PRIu64"", lba));
766d5156
DB
1029 return header;
1030invalid:
1031 free(header);
d71ef5a4 1032 free(ents);
45ddb828 1033
fdbd7bb9 1034 DBG(LABEL, ul_debug("read GPT Header on LBA %"PRIu64" failed", lba));
766d5156
DB
1035 return NULL;
1036}
1037
775001ad
KZ
1038
1039static int gpt_locate_disklabel(struct fdisk_context *cxt, int n,
9bbcf43f 1040 const char **name, uint64_t *offset, size_t *size)
775001ad
KZ
1041{
1042 struct fdisk_gpt_label *gpt;
1043
1044 assert(cxt);
1045
1046 *name = NULL;
1047 *offset = 0;
1048 *size = 0;
1049
1050 switch (n) {
1051 case 0:
1052 *name = "PMBR";
1053 *offset = 0;
1054 *size = 512;
1055 break;
1056 case 1:
1057 *name = _("GPT Header");
9bbcf43f 1058 *offset = (uint64_t) GPT_PRIMARY_PARTITION_TABLE_LBA * cxt->sector_size;
775001ad
KZ
1059 *size = sizeof(struct gpt_header);
1060 break;
1061 case 2:
1062 *name = _("GPT Entries");
1063 gpt = self_label(cxt);
1064 *offset = le64_to_cpu(gpt->pheader->partition_entry_lba) * cxt->sector_size;
1065 *size = le32_to_cpu(gpt->pheader->npartition_entries) *
1066 le32_to_cpu(gpt->pheader->sizeof_partition_entry);
1067 break;
1068 default:
1069 return 1; /* no more chunks */
1070 }
1071
1072 return 0;
1073}
1074
5989556a
KZ
1075static int gpt_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item)
1076{
1077 struct gpt_header *h;
1078 int rc = 0;
1079
1080 assert(cxt);
1081 assert(cxt->label);
1082 assert(fdisk_is_label(cxt, GPT));
1083
1084 h = self_label(cxt)->pheader;
775001ad 1085
5989556a
KZ
1086 switch (item->id) {
1087 case GPT_LABELITEM_ID:
1088 item->name = _("Disk identifier");
1089 item->type = 's';
1090 item->data.str = gpt_get_header_id(h);
1091 if (!item->data.str)
1092 rc = -ENOMEM;
1093 break;
1094 case GPT_LABELITEM_FIRSTLBA:
1095 item->name = _("First LBA");
1096 item->type = 'j';
1097 item->data.num64 = le64_to_cpu(h->first_usable_lba);
1098 break;
1099 case GPT_LABELITEM_LASTLBA:
1100 item->name = _("Last LBA");
1101 item->type = 'j';
1102 item->data.num64 = le64_to_cpu(h->last_usable_lba);
1103 break;
1104 case GPT_LABELITEM_ALTLBA:
1105 /* TRANSLATORS: The LBA (Logical Block Address) of the backup GPT header. */
1106 item->name = _("Alternative LBA");
1107 item->type = 'j';
1108 item->data.num64 = le64_to_cpu(h->alternative_lba);
1109 break;
1110 case GPT_LABELITEM_ENTRIESLBA:
1111 /* TRANSLATORS: The start of the array of partition entries. */
1112 item->name = _("Partition entries LBA");
1113 item->type = 'j';
1114 item->data.num64 = le64_to_cpu(h->partition_entry_lba);
1115 break;
1116 case GPT_LABELITEM_ENTRIESALLOC:
1117 item->name = _("Allocated partition entries");
1118 item->type = 'j';
1119 item->data.num64 = le32_to_cpu(h->npartition_entries);
1120 break;
1121 default:
1122 if (item->id < __FDISK_NLABELITEMS)
9e930041 1123 rc = 1; /* unsupported generic item */
5989556a
KZ
1124 else
1125 rc = 2; /* out of range */
1126 break;
1127 }
1128
1129 return rc;
1130}
775001ad 1131
766d5156
DB
1132/*
1133 * Returns the number of partitions that are in use.
1134 */
8ccfcf70
KZ
1135static unsigned partitions_in_use(struct gpt_header *header,
1136 struct gpt_entry *ents)
766d5156
DB
1137{
1138 uint32_t i, used = 0;
1139
8ccfcf70 1140 if (!header || ! ents)
766d5156
DB
1141 return 0;
1142
1143 for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
8ccfcf70 1144 if (!partition_unused(&ents[i]))
766d5156
DB
1145 used++;
1146 return used;
1147}
1148
766d5156
DB
1149
1150/*
1151 * Check if a partition is too big for the disk (sectors).
1152 * Returns the faulting partition number, otherwise 0.
1153 */
8ccfcf70
KZ
1154static uint32_t check_too_big_partitions(struct gpt_header *header,
1155 struct gpt_entry *ents, uint64_t sectors)
766d5156
DB
1156{
1157 uint32_t i;
1158
1159 for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
8ccfcf70 1160 if (partition_unused(&ents[i]))
766d5156 1161 continue;
8ccfcf70 1162 if (gpt_partition_end(&ents[i]) >= sectors)
766d5156 1163 return i + 1;
5dbff4c0 1164 }
766d5156
DB
1165
1166 return 0;
5dbff4c0
KZ
1167}
1168
766d5156
DB
1169/*
1170 * Check if a partition ends before it begins
1171 * Returns the faulting partition number, otherwise 0.
5dbff4c0 1172 */
9e930041 1173static uint32_t check_start_after_end_partitions(struct gpt_header *header,
8ccfcf70 1174 struct gpt_entry *ents)
5dbff4c0 1175{
766d5156 1176 uint32_t i;
5dbff4c0 1177
766d5156 1178 for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
8ccfcf70 1179 if (partition_unused(&ents[i]))
766d5156 1180 continue;
8ccfcf70 1181 if (gpt_partition_start(&ents[i]) > gpt_partition_end(&ents[i]))
766d5156 1182 return i + 1;
5dbff4c0 1183 }
766d5156
DB
1184
1185 return 0;
5dbff4c0
KZ
1186}
1187
766d5156 1188/*
09af3db4 1189 * Check if partition e1 overlaps with partition e2.
766d5156 1190 */
874aa9c3 1191static inline int partition_overlap(struct gpt_entry *e1, struct gpt_entry *e2)
5dbff4c0 1192{
874aa9c3
KZ
1193 uint64_t start1 = gpt_partition_start(e1);
1194 uint64_t end1 = gpt_partition_end(e1);
1195 uint64_t start2 = gpt_partition_start(e2);
1196 uint64_t end2 = gpt_partition_end(e2);
1197
1198 return (start1 && start2 && (start1 <= end2) != (end1 < start2));
766d5156
DB
1199}
1200
1201/*
09af3db4 1202 * Find any partitions that overlap.
766d5156 1203 */
8ccfcf70
KZ
1204static uint32_t check_overlap_partitions(struct gpt_header *header,
1205 struct gpt_entry *ents)
766d5156
DB
1206{
1207 uint32_t i, j;
1208
1209 for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
1210 for (j = 0; j < i; j++) {
8ccfcf70
KZ
1211 if (partition_unused(&ents[i]) ||
1212 partition_unused(&ents[j]))
766d5156 1213 continue;
8ccfcf70 1214 if (partition_overlap(&ents[i], &ents[j])) {
88141067 1215 DBG(LABEL, ul_debug("GPT partitions overlap detected [%u vs. %u]", i, j));
766d5156 1216 return i + 1;
c15aec86 1217 }
766d5156
DB
1218 }
1219
1220 return 0;
1221}
1222
1223/*
1224 * Find the first available block after the starting point; returns 0 if
1225 * there are no available blocks left, or error. From gdisk.
1226 */
1227static uint64_t find_first_available(struct gpt_header *header,
8ccfcf70 1228 struct gpt_entry *ents, uint64_t start)
766d5156
DB
1229{
1230 uint64_t first;
1231 uint32_t i, first_moved = 0;
1232
602ebe7d
KZ
1233 uint64_t fu, lu;
1234
8ccfcf70 1235 if (!header || !ents)
5dbff4c0 1236 return 0;
766d5156 1237
602ebe7d
KZ
1238 fu = le64_to_cpu(header->first_usable_lba);
1239 lu = le64_to_cpu(header->last_usable_lba);
1240
766d5156
DB
1241 /*
1242 * Begin from the specified starting point or from the first usable
1243 * LBA, whichever is greater...
1244 */
602ebe7d 1245 first = start < fu ? fu : start;
766d5156
DB
1246
1247 /*
1248 * Now search through all partitions; if first is within an
1249 * existing partition, move it to the next sector after that
1250 * partition and repeat. If first was moved, set firstMoved
1251 * flag; repeat until firstMoved is not set, so as to catch
1252 * cases where partitions are out of sequential order....
1253 */
1254 do {
1255 first_moved = 0;
1256 for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
8ccfcf70 1257 if (partition_unused(&ents[i]))
766d5156 1258 continue;
8ccfcf70 1259 if (first < gpt_partition_start(&ents[i]))
766d5156 1260 continue;
8ccfcf70
KZ
1261 if (first <= gpt_partition_end(&ents[i])) {
1262 first = gpt_partition_end(&ents[i]) + 1;
766d5156
DB
1263 first_moved = 1;
1264 }
1265 }
1266 } while (first_moved == 1);
1267
602ebe7d 1268 if (first > lu)
766d5156
DB
1269 first = 0;
1270
1271 return first;
5dbff4c0
KZ
1272}
1273
766d5156
DB
1274
1275/* Returns last available sector in the free space pointed to by start. From gdisk. */
1276static uint64_t find_last_free(struct gpt_header *header,
8ccfcf70 1277 struct gpt_entry *ents, uint64_t start)
5dbff4c0 1278{
766d5156
DB
1279 uint32_t i;
1280 uint64_t nearest_start;
1281
8ccfcf70 1282 if (!header || !ents)
766d5156
DB
1283 return 0;
1284
602ebe7d
KZ
1285 nearest_start = le64_to_cpu(header->last_usable_lba);
1286
766d5156 1287 for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
8ccfcf70 1288 uint64_t ps = gpt_partition_start(&ents[i]);
602ebe7d
KZ
1289
1290 if (nearest_start > ps && ps > start)
0a7cdf80 1291 nearest_start = ps - 1ULL;
5dbff4c0 1292 }
766d5156
DB
1293
1294 return nearest_start;
5dbff4c0 1295}
766d5156
DB
1296
1297/* Returns the last free sector on the disk. From gdisk. */
1298static uint64_t find_last_free_sector(struct gpt_header *header,
8ccfcf70 1299 struct gpt_entry *ents)
766d5156
DB
1300{
1301 uint32_t i, last_moved;
1302 uint64_t last = 0;
1303
8ccfcf70 1304 if (!header || !ents)
766d5156
DB
1305 goto done;
1306
1307 /* start by assuming the last usable LBA is available */
602ebe7d 1308 last = le64_to_cpu(header->last_usable_lba);
766d5156
DB
1309 do {
1310 last_moved = 0;
1311 for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
8ccfcf70
KZ
1312 if ((last >= gpt_partition_start(&ents[i])) &&
1313 (last <= gpt_partition_end(&ents[i]))) {
0a7cdf80 1314 last = gpt_partition_start(&ents[i]) - 1ULL;
766d5156
DB
1315 last_moved = 1;
1316 }
1317 }
1318 } while (last_moved == 1);
1319done:
1320 return last;
1321}
1322
1323/*
1324 * Finds the first available sector in the largest block of unallocated
1325 * space on the disk. Returns 0 if there are no available blocks left.
1326 * From gdisk.
1327 */
8ccfcf70
KZ
1328static uint64_t find_first_in_largest(struct gpt_header *header,
1329 struct gpt_entry *ents)
766d5156
DB
1330{
1331 uint64_t start = 0, first_sect, last_sect;
1332 uint64_t segment_size, selected_size = 0, selected_segment = 0;
1333
8ccfcf70 1334 if (!header || !ents)
766d5156
DB
1335 goto done;
1336
1337 do {
8ccfcf70 1338 first_sect = find_first_available(header, ents, start);
766d5156 1339 if (first_sect != 0) {
8ccfcf70 1340 last_sect = find_last_free(header, ents, first_sect);
0a7cdf80 1341 segment_size = last_sect - first_sect + 1ULL;
766d5156
DB
1342
1343 if (segment_size > selected_size) {
1344 selected_size = segment_size;
1345 selected_segment = first_sect;
1346 }
0a7cdf80 1347 start = last_sect + 1ULL;
766d5156
DB
1348 }
1349 } while (first_sect != 0);
1350
1351done:
1352 return selected_segment;
1353}
1354
1355/*
1356 * Find the total number of free sectors, the number of segments in which
1357 * they reside, and the size of the largest of those segments. From gdisk.
1358 */
1359static uint64_t get_free_sectors(struct fdisk_context *cxt, struct gpt_header *header,
8ccfcf70 1360 struct gpt_entry *ents, uint32_t *nsegments,
766d5156
DB
1361 uint64_t *largest_segment)
1362{
1363 uint32_t num = 0;
1364 uint64_t first_sect, last_sect;
1365 uint64_t largest_seg = 0, segment_sz;
1366 uint64_t totfound = 0, start = 0; /* starting point for each search */
1367
1368 if (!cxt->total_sectors)
1369 goto done;
1370
1371 do {
8ccfcf70 1372 first_sect = find_first_available(header, ents, start);
766d5156 1373 if (first_sect) {
8ccfcf70 1374 last_sect = find_last_free(header, ents, first_sect);
766d5156
DB
1375 segment_sz = last_sect - first_sect + 1;
1376
1377 if (segment_sz > largest_seg)
1378 largest_seg = segment_sz;
1379 totfound += segment_sz;
1380 num++;
0a7cdf80 1381 start = last_sect + 1ULL;
766d5156
DB
1382 }
1383 } while (first_sect);
1384
1385done:
512a430f
KZ
1386 if (nsegments)
1387 *nsegments = num;
1388 if (largest_segment)
1389 *largest_segment = largest_seg;
766d5156
DB
1390
1391 return totfound;
1392}
1393
9ffeb235 1394static int gpt_probe_label(struct fdisk_context *cxt)
766d5156
DB
1395{
1396 int mbr_type;
9ffeb235 1397 struct fdisk_gpt_label *gpt;
766d5156 1398
9ffeb235
KZ
1399 assert(cxt);
1400 assert(cxt->label);
aa36c2cf 1401 assert(fdisk_is_label(cxt, GPT));
9ffeb235
KZ
1402
1403 gpt = self_label(cxt);
766d5156 1404
d2d9efa1
KZ
1405 /* TODO: it would be nice to support scenario when GPT headers are OK,
1406 * but PMBR is corrupt */
766d5156
DB
1407 mbr_type = valid_pmbr(cxt);
1408 if (!mbr_type)
1409 goto failed;
1410
88141067 1411 DBG(LABEL, ul_debug("found a %s MBR", mbr_type == GPT_MBR_PROTECTIVE ?
766d5156
DB
1412 "protective" : "hybrid"));
1413
d71ef5a4
KZ
1414 /* primary header */
1415 gpt->pheader = gpt_read_header(cxt, GPT_PRIMARY_PARTITION_TABLE_LBA,
1416 &gpt->ents);
45ddb828
KZ
1417
1418 if (gpt->pheader)
1419 /* primary OK, try backup from alternative LBA */
1420 gpt->bheader = gpt_read_header(cxt,
1421 le64_to_cpu(gpt->pheader->alternative_lba),
1422 NULL);
1423 else
1424 /* primary corrupted -- try last LBA */
1425 gpt->bheader = gpt_read_header(cxt, last_lba(cxt), &gpt->ents);
766d5156 1426
d2d9efa1 1427 if (!gpt->pheader && !gpt->bheader)
766d5156
DB
1428 goto failed;
1429
d2d9efa1
KZ
1430 /* primary OK, backup corrupted -- recovery */
1431 if (gpt->pheader && !gpt->bheader) {
1432 fdisk_warnx(cxt, _("The backup GPT table is corrupt, but the "
1433 "primary appears OK, so that will be used."));
45ddb828
KZ
1434 gpt->bheader = gpt_copy_header(cxt, gpt->pheader);
1435 if (!gpt->bheader)
d2d9efa1 1436 goto failed;
d2d9efa1
KZ
1437 gpt_recompute_crc(gpt->bheader, gpt->ents);
1438
1439 /* primary corrupted, backup OK -- recovery */
1440 } else if (!gpt->pheader && gpt->bheader) {
1441 fdisk_warnx(cxt, _("The primary GPT table is corrupt, but the "
1442 "backup appears OK, so that will be used."));
45ddb828
KZ
1443 gpt->pheader = gpt_copy_header(cxt, gpt->bheader);
1444 if (!gpt->pheader)
d2d9efa1 1445 goto failed;
d2d9efa1
KZ
1446 gpt_recompute_crc(gpt->pheader, gpt->ents);
1447 }
d71ef5a4 1448
9ffeb235
KZ
1449 cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
1450 cxt->label->nparts_cur = partitions_in_use(gpt->pheader, gpt->ents);
766d5156
DB
1451 return 1;
1452failed:
88141067 1453 DBG(LABEL, ul_debug("GPT probe failed"));
9ffeb235 1454 gpt_deinit(cxt->label);
766d5156
DB
1455 return 0;
1456}
1457
1458/*
1459 * Stolen from libblkid - can be removed once partition semantics
1460 * are added to the fdisk API.
1461 */
1462static char *encode_to_utf8(unsigned char *src, size_t count)
1463{
1464 uint16_t c;
d06f321d 1465 char *dest;
766d5156 1466 size_t i, j, len = count;
3f731001 1467
d06f321d
KZ
1468 dest = calloc(1, count);
1469 if (!dest)
1470 return NULL;
766d5156
DB
1471
1472 for (j = i = 0; i + 2 <= count; i += 2) {
1473 /* always little endian */
1474 c = (src[i+1] << 8) | src[i];
1475 if (c == 0) {
1476 dest[j] = '\0';
1477 break;
1478 } else if (c < 0x80) {
1479 if (j+1 >= len)
1480 break;
1481 dest[j++] = (uint8_t) c;
1482 } else if (c < 0x800) {
1483 if (j+2 >= len)
1484 break;
1485 dest[j++] = (uint8_t) (0xc0 | (c >> 6));
1486 dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
1487 } else {
1488 if (j+3 >= len)
1489 break;
1490 dest[j++] = (uint8_t) (0xe0 | (c >> 12));
1491 dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
1492 dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
1493 }
1494 }
1495 dest[j] = '\0';
1496
1497 return dest;
1498}
1499
01086b80 1500static int gpt_entry_attrs_to_string(struct gpt_entry *e, char **res)
c83f772e 1501{
01086b80
KZ
1502 unsigned int n, count = 0;
1503 size_t l;
1504 char *bits, *p;
1505 uint64_t attrs;
1506
1507 assert(e);
1508 assert(res);
1509
1510 *res = NULL;
159652d9 1511 attrs = e->attrs;
01086b80
KZ
1512 if (!attrs)
1513 return 0; /* no attributes at all */
1514
1515 bits = (char *) &attrs;
1516
1517 /* Note that sizeof() is correct here, we need separators between
1518 * the strings so also count \0 is correct */
1519 *res = calloc(1, sizeof(GPT_ATTRSTR_NOBLOCK) +
1520 sizeof(GPT_ATTRSTR_REQ) +
1521 sizeof(GPT_ATTRSTR_LEGACY) +
1522 sizeof("GUID:") + (GPT_ATTRBIT_GUID_COUNT * 3));
c83f772e 1523 if (!*res)
01086b80
KZ
1524 return -errno;
1525
1526 p = *res;
1527 if (isset(bits, GPT_ATTRBIT_REQ)) {
1528 memcpy(p, GPT_ATTRSTR_REQ, (l = sizeof(GPT_ATTRSTR_REQ)));
1529 p += l - 1;
1530 }
1531 if (isset(bits, GPT_ATTRBIT_NOBLOCK)) {
1532 if (p > *res)
1533 *p++ = ' ';
1534 memcpy(p, GPT_ATTRSTR_NOBLOCK, (l = sizeof(GPT_ATTRSTR_NOBLOCK)));
1535 p += l - 1;
1536 }
1537 if (isset(bits, GPT_ATTRBIT_LEGACY)) {
1538 if (p > *res)
1539 *p++ = ' ';
1540 memcpy(p, GPT_ATTRSTR_LEGACY, (l = sizeof(GPT_ATTRSTR_LEGACY)));
1541 p += l - 1;
1542 }
1543
1544 for (n = GPT_ATTRBIT_GUID_FIRST;
1545 n < GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT; n++) {
c83f772e 1546
01086b80 1547 if (!isset(bits, n))
c83f772e 1548 continue;
01086b80
KZ
1549 if (!count) {
1550 if (p > *res)
1551 *p++ = ' ';
1552 p += sprintf(p, "GUID:%u", n);
1553 } else
1554 p += sprintf(p, ",%u", n);
c83f772e 1555 count++;
c83f772e
KZ
1556 }
1557
01086b80 1558 return 0;
c83f772e
KZ
1559}
1560
c77ba531
KZ
1561static int gpt_entry_attrs_from_string(
1562 struct fdisk_context *cxt,
1563 struct gpt_entry *e,
1564 const char *str)
1565{
1566 const char *p = str;
1567 uint64_t attrs = 0;
1568 char *bits;
1569
1570 assert(e);
1571 assert(p);
1572
1573 DBG(LABEL, ul_debug("GPT: parsing string attributes '%s'", p));
1574
1575 bits = (char *) &attrs;
1576
1577 while (p && *p) {
1578 int bit = -1;
1579
1580 while (isblank(*p)) p++;
1581 if (!*p)
1582 break;
1583
1584 DBG(LABEL, ul_debug(" parsing item '%s'", p));
1585
1586 if (strncmp(p, "GUID:", 5) == 0) {
1587 p += 5;
1588 continue;
1589 } else if (strncmp(p, GPT_ATTRSTR_REQ,
1590 sizeof(GPT_ATTRSTR_REQ) - 1) == 0) {
1591 bit = GPT_ATTRBIT_REQ;
1592 p += sizeof(GPT_ATTRSTR_REQ) - 1;
d1b7bfe5
SR
1593 } else if (strncmp(p, GPT_ATTRSTR_REQ_TYPO,
1594 sizeof(GPT_ATTRSTR_REQ_TYPO) - 1) == 0) {
1595 bit = GPT_ATTRBIT_REQ;
1596 p += sizeof(GPT_ATTRSTR_REQ_TYPO) - 1;
c77ba531
KZ
1597 } else if (strncmp(p, GPT_ATTRSTR_LEGACY,
1598 sizeof(GPT_ATTRSTR_LEGACY) - 1) == 0) {
1599 bit = GPT_ATTRBIT_LEGACY;
1600 p += sizeof(GPT_ATTRSTR_LEGACY) - 1;
1601 } else if (strncmp(p, GPT_ATTRSTR_NOBLOCK,
1602 sizeof(GPT_ATTRSTR_NOBLOCK) - 1) == 0) {
1603 bit = GPT_ATTRBIT_NOBLOCK;
1604 p += sizeof(GPT_ATTRSTR_NOBLOCK) - 1;
1605 } else if (isdigit((unsigned int) *p)) {
1606 char *end = NULL;
1607
1608 errno = 0;
1609 bit = strtol(p, &end, 0);
1610 if (errno || !end || end == str
1611 || bit < GPT_ATTRBIT_GUID_FIRST
1612 || bit >= GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT)
1613 bit = -1;
1614 else
1615 p = end;
1616 }
1617
1618 if (bit < 0) {
54fefa07 1619 fdisk_warnx(cxt, _("unsupported GPT attribute bit '%s'"), p);
c77ba531
KZ
1620 return -EINVAL;
1621 }
1622
1623 setbit(bits, bit);
1624
1625 while (isblank(*p)) p++;
1626 if (*p == ',')
1627 p++;
1628 }
1629
159652d9 1630 e->attrs = attrs;
c77ba531
KZ
1631 return 0;
1632}
1633
8c0a7f91
KZ
1634static int gpt_get_partition(struct fdisk_context *cxt, size_t n,
1635 struct fdisk_partition *pa)
766d5156 1636{
9ffeb235 1637 struct fdisk_gpt_label *gpt;
6941952e 1638 struct gpt_entry *e;
01086b80
KZ
1639 char u_str[37];
1640 int rc = 0;
9ffeb235
KZ
1641
1642 assert(cxt);
1643 assert(cxt->label);
aa36c2cf 1644 assert(fdisk_is_label(cxt, GPT));
9ffeb235
KZ
1645
1646 gpt = self_label(cxt);
766d5156 1647
6941952e
KZ
1648 if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
1649 return -EINVAL;
3c5fb475 1650
6941952e
KZ
1651 gpt = self_label(cxt);
1652 e = &gpt->ents[n];
b1920e0b 1653
8c0a7f91
KZ
1654 pa->used = !partition_unused(e) || gpt_partition_start(e);
1655 if (!pa->used)
1656 return 0;
766d5156 1657
8c0a7f91 1658 pa->start = gpt_partition_start(e);
77d6a70a 1659 pa->size = gpt_partition_size(e);
7f539277 1660 pa->type = gpt_partition_parttype(cxt, e);
766d5156 1661
8c0a7f91
KZ
1662 if (guid_to_string(&e->partition_guid, u_str)) {
1663 pa->uuid = strdup(u_str);
01086b80
KZ
1664 if (!pa->uuid) {
1665 rc = -errno;
1666 goto done;
1667 }
8c0a7f91
KZ
1668 } else
1669 pa->uuid = NULL;
1670
01086b80
KZ
1671 rc = gpt_entry_attrs_to_string(e, &pa->attrs);
1672 if (rc)
1673 goto done;
b1920e0b 1674
8c0a7f91 1675 pa->name = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
8c0a7f91 1676 return 0;
01086b80 1677done:
8c0a7f91 1678 fdisk_reset_partition(pa);
01086b80 1679 return rc;
6941952e 1680}
766d5156 1681
9348ef25 1682
b0a484a8
KZ
1683static int gpt_set_partition(struct fdisk_context *cxt, size_t n,
1684 struct fdisk_partition *pa)
1685{
1686 struct fdisk_gpt_label *gpt;
1687 struct gpt_entry *e;
1688 int rc = 0;
9146a008 1689 uint64_t start, end;
b0a484a8
KZ
1690
1691 assert(cxt);
1692 assert(cxt->label);
1693 assert(fdisk_is_label(cxt, GPT));
1694
1695 gpt = self_label(cxt);
1696
1697 if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
1698 return -EINVAL;
1699
9146a008
KZ
1700 FDISK_INIT_UNDEF(start);
1701 FDISK_INIT_UNDEF(end);
1702
b0a484a8
KZ
1703 gpt = self_label(cxt);
1704 e = &gpt->ents[n];
1705
1706 if (pa->uuid) {
6936c081
KZ
1707 char new_u[37], old_u[37];
1708
1709 guid_to_string(&e->partition_guid, old_u);
b0a484a8
KZ
1710 rc = gpt_entry_set_uuid(e, pa->uuid);
1711 if (rc)
1712 return rc;
6936c081 1713 guid_to_string(&e->partition_guid, new_u);
0477369a 1714 fdisk_info(cxt, _("Partition UUID changed from %s to %s."),
6936c081 1715 old_u, new_u);
b0a484a8
KZ
1716 }
1717
6936c081
KZ
1718 if (pa->name) {
1719 char *old = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
b0a484a8
KZ
1720 gpt_entry_set_name(e, pa->name);
1721
0477369a 1722 fdisk_info(cxt, _("Partition name changed from '%s' to '%.*s'."),
6936c081
KZ
1723 old, (int) GPT_PART_NAME_LEN, pa->name);
1724 free(old);
1725 }
1726
b0a484a8
KZ
1727 if (pa->type && pa->type->typestr) {
1728 struct gpt_guid typeid;
1729
a48c0985
KZ
1730 rc = string_to_guid(pa->type->typestr, &typeid);
1731 if (rc)
1732 return rc;
b0a484a8
KZ
1733 gpt_entry_set_type(e, &typeid);
1734 }
c77ba531
KZ
1735 if (pa->attrs) {
1736 rc = gpt_entry_attrs_from_string(cxt, e, pa->attrs);
1737 if (rc)
1738 return rc;
1739 }
b0a484a8 1740
ecf40cda 1741 if (fdisk_partition_has_start(pa))
9146a008 1742 start = pa->start;
76785052
KZ
1743 if (fdisk_partition_has_size(pa) || fdisk_partition_has_start(pa)) {
1744 uint64_t xstart = fdisk_partition_has_start(pa) ? pa->start : gpt_partition_start(e);
1745 uint64_t xsize = fdisk_partition_has_size(pa) ? pa->size : gpt_partition_size(e);
1746 end = xstart + xsize - 1ULL;
1747 }
9146a008 1748
c949fa98
KZ
1749 if (!FDISK_IS_UNDEF(start)) {
1750 if (start < le64_to_cpu(gpt->pheader->first_usable_lba)) {
614ddddf 1751 fdisk_warnx(cxt, _("The start of the partition understeps FirstUsableLBA."));
c949fa98
KZ
1752 return -EINVAL;
1753 }
9146a008 1754 e->lba_start = cpu_to_le64(start);
c949fa98
KZ
1755 }
1756 if (!FDISK_IS_UNDEF(end)) {
1757 if (end > le64_to_cpu(gpt->pheader->last_usable_lba)) {
614ddddf 1758 fdisk_warnx(cxt, _("The end of the partition oversteps LastUsableLBA."));
c949fa98
KZ
1759 return -EINVAL;
1760 }
9146a008 1761 e->lba_end = cpu_to_le64(end);
c949fa98 1762 }
b0a484a8
KZ
1763 gpt_recompute_crc(gpt->pheader, gpt->ents);
1764 gpt_recompute_crc(gpt->bheader, gpt->ents);
1765
1766 fdisk_label_set_changed(cxt->label, 1);
1767 return rc;
1768}
1769
1770
766d5156
DB
1771
1772/*
1773 * Write partitions.
1774 * Returns 0 on success, or corresponding error otherwise.
1775 */
1776static int gpt_write_partitions(struct fdisk_context *cxt,
d71ef5a4 1777 struct gpt_header *header, struct gpt_entry *ents)
766d5156
DB
1778{
1779 off_t offset = le64_to_cpu(header->partition_entry_lba) * cxt->sector_size;
1780 uint32_t nparts = le32_to_cpu(header->npartition_entries);
1781 uint32_t totwrite = nparts * le32_to_cpu(header->sizeof_partition_entry);
130820a8 1782 ssize_t rc;
766d5156
DB
1783
1784 if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
1785 goto fail;
d71ef5a4
KZ
1786
1787 rc = write(cxt->dev_fd, ents, totwrite);
130820a8 1788 if (rc > 0 && totwrite == (uint32_t) rc)
766d5156
DB
1789 return 0;
1790fail:
1791 return -errno;
1792}
1793
1794/*
4bb82a45
KZ
1795 * Write a GPT header to a specified LBA.
1796 *
1797 * We read all sector, so we have to write all sector back
1798 * to the device -- never ever rely on sizeof(struct gpt_header)!
1799 *
766d5156
DB
1800 * Returns 0 on success, or corresponding error otherwise.
1801 */
1802static int gpt_write_header(struct fdisk_context *cxt,
1803 struct gpt_header *header, uint64_t lba)
1804{
1805 off_t offset = lba * cxt->sector_size;
1806
1807 if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
1808 goto fail;
1809 if (cxt->sector_size ==
1810 (size_t) write(cxt->dev_fd, header, cxt->sector_size))
1811 return 0;
1812fail:
1813 return -errno;
1814}
1815
1816/*
1817 * Write the protective MBR.
1818 * Returns 0 on success, or corresponding error otherwise.
1819 */
1820static int gpt_write_pmbr(struct fdisk_context *cxt)
1821{
1822 off_t offset;
1823 struct gpt_legacy_mbr *pmbr = NULL;
1824
9ffeb235
KZ
1825 assert(cxt);
1826 assert(cxt->firstsector);
766d5156
DB
1827
1828 pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
1829
1830 /* zero out the legacy partitions */
1831 memset(pmbr->partition_record, 0, sizeof(pmbr->partition_record));
1832
1833 pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
1834 pmbr->partition_record[0].os_type = EFI_PMBR_OSTYPE;
1835 pmbr->partition_record[0].start_sector = 1;
1836 pmbr->partition_record[0].end_head = 0xFE;
1837 pmbr->partition_record[0].end_sector = 0xFF;
1838 pmbr->partition_record[0].end_track = 0xFF;
1839 pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
1840
1841 /*
1842 * Set size_in_lba to the size of the disk minus one. If the size of the disk
1843 * is too large to be represented by a 32bit LBA (2Tb), set it to 0xFFFFFFFF.
1844 */
0a7cdf80 1845 if (cxt->total_sectors - 1ULL > 0xFFFFFFFFULL)
766d5156
DB
1846 pmbr->partition_record[0].size_in_lba = cpu_to_le32(0xFFFFFFFF);
1847 else
1848 pmbr->partition_record[0].size_in_lba =
0a7cdf80 1849 cpu_to_le32((uint32_t) (cxt->total_sectors - 1ULL));
766d5156
DB
1850
1851 offset = GPT_PMBR_LBA * cxt->sector_size;
1852 if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
1853 goto fail;
1854
19613111
DB
1855 /* pMBR covers the first sector (LBA) of the disk */
1856 if (write_all(cxt->dev_fd, pmbr, cxt->sector_size))
1857 goto fail;
1858 return 0;
766d5156
DB
1859fail:
1860 return -errno;
1861}
1862
1863/*
1864 * Writes in-memory GPT and pMBR data to disk.
1865 * Returns 0 if successful write, otherwise, a corresponding error.
1866 * Any indication of error will abort the operation.
1867 */
9ffeb235 1868static int gpt_write_disklabel(struct fdisk_context *cxt)
766d5156 1869{
9ffeb235 1870 struct fdisk_gpt_label *gpt;
433d05ff 1871 int mbr_type;
d71ef5a4 1872
9ffeb235
KZ
1873 assert(cxt);
1874 assert(cxt->label);
aa36c2cf 1875 assert(fdisk_is_label(cxt, GPT));
9ffeb235
KZ
1876
1877 gpt = self_label(cxt);
433d05ff 1878 mbr_type = valid_pmbr(cxt);
766d5156
DB
1879
1880 /* check that disk is big enough to handle the backup header */
c15aec86 1881 if (le64_to_cpu(gpt->pheader->alternative_lba) > cxt->total_sectors)
766d5156
DB
1882 goto err0;
1883
1884 /* check that the backup header is properly placed */
0a7cdf80 1885 if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1ULL)
766d5156
DB
1886 /* TODO: correct this (with user authorization) and write */
1887 goto err0;
1888
8ccfcf70 1889 if (check_overlap_partitions(gpt->pheader, gpt->ents))
766d5156
DB
1890 goto err0;
1891
1892 /* recompute CRCs for both headers */
d71ef5a4
KZ
1893 gpt_recompute_crc(gpt->pheader, gpt->ents);
1894 gpt_recompute_crc(gpt->bheader, gpt->ents);
766d5156
DB
1895
1896 /*
1897 * UEFI requires writing in this specific order:
1898 * 1) backup partition tables
1899 * 2) backup GPT header
1900 * 3) primary partition tables
1901 * 4) primary GPT header
1902 * 5) protective MBR
1903 *
1904 * If any write fails, we abort the rest.
1905 */
d71ef5a4 1906 if (gpt_write_partitions(cxt, gpt->bheader, gpt->ents) != 0)
766d5156 1907 goto err1;
c15aec86
KZ
1908 if (gpt_write_header(cxt, gpt->bheader,
1909 le64_to_cpu(gpt->pheader->alternative_lba)) != 0)
766d5156 1910 goto err1;
d71ef5a4 1911 if (gpt_write_partitions(cxt, gpt->pheader, gpt->ents) != 0)
766d5156 1912 goto err1;
d71ef5a4 1913 if (gpt_write_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA) != 0)
766d5156 1914 goto err1;
433d05ff
KZ
1915
1916 if (mbr_type == GPT_MBR_HYBRID)
1917 fdisk_warnx(cxt, _("The device contains hybrid MBR -- writing GPT only. "
1918 "You have to sync the MBR manually."));
1919 else if (gpt_write_pmbr(cxt) != 0)
766d5156
DB
1920 goto err1;
1921
88141067 1922 DBG(LABEL, ul_debug("GPT write success"));
766d5156
DB
1923 return 0;
1924err0:
88141067 1925 DBG(LABEL, ul_debug("GPT write failed: incorrect input"));
c15aec86 1926 errno = EINVAL;
766d5156
DB
1927 return -EINVAL;
1928err1:
88141067 1929 DBG(LABEL, ul_debug("GPT write failed: %m"));
766d5156
DB
1930 return -errno;
1931}
1932
1933/*
1934 * Verify data integrity and report any found problems for:
1935 * - primary and backup header validations
9e930041 1936 * - partition validations
766d5156 1937 */
9ffeb235 1938static int gpt_verify_disklabel(struct fdisk_context *cxt)
766d5156 1939{
83df5feb
KZ
1940 int nerror = 0;
1941 unsigned int ptnum;
9ffeb235
KZ
1942 struct fdisk_gpt_label *gpt;
1943
1944 assert(cxt);
1945 assert(cxt->label);
aa36c2cf 1946 assert(fdisk_is_label(cxt, GPT));
9ffeb235
KZ
1947
1948 gpt = self_label(cxt);
e820595b
KZ
1949 if (!gpt)
1950 return -EINVAL;
766d5156 1951
e820595b 1952 if (!gpt->bheader) {
766d5156 1953 nerror++;
83df5feb 1954 fdisk_warnx(cxt, _("Disk does not contain a valid backup header."));
766d5156
DB
1955 }
1956
d71ef5a4 1957 if (!gpt_check_header_crc(gpt->pheader, gpt->ents)) {
766d5156 1958 nerror++;
83df5feb 1959 fdisk_warnx(cxt, _("Invalid primary header CRC checksum."));
766d5156 1960 }
d71ef5a4 1961 if (gpt->bheader && !gpt_check_header_crc(gpt->bheader, gpt->ents)) {
766d5156 1962 nerror++;
83df5feb 1963 fdisk_warnx(cxt, _("Invalid backup header CRC checksum."));
766d5156
DB
1964 }
1965
d71ef5a4 1966 if (!gpt_check_entryarr_crc(gpt->pheader, gpt->ents)) {
766d5156 1967 nerror++;
83df5feb 1968 fdisk_warnx(cxt, _("Invalid partition entry checksum."));
766d5156
DB
1969 }
1970
d71ef5a4 1971 if (!gpt_check_lba_sanity(cxt, gpt->pheader)) {
766d5156 1972 nerror++;
83df5feb 1973 fdisk_warnx(cxt, _("Invalid primary header LBA sanity checks."));
766d5156 1974 }
d71ef5a4 1975 if (gpt->bheader && !gpt_check_lba_sanity(cxt, gpt->bheader)) {
766d5156 1976 nerror++;
83df5feb 1977 fdisk_warnx(cxt, _("Invalid backup header LBA sanity checks."));
766d5156
DB
1978 }
1979
d71ef5a4 1980 if (le64_to_cpu(gpt->pheader->my_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) {
766d5156 1981 nerror++;
83df5feb 1982 fdisk_warnx(cxt, _("MyLBA mismatch with real position at primary header."));
766d5156 1983 }
d71ef5a4 1984 if (gpt->bheader && le64_to_cpu(gpt->bheader->my_lba) != last_lba(cxt)) {
766d5156 1985 nerror++;
83df5feb 1986 fdisk_warnx(cxt, _("MyLBA mismatch with real position at backup header."));
766d5156
DB
1987
1988 }
c15aec86 1989 if (le64_to_cpu(gpt->pheader->alternative_lba) >= cxt->total_sectors) {
766d5156 1990 nerror++;
a1e276ae 1991 fdisk_warnx(cxt, _("Disk is too small to hold all data."));
766d5156
DB
1992 }
1993
1994 /*
1995 * if the GPT is the primary table, check the alternateLBA
1996 * to see if it is a valid GPT
1997 */
c15aec86
KZ
1998 if (gpt->bheader && (le64_to_cpu(gpt->pheader->my_lba) !=
1999 le64_to_cpu(gpt->bheader->alternative_lba))) {
766d5156 2000 nerror++;
83df5feb 2001 fdisk_warnx(cxt, _("Primary and backup header mismatch."));
766d5156
DB
2002 }
2003
8ccfcf70 2004 ptnum = check_overlap_partitions(gpt->pheader, gpt->ents);
766d5156
DB
2005 if (ptnum) {
2006 nerror++;
83df5feb
KZ
2007 fdisk_warnx(cxt, _("Partition %u overlaps with partition %u."),
2008 ptnum, ptnum+1);
766d5156
DB
2009 }
2010
8ccfcf70 2011 ptnum = check_too_big_partitions(gpt->pheader, gpt->ents, cxt->total_sectors);
766d5156
DB
2012 if (ptnum) {
2013 nerror++;
83df5feb
KZ
2014 fdisk_warnx(cxt, _("Partition %u is too big for the disk."),
2015 ptnum);
766d5156
DB
2016 }
2017
9e930041 2018 ptnum = check_start_after_end_partitions(gpt->pheader, gpt->ents);
766d5156
DB
2019 if (ptnum) {
2020 nerror++;
83df5feb
KZ
2021 fdisk_warnx(cxt, _("Partition %u ends before it starts."),
2022 ptnum);
766d5156
DB
2023 }
2024
2025 if (!nerror) { /* yay :-) */
2026 uint32_t nsegments = 0;
2027 uint64_t free_sectors = 0, largest_segment = 0;
6d0ed4cb 2028 char *strsz = NULL;
766d5156 2029
ac1a559a 2030 fdisk_info(cxt, _("No errors detected."));
83df5feb
KZ
2031 fdisk_info(cxt, _("Header version: %s"), gpt_get_header_revstr(gpt->pheader));
2032 fdisk_info(cxt, _("Using %u out of %d partitions."),
d71ef5a4
KZ
2033 partitions_in_use(gpt->pheader, gpt->ents),
2034 le32_to_cpu(gpt->pheader->npartition_entries));
766d5156 2035
d71ef5a4 2036 free_sectors = get_free_sectors(cxt, gpt->pheader, gpt->ents,
766d5156 2037 &nsegments, &largest_segment);
6d0ed4cb
KZ
2038 if (largest_segment)
2039 strsz = size_to_human_string(SIZE_SUFFIX_SPACE | SIZE_SUFFIX_3LETTER,
2040 largest_segment * cxt->sector_size);
2041
4ae11fe8 2042 fdisk_info(cxt,
829f4206
KZ
2043 P_("A total of %ju free sectors is available in %u segment.",
2044 "A total of %ju free sectors is available in %u segments "
6d0ed4cb
KZ
2045 "(the largest is %s).", nsegments),
2046 free_sectors, nsegments, strsz);
2047 free(strsz);
2048
766d5156 2049 } else
a1e276ae 2050 fdisk_warnx(cxt,
8e7f944d 2051 P_("%d error detected.", "%d errors detected.", nerror),
a1e276ae 2052 nerror);
766d5156
DB
2053
2054 return 0;
2055}
2056
2057/* Delete a single GPT partition, specified by partnum. */
8a95621d 2058static int gpt_delete_partition(struct fdisk_context *cxt,
9ffeb235 2059 size_t partnum)
766d5156 2060{
9ffeb235 2061 struct fdisk_gpt_label *gpt;
d71ef5a4 2062
9ffeb235
KZ
2063 assert(cxt);
2064 assert(cxt->label);
aa36c2cf 2065 assert(fdisk_is_label(cxt, GPT));
d71ef5a4 2066
9ffeb235
KZ
2067 gpt = self_label(cxt);
2068
2069 if (partnum >= cxt->label->nparts_max
2070 || partition_unused(&gpt->ents[partnum]))
1f5eb51b 2071 return -EINVAL;
766d5156
DB
2072
2073 /* hasta la vista, baby! */
d71ef5a4
KZ
2074 memset(&gpt->ents[partnum], 0, sizeof(struct gpt_entry));
2075 if (!partition_unused(&gpt->ents[partnum]))
1f5eb51b 2076 return -EINVAL;
766d5156 2077 else {
d71ef5a4
KZ
2078 gpt_recompute_crc(gpt->pheader, gpt->ents);
2079 gpt_recompute_crc(gpt->bheader, gpt->ents);
9ffeb235
KZ
2080 cxt->label->nparts_cur--;
2081 fdisk_label_set_changed(cxt->label, 1);
766d5156 2082 }
1f5eb51b
DB
2083
2084 return 0;
766d5156
DB
2085}
2086
080633e4 2087
766d5156 2088/* Performs logical checks to add a new partition entry */
8a95621d
KZ
2089static int gpt_add_partition(
2090 struct fdisk_context *cxt,
c3bc7483
KZ
2091 struct fdisk_partition *pa,
2092 size_t *partno)
766d5156 2093{
512a430f
KZ
2094 uint64_t user_f, user_l; /* user input ranges for first and last sectors */
2095 uint64_t disk_f, disk_l; /* first and last available sector ranges on device*/
2096 uint64_t dflt_f, dflt_l; /* largest segment (default) */
c0d14b09 2097 struct gpt_guid typeid;
9ffeb235 2098 struct fdisk_gpt_label *gpt;
d71ef5a4 2099 struct gpt_header *pheader;
8d95e7e0 2100 struct gpt_entry *e, *ents;
4114da08 2101 struct fdisk_ask *ask = NULL;
77d6a70a 2102 size_t partnum;
4114da08 2103 int rc;
766d5156 2104
9ffeb235
KZ
2105 assert(cxt);
2106 assert(cxt->label);
aa36c2cf 2107 assert(fdisk_is_label(cxt, GPT));
9ffeb235
KZ
2108
2109 gpt = self_label(cxt);
d71ef5a4
KZ
2110 pheader = gpt->pheader;
2111 ents = gpt->ents;
2112
6c89f750 2113 rc = fdisk_partition_next_partno(pa, cxt, &partnum);
1240f549 2114 if (rc) {
88141067 2115 DBG(LABEL, ul_debug("GPT failed to get next partno"));
77d6a70a 2116 return rc;
1240f549 2117 }
874aa9c3 2118 if (!partition_unused(&ents[partnum])) {
829f4206 2119 fdisk_warnx(cxt, _("Partition %zu is already defined. "
83217641 2120 "Delete it before re-adding it."), partnum +1);
77d6a70a 2121 return -ERANGE;
766d5156 2122 }
d71ef5a4
KZ
2123 if (le32_to_cpu(pheader->npartition_entries) ==
2124 partitions_in_use(pheader, ents)) {
83df5feb 2125 fdisk_warnx(cxt, _("All partitions are already in use."));
77d6a70a 2126 return -ENOSPC;
766d5156 2127 }
512a430f 2128 if (!get_free_sectors(cxt, pheader, ents, NULL, NULL)) {
83df5feb 2129 fdisk_warnx(cxt, _("No free sectors available."));
8254c3a5 2130 return -ENOSPC;
766d5156
DB
2131 }
2132
4044d244 2133 rc = string_to_guid(pa && pa->type && pa->type->typestr ?
77d6a70a
KZ
2134 pa->type->typestr:
2135 GPT_DEFAULT_ENTRY_TYPE, &typeid);
4044d244
KZ
2136 if (rc)
2137 return rc;
77d6a70a 2138
43a2b094 2139 disk_f = find_first_available(pheader, ents, le64_to_cpu(pheader->first_usable_lba));
4a4616b2
KZ
2140
2141 /* if first sector no explicitly defined then ignore small gaps before
2142 * the first partition */
2143 if ((!pa || !fdisk_partition_has_start(pa))
2144 && !partition_unused(&ents[0])
2145 && disk_f < gpt_partition_start(&ents[0])) {
2146
2147 do {
2148 uint64_t x;
fdbd7bb9 2149 DBG(LABEL, ul_debug("testing first sector %"PRIu64"", disk_f));
4a4616b2
KZ
2150 disk_f = find_first_available(pheader, ents, disk_f);
2151 if (!disk_f)
2152 break;
2153 x = find_last_free(pheader, ents, disk_f);
2154 if (x - disk_f >= cxt->grain / cxt->sector_size)
2155 break;
fdbd7bb9 2156 DBG(LABEL, ul_debug("first sector %"PRIu64" addresses to small space, continue...", disk_f));
0a7cdf80 2157 disk_f = x + 1ULL;
4a4616b2
KZ
2158 } while(1);
2159
2160 if (disk_f == 0)
43a2b094 2161 disk_f = find_first_available(pheader, ents, le64_to_cpu(pheader->first_usable_lba));
4a4616b2
KZ
2162 }
2163
512a430f
KZ
2164 disk_l = find_last_free_sector(pheader, ents);
2165
2166 /* the default is the largest free space */
2167 dflt_f = find_first_in_largest(pheader, ents);
2168 dflt_l = find_last_free(pheader, ents, dflt_f);
2169
2170 /* align the default in range <dflt_f,dflt_l>*/
9475cc78 2171 dflt_f = fdisk_align_lba_in_range(cxt, dflt_f, dflt_f, dflt_l);
766d5156 2172
77d6a70a 2173 /* first sector */
ecf40cda
KZ
2174 if (pa && pa->start_follow_default) {
2175 user_f = dflt_f;
2176
2177 } else if (pa && fdisk_partition_has_start(pa)) {
fdbd7bb9 2178 DBG(LABEL, ul_debug("first sector defined: %ju", (uintmax_t)pa->start));
77d6a70a 2179 if (pa->start != find_first_available(pheader, ents, pa->start)) {
fdbd7bb9 2180 fdisk_warnx(cxt, _("Sector %ju already used."), (uintmax_t)pa->start);
77d6a70a 2181 return -ERANGE;
e3443e8f 2182 }
77d6a70a 2183 user_f = pa->start;
77d6a70a
KZ
2184 } else {
2185 /* ask by dialog */
2186 for (;;) {
2187 if (!ask)
2188 ask = fdisk_new_ask();
2189 else
2190 fdisk_reset_ask(ask);
2191
2192 /* First sector */
2193 fdisk_ask_set_query(ask, _("First sector"));
2194 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
2195 fdisk_ask_number_set_low(ask, disk_f); /* minimal */
2196 fdisk_ask_number_set_default(ask, dflt_f); /* default */
2197 fdisk_ask_number_set_high(ask, disk_l); /* maximal */
2198
2199 rc = fdisk_do_ask(cxt, ask);
2200 if (rc)
2201 goto done;
2202
2203 user_f = fdisk_ask_number_get_result(ask);
2204 if (user_f != find_first_available(pheader, ents, user_f)) {
2205 fdisk_warnx(cxt, _("Sector %ju already used."), user_f);
2206 continue;
2207 }
512a430f 2208 break;
77d6a70a
KZ
2209 }
2210 }
2211
1240f549 2212
77d6a70a
KZ
2213 /* Last sector */
2214 dflt_l = find_last_free(pheader, ents, user_f);
2215
ecf40cda
KZ
2216 if (pa && pa->end_follow_default) {
2217 user_l = dflt_l;
2218
2219 } else if (pa && fdisk_partition_has_size(pa)) {
ee50336c 2220 user_l = user_f + pa->size - 1;
fdbd7bb9
RM
2221 DBG(LABEL, ul_debug("size defined: %ju, end: %"PRIu64" (last possible: %"PRIu64")",
2222 (uintmax_t)pa->size, user_l, dflt_l));
68fe4b28 2223 if (user_l != dflt_l && !pa->size_explicit
d527d2dd 2224 && user_l - user_f > (cxt->grain / fdisk_get_sector_size(cxt))) {
68fe4b28
KZ
2225 user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l);
2226 if (user_l > user_f)
0a7cdf80 2227 user_l -= 1ULL;
68fe4b28 2228 }
77d6a70a
KZ
2229 } else {
2230 for (;;) {
2231 if (!ask)
2232 ask = fdisk_new_ask();
2233 else
2234 fdisk_reset_ask(ask);
7c43fd23
KZ
2235 if (!ask)
2236 return -ENOMEM;
77d6a70a
KZ
2237
2238 fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
2239 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
2240 fdisk_ask_number_set_low(ask, user_f); /* minimal */
2241 fdisk_ask_number_set_default(ask, dflt_l); /* default */
2242 fdisk_ask_number_set_high(ask, dflt_l); /* maximal */
2243 fdisk_ask_number_set_base(ask, user_f); /* base for relative input */
2244 fdisk_ask_number_set_unit(ask, cxt->sector_size);
2245
2246 rc = fdisk_do_ask(cxt, ask);
2247 if (rc)
2248 goto done;
2249
2250 user_l = fdisk_ask_number_get_result(ask);
1240f549 2251 if (fdisk_ask_number_is_relative(ask)) {
765004f3
KZ
2252 user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l);
2253 if (user_l > user_f)
0a7cdf80 2254 user_l -= 1ULL;
0c344037 2255 }
18b266ce 2256
765004f3 2257 if (user_l >= user_f && user_l <= disk_l)
77d6a70a 2258 break;
8c73e509
KZ
2259
2260 fdisk_warnx(cxt, _("Value out of range."));
77d6a70a 2261 }
766d5156
DB
2262 }
2263
8d95e7e0
KZ
2264
2265 if (user_f > user_l || partnum >= cxt->label->nparts_max) {
27aadd8b 2266 fdisk_warnx(cxt, _("Could not create partition %zu"), partnum + 1);
8d95e7e0 2267 rc = -EINVAL;
1240f549 2268 goto done;
8d95e7e0
KZ
2269 }
2270
3fd1f771 2271 /* Be paranoid and check against on-disk setting rather than against libfdisk cxt */
9d9a1b87
KZ
2272 if (user_l > le64_to_cpu(pheader->last_usable_lba)) {
2273 fdisk_warnx(cxt, _("The last usable GPT sector is %ju, but %ju is requested."),
2274 le64_to_cpu(pheader->last_usable_lba), user_l);
2275 rc = -EINVAL;
2276 goto done;
2277 }
2278
2279 if (user_f < le64_to_cpu(pheader->first_usable_lba)) {
2280 fdisk_warnx(cxt, _("The first usable GPT sector is %ju, but %ju is requested."),
2281 le64_to_cpu(pheader->first_usable_lba), user_f);
2282 rc = -EINVAL;
2283 goto done;
2284 }
2285
ecf40cda
KZ
2286 assert(!FDISK_IS_UNDEF(user_l));
2287 assert(!FDISK_IS_UNDEF(user_f));
2288
8d95e7e0
KZ
2289 e = &ents[partnum];
2290 e->lba_end = cpu_to_le64(user_l);
2291 e->lba_start = cpu_to_le64(user_f);
2292
2293 gpt_entry_set_type(e, &typeid);
2294
2295 if (pa && pa->uuid) {
2296 /* Sometimes it's necessary to create a copy of the PT and
2297 * reuse already defined UUID
2298 */
2299 rc = gpt_entry_set_uuid(e, pa->uuid);
2300 if (rc)
2301 goto done;
1240f549 2302 } else {
8d95e7e0
KZ
2303 /* Any time a new partition entry is created a new GUID must be
2304 * generated for that partition, and every partition is guaranteed
2305 * to have a unique GUID.
2306 */
2307 uuid_generate_random((unsigned char *) &e->partition_guid);
2308 swap_efi_guid(&e->partition_guid);
2309 }
2310
2311 if (pa && pa->name && *pa->name)
2312 gpt_entry_set_name(e, pa->name);
c77ba531
KZ
2313 if (pa && pa->attrs)
2314 gpt_entry_attrs_from_string(cxt, e, pa->attrs);
8d95e7e0 2315
fdbd7bb9 2316 DBG(LABEL, ul_debug("GPT new partition: partno=%zu, start=%"PRIu64", end=%"PRIu64", size=%"PRIu64"",
ee50336c
KZ
2317 partnum,
2318 gpt_partition_start(e),
2319 gpt_partition_end(e),
2320 gpt_partition_size(e)));
2321
8d95e7e0
KZ
2322 gpt_recompute_crc(gpt->pheader, ents);
2323 gpt_recompute_crc(gpt->bheader, ents);
2324
2325 /* report result */
2326 {
a01b5b70
KZ
2327 struct fdisk_parttype *t;
2328
9ffeb235
KZ
2329 cxt->label->nparts_cur++;
2330 fdisk_label_set_changed(cxt->label, 1);
a01b5b70 2331
7f539277 2332 t = gpt_partition_parttype(cxt, &ents[partnum]);
a01b5b70 2333 fdisk_info_new_partition(cxt, partnum + 1, user_f, user_l, t);
dfc6db2a 2334 fdisk_unref_parttype(t);
9fcd49d5 2335 }
8254c3a5 2336
4114da08 2337 rc = 0;
c3bc7483
KZ
2338 if (partno)
2339 *partno = partnum;
4114da08 2340done:
a3d83488 2341 fdisk_unref_ask(ask);
4114da08 2342 return rc;
766d5156
DB
2343}
2344
3f731001
DB
2345/*
2346 * Create a new GPT disklabel - destroys any previous data.
2347 */
9ffeb235 2348static int gpt_create_disklabel(struct fdisk_context *cxt)
3f731001
DB
2349{
2350 int rc = 0;
46667ba4 2351 ssize_t esz = 0;
21fe3dde 2352 char str[37];
9ffeb235
KZ
2353 struct fdisk_gpt_label *gpt;
2354
2355 assert(cxt);
2356 assert(cxt->label);
aa36c2cf 2357 assert(fdisk_is_label(cxt, GPT));
9ffeb235
KZ
2358
2359 gpt = self_label(cxt);
3f731001 2360
d71ef5a4 2361 /* label private stuff has to be empty, see gpt_deinit() */
d71ef5a4
KZ
2362 assert(gpt->pheader == NULL);
2363 assert(gpt->bheader == NULL);
4e0e8253 2364
3f731001 2365 /*
3f731001
DB
2366 * When no header, entries or pmbr is set, we're probably
2367 * dealing with a new, empty disk - so always allocate memory
2368 * to deal with the data structures whatever the case is.
2369 */
3f731001
DB
2370 rc = gpt_mknew_pmbr(cxt);
2371 if (rc < 0)
2372 goto done;
2373
4bb82a45
KZ
2374 assert(cxt->sector_size >= sizeof(struct gpt_header));
2375
d71ef5a4 2376 /* primary */
4bb82a45 2377 gpt->pheader = calloc(1, cxt->sector_size);
46667ba4
KZ
2378 if (!gpt->pheader) {
2379 rc = -ENOMEM;
2380 goto done;
2381 }
d71ef5a4 2382 rc = gpt_mknew_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA);
3f731001
DB
2383 if (rc < 0)
2384 goto done;
2385
d71ef5a4 2386 /* backup ("copy" primary) */
4bb82a45 2387 gpt->bheader = calloc(1, cxt->sector_size);
46667ba4
KZ
2388 if (!gpt->bheader) {
2389 rc = -ENOMEM;
2390 goto done;
2391 }
d71ef5a4
KZ
2392 rc = gpt_mknew_header_from_bkp(cxt, gpt->bheader,
2393 last_lba(cxt), gpt->pheader);
3f731001
DB
2394 if (rc < 0)
2395 goto done;
2396
46667ba4
KZ
2397 esz = le32_to_cpu(gpt->pheader->npartition_entries) *
2398 le32_to_cpu(gpt->pheader->sizeof_partition_entry);
2399 gpt->ents = calloc(1, esz);
2400 if (!gpt->ents) {
2401 rc = -ENOMEM;
2402 goto done;
2403 }
d71ef5a4
KZ
2404 gpt_recompute_crc(gpt->pheader, gpt->ents);
2405 gpt_recompute_crc(gpt->bheader, gpt->ents);
3f731001 2406
9ffeb235
KZ
2407 cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
2408 cxt->label->nparts_cur = 0;
9fcd49d5 2409
21fe3dde 2410 guid_to_string(&gpt->pheader->disk_guid, str);
9ffeb235 2411 fdisk_label_set_changed(cxt->label, 1);
0477369a 2412 fdisk_info(cxt, _("Created a new GPT disklabel (GUID: %s)."), str);
3f731001
DB
2413done:
2414 return rc;
2415}
2416
35b1f0a4
KZ
2417static int gpt_set_disklabel_id(struct fdisk_context *cxt)
2418{
2419 struct fdisk_gpt_label *gpt;
2420 struct gpt_guid uuid;
2421 char *str, *old, *new;
2422 int rc;
2423
2424 assert(cxt);
2425 assert(cxt->label);
aa36c2cf 2426 assert(fdisk_is_label(cxt, GPT));
35b1f0a4
KZ
2427
2428 gpt = self_label(cxt);
2429 if (fdisk_ask_string(cxt,
2430 _("Enter new disk UUID (in 8-4-4-4-12 format)"), &str))
2431 return -EINVAL;
2432
2433 rc = string_to_guid(str, &uuid);
2434 free(str);
2435
2436 if (rc) {
2437 fdisk_warnx(cxt, _("Failed to parse your UUID."));
2438 return rc;
2439 }
2440
5989556a 2441 old = gpt_get_header_id(gpt->pheader);
35b1f0a4
KZ
2442
2443 gpt->pheader->disk_guid = uuid;
2444 gpt->bheader->disk_guid = uuid;
2445
2446 gpt_recompute_crc(gpt->pheader, gpt->ents);
2447 gpt_recompute_crc(gpt->bheader, gpt->ents);
2448
5989556a 2449 new = gpt_get_header_id(gpt->pheader);
35b1f0a4 2450
0477369a 2451 fdisk_info(cxt, _("Disk identifier changed from %s to %s."), old, new);
35b1f0a4
KZ
2452
2453 free(old);
2454 free(new);
2455 fdisk_label_set_changed(cxt->label, 1);
2456 return 0;
2457}
2458
a18e726c
SP
2459static int gpt_check_table_overlap(struct fdisk_context *cxt,
2460 uint64_t first_usable,
2461 uint64_t last_usable)
2462{
2463 struct fdisk_gpt_label *gpt = self_label(cxt);
2464 unsigned int i;
2465 int rc = 0;
2466
2467 /* First check if there's enough room for the table. last_lba may have wrapped */
2468 if (first_usable > cxt->total_sectors || /* far too little space */
2469 last_usable > cxt->total_sectors || /* wrapped */
2470 first_usable > last_usable) { /* too little space */
2471 fdisk_warnx(cxt, _("Not enough space for new partition table!"));
2472 return -ENOSPC;
2473 }
2474
2475 /* check that all partitions fit in the remaining space */
2476 for (i = 0; i < le32_to_cpu(gpt->pheader->npartition_entries); i++) {
2477 if (partition_unused(&gpt->ents[i]))
2478 continue;
2479 if (gpt_partition_start(&gpt->ents[i]) < first_usable) {
2480 fdisk_warnx(cxt, _("Partition #%u out of range (minimal start is %"PRIu64" sectors)"),
2481 i + 1, first_usable);
2482 rc = -EINVAL;
2483 }
2484 if (gpt_partition_end(&gpt->ents[i]) > last_usable) {
2485 fdisk_warnx(cxt, _("Partition #%u out of range (maximal end is %"PRIu64" sectors)"),
0a7cdf80 2486 i + 1, last_usable - 1ULL);
a18e726c
SP
2487 rc = -EINVAL;
2488 }
2489 }
2490 return rc;
2491}
2492
f88eeb25
KZ
2493/**
2494 * fdisk_gpt_set_npartitions:
2495 * @cxt: context
81b176c4 2496 * @entries: new size
f88eeb25
KZ
2497 *
2498 * Elarge GPT entries array if possible. The function check if an existing
2499 * partition does not overlap the entries array area. If yes, then it report
2500 * warning and returns -EINVAL.
2501 *
2502 * Returns: 0 on success, < 0 on error.
81b176c4 2503 * Since: 2.29
f88eeb25 2504 */
81b176c4 2505int fdisk_gpt_set_npartitions(struct fdisk_context *cxt, uint32_t entries)
a18e726c
SP
2506{
2507 struct fdisk_gpt_label *gpt;
2508 size_t old_size, new_size;
f88eeb25 2509 uint32_t old;
a18e726c
SP
2510 struct gpt_entry *ents;
2511 uint64_t first_usable, last_usable;
2512 int rc;
2513
2514 assert(cxt);
2515 assert(cxt->label);
2516 assert(fdisk_is_label(cxt, GPT));
2517
2518 gpt = self_label(cxt);
2519
2520 old = le32_to_cpu(gpt->pheader->npartition_entries);
81b176c4 2521 if (old == entries)
a67054f9 2522 return 0; /* do nothing, say nothing */
a18e726c
SP
2523
2524 /* calculate the size (bytes) of the entries array */
81b176c4 2525 new_size = entries * le32_to_cpu(gpt->pheader->sizeof_partition_entry);
a18e726c
SP
2526 old_size = old * le32_to_cpu(gpt->pheader->sizeof_partition_entry);
2527
2528 /* calculate new range of usable LBAs */
f88eeb25
KZ
2529 first_usable = (uint64_t) (new_size / cxt->sector_size) + 2ULL;
2530 last_usable = cxt->total_sectors - 2ULL - (uint64_t) (new_size / cxt->sector_size);
a18e726c
SP
2531
2532 /* if expanding the table, first check that everything fits,
2533 * then allocate more memory and zero. */
81b176c4 2534 if (entries > old) {
a18e726c
SP
2535 rc = gpt_check_table_overlap(cxt, first_usable, last_usable);
2536 if (rc)
2537 return rc;
2538 ents = realloc(gpt->ents, new_size);
2539 if (!ents) {
2540 fdisk_warnx(cxt, _("Cannot allocate memory!"));
2541 return -ENOMEM;
2542 }
2543 memset(ents + old, 0, new_size - old_size);
2544 gpt->ents = ents;
2545 }
2546
2547 /* everything's ok, apply the new size */
81b176c4
KZ
2548 gpt->pheader->npartition_entries = cpu_to_le32(entries);
2549 gpt->bheader->npartition_entries = cpu_to_le32(entries);
a18e726c
SP
2550
2551 /* usable LBA addresses will have changed */
2552 fdisk_set_first_lba(cxt, first_usable);
2553 fdisk_set_last_lba(cxt, last_usable);
2554 gpt->pheader->first_usable_lba = cpu_to_le64(first_usable);
2555 gpt->bheader->first_usable_lba = cpu_to_le64(first_usable);
2556 gpt->pheader->last_usable_lba = cpu_to_le64(last_usable);
2557 gpt->bheader->last_usable_lba = cpu_to_le64(last_usable);
2558
2559
2560 /* The backup header must be recalculated */
2561 gpt_mknew_header_common(cxt, gpt->bheader, le64_to_cpu(gpt->pheader->alternative_lba));
2562
2563 /* CRCs will have changed */
2564 gpt_recompute_crc(gpt->pheader, gpt->ents);
2565 gpt_recompute_crc(gpt->bheader, gpt->ents);
2566
81b176c4 2567 fdisk_info(cxt, _("Partition table length changed from %"PRIu32" to %"PRIu64"."), old, entries);
a18e726c
SP
2568
2569 fdisk_label_set_changed(cxt->label, 1);
2570 return 0;
2571}
2572
8c0a7f91 2573static int gpt_part_is_used(struct fdisk_context *cxt, size_t i)
47b8e7c0 2574{
9ffeb235 2575 struct fdisk_gpt_label *gpt;
47b8e7c0
KZ
2576 struct gpt_entry *e;
2577
9ffeb235
KZ
2578 assert(cxt);
2579 assert(cxt->label);
aa36c2cf 2580 assert(fdisk_is_label(cxt, GPT));
9ffeb235
KZ
2581
2582 gpt = self_label(cxt);
2583
8c0a7f91
KZ
2584 if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
2585 return 0;
47b8e7c0 2586 e = &gpt->ents[i];
47b8e7c0 2587
8c0a7f91 2588 return !partition_unused(e) || gpt_partition_start(e);
47b8e7c0
KZ
2589}
2590
0077e7cd
KZ
2591/**
2592 * fdisk_gpt_is_hybrid:
2593 * @cxt: context
2594 *
2595 * The regular GPT contains PMBR (dummy protective MBR) where the protective
2596 * MBR does not address any partitions.
2597 *
2598 * Hybrid GPT contains regular MBR where this partition table addresses the
2599 * same partitions as GPT. It's recommended to not use hybrid GPT due to MBR
2600 * limits.
2601 *
2602 * The libfdisk does not provide functionality to sync GPT and MBR, you have to
2603 * directly access and modify (P)MBR (see fdisk_new_nested_context()).
2604 *
2605 * Returns: 1 if partition table detected as hybrid otherwise return 0
2606 */
433d05ff
KZ
2607int fdisk_gpt_is_hybrid(struct fdisk_context *cxt)
2608{
2609 assert(cxt);
2610 return valid_pmbr(cxt) == GPT_MBR_HYBRID;
2611}
2612
4a4a0927
MM
2613/**
2614 * fdisk_gpt_get_partition_attrs:
2615 * @cxt: context
2616 * @partnum: partition number
2617 * @attrs: GPT partition attributes
2618 *
2619 * Sets @attrs for the given partition
2620 *
2621 * Returns: 0 on success, <0 on error.
2622 */
2623int fdisk_gpt_get_partition_attrs(
2624 struct fdisk_context *cxt,
2625 size_t partnum,
2626 uint64_t *attrs)
2627{
2628 struct fdisk_gpt_label *gpt;
2629
2630 assert(cxt);
2631 assert(cxt->label);
2632 assert(fdisk_is_label(cxt, GPT));
2633
2634 gpt = self_label(cxt);
2635
2636 if ((uint32_t) partnum >= le32_to_cpu(gpt->pheader->npartition_entries))
2637 return -EINVAL;
2638
2639 *attrs = le64_to_cpu(gpt->ents[partnum].attrs);
2640 return 0;
2641}
2642
2643/**
2644 * fdisk_gpt_set_partition_attrs:
2645 * @cxt: context
2646 * @partnum: partition number
2647 * @attrs: GPT partition attributes
2648 *
2649 * Sets the GPT partition attributes field to @attrs.
2650 *
2651 * Returns: 0 on success, <0 on error.
2652 */
2653int fdisk_gpt_set_partition_attrs(
2654 struct fdisk_context *cxt,
2655 size_t partnum,
2656 uint64_t attrs)
2657{
2658 struct fdisk_gpt_label *gpt;
2659
2660 assert(cxt);
2661 assert(cxt->label);
2662 assert(fdisk_is_label(cxt, GPT));
2663
2664 DBG(LABEL, ul_debug("GPT entry attributes change requested partno=%zu", partnum));
2665 gpt = self_label(cxt);
2666
2667 if ((uint32_t) partnum >= le32_to_cpu(gpt->pheader->npartition_entries))
2668 return -EINVAL;
2669
2670 gpt->ents[partnum].attrs = cpu_to_le64(attrs);
2671 fdisk_info(cxt, _("The attributes on partition %zu changed to 0x%016" PRIx64 "."),
2672 partnum + 1, attrs);
2673
2674 gpt_recompute_crc(gpt->pheader, gpt->ents);
2675 gpt_recompute_crc(gpt->bheader, gpt->ents);
2676 fdisk_label_set_changed(cxt->label, 1);
2677 return 0;
2678}
2679
c83f772e
KZ
2680static int gpt_toggle_partition_flag(
2681 struct fdisk_context *cxt,
2682 size_t i,
2683 unsigned long flag)
2684{
2685 struct fdisk_gpt_label *gpt;
262c94c2
RM
2686 uint64_t attrs;
2687 uintmax_t tmp;
01086b80
KZ
2688 char *bits;
2689 const char *name = NULL;
2690 int bit = -1, rc;
c83f772e
KZ
2691
2692 assert(cxt);
2693 assert(cxt->label);
aa36c2cf 2694 assert(fdisk_is_label(cxt, GPT));
c83f772e 2695
88141067 2696 DBG(LABEL, ul_debug("GPT entry attribute change requested partno=%zu", i));
c83f772e
KZ
2697 gpt = self_label(cxt);
2698
2699 if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
2700 return -EINVAL;
2701
159652d9 2702 attrs = gpt->ents[i].attrs;
01086b80 2703 bits = (char *) &attrs;
c83f772e
KZ
2704
2705 switch (flag) {
2706 case GPT_FLAG_REQUIRED:
01086b80
KZ
2707 bit = GPT_ATTRBIT_REQ;
2708 name = GPT_ATTRSTR_REQ;
c83f772e
KZ
2709 break;
2710 case GPT_FLAG_NOBLOCK:
01086b80
KZ
2711 bit = GPT_ATTRBIT_NOBLOCK;
2712 name = GPT_ATTRSTR_NOBLOCK;
c83f772e
KZ
2713 break;
2714 case GPT_FLAG_LEGACYBOOT:
01086b80
KZ
2715 bit = GPT_ATTRBIT_LEGACY;
2716 name = GPT_ATTRSTR_LEGACY;
c83f772e
KZ
2717 break;
2718 case GPT_FLAG_GUIDSPECIFIC:
01086b80 2719 rc = fdisk_ask_number(cxt, 48, 48, 63, _("Enter GUID specific bit"), &tmp);
c83f772e
KZ
2720 if (rc)
2721 return rc;
01086b80
KZ
2722 bit = tmp;
2723 break;
773aae5c
KZ
2724 default:
2725 /* already specified PT_FLAG_GUIDSPECIFIC bit */
2726 if (flag >= 48 && flag <= 63) {
2727 bit = flag;
2728 flag = GPT_FLAG_GUIDSPECIFIC;
2729 }
2730 break;
01086b80 2731 }
c83f772e 2732
773aae5c
KZ
2733 if (bit < 0) {
2734 fdisk_warnx(cxt, _("failed to toggle unsupported bit %lu"), flag);
01086b80 2735 return -EINVAL;
773aae5c 2736 }
01086b80
KZ
2737
2738 if (!isset(bits, bit))
2739 setbit(bits, bit);
2740 else
2741 clrbit(bits, bit);
2742
159652d9 2743 gpt->ents[i].attrs = attrs;
01086b80
KZ
2744
2745 if (flag == GPT_FLAG_GUIDSPECIFIC)
0477369a 2746 fdisk_info(cxt, isset(bits, bit) ?
01086b80
KZ
2747 _("The GUID specific bit %d on partition %zu is enabled now.") :
2748 _("The GUID specific bit %d on partition %zu is disabled now."),
c83f772e 2749 bit, i + 1);
01086b80 2750 else
0477369a 2751 fdisk_info(cxt, isset(bits, bit) ?
01086b80
KZ
2752 _("The %s flag on partition %zu is enabled now.") :
2753 _("The %s flag on partition %zu is disabled now."),
2754 name, i + 1);
c83f772e
KZ
2755
2756 gpt_recompute_crc(gpt->pheader, gpt->ents);
2757 gpt_recompute_crc(gpt->bheader, gpt->ents);
01086b80 2758 fdisk_label_set_changed(cxt->label, 1);
c83f772e
KZ
2759 return 0;
2760}
1054699c 2761
9348ef25
KZ
2762static int gpt_entry_cmp_start(const void *a, const void *b)
2763{
2764 struct gpt_entry *ae = (struct gpt_entry *) a,
2765 *be = (struct gpt_entry *) b;
2766 int au = partition_unused(ae),
2767 bu = partition_unused(be);
2768
2769 if (au && bu)
2770 return 0;
2771 if (au)
2772 return 1;
2773 if (bu)
2774 return -1;
2775
19ff8ff7 2776 return cmp_numbers(gpt_partition_start(ae), gpt_partition_start(be));
9348ef25
KZ
2777}
2778
2779/* sort partition by start sector */
2780static int gpt_reorder(struct fdisk_context *cxt)
2781{
2782 struct fdisk_gpt_label *gpt;
dd49c7d6 2783 size_t i, nparts, mess;
9348ef25
KZ
2784
2785 assert(cxt);
2786 assert(cxt->label);
aa36c2cf 2787 assert(fdisk_is_label(cxt, GPT));
9348ef25
KZ
2788
2789 gpt = self_label(cxt);
2790 nparts = le32_to_cpu(gpt->pheader->npartition_entries);
2791
dd49c7d6
KZ
2792 for (i = 0, mess = 0; mess == 0 && i + 1 < nparts; i++)
2793 mess = gpt_entry_cmp_start(
2794 (const void *) &gpt->ents[i],
2795 (const void *) &gpt->ents[i + 1]) > 0;
2796
2797 if (!mess) {
2798 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
2799 return 1;
2800 }
2801
9348ef25
KZ
2802 qsort(gpt->ents, nparts, sizeof(struct gpt_entry),
2803 gpt_entry_cmp_start);
2804
2805 gpt_recompute_crc(gpt->pheader, gpt->ents);
2806 gpt_recompute_crc(gpt->bheader, gpt->ents);
2807 fdisk_label_set_changed(cxt->label, 1);
2808
9348ef25
KZ
2809 return 0;
2810}
2811
1240f549
KZ
2812static int gpt_reset_alignment(struct fdisk_context *cxt)
2813{
2814 struct fdisk_gpt_label *gpt;
2815 struct gpt_header *h;
2816
2817 assert(cxt);
2818 assert(cxt->label);
aa36c2cf 2819 assert(fdisk_is_label(cxt, GPT));
1240f549
KZ
2820
2821 gpt = self_label(cxt);
2822 h = gpt ? gpt->pheader : NULL;
2823
2824 if (h) {
2825 /* always follow existing table */
43a2b094
KZ
2826 cxt->first_lba = le64_to_cpu(h->first_usable_lba);
2827 cxt->last_lba = le64_to_cpu(h->last_usable_lba);
1240f549
KZ
2828 } else {
2829 /* estimate ranges for GPT */
2830 uint64_t first, last;
2831
2832 count_first_last_lba(cxt, &first, &last);
2833
2834 if (cxt->first_lba < first)
2835 cxt->first_lba = first;
2836 if (cxt->last_lba > last)
2837 cxt->last_lba = last;
2838 }
2839
2840 return 0;
2841}
4e0e8253
KZ
2842/*
2843 * Deinitialize fdisk-specific variables
2844 */
d71ef5a4 2845static void gpt_deinit(struct fdisk_label *lb)
4e0e8253 2846{
d71ef5a4
KZ
2847 struct fdisk_gpt_label *gpt = (struct fdisk_gpt_label *) lb;
2848
2849 if (!gpt)
2850 return;
2851
2852 free(gpt->ents);
2853 free(gpt->pheader);
2854 free(gpt->bheader);
2855
2856 gpt->ents = NULL;
2857 gpt->pheader = NULL;
2858 gpt->bheader = NULL;
4e0e8253
KZ
2859}
2860
0c5d095e 2861static const struct fdisk_label_operations gpt_operations =
766d5156 2862{
0c5d095e
KZ
2863 .probe = gpt_probe_label,
2864 .write = gpt_write_disklabel,
2865 .verify = gpt_verify_disklabel,
2866 .create = gpt_create_disklabel,
775001ad 2867 .locate = gpt_locate_disklabel,
5989556a 2868 .get_item = gpt_get_disklabel_item,
35b1f0a4 2869 .set_id = gpt_set_disklabel_id,
21fe3dde 2870
8c0a7f91 2871 .get_part = gpt_get_partition,
b0a484a8 2872 .set_part = gpt_set_partition,
77d6a70a 2873 .add_part = gpt_add_partition,
e11c6684 2874 .del_part = gpt_delete_partition,
5989556a 2875 .reorder = gpt_reorder,
8c0a7f91
KZ
2876
2877 .part_is_used = gpt_part_is_used,
c83f772e 2878 .part_toggle_flag = gpt_toggle_partition_flag,
4e0e8253 2879
1240f549
KZ
2880 .deinit = gpt_deinit,
2881
2882 .reset_alignment = gpt_reset_alignment
766d5156 2883};
0c5d095e 2884
bd85d11f 2885static const struct fdisk_field gpt_fields[] =
6941952e
KZ
2886{
2887 /* basic */
bd85d11f
KZ
2888 { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
2889 { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
2890 { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
2891 { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
bd85d11f
KZ
2892 { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2893 { FDISK_FIELD_TYPE, N_("Type"), 0.1, FDISK_FIELDFL_EYECANDY },
6941952e 2894 /* expert */
bd85d11f
KZ
2895 { FDISK_FIELD_TYPEID, N_("Type-UUID"), 36, FDISK_FIELDFL_DETAIL },
2896 { FDISK_FIELD_UUID, N_("UUID"), 36, FDISK_FIELDFL_DETAIL },
2897 { FDISK_FIELD_NAME, N_("Name"), 0.2, FDISK_FIELDFL_DETAIL },
2898 { FDISK_FIELD_ATTR, N_("Attrs"), 0, FDISK_FIELDFL_DETAIL }
6941952e
KZ
2899};
2900
0c5d095e
KZ
2901/*
2902 * allocates GPT in-memory stuff
2903 */
2904struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt)
2905{
2906 struct fdisk_label *lb;
2907 struct fdisk_gpt_label *gpt;
2908
2909 assert(cxt);
2910
2911 gpt = calloc(1, sizeof(*gpt));
2912 if (!gpt)
2913 return NULL;
2914
2915 /* initialize generic part of the driver */
2916 lb = (struct fdisk_label *) gpt;
2917 lb->name = "gpt";
53b422ab 2918 lb->id = FDISK_DISKLABEL_GPT;
0c5d095e
KZ
2919 lb->op = &gpt_operations;
2920 lb->parttypes = gpt_parttypes;
2921 lb->nparttypes = ARRAY_SIZE(gpt_parttypes);
2922
bd85d11f
KZ
2923 lb->fields = gpt_fields;
2924 lb->nfields = ARRAY_SIZE(gpt_fields);
6941952e 2925
0c5d095e
KZ
2926 return lb;
2927}
4a4a0927
MM
2928
2929#ifdef TEST_PROGRAM
5fde1d9f 2930static int test_getattr(struct fdisk_test *ts, int argc, char *argv[])
4a4a0927
MM
2931{
2932 const char *disk = argv[1];
2933 size_t part = strtoul(argv[2], NULL, 0) - 1;
2934 struct fdisk_context *cxt;
2935 uint64_t atters = 0;
2936
2937 cxt = fdisk_new_context();
2938 fdisk_assign_device(cxt, disk, 1);
2939
2940 if (!fdisk_is_label(cxt, GPT))
2941 return EXIT_FAILURE;
2942
2943 if (fdisk_gpt_get_partition_attrs(cxt, part, &atters))
2944 return EXIT_FAILURE;
2945
2946 printf("%s: 0x%016" PRIx64 "\n", argv[2], atters);
2947
2948 fdisk_unref_context(cxt);
2949 return 0;
2950}
2951
5fde1d9f 2952static int test_setattr(struct fdisk_test *ts, int argc, char *argv[])
4a4a0927
MM
2953{
2954 const char *disk = argv[1];
2955 size_t part = strtoul(argv[2], NULL, 0) - 1;
2956 uint64_t atters = strtoull(argv[3], NULL, 0);
2957 struct fdisk_context *cxt;
2958
2959 cxt = fdisk_new_context();
2960 fdisk_assign_device(cxt, disk, 0);
2961
2962 if (!fdisk_is_label(cxt, GPT))
2963 return EXIT_FAILURE;
2964
2965 if (fdisk_gpt_set_partition_attrs(cxt, part, atters))
2966 return EXIT_FAILURE;
2967
2968 if (fdisk_write_disklabel(cxt))
2969 return EXIT_FAILURE;
2970
2971 fdisk_unref_context(cxt);
2972 return 0;
2973}
2974
2975int main(int argc, char *argv[])
2976{
2977 struct fdisk_test tss[] = {
2978 { "--getattr", test_getattr, "<disk> <partition> print attributes" },
2979 { "--setattr", test_setattr, "<disk> <partition> <value> set attributes" },
2980 { NULL }
2981 };
2982
2983 return fdisk_run_test(tss, argc, argv);
2984}
2985
2986#endif