Check for SYS/GL during library init. Reason is that
[AROS.git] / rom / partition / partitiongpt.c
blob01ae8daed85c77a741a6e2ae58cf506aa5b2bd66
1 /*
2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: GPT partition table handler
6 */
8 /*
9 #undef DEBUG
10 #define DEBUG 1
11 #define DEBUG_UUID
14 #define DREAD(x)
15 #define DWRITE(x)
18 * Note that we use KPrintF() for debugging in some places.
19 * KPrintF() uses RawDoFmt() for formatting, which (if patched by locale.library)
20 * correctly supports %llu, unlike kprintf().
21 * This will change when i386 port gets kernel.resource. After this kprintf()
22 * will be moved to libdebug.a and rewritten to simply call KrnBug().
25 #include <exec/memory.h>
26 #include <libraries/partition.h>
27 #include <proto/debug.h>
28 #include <proto/exec.h>
29 #include <proto/partition.h>
30 #include <proto/utility.h>
32 #include "partition_support.h"
33 #include "partition_types.h"
34 #include "partitiongpt.h"
35 #include "partitionmbr.h"
36 #include "platform.h"
37 #include "debug.h"
39 /* Some error code that collides with neither trackdisk.device not dos.library error codes */
40 #define ERROR_BAD_CRC 255
42 struct GPTPartitionHandle
44 struct PartitionHandle ph; /* Public part */
45 ULONG entrySize; /* Size of table entry */
46 char name[36]; /* Name in ASCII */
47 /* Actual table entry follows */
50 #define GPTH(ph) ((struct GPTPartitionHandle *)ph)
52 static const uuid_t GPT_Type_Unused = MAKE_UUID(0x00000000, 0x0000, 0x0000, 0x0000, 0x000000000000ULL);
54 * This is a bit special.
55 * The first four bytes (time_low) hold DOS Type ID (for simple mapping),
56 * so we set them to zero here. We ignore it during comparison.
57 * I hope this won't create any significant problems. Even if some ID ever collides, it will
58 * unlikely collide with existing DOSTypes being used, so it can be blacklisted then.
60 static const uuid_t GPT_Type_AROS = MAKE_UUID(0x00000000, 0xBB67, 0x46C5, 0xAA4A, 0xF502CA018E5EULL);
64 * UTF16-LE conversion.
65 * Currently these are very basic routines which handle only Latin-1 character set.
66 * If needed, conversion can be performed using codesets.library (but don't forget
67 * that you can run early during system bootup and codesets.library won't be
68 * available by that time).
71 static void FromUTF16(char *to, char *from, ULONG len)
73 ULONG i;
75 for (i = 0; i < len; i++)
77 /* Currently we know only 7-bit ASCII characters */
78 *to++ = from[0];
80 if (!from[0])
81 return;
83 from += 2;
87 static void ToUTF16(char *to, char *from, ULONG len)
89 ULONG i;
91 for (i = 0; i < len; i++)
93 /* Currently we know only 7-bit ASCII characters */
94 *to++ = *from;
95 *to++ = 0;
97 if (!*from++)
98 return;
103 * Little-endian UUID conversion and comparison.
104 * We can't use uuid.library here because it's not available during
105 * system bootup. However, we are going to use it for generation.
107 static inline void uuid_from_le(uuid_t *to, uuid_t *id)
109 to->time_low = AROS_LE2LONG(id->time_low);
110 to->time_mid = AROS_LE2WORD(id->time_mid);
111 to->time_hi_and_version = AROS_LE2WORD(id->time_hi_and_version);
113 /* Do not replace it with CopyMem(), gcc optimizes this nicely */
114 memcpy(&to->clock_seq_hi_and_reserved, &id->clock_seq_hi_and_reserved, 8);
117 static inline void uuid_to_le(uuid_t *to, uuid_t *id)
119 to->time_low = AROS_LONG2LE(id->time_low);
120 to->time_mid = AROS_WORD2LE(id->time_mid);
121 to->time_hi_and_version = AROS_WORD2LE(id->time_hi_and_version);
123 /* Do not replace it with CopyMem(), gcc optimizes this nicely */
124 memcpy(&to->clock_seq_hi_and_reserved, &id->clock_seq_hi_and_reserved, 8);
127 static inline BOOL uuid_cmp_le(uuid_t *leid, const uuid_t *id)
129 if (AROS_LE2LONG(leid->time_low) != id->time_low)
130 return FALSE;
131 if (AROS_LE2WORD(leid->time_mid) != id->time_mid)
132 return FALSE;
133 if (AROS_LE2WORD(leid->time_hi_and_version) != id->time_hi_and_version)
134 return FALSE;
136 return !memcmp(&leid->clock_seq_hi_and_reserved, &id->clock_seq_hi_and_reserved, 8);
139 /* For AROS we put DOS Type ID into first four bytes of UUID (time_low), so we ignore them. */
140 static inline BOOL is_aros_uuid_le(uuid_t *leid)
142 if (AROS_LE2WORD(leid->time_mid) != GPT_Type_AROS.time_mid)
143 return 0;
144 if (AROS_LE2WORD(leid->time_hi_and_version) != GPT_Type_AROS.time_hi_and_version)
145 return 0;
147 return !memcmp(&leid->clock_seq_hi_and_reserved, &GPT_Type_AROS.clock_seq_hi_and_reserved, 8);
150 #ifdef DEBUG_UUID
152 static void PRINT_LE_UUID(char *s, uuid_t *id)
154 unsigned int i;
156 bug("[GPT] %s UUID: 0x%08X-%04X-%04X-%02X%02X-", s,
157 AROS_LE2LONG(id->time_low), AROS_LE2WORD(id->time_mid), AROS_LE2WORD(id->time_hi_and_version),
158 id->clock_seq_hi_and_reserved, id->clock_seq_low);
160 for (i = 0; i < sizeof(id->node); i++)
161 bug("%02X", id->node[i]);
163 RawPutChar('\n');
166 #else
168 #define PRINT_LE_UUID(s, id)
170 #endif
172 #ifdef NO_WRITE
173 #undef WritePartitionDataQ
174 #define WritePartitionDataQ(root, table, tablesize, blk) TDERR_WriteProt
175 #define PartitionWriteBlock(base, root, blk, mem) TDERR_WriteProt
176 #endif
177 #ifdef SIM_WRITE
178 #undef WritePartitionDataQ
179 #define WritePartitionDataQ(root, table, tablesize, blk) 0
180 #define PartitionWriteBlock(base, root, blk, mem) 0
181 #endif
183 static void GPT_PatchDosEnvec(struct DosEnvec *de, struct GPTPartition *p)
185 ULONG type = 0;
186 LONG bootpri = 0;
188 if (is_aros_uuid_le(&p->TypeID))
190 type = AROS_LE2LONG(p->TypeID.time_low);
191 /* This casting is needed for proper sign expansion */
192 bootpri = (BYTE)(AROS_LE2LONG(p->Flags1) & GPT_PF1_AROS_BOOTPRI);
194 else
196 const struct TypeMapping *m;
198 for (m = PartTypes; m->DOSType; m++)
200 if (m->uuid && uuid_cmp_le(&p->TypeID, m->uuid))
202 type = m->DOSType;
203 break;
208 setDosType(de, type);
209 de->de_BootPri = bootpri;
212 static LONG GPTCheckHeader(struct Library *PartitionBase, struct PartitionHandle *root, struct GPTHeader *hdr, UQUAD block)
214 /* Load the GPT header */
215 if (!readBlock(PartitionBase, root, block, hdr))
217 ULONG hdrSize = AROS_LE2LONG(hdr->HeaderSize);
218 UQUAD currentblk = AROS_LE2QUAD(hdr->CurrentBlock);
220 D(bug("[GPT] Header size: specified %u, expected %u\n", hdrSize, GPT_MIN_HEADER_SIZE));
221 DREAD(KPrintF("[GPT] Read: Header block %llu, backup block %llu\n", currentblk, AROS_LE2QUAD(hdr->BackupBlock)));
223 /* Check signature, header size, and current block number */
224 if ((!memcmp(hdr->Signature, GPT_SIGNATURE, sizeof(hdr->Signature))) &&
225 (hdrSize >= GPT_MIN_HEADER_SIZE) && (currentblk == block))
228 * Use zlib routine for CRC32.
229 * CHECKME: is it correct on bigendian machines? It should, however who knows...
231 ULONG orig_crc = AROS_LE2LONG(hdr->HeaderCRC32);
232 ULONG crc;
234 hdr->HeaderCRC32 = 0;
235 crc = Crc32_ComputeBuf(0, hdr, hdrSize);
237 D(bug("[GPT] Header CRC: calculated 0x%08X, expected 0x%08X\n", crc, orig_crc));
239 return (crc == orig_crc) ? 1 : 2;
242 return 0;
245 static LONG PartitionGPTCheckPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
247 APTR blk;
248 LONG res = 0;
250 /* GPT can be placed only in the root of the disk */
251 if (root->root)
252 return 0;
254 blk = AllocMem(root->de.de_SizeBlock << 2, MEMF_ANY);
256 if (!blk)
257 return 0;
259 /* First of all, we must have valid MBR stub */
260 if (MBRCheckPartitionTable(PartitionBase, root, blk))
262 struct PCPartitionTable *pcpt = ((struct MBR *)blk)->pcpt;
264 D(bug("[GPT] MBR check passed, first partition type 0x%02X, start block %u\n", pcpt[0].type, pcpt[0].first_sector));
266 /* We must have partition 0 of type GPT starting at block 1 */
267 if ((pcpt[0].type == MBRT_GPT) && (AROS_LE2LONG(pcpt[0].first_sector) == 1))
269 res = GPTCheckHeader(PartitionBase, root, blk, 1);
271 /* 2 is a special return code for "bad CRC" */
272 if (res == ERROR_BAD_CRC)
274 /* Try to read backup header */
275 UQUAD block = AROS_LE2QUAD(((struct GPTHeader *)blk)->BackupBlock);
277 res = GPTCheckHeader(PartitionBase, root, blk, block);
279 /* There's no third backup :( */
280 if (res == ERROR_BAD_CRC)
281 res = 0;
286 FreeMem(blk, root->de.de_SizeBlock << 2);
287 return res;
290 static LONG GPTReadPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root, struct GPTHeader *hdr, UQUAD block)
292 LONG res;
293 LONG err = ERROR_NOT_A_DOS_DISK;
295 DREAD(KPrintF("[GPT] Read: header block %llu\n", block));
296 res = GPTCheckHeader(PartitionBase, root, hdr, block);
297 if (res == 2)
298 return ERROR_BAD_CRC;
300 if (res == 1)
302 struct GPTPartition *table;
303 ULONG cnt = AROS_LE2LONG(hdr->NumEntries);
304 ULONG entrysize = AROS_LE2LONG(hdr->EntrySize);
305 ULONG tablesize = AROS_ROUNDUP2(entrysize * cnt, root->de.de_SizeBlock << 2);
306 UQUAD startblk, endblk;
308 DREAD(bug("[GPT] Read: %u entries per %u bytes, %u bytes total\n", cnt, entrysize, tablesize));
310 table = AllocMem(tablesize, MEMF_ANY);
311 if (!table)
312 return ERROR_NO_FREE_STORE;
314 startblk = AROS_LE2QUAD(hdr->StartBlock);
316 DREAD(KPrintF("[GPT] Read: start block %llu\n", startblk));
317 res = ReadPartitionDataQ(root, table, tablesize, startblk);
318 if (!res)
320 ULONG orig_crc = AROS_LE2LONG(hdr->PartCRC32);
321 ULONG crc = Crc32_ComputeBuf(0, table, entrysize * cnt);
323 D(bug("[GPT] Data CRC: calculated 0x%08X, expected 0x%08X\n", crc, orig_crc));
325 if (crc == orig_crc)
327 struct GPTPartition *p = table;
328 ULONG i;
330 DREAD(bug("[GPT] Adding partitions...\n"));
331 err = 0;
333 for (i = 0; i < cnt; i++)
335 struct GPTPartitionHandle *gph;
337 startblk = AROS_LE2QUAD(p->StartBlock);
338 endblk = AROS_LE2QUAD(p->EndBlock);
341 * Skip unused entries. NumEntries in the header holds total number of preallocated entries,
342 * not the number of used ones.
343 * Normally GPT table has 128 preallocated entries, but only first of them are used.
344 * Just in case, we allow gaps between used entries. However (tested with MacOS X Disk Utility)
345 * partition editors seem to squeeze the table and do not leave empty entries when deleting
346 * partitions in the middle of the disk.
348 if (!memcmp(&p->TypeID, &GPT_Type_Unused, sizeof(uuid_t)))
349 continue;
351 DREAD(PRINT_LE_UUID("Type ", &p->TypeID));
352 DREAD(PRINT_LE_UUID("Partition", &p->PartitionID));
353 DREAD(KPrintF("[GPT] Blocks %llu - %llu\n", startblk, endblk));
354 DREAD(KPrintF("[GPT] Flags 0x%08lX 0x%08lX\n", AROS_LE2LONG(p->Flags0), AROS_LE2LONG(p->Flags1)));
355 DREAD(KPrintF("[GPT] Offset 0x%p\n", (APTR)p - (APTR)table));
357 gph = AllocVec(sizeof(struct GPTPartitionHandle) + entrysize, MEMF_CLEAR);
358 if (gph)
360 initPartitionHandle(root, &gph->ph, startblk, endblk - startblk + 1);
362 /* Map UUID to a DOSType */
363 GPT_PatchDosEnvec(&gph->ph.de, p);
365 /* Store the whole entry and convert name into ASCII form */
366 CopyMem(p, &gph[1], entrysize);
367 FromUTF16(gph->name, p->Name, 36);
369 gph->ph.ln.ln_Name = gph->name;
370 gph->entrySize = entrysize;
372 ADDTAIL(&root->table->list, gph);
373 DREAD(bug("[GPT] Added partition %u (%s), handle 0x%p\n", i, gph->name, gph));
375 else
377 err = ERROR_NO_FREE_STORE;
378 break;
381 /* Jump to next entry, skip 'entrysize' bytes */
382 p = (APTR)p + entrysize;
385 else
386 err = ERROR_BAD_CRC;
389 FreeMem(table, tablesize);
392 return err;
395 static void PartitionGPTClosePartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
397 struct PartitionHandle *ph, *ph2;
399 /* Free all partition entries */
400 ForeachNodeSafe(&root->table->list, ph, ph2)
401 FreeVec(ph);
403 FreeMem(root->table->data, root->de.de_SizeBlock<<2);
406 static LONG PartitionGPTOpenPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
408 LONG res;
411 * The header is attached to partition table handle.
412 * This allows us to write back the complete header and keep
413 * data we don't know about in future GPT revisions.
415 root->table->data = AllocMem(root->de.de_SizeBlock << 2, MEMF_ANY);
416 if (!root->table->data)
417 return ERROR_NO_FREE_STORE;
419 /* Read primary GPT table */
420 res = GPTReadPartitionTable(PartitionBase, root, root->table->data, 1);
422 if (res == ERROR_BAD_CRC)
424 /* If CRC failed, read backup table */
425 struct GPTHeader *hdr = root->table->data;
426 UQUAD block = AROS_LE2QUAD(hdr->BackupBlock);
428 res = GPTReadPartitionTable(PartitionBase, root, hdr, block);
430 /* There's no third backup... */
431 if (res == ERROR_BAD_CRC)
432 res = ERROR_NOT_A_DOS_DISK;
435 /* Cleanup if reading failed */
436 if (res)
437 PartitionGPTClosePartitionTable(PartitionBase, root);
439 return res;
442 static LONG GPTWriteTable(struct Library *PartitionBase, struct PartitionHandle *root, struct GPTHeader *hdr, struct GPTPartition *table,
443 UQUAD headerblk, UQUAD backupblk, UQUAD startblk, ULONG tablesize)
445 LONG res;
446 ULONG crc;
448 hdr->CurrentBlock = AROS_QUAD2LE(headerblk);
449 hdr->BackupBlock = AROS_QUAD2LE(backupblk);
450 hdr->StartBlock = AROS_QUAD2LE(startblk);
451 hdr->HeaderCRC32 = 0;
453 /* We modify the header, so we have to recalculate its CRC */
454 crc = Crc32_ComputeBuf(0, hdr, AROS_LE2LONG(hdr->HeaderSize));
455 hdr->HeaderCRC32 = AROS_LONG2LE(crc);
456 DWRITE(bug("[GPT] New header CRC 0x%08X\n", crc));
458 DWRITE(KPrintF("[GPT] Write data: start block %llu\n", startblk));
459 res = WritePartitionDataQ(root, table, tablesize, startblk);
461 DWRITE(bug("[GPT] Write result: %u\n", res));
462 if (res == 0)
464 DWRITE(KPrintF("[GPT] Write header: start block %llu\n", headerblk));
465 res = PartitionWriteBlock(PartitionBase, root, headerblk, hdr);
467 DWRITE(bug("[GPT] Write result: %u\n", res));
470 return deviceError(res);
473 static LONG PartitionGPTWritePartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
475 struct GPTHeader *hdr = root->table->data;
476 ULONG cnt = AROS_LE2LONG(hdr->NumEntries);
477 ULONG entrysize = AROS_LE2LONG(hdr->EntrySize);
478 ULONG tablesize = AROS_ROUNDUP2(entrysize * cnt, root->de.de_SizeBlock << 2);
479 struct GPTPartition *table;
482 * TODO: Update legacy MBR data here when adding/moving is implemented. IntelMacs have
483 * legacy MBR filled in with copies of four first entries in GPT table if at least one
484 * FAT partition is defined on the drive (to support Windows XP).
485 * CHS data for these entries is always set to (c=1023, h=254, s=63), however start and end
486 * block numbers reflect the real position. Apple's disk utility always keeps these entries
487 * in sync with their respective GPT entries. We need to do the same. Also remember to keep
488 * boot code in sector 0.
491 DWRITE(bug("[GPT] Write: %u entries per %u bytes, %u bytes total\n", cnt, entrysize, tablesize));
493 /* Allocate buffer for the whole table */
494 table = AllocMem(tablesize, MEMF_CLEAR);
495 if (table)
497 ULONG crc;
498 struct GPTPartition *p = table;
499 struct GPTPartitionHandle *gph;
500 UQUAD backup;
501 LONG res;
504 * Collect our entries and build up the whole table.
505 * At this point we are guaranteed to have no more entries than
506 * can fit into reserved space. It's AddPartition()'s job to ensure this.
508 ForeachNode(&root->table->list, gph)
510 DWRITE(bug("[GPT] Writing partition %s, handle 0x%p\n", gph->name, gph));
513 * Put our entry into the buffer.
514 * Use entry's own length, because if this entry is created by AddPartition(),
515 * it can be shorter than on-disk one (if someone uses extended length we don't know about).
517 CopyMem(&gph[1], p, gph->entrySize);
519 DWRITE(PRINT_LE_UUID("Type ", &p->TypeID));
520 DWRITE(PRINT_LE_UUID("Partition", &p->PartitionID));
521 DWRITE(KPrintF("[GPT] Blocks %llu - %llu\n", AROS_LE2QUAD(p->StartBlock), AROS_LE2QUAD(p->EndBlock)));
522 DWRITE(KPrintF("[GPT] Flags 0x%08lX 0x%08lX\n", AROS_LE2LONG(p->Flags0), AROS_LE2LONG(p->Flags1)));
523 DWRITE(KPrintF("[GPT] Offset 0x%p\n", (APTR)p - (APTR)table));
525 /* Jump to next entry */
526 p = (APTR)p + entrysize;
529 crc = Crc32_ComputeBuf(0, table, entrysize * cnt);
530 hdr->PartCRC32 = AROS_LONG2LE(crc);
531 DWRITE(bug("[GPT] New data CRC 0x%08X\n", crc));
533 /* First we attempt to write a backup table. It's placed in the end. */
534 backup = root->dg.dg_TotalSectors - 1;
535 res = GPTWriteTable(PartitionBase, root, hdr, table, backup, 1, backup - tablesize, tablesize);
537 if (!res)
540 * And only if succeeded, write a primary one.
541 * This gives us a chance to discard writing if something goes wrong with disk/device/whatever.
543 res = GPTWriteTable(PartitionBase, root, hdr, table, 1, backup, 2, tablesize);
546 FreeMem(table, tablesize);
548 return res;
551 return ERROR_NO_FREE_STORE;
554 static LONG PartitionGPTGetPartitionAttr(struct Library *PartitionBase, struct PartitionHandle *ph, struct TagItem *tag)
556 struct GPTPartition *part = (APTR)ph + sizeof(struct GPTPartitionHandle);
558 switch (tag->ti_Tag)
560 case PT_TYPE:
561 uuid_from_le((uuid_t *)tag->ti_Data, &part->TypeID);
562 PTYPE(tag->ti_Data)->id_len = sizeof(uuid_t);
563 return TRUE;
565 case PT_BOOTABLE:
566 /* This extra flag is valid only for AROS partitions */
567 if (is_aros_uuid_le(&part->TypeID))
568 *((ULONG *)tag->ti_Data) = (AROS_LE2LONG(part->Flags1) & GPT_PF1_AROS_BOOTABLE) ? TRUE : FALSE;
569 else
570 *((ULONG *)tag->ti_Data) = FALSE;
571 return TRUE;
573 case PT_AUTOMOUNT:
574 *((ULONG *)tag->ti_Data) = (AROS_LE2LONG(part->Flags1) & GPT_PF1_NOMOUNT) ? FALSE : TRUE;
575 return TRUE;
577 case PT_STARTBLOCK:
578 *((UQUAD *)tag->ti_Data) = AROS_LE2QUAD(part->StartBlock);
579 return TRUE;
581 case PT_ENDBLOCK:
582 *((UQUAD *)tag->ti_Data) = AROS_LE2QUAD(part->EndBlock);
583 return TRUE;
586 return 0;
589 static LONG PartitionGPTSetPartitionAttrs(struct Library *PartitionBase, struct PartitionHandle *ph, const struct TagItem *taglist)
591 struct GPTPartition *part = (APTR)ph + sizeof(struct GPTPartitionHandle);
592 struct TagItem *tag;
593 struct TagItem *bootable = NULL;
595 while ((tag = NextTagItem((struct TagItem **)&taglist)))
597 switch (tag->ti_Tag)
599 case PT_NAME:
600 strncpy(GPTH(ph)->name, (char *)tag->ti_Data, 36);
601 ToUTF16(part->Name, GPTH(ph)->name, 36);
602 break;
604 case PT_TYPE:
605 /* Foolproof check */
606 if (PTYPE(tag->ti_Data)->id_len == sizeof(uuid_t))
608 uuid_to_le(&part->TypeID, (uuid_t *)tag->ti_Data);
609 /* Update DOSType according to a new type ID */
610 GPT_PatchDosEnvec(&ph->de, part);
612 break;
614 case PT_BOOTABLE:
615 bootable = tag;
616 break;
618 case PT_AUTOMOUNT:
619 D(bug("[GPT] Setting automount flag to %ld\n", tag->ti_Data));
620 D(bug("[GPT] Partition handle 0x%p, flags 0x%08X\n", ph, part->Flags1));
622 if (tag->ti_Data)
623 part->Flags1 &= ~AROS_LONG2LE(GPT_PF1_NOMOUNT);
624 else
625 part->Flags1 |= AROS_LONG2LE(GPT_PF1_NOMOUNT);
627 D(bug("[GPT] New flags: 0x%08X\n", part->Flags1));
629 break;
631 /* TODO: implement the rest (geometry, dosenvec, start/end block) */
636 * Now check bootable attribute.
637 * It is applicable only to AROS partitions, so we check it here,
638 * after possible type change.
640 if (bootable && is_aros_uuid_le(&part->TypeID))
642 if (bootable->ti_Data)
643 part->Flags1 |= AROS_LONG2LE(GPT_PF1_AROS_BOOTABLE);
644 else
645 part->Flags1 &= ~AROS_LONG2LE(GPT_PF1_AROS_BOOTABLE);
648 return 0;
651 static ULONG PartitionGPTDestroyPartitionTable(struct Library *PartitionBase,
652 struct PartitionHandle *root)
654 return PartitionMBRDestroyPartitionTable(PartitionBase, root);
657 static const struct PartitionAttribute PartitionGPTPartitionTableAttrs[]=
659 {PTT_TYPE, PLAM_READ},
660 {TAG_DONE, 0}
663 static const struct PartitionAttribute PartitionGPTPartitionAttrs[]=
665 {PT_GEOMETRY, PLAM_READ},
666 {PT_TYPE, PLAM_READ|PLAM_WRITE},
667 {PT_POSITION, PLAM_READ},
668 {PT_NAME, PLAM_READ|PLAM_WRITE},
669 {PT_BOOTABLE, PLAM_READ|PLAM_WRITE},
670 {PT_AUTOMOUNT, PLAM_READ|PLAM_WRITE},
671 {PT_STARTBLOCK, PLAM_READ},
672 {PT_ENDBLOCK, PLAM_READ},
673 {TAG_DONE, 0}
676 const struct PTFunctionTable PartitionGPT =
678 PHPTT_GPT,
679 "GPT",
680 PartitionGPTCheckPartitionTable,
681 PartitionGPTOpenPartitionTable,
682 PartitionGPTClosePartitionTable,
683 PartitionGPTWritePartitionTable,
684 NULL,
685 NULL,
686 NULL,
687 NULL,
688 NULL,
689 PartitionGPTGetPartitionAttr,
690 PartitionGPTSetPartitionAttrs,
691 PartitionGPTPartitionTableAttrs,
692 PartitionGPTPartitionAttrs,
693 PartitionGPTDestroyPartitionTable,
694 NULL