xref: /haiku/src/add-ons/kernel/partitioning_systems/gpt/gpt.cpp (revision bb83316a5811a550c4f850d07fa8e328e7ac0a94)
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
47 static off_t
48 block_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 
64 static status_t
65 efi_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 
77 static float
78 efi_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 
102 static status_t
103 efi_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 
157 static void
158 efi_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 
164 static void
165 efi_gpt_free_partition_content_cookie(partition_data* partition)
166 {
167 	delete (EFI::Header*)partition->content_cookie;
168 }
169 
170 
171 #ifndef _BOOT_MODE
172 static uint32
173 efi_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 
187 static uint32
188 efi_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 
198 static bool
199 efi_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 
206 static bool
207 efi_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 
241 static bool
242 efi_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 
280 static bool
281 efi_gpt_validate_move(partition_data* partition, off_t* start)
282 {
283 	// nothing to do
284 	return true;
285 }
286 
287 
288 static bool
289 efi_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 
331 static bool
332 efi_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 
342 static bool
343 efi_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 
350 static bool
351 efi_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 
366 static bool
367 efi_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 
427 static status_t
428 efi_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 
436 static status_t
437 efi_gpt_get_next_supported_type(partition_data* partition, int32* cookie,
438 	char* type)
439 {
440 	// TODO: implement
441 	return B_ERROR;
442 }
443 
444 
445 static status_t
446 efi_gpt_shadow_changed(partition_data* partition, partition_data* child,
447 	uint32 operation)
448 {
449 	// TODO: implement
450 	return B_ERROR;
451 }
452 
453 
454 static status_t
455 efi_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 
462 static status_t
463 efi_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 
491 static status_t
492 efi_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 
544 static status_t
545 efi_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 
552 static status_t
553 efi_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 
613 static status_t
614 efi_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 
659 static status_t
660 efi_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 
707 static status_t
708 efi_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 
740 static status_t
741 efi_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 
779 static status_t
780 efi_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 
853 static status_t
854 efi_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
901 static partition_module_info sEFIPartitionModule = {
902 #else
903 partition_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
978 partition_module_info* modules[] = {
979 	&sEFIPartitionModule,
980 	NULL
981 };
982 #endif
983