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
block_align(partition_data * partition,off_t offset,bool upwards)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
efi_gpt_std_ops(int32 op,...)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
efi_gpt_identify_partition(int fd,partition_data * partition,void ** _cookie)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
efi_gpt_scan_partition(int fd,partition_data * partition,void * _cookie)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
efi_gpt_free_identify_partition_cookie(partition_data * partition,void * _cookie)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
efi_gpt_free_partition_content_cookie(partition_data * partition)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
efi_gpt_get_supported_operations(partition_data * partition,uint32 mask)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
efi_gpt_get_supported_child_operations(partition_data * partition,partition_data * child,uint32 mask)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
efi_gpt_is_sub_system_for(partition_data * partition)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
efi_gpt_validate_resize(partition_data * partition,off_t * size)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
efi_gpt_validate_resize_child(partition_data * partition,partition_data * child,off_t * size)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
efi_gpt_validate_move(partition_data * partition,off_t * start)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
efi_gpt_validate_move_child(partition_data * partition,partition_data * child,off_t * start)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
efi_gpt_validate_set_name(partition_data * partition,char * name)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
efi_gpt_validate_set_type(partition_data * partition,const char * type)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
efi_gpt_validate_initialize(partition_data * partition,char * name,const char * parameters)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
efi_gpt_validate_create_child(partition_data * partition,off_t * start,off_t * size,const char * type,const char * name,const char * parameters,int32 * index)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
efi_gpt_get_partitionable_spaces(partition_data * partition,partitionable_space_data * buffer,int32 count,int32 * actualCount)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
efi_gpt_get_next_supported_type(partition_data * partition,int32 * cookie,char * type)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
efi_gpt_shadow_changed(partition_data * partition,partition_data * child,uint32 operation)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
efi_gpt_repair(int fd,partition_id partition,bool checkOnly,disk_job_id job)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
efi_gpt_resize(int fd,partition_id partitionID,off_t size,disk_job_id job)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
efi_gpt_resize_child(int fd,partition_id partitionID,off_t size,disk_job_id job)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
efi_gpt_move(int fd,partition_id partition,off_t offset,disk_job_id job)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
efi_gpt_move_child(int fd,partition_id partitionID,partition_id childID,off_t offset,disk_job_id job)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
efi_gpt_set_name(int fd,partition_id partitionID,const char * name,disk_job_id job)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
efi_gpt_set_type(int fd,partition_id partitionID,const char * type,disk_job_id job)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
efi_gpt_initialize(int fd,partition_id partitionID,const char * name,const char * parameters,off_t partitionSize,disk_job_id job)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
efi_gpt_uninitialize(int fd,partition_id partitionID,off_t partitionSize,uint32 blockSize,disk_job_id job)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
efi_gpt_create_child(int fd,partition_id partitionID,off_t offset,off_t size,const char * type,const char * name,const char * parameters,disk_job_id job,partition_id * childID)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
efi_gpt_delete_child(int fd,partition_id partitionID,partition_id childID,disk_job_id job)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