1/*
2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
3 * Copyright 2007-2013, Axel D��rfler, axeld@pinc-software.de.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8
9#include "gpt.h"
10
11#include <KernelExport.h>
12#include <disk_device_manager/ddm_modules.h>
13#include <disk_device_types.h>
14#ifdef _BOOT_MODE
15#	include <boot/partitions.h>
16#else
17#	include <DiskDeviceTypes.h>
18#	include "PartitionLocker.h"
19#endif
20#include <util/kernel_cpp.h>
21
22#include <errno.h>
23#include <unistd.h>
24#include <stdio.h>
25#include <string.h>
26
27#ifndef _BOOT_MODE
28#include "uuid.h"
29#endif
30
31#include "Header.h"
32#include "utility.h"
33
34
35#define TRACE_EFI_GPT
36#ifdef TRACE_EFI_GPT
37#	define TRACE(x) dprintf x
38#else
39#	define TRACE(x) ;
40#endif
41
42
43#define EFI_PARTITION_MODULE_NAME "partitioning_systems/efi_gpt/v1"
44
45
46#ifndef _BOOT_MODE
47static off_t
48block_align(partition_data* partition, off_t offset, bool upwards)
49{
50	// Take HDs into account that hide the fact they are using a
51	// block size of 4096 bytes, and round to that.
52	uint32 blockSize = max_c(partition->block_size, 4096);
53	if (upwards)
54		return ((offset + blockSize - 1) / blockSize) * blockSize;
55
56	return (offset / blockSize) * blockSize;
57}
58#endif // !_BOOT_MODE
59
60
61//	#pragma mark - public module interface
62
63
64static status_t
65efi_gpt_std_ops(int32 op, ...)
66{
67	switch (op) {
68		case B_MODULE_INIT:
69		case B_MODULE_UNINIT:
70			return B_OK;
71	}
72
73	return B_ERROR;
74}
75
76
77static float
78efi_gpt_identify_partition(int fd, partition_data* partition, void** _cookie)
79{
80	EFI::Header* header = new (std::nothrow) EFI::Header(fd,
81		(partition->size - 1) / partition->block_size, partition->block_size);
82	status_t status = header->InitCheck();
83	if (status != B_OK) {
84		delete header;
85		return -1;
86	}
87
88	*_cookie = header;
89	if (header->IsDirty()) {
90		// Either the main or the backup table is missing, it looks like someone
91		// tried to erase the GPT with something else. Let's lower the priority,
92		// so that other partitioning systems which use either only the start or
93		// only the end of the drive, have a chance to run instead.
94		return 0.75;
95	}
96	return 0.96;
97		// This must be higher as Intel partitioning, as EFI can contain this
98		// partitioning for compatibility
99}
100
101
102static status_t
103efi_gpt_scan_partition(int fd, partition_data* partition, void* _cookie)
104{
105	TRACE(("efi_gpt_scan_partition(cookie = %p)\n", _cookie));
106	EFI::Header* header = (EFI::Header*)_cookie;
107
108	partition->status = B_PARTITION_VALID;
109	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
110	partition->content_size = partition->size;
111	partition->content_cookie = header;
112
113	// scan all children
114
115	uint32 index = 0;
116
117	for (uint32 i = 0; i < header->EntryCount(); i++) {
118		const gpt_partition_entry& entry = header->EntryAt(i);
119
120		if (entry.partition_type == kEmptyGUID)
121			continue;
122
123		if (entry.EndBlock() * partition->block_size
124				> (uint64)partition->size) {
125			TRACE(("efi_gpt: child partition exceeds existing space (ends at "
126				"block %" B_PRIu64 ")\n", entry.EndBlock()));
127			continue;
128		}
129
130		if (entry.StartBlock() * partition->block_size == 0) {
131			TRACE(("efi_gpt: child partition starts at 0 (recursive entry)\n"));
132			continue;
133		}
134
135		partition_data* child = create_child_partition(partition->id, index++,
136			partition->offset + entry.StartBlock() * partition->block_size,
137			entry.BlockCount() * partition->block_size, -1);
138		if (child == NULL) {
139			TRACE(("efi_gpt: Creating child at index %" B_PRIu32 " failed\n",
140				index - 1));
141			return B_ERROR;
142		}
143
144		char name[B_OS_NAME_LENGTH];
145		to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name));
146		child->name = strdup(name);
147		child->type = strdup(get_partition_type(entry.partition_type));
148		child->block_size = partition->block_size;
149		child->cookie = (void*)(addr_t)i;
150		child->content_cookie = header;
151	}
152
153	return B_OK;
154}
155
156
157static void
158efi_gpt_free_identify_partition_cookie(partition_data* partition, void* _cookie)
159{
160	// Cookie is freed in efi_gpt_free_partition_content_cookie().
161}
162
163
164static void
165efi_gpt_free_partition_content_cookie(partition_data* partition)
166{
167	delete (EFI::Header*)partition->content_cookie;
168}
169
170
171#ifndef _BOOT_MODE
172static uint32
173efi_gpt_get_supported_operations(partition_data* partition, uint32 mask)
174{
175	uint32 flags = B_DISK_SYSTEM_SUPPORTS_INITIALIZING
176		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
177		| B_DISK_SYSTEM_SUPPORTS_MOVING
178		| B_DISK_SYSTEM_SUPPORTS_RESIZING
179		| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
180		// TODO: check for available entries and partitionable space and only
181		// add creating child support if both is valid
182
183	return flags;
184}
185
186
187static uint32
188efi_gpt_get_supported_child_operations(partition_data* partition,
189	partition_data* child, uint32 mask)
190{
191	return B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
192		| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
193		| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
194		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
195}
196
197
198static bool
199efi_gpt_is_sub_system_for(partition_data* partition)
200{
201	// a GUID Partition Table doesn't usually live inside another partition
202	return false;
203}
204
205
206static bool
207efi_gpt_validate_resize(partition_data* partition, off_t* size)
208{
209	off_t newSize = *size;
210	if (newSize == partition->size)
211		return true;
212
213	if (newSize < 0)
214		newSize = 0;
215	else
216		newSize = block_align(partition, newSize, false);
217
218	// growing
219	if (newSize > partition->size) {
220		*size = newSize;
221		return true;
222	}
223
224	// shrinking, only so that no child would be truncated
225	off_t newEnd = partition->offset + newSize;
226	for (int32 i = 0; i < partition->child_count; i++) {
227		partition_data* child = get_child_partition(partition->id, i);
228		if (child == NULL)
229			continue;
230
231		if (child->offset + child->size > newEnd)
232			newEnd = child->offset + child->size;
233	}
234
235	newSize = block_align(partition, newEnd - partition->offset, true);
236	*size = newSize;
237	return true;
238}
239
240
241static bool
242efi_gpt_validate_resize_child(partition_data* partition, partition_data* child,
243	off_t* size)
244{
245	off_t newSize = *size;
246	if (newSize == child->size)
247		return true;
248
249	// shrinking
250	if (newSize < child->size) {
251		if (newSize < 0)
252			newSize = 0;
253
254		*size = block_align(partition, newSize, false);
255		return true;
256	}
257
258	// growing, but only so much that the child doesn't get bigger than
259	// the parent
260	if (child->offset + newSize > partition->offset + partition->size)
261		newSize = partition->offset + partition->size - child->offset;
262
263	// make sure that the child doesn't overlap any sibling partitions
264	off_t newEnd = child->offset + newSize;
265	for (int32 i = 0; i < partition->child_count; i++) {
266		partition_data* other = get_child_partition(partition->id, i);
267		if (other == NULL || other->id == child->id
268			|| other->offset < child->offset)
269			continue;
270
271		if (newEnd > other->offset)
272			newEnd = other->offset;
273	}
274
275	*size = block_align(partition, newEnd - child->offset, false);
276	return true;
277}
278
279
280static bool
281efi_gpt_validate_move(partition_data* partition, off_t* start)
282{
283	// nothing to do
284	return true;
285}
286
287
288static bool
289efi_gpt_validate_move_child(partition_data* partition, partition_data* child,
290	off_t* start)
291{
292	off_t newStart = *start;
293	if (newStart < 0)
294		newStart = 0;
295
296	if (newStart + child->size > partition->size)
297		newStart = partition->size - child->size;
298
299	newStart = block_align(partition, newStart, false);
300	if (newStart > child->offset) {
301		for (int32 i = 0; i < partition->child_count; i++) {
302			partition_data* other = get_child_partition(partition->id, i);
303			if (other == NULL || other->id == child->id
304				|| other->offset < child->offset)
305				continue;
306
307			if (other->offset < newStart + child->size)
308				newStart = other->offset - child->size;
309		}
310
311		newStart = block_align(partition, newStart, false);
312	} else {
313		for (int32 i = 0; i < partition->child_count; i++) {
314			partition_data* other = get_child_partition(partition->id, i);
315			if (other == NULL || other->id == child->id
316				|| other->offset > child->offset)
317				continue;
318
319			if (other->offset + other->size > newStart)
320				newStart = other->offset + other->size;
321		}
322
323		newStart = block_align(partition, newStart, true);
324	}
325
326	*start = newStart;
327	return true;
328}
329
330
331static bool
332efi_gpt_validate_set_name(partition_data* partition, char* name)
333{
334	// TODO: should validate that the utf-8 -> ucs-2 is valid
335	// TODO: should count actual utf-8 chars
336	if (strlen(name) > EFI_PARTITION_NAME_LENGTH)
337		name[EFI_PARTITION_NAME_LENGTH - 1] = 0;
338	return true;
339}
340
341
342static bool
343efi_gpt_validate_set_type(partition_data* partition, const char* type)
344{
345	guid_t typeGUID;
346	return get_guid_for_partition_type(type, typeGUID);
347}
348
349
350static bool
351efi_gpt_validate_initialize(partition_data* partition, char* name,
352	const char* parameters)
353{
354	if ((efi_gpt_get_supported_operations(partition, ~0)
355		& B_DISK_SYSTEM_SUPPORTS_INITIALIZING) == 0)
356		return false;
357
358	// name and parameters are ignored
359	if (name != NULL)
360		name[0] = 0;
361
362	return true;
363}
364
365
366static bool
367efi_gpt_validate_create_child(partition_data* partition, off_t* start,
368	off_t* size, const char* type, const char* name, const char* parameters,
369	int32* index)
370{
371	if ((efi_gpt_get_supported_operations(partition, ~0)
372			& B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) == 0)
373		return false;
374
375	if (!efi_gpt_validate_set_type(partition, type))
376		return false;
377
378	EFI::Header* header = (EFI::Header*)partition->content_cookie;
379	int32 entryIndex = -1;
380	for (uint32 i = 0; i < header->EntryCount(); i++) {
381		const gpt_partition_entry& entry = header->EntryAt(i);
382		if (entry.partition_type == kEmptyGUID) {
383			entryIndex = i;
384			break;
385		}
386	}
387
388	if (entryIndex < 0)
389		return false;
390
391	*index = entryIndex;
392
393	// ensure that child lies between first and last usable block
394	off_t firstUsable = header->FirstUsableBlock() * partition->block_size;
395	if (*start < firstUsable)
396		*start = firstUsable;
397
398	off_t lastUsable = header->LastUsableBlock() * partition->block_size;
399	if (*start + *size > lastUsable) {
400		if (*start > lastUsable)
401			return false;
402
403		*size = lastUsable - *start;
404	}
405
406	// ensure that we don't overlap any siblings
407	for (int32 i = 0; i < partition->child_count; i++) {
408		partition_data* other = get_child_partition(partition->id, i);
409		if (other == NULL)
410			continue;
411
412		if (other->offset < *start && other->offset + other->size > *start)
413			*start = other->offset + other->size;
414
415		if (other->offset > *start && other->offset < *start + *size)
416			*size = other->offset - *start;
417	}
418
419	*start = block_align(partition, *start, true);
420	*size = block_align(partition, *size, false);
421
422	// TODO: support parameters
423	return true;
424}
425
426
427static status_t
428efi_gpt_get_partitionable_spaces(partition_data* partition,
429	partitionable_space_data* buffer, int32 count, int32* actualCount)
430{
431	// TODO: implement
432	return B_ERROR;
433}
434
435
436static status_t
437efi_gpt_get_next_supported_type(partition_data* partition, int32* cookie,
438	char* type)
439{
440	// TODO: implement
441	return B_ERROR;
442}
443
444
445static status_t
446efi_gpt_shadow_changed(partition_data* partition, partition_data* child,
447	uint32 operation)
448{
449	// TODO: implement
450	return B_ERROR;
451}
452
453
454static status_t
455efi_gpt_repair(int fd, partition_id partition, bool checkOnly, disk_job_id job)
456{
457	// TODO: implement, validate CRCs and restore from backup area if corrupt
458	return B_ERROR;
459}
460
461
462static status_t
463efi_gpt_resize(int fd, partition_id partitionID, off_t size, disk_job_id job)
464{
465	if (fd < 0)
466		return B_ERROR;
467
468	PartitionWriteLocker locker(partitionID);
469	if (!locker.IsLocked())
470		return B_ERROR;
471
472	partition_data* partition = get_partition(partitionID);
473	if (partition == NULL)
474		return B_BAD_VALUE;
475
476	off_t validatedSize = size;
477	if (!efi_gpt_validate_resize(partition, &validatedSize))
478		return B_BAD_VALUE;
479
480	update_disk_device_job_progress(job, 0.0);
481
482	partition->size = validatedSize;
483	partition->content_size = validatedSize;
484
485	update_disk_device_job_progress(job, 1.0);
486	partition_modified(partitionID);
487	return B_OK;
488}
489
490
491static status_t
492efi_gpt_resize_child(int fd, partition_id partitionID, off_t size,
493	disk_job_id job)
494{
495	if (fd < 0)
496		return B_ERROR;
497
498	PartitionWriteLocker locker(partitionID);
499	if (!locker.IsLocked())
500		return B_ERROR;
501
502	partition_data* child = get_partition(partitionID);
503	if (child == NULL)
504		return B_BAD_VALUE;
505
506	partition_data* partition = get_parent_partition(partitionID);
507	if (partition == NULL)
508		return B_BAD_VALUE;
509
510	EFI::Header* header = (EFI::Header*)partition->content_cookie;
511	if (header == NULL)
512		return B_BAD_VALUE;
513
514	uint32 entryIndex = (uint32)(addr_t)child->cookie;
515	if (entryIndex >= header->EntryCount())
516		return B_BAD_VALUE;
517
518	off_t validatedSize = size;
519	if (!efi_gpt_validate_resize_child(partition, child, &validatedSize))
520		return B_BAD_VALUE;
521
522	if (child->size == validatedSize)
523		return B_OK;
524
525	update_disk_device_job_progress(job, 0.0);
526
527	gpt_partition_entry& entry = header->EntryAt(entryIndex);
528	entry.SetBlockCount(validatedSize / partition->block_size);
529
530	status_t result = header->WriteEntry(fd, entryIndex);
531	if (result != B_OK) {
532		entry.SetBlockCount(child->size / partition->block_size);
533		return result;
534	}
535
536	child->size = validatedSize;
537
538	update_disk_device_job_progress(job, 1.0);
539	partition_modified(partitionID);
540	return B_OK;
541}
542
543
544static status_t
545efi_gpt_move(int fd, partition_id partition, off_t offset, disk_job_id job)
546{
547	// nothing to do here
548	return B_OK;
549}
550
551
552static status_t
553efi_gpt_move_child(int fd, partition_id partitionID, partition_id childID,
554	off_t offset, disk_job_id job)
555{
556	if (fd < 0)
557		return B_ERROR;
558
559	PartitionWriteLocker locker(partitionID);
560	if (!locker.IsLocked())
561		return B_ERROR;
562
563	partition_data* partition = get_partition(partitionID);
564	if (partition == NULL)
565		return B_BAD_VALUE;
566
567	partition_data* child = get_partition(childID);
568	if (child == NULL)
569		return B_BAD_VALUE;
570
571	EFI::Header* header = (EFI::Header*)partition->content_cookie;
572	if (header == NULL)
573		return B_BAD_VALUE;
574
575	uint32 entryIndex = (uint32)(addr_t)child->cookie;
576	if (entryIndex >= header->EntryCount())
577		return B_BAD_VALUE;
578
579	off_t validatedOffset = offset;
580	if (!efi_gpt_validate_move_child(partition, child, &validatedOffset))
581		return B_BAD_VALUE;
582
583	if (child->offset == validatedOffset)
584		return B_OK;
585
586	// TODO: implement actual moving, need to move the partition content
587	// (the raw data) here and need to take overlap into account
588	return B_ERROR;
589
590	update_disk_device_job_progress(job, 0.0);
591
592	gpt_partition_entry& entry = header->EntryAt(entryIndex);
593	uint64 blockCount = entry.BlockCount();
594	entry.SetStartBlock((validatedOffset - partition->offset)
595		/ partition->block_size);
596	entry.SetBlockCount(blockCount);
597
598	status_t result = header->WriteEntry(fd, entryIndex);
599	if (result != B_OK) {
600		// fatal error: the data has been moved but the partition table could
601		// not be updated to reflect that change!
602		return result;
603	}
604
605	child->offset = validatedOffset;
606
607	update_disk_device_job_progress(job, 1.0);
608	partition_modified(childID);
609	return B_OK;
610}
611
612
613static status_t
614efi_gpt_set_name(int fd, partition_id partitionID, const char* name,
615	disk_job_id job)
616{
617	if (fd < 0)
618		return B_ERROR;
619
620	PartitionWriteLocker locker(partitionID);
621	if (!locker.IsLocked())
622		return B_ERROR;
623
624	partition_data* child = get_partition(partitionID);
625	if (child == NULL)
626		return B_BAD_VALUE;
627
628	partition_data* partition = get_parent_partition(partitionID);
629	if (partition == NULL)
630		return B_BAD_VALUE;
631
632	EFI::Header* header = (EFI::Header*)partition->content_cookie;
633	if (header == NULL)
634		return B_BAD_VALUE;
635
636	uint32 entryIndex = (uint32)(addr_t)child->cookie;
637	if (entryIndex >= header->EntryCount())
638		return B_BAD_VALUE;
639
640	update_disk_device_job_progress(job, 0.0);
641
642	gpt_partition_entry& entry = header->EntryAt(entryIndex);
643	to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);
644
645	status_t result = header->WriteEntry(fd, entryIndex);
646	if (result != B_OK)
647		return result;
648
649	char newName[B_OS_NAME_LENGTH];
650	to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, newName, sizeof(newName));
651	child->name = strdup(newName);
652
653	update_disk_device_job_progress(job, 1.0);
654	partition_modified(partitionID);
655	return B_OK;
656}
657
658
659static status_t
660efi_gpt_set_type(int fd, partition_id partitionID, const char* type,
661	disk_job_id job)
662{
663	if (fd < 0)
664		return B_ERROR;
665
666	PartitionWriteLocker locker(partitionID);
667	if (!locker.IsLocked())
668		return B_ERROR;
669
670	partition_data* child = get_partition(partitionID);
671	if (child == NULL)
672		return B_BAD_VALUE;
673
674	partition_data* partition = get_parent_partition(partitionID);
675	if (partition == NULL)
676		return B_BAD_VALUE;
677
678	EFI::Header* header = (EFI::Header*)partition->content_cookie;
679	if (header == NULL)
680		return B_BAD_VALUE;
681
682	uint32 entryIndex = (uint32)(addr_t)child->cookie;
683	if (entryIndex >= header->EntryCount())
684		return B_BAD_VALUE;
685
686	guid_t typeGUID;
687	if (!get_guid_for_partition_type(type, typeGUID))
688		return B_BAD_VALUE;
689
690	update_disk_device_job_progress(job, 0.0);
691
692	gpt_partition_entry& entry = header->EntryAt(entryIndex);
693	entry.partition_type = typeGUID;
694
695	status_t result = header->WriteEntry(fd, entryIndex);
696	if (result != B_OK)
697		return result;
698
699	child->type = strdup(type);
700
701	update_disk_device_job_progress(job, 1.0);
702	partition_modified(partitionID);
703	return B_OK;
704}
705
706
707static status_t
708efi_gpt_initialize(int fd, partition_id partitionID, const char* name,
709	const char* parameters, off_t partitionSize, disk_job_id job)
710{
711	if (fd < 0)
712		return B_ERROR;
713
714	partition_data* partition = get_partition(partitionID);
715	if (partition == NULL)
716		return B_BAD_VALUE;
717
718	update_disk_device_job_progress(job, 0.0);
719
720	EFI::Header header((partitionSize - 1) / partition->block_size,
721		partition->block_size);
722	status_t result = header.InitCheck();
723	if (result != B_OK)
724		return result;
725
726	result = header.Write(fd);
727	if (result != B_OK)
728		return result;
729
730	result = scan_partition(partitionID);
731	if (result != B_OK)
732		return result;
733
734	update_disk_device_job_progress(job, 1.0);
735	partition_modified(partitionID);
736	return B_OK;
737}
738
739
740static status_t
741efi_gpt_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
742	uint32 blockSize, disk_job_id job)
743{
744	if (fd < 0)
745		return B_ERROR;
746
747	partition_data* partition = get_partition(partitionID);
748	if (partition == NULL)
749		return B_BAD_VALUE;
750
751	update_disk_device_job_progress(job, 0.0);
752
753	const int headerSize = partition->block_size * 3;
754	// The first block is the protective MBR
755	// The second block is the GPT header
756	// The third block is the start of the partition list (it can span more
757	// blocks, but that doesn't matter as soon as the header is erased).
758
759	uint8 buffer[headerSize];
760	memset(buffer, 0xE5, sizeof(buffer));
761
762	// Erase the first blocks
763	if (write_pos(fd, 0, &buffer, headerSize) < 0)
764		return errno;
765
766	// Erase the last blocks
767	// Only 2 blocks, as there is no protective MBR
768	if (write_pos(fd, partitionSize - 2 * partition->block_size,
769			&buffer, 2 * partition->block_size) < 0) {
770		return errno;
771	}
772
773	update_disk_device_job_progress(job, 1.0);
774
775	return B_OK;
776}
777
778
779static status_t
780efi_gpt_create_child(int fd, partition_id partitionID, off_t offset,
781	off_t size, const char* type, const char* name, const char* parameters,
782	disk_job_id job, partition_id* childID)
783{
784	if (fd < 0)
785		return B_ERROR;
786
787	PartitionWriteLocker locker(partitionID);
788	if (!locker.IsLocked())
789		return B_ERROR;
790
791	partition_data* partition = get_partition(partitionID);
792	if (partition == NULL)
793		return B_BAD_VALUE;
794
795	EFI::Header* header = (EFI::Header*)partition->content_cookie;
796	if (header == NULL)
797		return B_BAD_VALUE;
798
799	off_t validatedOffset = offset;
800	off_t validatedSize = size;
801	uint32 entryIndex = 0;
802
803	if (!efi_gpt_validate_create_child(partition, &validatedOffset,
804			&validatedSize, type, name, parameters, (int32*)&entryIndex))
805		return B_BAD_VALUE;
806
807	guid_t typeGUID;
808	if (!get_guid_for_partition_type(type, typeGUID))
809		return B_BAD_VALUE;
810
811	update_disk_device_job_progress(job, 0.0);
812
813	partition_data* child = create_child_partition(partition->id, entryIndex,
814		validatedOffset, validatedSize, *childID);
815	if (child == NULL)
816		return B_ERROR;
817
818	gpt_partition_entry& entry = header->EntryAt(entryIndex);
819	entry.partition_type = typeGUID;
820	uuid_t uuid;
821	uuid_generate_random(uuid);
822	memcpy((uint8*)&entry.unique_guid, uuid, sizeof(guid_t));
823	to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);
824	entry.SetStartBlock((validatedOffset - partition->offset)
825		/ partition->block_size);
826	entry.SetBlockCount(validatedSize / partition->block_size);
827	entry.SetAttributes(0); // TODO
828
829	status_t result = header->WriteEntry(fd, entryIndex);
830	if (result != B_OK) {
831		delete_partition(child->id);
832		return result;
833	}
834
835	*childID = child->id;
836	child->block_size = partition->block_size;
837	child->name = strdup(name);
838	child->type = strdup(type);
839	child->parameters = strdup(parameters);
840	child->cookie = (void*)(addr_t)entryIndex;
841
842	if (child->type == NULL || child->parameters == NULL) {
843		delete_partition(child->id);
844		return B_NO_MEMORY;
845	}
846
847	update_disk_device_job_progress(job, 1.0);
848	partition_modified(partitionID);
849	return B_OK;
850}
851
852
853static status_t
854efi_gpt_delete_child(int fd, partition_id partitionID, partition_id childID,
855	disk_job_id job)
856{
857	if (fd < 0)
858		return B_ERROR;
859
860	PartitionWriteLocker locker(partitionID);
861	if (!locker.IsLocked())
862		return B_ERROR;
863
864	partition_data* partition = get_partition(partitionID);
865	if (partition == NULL)
866		return B_BAD_VALUE;
867
868	partition_data* child = get_partition(childID);
869	if (child == NULL)
870		return B_BAD_VALUE;
871
872	EFI::Header* header = (EFI::Header*)partition->content_cookie;
873	if (header == NULL)
874		return B_BAD_VALUE;
875
876	uint32 entryIndex = (uint32)(addr_t)child->cookie;
877	if (entryIndex >= header->EntryCount())
878		return B_BAD_VALUE;
879
880	update_disk_device_job_progress(job, 0.0);
881
882	if (!delete_partition(childID))
883		return B_ERROR;
884
885	gpt_partition_entry& entry = header->EntryAt(entryIndex);
886	memset(&entry, 0, sizeof(gpt_partition_entry));
887	entry.partition_type = kEmptyGUID;
888
889	status_t result = header->WriteEntry(fd, entryIndex);
890	if (result != B_OK)
891		return result;
892
893	update_disk_device_job_progress(job, 1.0);
894	partition_modified(partitionID);
895	return B_OK;
896}
897#endif // !_BOOT_MODE
898
899
900#ifndef _BOOT_MODE
901static partition_module_info sEFIPartitionModule = {
902#else
903partition_module_info gEFIPartitionModule = {
904#endif
905	{
906		EFI_PARTITION_MODULE_NAME,
907		0,
908		efi_gpt_std_ops
909	},
910	"gpt",									// short_name
911	EFI_PARTITION_NAME,						// pretty_name
912	0										// flags
913	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
914	| B_DISK_SYSTEM_SUPPORTS_MOVING
915	| B_DISK_SYSTEM_SUPPORTS_RESIZING
916	| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
917	| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
918	| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
919	| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
920	| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
921	| B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
922	| B_DISK_SYSTEM_SUPPORTS_NAME
923	,
924
925	// scanning
926	efi_gpt_identify_partition,
927	efi_gpt_scan_partition,
928	efi_gpt_free_identify_partition_cookie,
929	NULL, // free_partition_cookie
930	efi_gpt_free_partition_content_cookie,
931
932#ifndef _BOOT_MODE
933	// querying
934	efi_gpt_get_supported_operations,
935	efi_gpt_get_supported_child_operations,
936	NULL, // supports_initializing_child
937	efi_gpt_is_sub_system_for,
938
939	efi_gpt_validate_resize,
940	efi_gpt_validate_resize_child,
941	efi_gpt_validate_move,
942	efi_gpt_validate_move_child,
943	efi_gpt_validate_set_name,
944	NULL, // validate_set_content_name
945	efi_gpt_validate_set_type,
946	NULL, // validate_set_parameters
947	NULL, // validate_set_content_parameters
948	efi_gpt_validate_initialize,
949	efi_gpt_validate_create_child,
950	efi_gpt_get_partitionable_spaces,
951	efi_gpt_get_next_supported_type,
952	NULL, // get_type_for_content_type
953
954	// shadow partition modification
955	efi_gpt_shadow_changed,
956
957	// writing
958	efi_gpt_repair,
959	efi_gpt_resize,
960	efi_gpt_resize_child,
961	efi_gpt_move,
962	efi_gpt_move_child,
963	efi_gpt_set_name,
964	NULL, // set_content_name
965	efi_gpt_set_type,
966	NULL, // set_parameters
967	NULL, // set_content_parameters
968	efi_gpt_initialize,
969	efi_gpt_uninitialize,
970	efi_gpt_create_child,
971	efi_gpt_delete_child
972#else
973	NULL
974#endif // _BOOT_MODE
975};
976
977#ifndef _BOOT_MODE
978partition_module_info* modules[] = {
979	&sEFIPartitionModule,
980	NULL
981};
982#endif
983