Init control word to zero, cdrom-handler reads this and will crash if it is non-zero...
[AROS.git] / rom / partition / partitiongpt.c
blobdd879c32d0dbb6bac18b3d8fcdb07638a726d932
1 /*
2 Copyright © 1995-2011, 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, 0x000000000000);
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, 0xF502CA018E5E);
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 #define writeDataFromBlock(root, blk, tablesize, table) TDERR_WriteProt
174 #define PartitionWriteBlock(base, root, blk, mem) TDERR_WriteProt
175 #endif
176 #ifdef SIM_WRITE
177 #define writeDataFromBlock(root, blk, tablesize, table) 0
178 #define PartitionWriteBlock(base, root, blk, mem) 0
179 #endif
181 static void GPT_PatchDosEnvec(struct DosEnvec *de, struct GPTPartition *p)
183 ULONG type = 0;
184 LONG bootpri = 0;
186 if (is_aros_uuid_le(&p->TypeID))
188 type = AROS_LE2LONG(p->TypeID.time_low);
189 /* This casting is needed for proper sign expansion */
190 bootpri = (BYTE)(AROS_LE2LONG(p->Flags1) & GPT_PF1_AROS_BOOTPRI);
192 else
194 const struct TypeMapping *m;
196 for (m = PartTypes; m->DOSType; m++)
198 if (m->uuid && uuid_cmp_le(&p->TypeID, m->uuid))
200 type = m->DOSType;
201 break;
206 setDosType(de, type);
207 de->de_BootPri = bootpri;
210 static LONG GPTCheckHeader(struct Library *PartitionBase, struct PartitionHandle *root, struct GPTHeader *hdr, UQUAD block)
212 /* Load the GPT header */
213 if (!readBlock(PartitionBase, root, block, hdr))
215 ULONG hdrSize = AROS_LE2LONG(hdr->HeaderSize);
216 UQUAD currentblk = AROS_LE2QUAD(hdr->CurrentBlock);
218 D(bug("[GPT] Header size: specified %u, expected %u\n", hdrSize, GPT_MIN_HEADER_SIZE));
219 DREAD(KPrintF("[GPT] Read: Header block %llu, backup block %llu\n", currentblk, AROS_LE2QUAD(hdr->BackupBlock)));
221 /* Check signature, header size, and current block number */
222 if ((!memcmp(hdr->Signature, GPT_SIGNATURE, sizeof(hdr->Signature))) &&
223 (hdrSize >= GPT_MIN_HEADER_SIZE) && (currentblk == block))
226 * Use zlib routine for CRC32.
227 * CHECKME: is it correct on bigendian machines? It should, however who knows...
229 ULONG orig_crc = AROS_LE2LONG(hdr->HeaderCRC32);
230 ULONG crc;
232 hdr->HeaderCRC32 = 0;
233 crc = Crc32_ComputeBuf(0, hdr, hdrSize);
235 D(bug("[GPT] Header CRC: calculated 0x%08X, expected 0x%08X\n", crc, orig_crc));
237 return (crc == orig_crc) ? 1 : 2;
240 return 0;
243 static LONG PartitionGPTCheckPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
245 APTR blk;
246 LONG res = 0;
248 /* GPT can be placed only in the root of the disk */
249 if (root->root)
250 return 0;
252 blk = AllocMem(root->de.de_SizeBlock << 2, MEMF_ANY);
254 if (!blk)
255 return 0;
257 /* First of all, we must have valid MBR stub */
258 if (MBRCheckPartitionTable(PartitionBase, root, blk))
260 struct PCPartitionTable *pcpt = ((struct MBR *)blk)->pcpt;
262 D(bug("[GPT] MBR check passed, first partition type 0x%02X, start block %u\n", pcpt[0].type, pcpt[0].first_sector));
264 /* We must have partition 0 of type GPT starting at block 1 */
265 if ((pcpt[0].type == MBRT_GPT) && (AROS_LE2LONG(pcpt[0].first_sector) == 1))
267 res = GPTCheckHeader(PartitionBase, root, blk, 1);
269 /* 2 is a special return code for "bad CRC" */
270 if (res == ERROR_BAD_CRC)
272 /* Try to read backup header */
273 UQUAD block = AROS_LE2QUAD(((struct GPTHeader *)blk)->BackupBlock);
275 res = GPTCheckHeader(PartitionBase, root, blk, block);
277 /* There's no third backup :( */
278 if (res == ERROR_BAD_CRC)
279 res = 0;
284 FreeMem(blk, root->de.de_SizeBlock << 2);
285 return res;
288 static LONG GPTReadPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root, struct GPTHeader *hdr, UQUAD block)
290 LONG res;
291 LONG err = ERROR_NOT_A_DOS_DISK;
293 DREAD(KPrintF("[GPT] Read: header block %llu\n", block));
294 res = GPTCheckHeader(PartitionBase, root, hdr, block);
295 if (res == 2)
296 return ERROR_BAD_CRC;
298 if (res == 1)
300 struct GPTPartition *table;
301 ULONG cnt = AROS_LE2LONG(hdr->NumEntries);
302 ULONG entrysize = AROS_LE2LONG(hdr->EntrySize);
303 ULONG tablesize = AROS_ROUNDUP2(entrysize * cnt, root->de.de_SizeBlock << 2);
304 UQUAD startblk, endblk;
306 DREAD(bug("[GPT] Read: %u entries per %u bytes, %u bytes total\n", cnt, entrysize, tablesize));
308 table = AllocMem(tablesize, MEMF_ANY);
309 if (!table)
310 return ERROR_NO_FREE_STORE;
312 startblk = AROS_LE2QUAD(hdr->StartBlock);
314 DREAD(KPrintF("[GPT] Read: start block %llu\n", startblk));
315 res = readDataFromBlock(root, startblk, tablesize, table);
316 if (!res)
318 ULONG orig_crc = AROS_LE2LONG(hdr->PartCRC32);
319 ULONG crc = Crc32_ComputeBuf(0, table, entrysize * cnt);
321 D(bug("[GPT] Data CRC: calculated 0x%08X, expected 0x%08X\n", crc, orig_crc));
323 if (crc == orig_crc)
325 struct GPTPartition *p = table;
326 ULONG i;
328 DREAD(bug("[GPT] Adding partitions...\n"));
329 err = 0;
331 for (i = 0; i < cnt; i++)
333 struct GPTPartitionHandle *gph;
335 startblk = AROS_LE2QUAD(p->StartBlock);
336 endblk = AROS_LE2QUAD(p->EndBlock);
339 * Skip unused entries. NumEntries in the header holds total number of preallocated entries,
340 * not the number of used ones.
341 * Normally GPT table has 128 preallocated entries, but only first of them are used.
342 * Just in case, we allow gaps between used entries. However (tested with MacOS X Disk Utility)
343 * partition editors seem to squeeze the table and do not leave empty entries when deleting
344 * partitions in the middle of the disk.
346 if (!memcmp(&p->TypeID, &GPT_Type_Unused, sizeof(uuid_t)))
347 continue;
349 DREAD(PRINT_LE_UUID("Type ", &p->TypeID));
350 DREAD(PRINT_LE_UUID("Partition", &p->PartitionID));
351 DREAD(KPrintF("[GPT] Blocks %llu - %llu\n", startblk, endblk));
352 DREAD(KPrintF("[GPT] Flags 0x%08lX 0x%08lX\n", AROS_LE2LONG(p->Flags0), AROS_LE2LONG(p->Flags1)));
353 DREAD(KPrintF("[GPT] Offset 0x%p\n", (APTR)p - (APTR)table));
355 gph = AllocVec(sizeof(struct GPTPartitionHandle) + entrysize, MEMF_CLEAR);
356 if (gph)
358 initPartitionHandle(root, &gph->ph, startblk, endblk - startblk + 1);
360 /* Map UUID to a DOSType */
361 GPT_PatchDosEnvec(&gph->ph.de, p);
363 /* Store the whole entry and convert name into ASCII form */
364 CopyMem(p, &gph[1], entrysize);
365 FromUTF16(gph->name, p->Name, 36);
367 gph->ph.ln.ln_Name = gph->name;
368 gph->entrySize = entrysize;
370 ADDTAIL(&root->table->list, gph);
371 DREAD(bug("[GPT] Added partition %u (%s), handle 0x%p\n", i, gph->name, gph));
373 else
375 err = ERROR_NO_FREE_STORE;
376 break;
379 /* Jump to next entry, skip 'entrysize' bytes */
380 p = (APTR)p + entrysize;
383 else
384 err = ERROR_BAD_CRC;
387 FreeMem(table, tablesize);
390 return err;
393 static void PartitionGPTClosePartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
395 struct PartitionHandle *ph, *ph2;
397 /* Free all partition entries */
398 ForeachNodeSafe(&root->table->list, ph, ph2)
399 FreeVec(ph);
401 FreeMem(root->table->data, root->de.de_SizeBlock<<2);
404 static LONG PartitionGPTOpenPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
406 LONG res;
409 * The header is attached to partition table handle.
410 * This allows us to write back the complete header and keep
411 * data we don't know about in future GPT revisions.
413 root->table->data = AllocMem(root->de.de_SizeBlock << 2, MEMF_ANY);
414 if (!root->table->data)
415 return ERROR_NO_FREE_STORE;
417 /* Read primary GPT table */
418 res = GPTReadPartitionTable(PartitionBase, root, root->table->data, 1);
420 if (res == ERROR_BAD_CRC)
422 /* If CRC failed, read backup table */
423 struct GPTHeader *hdr = root->table->data;
424 UQUAD block = AROS_LE2QUAD(hdr->BackupBlock);
426 res = GPTReadPartitionTable(PartitionBase, root, hdr, block);
428 /* There's no third backup... */
429 if (res == ERROR_BAD_CRC)
430 res = ERROR_NOT_A_DOS_DISK;
433 /* Cleanup if reading failed */
434 if (res)
435 PartitionGPTClosePartitionTable(PartitionBase, root);
437 return res;
440 static LONG GPTWriteTable(struct Library *PartitionBase, struct PartitionHandle *root, struct GPTHeader *hdr, struct GPTPartition *table,
441 UQUAD headerblk, UQUAD backupblk, UQUAD startblk, ULONG tablesize)
443 LONG res;
444 ULONG crc;
446 hdr->CurrentBlock = AROS_QUAD2LE(headerblk);
447 hdr->BackupBlock = AROS_QUAD2LE(backupblk);
448 hdr->StartBlock = AROS_QUAD2LE(startblk);
449 hdr->HeaderCRC32 = 0;
451 /* We modify the header, so we have to recalculate its CRC */
452 crc = Crc32_ComputeBuf(0, hdr, AROS_LE2LONG(hdr->HeaderSize));
453 hdr->HeaderCRC32 = AROS_LONG2LE(crc);
454 DWRITE(bug("[GPT] New header CRC 0x%08X\n", crc));
456 DWRITE(KPrintF("[GPT] Write data: start block %llu\n", startblk));
457 res = writeDataFromBlock(root, startblk, tablesize, table);
459 DWRITE(bug("[GPT] Write result: %u\n", res));
460 if (res == 0)
462 DWRITE(KPrintF("[GPT] Write header: start block %llu\n", headerblk));
463 res = PartitionWriteBlock(PartitionBase, root, headerblk, hdr);
465 DWRITE(bug("[GPT] Write result: %u\n", res));
468 return deviceError(res);
471 static LONG PartitionGPTWritePartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
473 struct GPTHeader *hdr = root->table->data;
474 ULONG cnt = AROS_LE2LONG(hdr->NumEntries);
475 ULONG entrysize = AROS_LE2LONG(hdr->EntrySize);
476 ULONG tablesize = AROS_ROUNDUP2(entrysize * cnt, root->de.de_SizeBlock << 2);
477 struct GPTPartition *table;
480 * TODO: Update legacy MBR data here when adding/moving is implemented. IntelMacs have
481 * legacy MBR filled in with copies of four first entries in GPT table if at least one
482 * FAT partition is defined on the drive (to support Windows XP).
483 * CHS data for these entries is always set to (c=1023, h=254, s=63), however start and end
484 * block numbers reflect the real position. Apple's disk utility always keeps these entries
485 * in sync with their respective GPT entries. We need to do the same. Also remember to keep
486 * boot code in sector 0.
489 DWRITE(bug("[GPT] Write: %u entries per %u bytes, %u bytes total\n", cnt, entrysize, tablesize));
491 /* Allocate buffer for the whole table */
492 table = AllocMem(tablesize, MEMF_CLEAR);
493 if (table)
495 ULONG crc;
496 struct GPTPartition *p = table;
497 struct GPTPartitionHandle *gph;
498 UQUAD backup;
499 LONG res;
502 * Collect our entries and build up the whole table.
503 * At this point we are guaranteed to have no more entries than
504 * can fit into reserved space. It's AddPartition()'s job to ensure this.
506 ForeachNode(&root->table->list, gph)
508 DWRITE(bug("[GPT] Writing partition %s, handle 0x%p\n", gph->name, gph));
511 * Put our entry into the buffer.
512 * Use entry's own length, because if this entry is created by AddPartition(),
513 * it can be shorter than on-disk one (if someone uses extended length we don't know about).
515 CopyMem(&gph[1], p, gph->entrySize);
517 DWRITE(PRINT_LE_UUID("Type ", &p->TypeID));
518 DWRITE(PRINT_LE_UUID("Partition", &p->PartitionID));
519 DWRITE(KPrintF("[GPT] Blocks %llu - %llu\n", AROS_LE2QUAD(p->StartBlock), AROS_LE2QUAD(p->EndBlock)));
520 DWRITE(KPrintF("[GPT] Flags 0x%08lX 0x%08lX\n", AROS_LE2LONG(p->Flags0), AROS_LE2LONG(p->Flags1)));
521 DWRITE(KPrintF("[GPT] Offset 0x%p\n", (APTR)p - (APTR)table));
523 /* Jump to next entry */
524 p = (APTR)p + entrysize;
527 crc = Crc32_ComputeBuf(0, table, entrysize * cnt);
528 hdr->PartCRC32 = AROS_LONG2LE(crc);
529 DWRITE(bug("[GPT] New data CRC 0x%08X\n", crc));
531 /* First we attempt to write a backup table. It's placed in the end. */
532 backup = root->dg.dg_TotalSectors - 1;
533 res = GPTWriteTable(PartitionBase, root, hdr, table, backup, 1, backup - tablesize, tablesize);
535 if (!res)
538 * And only if succeeded, write a primary one.
539 * This gives us a chance to discard writing if something goes wrong with disk/device/whatever.
541 res = GPTWriteTable(PartitionBase, root, hdr, table, 1, backup, 2, tablesize);
544 FreeMem(table, tablesize);
546 return res;
549 return ERROR_NO_FREE_STORE;
552 static LONG PartitionGPTGetPartitionAttr(struct Library *PartitionBase, struct PartitionHandle *ph, struct TagItem *tag)
554 struct GPTPartition *part = (APTR)ph + sizeof(struct GPTPartitionHandle);
556 switch (tag->ti_Tag)
558 case PT_TYPE:
559 uuid_from_le((uuid_t *)tag->ti_Data, &part->TypeID);
560 PTYPE(tag->ti_Data)->id_len = sizeof(uuid_t);
561 return TRUE;
563 case PT_BOOTABLE:
564 /* This extra flag is valid only for AROS partitions */
565 if (is_aros_uuid_le(&part->TypeID))
566 *((ULONG *)tag->ti_Data) = (AROS_LE2LONG(part->Flags1) & GPT_PF1_AROS_BOOTABLE) ? TRUE : FALSE;
567 else
568 *((ULONG *)tag->ti_Data) = FALSE;
569 return TRUE;
571 case PT_AUTOMOUNT:
572 *((ULONG *)tag->ti_Data) = (AROS_LE2LONG(part->Flags1) & GPT_PF1_NOMOUNT) ? FALSE : TRUE;
573 return TRUE;
575 case PT_STARTBLOCK:
576 *((ULONG *)tag->ti_Data) = AROS_LE2LONG(part->StartBlock);
577 return TRUE;
579 case PT_ENDBLOCK:
580 *((ULONG *)tag->ti_Data) = AROS_LE2LONG(part->EndBlock);
581 return TRUE;
584 return 0;
587 static LONG PartitionGPTSetPartitionAttrs(struct Library *PartitionBase, struct PartitionHandle *ph, const struct TagItem *taglist)
589 struct GPTPartition *part = (APTR)ph + sizeof(struct GPTPartitionHandle);
590 struct TagItem *tag;
591 struct TagItem *bootable = NULL;
593 while ((tag = NextTagItem(&taglist)))
595 switch (tag->ti_Tag)
597 case PT_NAME:
598 strncpy(GPTH(ph)->name, (char *)tag->ti_Data, 36);
599 ToUTF16(part->Name, GPTH(ph)->name, 36);
600 break;
602 case PT_TYPE:
603 /* Foolproof check */
604 if (PTYPE(tag->ti_Data)->id_len == sizeof(uuid_t))
606 uuid_to_le(&part->TypeID, (uuid_t *)tag->ti_Data);
607 /* Update DOSType according to a new type ID */
608 GPT_PatchDosEnvec(&ph->de, part);
610 break;
612 case PT_BOOTABLE:
613 bootable = tag;
614 break;
616 case PT_AUTOMOUNT:
617 D(bug("[GPT] Setting automount flag to %ld\n", tag->ti_Data));
618 D(bug("[GPT] Partition handle 0x%p, flags 0x%08X\n", ph, part->Flags1));
620 if (tag->ti_Data)
621 part->Flags1 &= ~AROS_LONG2LE(GPT_PF1_NOMOUNT);
622 else
623 part->Flags1 |= AROS_LONG2LE(GPT_PF1_NOMOUNT);
625 D(bug("[GPT] New flags: 0x%08X\n", part->Flags1));
627 break;
629 /* TODO: implement the rest (geometry, dosenvec, start/end block) */
634 * Now check bootable attribute.
635 * It is applicable only to AROS partitions, so we check it here,
636 * after possible type change.
638 if (bootable && is_aros_uuid_le(&part->TypeID))
640 if (bootable->ti_Data)
641 part->Flags1 |= AROS_LONG2LE(GPT_PF1_AROS_BOOTABLE);
642 else
643 part->Flags1 &= ~AROS_LONG2LE(GPT_PF1_AROS_BOOTABLE);
646 return 0;
649 static const struct PartitionAttribute PartitionGPTPartitionTableAttrs[]=
651 {PTT_TYPE, PLAM_READ},
652 {TAG_DONE, 0}
655 static const struct PartitionAttribute PartitionGPTPartitionAttrs[]=
657 {PT_GEOMETRY, PLAM_READ},
658 {PT_TYPE, PLAM_READ|PLAM_WRITE},
659 {PT_POSITION, PLAM_READ},
660 {PT_NAME, PLAM_READ|PLAM_WRITE},
661 {PT_BOOTABLE, PLAM_READ|PLAM_WRITE},
662 {PT_AUTOMOUNT, PLAM_READ|PLAM_WRITE},
663 {PT_STARTBLOCK, PLAM_READ},
664 {PT_ENDBLOCK, PLAM_READ},
665 {TAG_DONE, 0}
668 const struct PTFunctionTable PartitionGPT =
670 PHPTT_GPT,
671 "GPT",
672 PartitionGPTCheckPartitionTable,
673 PartitionGPTOpenPartitionTable,
674 PartitionGPTClosePartitionTable,
675 PartitionGPTWritePartitionTable,
676 NULL,
677 NULL,
678 NULL,
679 NULL,
680 NULL,
681 PartitionGPTGetPartitionAttr,
682 PartitionGPTSetPartitionAttrs,
683 PartitionGPTPartitionTableAttrs,
684 PartitionGPTPartitionAttrs,
685 NULL,
686 NULL