xref: /haiku/src/add-ons/kernel/partitioning_systems/intel/intel.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 //----------------------------------------------------------------------
2 //  This software is part of the Haiku distribution and is covered
3 //  by the Haiku license.
4 //---------------------------------------------------------------------
5 /*!
6 	\file intel.cpp
7 	\brief disk_scanner partition module for "intel" style partitions.
8 */
9 
10 // TODO: The implementation is very strict right now. It rejects a partition
11 // completely, if it finds an error in its partition tables. We should see,
12 // what error can be handled gracefully, e.g. by ignoring the partition
13 // descriptor or the whole partition table sector.
14 
15 #include <errno.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include <util/kernel_cpp.h>
21 #include <AutoDeleter.h>
22 #include <ddm_modules.h>
23 #ifndef _BOOT_MODE
24 #	include <DiskDeviceTypes.h>
25 #else
26 #	include <boot/partitions.h>
27 //#	include "DiskDeviceUtils.h"
28 #endif
29 #include <KernelExport.h>
30 
31 #include "PartitionMap.h"
32 #include "PartitionMapParser.h"
33 
34 #define TRACE(x) ;
35 //#define TRACE(x) dprintf x
36 
37 // module names
38 #define INTEL_PARTITION_MODULE_NAME "partitioning_systems/intel/map/v1"
39 #define INTEL_EXTENDED_PARTITION_MODULE_NAME "partitioning_systems/intel/extended/v1"
40 
41 // no atomic_add() in the boot loader
42 #ifdef _BOOT_MODE
43 
44 inline int32
45 atomic_add(int32 *a, int32 num)
46 {
47 	int32 oldA = *a;
48 	*a += num;
49 	return oldA;
50 }
51 
52 #endif
53 
54 
55 // A PartitionMap with reference count.
56 struct PartitionMapCookie : PartitionMap {
57 	int32	ref_count;
58 };
59 
60 
61 // intel partition map module
62 
63 // module
64 static status_t pm_std_ops(int32 op, ...);
65 
66 // scanning
67 static float pm_identify_partition(int fd, partition_data *partition,
68 		void **cookie);
69 static status_t pm_scan_partition(int fd, partition_data *partition,
70 		void *cookie);
71 static void pm_free_identify_partition_cookie(partition_data *partition,
72 		void *cookie);
73 static void pm_free_partition_cookie(partition_data *partition);
74 static void pm_free_partition_content_cookie(partition_data *partition);
75 
76 #ifndef _BOOT_MODE
77 // querying
78 static bool pm_supports_resizing_child(partition_data *partition,
79 									   partition_data *child);
80 
81 static bool pm_validate_resize_child(partition_data *partition,
82 									 partition_data *child, off_t *size);
83 
84 // writing
85 static status_t pm_resize_child(int fd, partition_id partition, off_t size,
86 								disk_job_id job);
87 #endif
88 
89 #ifdef _BOOT_MODE
90 partition_module_info gIntelPartitionMapModule =
91 #else
92 static partition_module_info intel_partition_map_module =
93 #endif
94 {
95 	{
96 		INTEL_PARTITION_MODULE_NAME,
97 		0,
98 		pm_std_ops
99 	},
100 	INTEL_PARTITION_NAME,				// pretty_name
101 	0,									// flags
102 
103 	// scanning
104 	pm_identify_partition,				// identify_partition
105 	pm_scan_partition,					// scan_partition
106 	pm_free_identify_partition_cookie,	// free_identify_partition_cookie
107 	pm_free_partition_cookie,			// free_partition_cookie
108 	pm_free_partition_content_cookie,	// free_partition_content_cookie
109 
110 #ifndef _BOOT_MODE
111 	// querying
112 	NULL,								// supports_repairing
113 	NULL,								// supports_resizing
114 	pm_supports_resizing_child,			// supports_resizing_child
115 	NULL,								// supports_moving
116 	NULL,								// supports_moving_child
117 	NULL,								// supports_setting_name
118 	NULL,								// supports_setting_content_name
119 	NULL,								// supports_setting_type
120 	NULL,								// supports_setting_parameters
121 	NULL,								// supports_setting_content_parameters
122 	NULL,								// supports_initializing
123 	NULL,								// supports_initializing_child
124 	NULL,								// supports_creating_child
125 	NULL,								// supports_deleting_child
126 	NULL,								// is_sub_system_for
127 
128 	NULL,								// validate_resize
129 	pm_validate_resize_child,			// validate_resize_child
130 	NULL,								// validate_move
131 	NULL,								// validate_move_child
132 	NULL,								// validate_set_name
133 	NULL,								// validate_set_content_name
134 	NULL,								// validate_set_type
135 	NULL,								// validate_set_parameters
136 	NULL,								// validate_set_content_parameters
137 	NULL,								// validate_initialize
138 	NULL,								// validate_create_child
139 	NULL,								// get_partitionable_spaces
140 	NULL,								// get_next_supported_type
141 	NULL,								// get_type_for_content_type
142 
143 	// shadow partition modification
144 	NULL,								// shadow_changed
145 
146 	// writing
147 	NULL,								// repair
148 	NULL,								// resize
149 	pm_resize_child,					// resize_child
150 	NULL,								// move
151 	NULL,								// move_child
152 	NULL,								// set_name
153 	NULL,								// set_content_name
154 	NULL,								// set_type
155 	NULL,								// set_parameters
156 	NULL,								// set_content_parameters
157 	NULL,								// initialize
158 	NULL,								// create_child
159 	NULL,								// delete_child
160 #else
161 	NULL
162 #endif	// _BOOT_MODE
163 };
164 
165 
166 // intel extended partition module
167 
168 // module
169 static status_t ep_std_ops(int32 op, ...);
170 
171 // scanning
172 static float ep_identify_partition(int fd, partition_data *partition,
173 		void **cookie);
174 static status_t ep_scan_partition(int fd, partition_data *partition,
175 		void *cookie);
176 static void ep_free_identify_partition_cookie(partition_data *partition,
177 		void *cookie);
178 static void ep_free_partition_cookie(partition_data *partition);
179 static void ep_free_partition_content_cookie(partition_data *partition);
180 
181 #ifdef _BOOT_MODE
182 partition_module_info gIntelExtendedPartitionModule =
183 #else
184 static partition_module_info intel_extended_partition_module =
185 #endif
186 {
187 	{
188 		INTEL_EXTENDED_PARTITION_MODULE_NAME,
189 		0,
190 		ep_std_ops
191 	},
192 	INTEL_EXTENDED_PARTITION_NAME,		// pretty_name
193 	0,									// flags
194 
195 	// scanning
196 	ep_identify_partition,				// identify_partition
197 	ep_scan_partition,					// scan_partition
198 	ep_free_identify_partition_cookie,	// free_identify_partition_cookie
199 	ep_free_partition_cookie,			// free_partition_cookie
200 	ep_free_partition_content_cookie,	// free_partition_content_cookie
201 
202 #ifndef _BOOT_MODE
203 	// querying
204 	NULL,								// supports_repairing
205 	NULL,								// supports_resizing
206 	NULL,								// supports_resizing_child
207 	NULL,								// supports_moving
208 	NULL,								// supports_moving_child
209 	NULL,								// supports_setting_name
210 	NULL,								// supports_setting_content_name
211 	NULL,								// supports_setting_type
212 	NULL,								// supports_setting_parameters
213 	NULL,								// supports_setting_content_parameters
214 	NULL,								// supports_initializing
215 	NULL,								// supports_initializing_child
216 	NULL,								// supports_creating_child
217 	NULL,								// supports_deleting_child
218 	NULL,								// is_sub_system_for
219 
220 	NULL,								// validate_resize
221 	NULL,								// validate_resize_child
222 	NULL,								// validate_move
223 	NULL,								// validate_move_child
224 	NULL,								// validate_set_name
225 	NULL,								// validate_set_content_name
226 	NULL,								// validate_set_type
227 	NULL,								// validate_set_parameters
228 	NULL,								// validate_set_content_parameters
229 	NULL,								// validate_initialize
230 	NULL,								// validate_create_child
231 	NULL,								// get_partitionable_spaces
232 	NULL,								// get_next_supported_type
233 	NULL,								// get_type_for_content_type
234 
235 	// shadow partition modification
236 	NULL,								// shadow_changed
237 
238 	// writing
239 	NULL,								// repair
240 	NULL,								// resize
241 	NULL,								// resize_child
242 	NULL,								// move
243 	NULL,								// move_child
244 	NULL,								// set_name
245 	NULL,								// set_content_name
246 	NULL,								// set_type
247 	NULL,								// set_parameters
248 	NULL,								// set_content_parameters
249 	NULL,								// initialize
250 	NULL,								// create_child
251 	NULL,								// delete_child
252 #else
253 	NULL
254 #endif	// _BOOT_MODE
255 };
256 
257 
258 #ifndef _BOOT_MODE
259 extern "C" partition_module_info *modules[];
260 _EXPORT partition_module_info *modules[] =
261 {
262 	&intel_partition_map_module,
263 	&intel_extended_partition_module,
264 	NULL
265 };
266 #endif
267 
268 
269 // intel partition map module
270 
271 // pm_std_ops
272 static
273 status_t
274 pm_std_ops(int32 op, ...)
275 {
276 	TRACE(("intel: pm_std_ops(0x%lx)\n", op));
277 	switch(op) {
278 		case B_MODULE_INIT:
279 		case B_MODULE_UNINIT:
280 			return B_OK;
281 	}
282 	return B_ERROR;
283 }
284 
285 // pm_identify_partition
286 static
287 float
288 pm_identify_partition(int fd, partition_data *partition, void **cookie)
289 {
290 	// check parameters
291 	if (fd < 0 || !partition || !cookie)
292 		return -1;
293 
294 	TRACE(("intel: pm_identify_partition(%d, %ld: %lld, %lld, %ld)\n", fd,
295 		   partition->id, partition->offset, partition->size,
296 		   partition->block_size));
297 
298 	// reject extended partitions
299 	if (partition->type
300 		&& !strcmp(partition->type, kPartitionTypeIntelExtended)) {
301 		return -1;
302 	}
303 
304 	// check block size
305 	uint32 blockSize = partition->block_size;
306 	if (blockSize < sizeof(partition_table_sector)) {
307 		TRACE(("intel: read_partition_map: bad block size: %ld, should be "
308 			   ">= %ld\n", blockSize, sizeof(partition_table_sector)));
309 		return -1;
310 	}
311 
312 	// allocate a PartitionMap
313 	PartitionMapCookie *map = new(nothrow) PartitionMapCookie;
314 	if (!map)
315 		return -1;
316 	map->ref_count = 1;
317 
318 	// read the partition structure
319 	PartitionMapParser parser(fd, 0, partition->size, blockSize);
320 	status_t error = parser.Parse(NULL, map);
321 	if (error == B_OK) {
322 		*cookie = map;
323 		return 0.5;
324 	}
325 
326 	// cleanup, if not detected
327 	delete map;
328 	return -1;
329 }
330 
331 // pm_scan_partition
332 static
333 status_t
334 pm_scan_partition(int fd, partition_data *partition, void *cookie)
335 {
336 	// check parameters
337 	if (fd < 0 || !partition || !cookie)
338 		return B_ERROR;
339 
340 	TRACE(("intel: pm_scan_partition(%d, %ld: %lld, %lld, %ld)\n", fd,
341 		   partition->id, partition->offset, partition->size,
342 		   partition->block_size));
343 
344 	PartitionMapCookie *map = (PartitionMapCookie*)cookie;
345 	// fill in the partition_data structure
346 	partition->status = B_PARTITION_VALID;
347 	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM
348 						| B_PARTITION_READ_ONLY
349 						| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD;
350 		// TODO: Update when write functionality is implemented.
351 	partition->content_size = partition->size;
352 	// (no content_name and content_parameters)
353 	// (content_type is set by the system)
354 	partition->content_cookie = map;
355 	// children
356 	status_t error = B_OK;
357 	int32 index = 0;
358 	for (int32 i = 0; i < 4; i++) {
359 		PrimaryPartition *primary = map->PrimaryPartitionAt(i);
360 		if (!primary->IsEmpty()) {
361 			partition_data *child = create_child_partition(partition->id,
362 														   index, -1);
363 			index++;
364 			if (!child) {
365 				// something went wrong
366 				error = B_ERROR;
367 				break;
368 			}
369 			child->offset = partition->offset + primary->Offset();
370 			child->size = primary->Size();
371 			child->block_size = partition->block_size;
372 			// (no name)
373 			char type[B_FILE_NAME_LENGTH];
374 			primary->GetTypeString(type);
375 			child->type = strdup(type);
376 			// parameters
377 			char buffer[128];
378 			sprintf(buffer, "type = %u ; active = %d", primary->Type(),
379 					primary->Active());
380 			child->parameters = strdup(buffer);
381 			child->cookie = primary;
382 			// check for allocation problems
383 			if (!child->type || !child->parameters) {
384 				error = B_NO_MEMORY;
385 				break;
386 			}
387 		}
388 	}
389 
390 	// keep map on success or cleanup on error
391 	if (error == B_OK) {
392 		atomic_add(&map->ref_count, 1);
393 	} else {
394 		partition->content_cookie = NULL;
395 		for (int32 i = 0; i < partition->child_count; i++) {
396 			if (partition_data *child = get_child_partition(partition->id, i))
397 				child->cookie = NULL;
398 		}
399 	}
400 	return error;
401 }
402 
403 // pm_free_identify_partition_cookie
404 static
405 void
406 pm_free_identify_partition_cookie(partition_data */*partition*/, void *cookie)
407 {
408 	if (cookie) {
409 		PartitionMapCookie *map = (PartitionMapCookie*)cookie;
410 		if (atomic_add(&map->ref_count, -1) == 1)
411 			delete map;
412 	}
413 }
414 
415 // pm_free_partition_cookie
416 static
417 void
418 pm_free_partition_cookie(partition_data *partition)
419 {
420 	// called for the primary partitions: the PrimaryPartition is allocated
421 	// by the partition containing the partition map
422 	if (partition)
423 		partition->cookie = NULL;
424 }
425 
426 // pm_free_partition_content_cookie
427 static
428 void
429 pm_free_partition_content_cookie(partition_data *partition)
430 {
431 	if (partition && partition->content_cookie) {
432 		pm_free_identify_partition_cookie(partition, partition->content_cookie);
433 		partition->content_cookie = NULL;
434 	}
435 }
436 
437 #ifndef _BOOT_MODE
438 // pm_supports_resizing_child
439 static
440 bool
441 pm_supports_resizing_child(partition_data *partition, partition_data *child)
442 {
443 	return (partition && child && partition->content_type
444 			&& !strcmp(partition->content_type, kPartitionTypeIntel));
445 }
446 
447 // pm_validate_resize_child
448 static
449 bool
450 pm_validate_resize_child(partition_data *partition, partition_data *child,
451 						 off_t *size)
452 {
453 	if (!partition || !child || !partition->content_type
454 		|| strcmp(partition->content_type, kPartitionTypeIntel)
455 		|| !size) {
456 		return false;
457 	}
458 	// size remains the same?
459 	if (*size == child->size)
460 		return true;
461 	// shrink partition?
462 	if (*size < child->size) {
463 		if (*size < 0)
464 			*size = 0;
465 		// make the size a multiple of the block size
466 		*size = *size / partition->block_size * partition->block_size;
467 		return true;
468 	}
469 	// grow partition
470 	// child must completely lie within the parent partition
471 	if (child->offset + *size > partition->offset + partition->size)
472 		*size = partition->offset + partition->size - child->offset;
473 	// child must not intersect with sibling partitions
474 	for (int32 i = 0; i < partition->child_count; i++) {
475 		partition_data *sibling = get_child_partition(partition->id, i);
476 		if (sibling && sibling != child && sibling->offset > child->offset) {
477 			if (child->offset + *size > sibling->offset)
478 				*size = sibling->offset - child->offset;
479 		}
480 	}
481 	// make the size a multiple of the block size
482 	*size = *size / partition->block_size * partition->block_size;
483 	return true;
484 }
485 
486 // pm_resize_child
487 static
488 status_t
489 pm_resize_child(int fd, partition_id partitionID, off_t size, disk_job_id job)
490 {
491 /*
492 	disk_device_data *device = read_lock_disk_device(partitionID);
493 	if (!device)
494 		return B_BAD_VALUE;
495 	DeviceStructWriteLocker locker(device, true);
496 	// get partition and child and out partition map structure
497 	partition_data *partition = get_parent_partition(partitionID);
498 	partition_data *child = get_partition(partitionID);
499 	if (!partition || !child)
500 		return B_BAD_VALUE;
501 	PartitionMap *map = (PartitionMap*)partition->content_cookie;
502 	PrimaryPartition *primary = child->cookie;
503 	if (!map || !primary)
504 		return B_BAD_VALUE;
505 	// validate the new size
506 	off_t validatedSize = size;
507 	if (!pm_validate_resize_child(partition, child, validatedSize)
508 		return B_BAD_VALUE;
509 	if (child->size == validatedSize)
510 		return B_OK;
511 	// write the changes to disk
512 	primary->SetSize(validatedSize);
513 	// TODO: Write...
514 	// TODO: PartitionMapParser into separate file,
515 	//       implement PartitionMapWriter
516 */
517 set_disk_device_job_error_message(job, "Resize not implemented yet.");
518 return B_ERROR;
519 }
520 #endif	// _BOOT_MODE
521 
522 
523 // intel extended partition module
524 
525 // ep_std_ops
526 static
527 status_t
528 ep_std_ops(int32 op, ...)
529 {
530 	TRACE(("intel: ep_std_ops(0x%lx)\n", op));
531 	switch(op) {
532 		case B_MODULE_INIT:
533 		case B_MODULE_UNINIT:
534 			return B_OK;
535 	}
536 	return B_ERROR;
537 }
538 
539 // ep_identify_partition
540 static
541 float
542 ep_identify_partition(int fd, partition_data *partition, void **cookie)
543 {
544 	// check parameters
545 	if (fd < 0 || !partition || !cookie || !partition->cookie)
546 		return -1;
547 
548 	TRACE(("intel: ep_identify_partition(%d, %lld, %lld, %ld)\n", fd,
549 		   partition->offset, partition->size, partition->block_size));
550 
551 	// our parent must be a intel partition map partition and we must have
552 	// extended partition type
553 	if (!partition->type
554 		|| strcmp(partition->type, kPartitionTypeIntelExtended)) {
555 		return -1;
556 	}
557 	partition_data *parent = get_parent_partition(partition->id);
558 	if (!parent || !parent->content_type
559 		|| strcmp(parent->content_type, kPartitionTypeIntel)) {
560 		return -1;
561 	}
562 
563 	// things seem to be in order
564 	return 0.5;
565 }
566 
567 // ep_scan_partition
568 static
569 status_t
570 ep_scan_partition(int fd, partition_data *partition, void *cookie)
571 {
572 	// check parameters
573 	if (fd < 0 || !partition || !partition->cookie)
574 		return B_ERROR;
575 
576 	TRACE(("intel: ep_scan_partition(%d, %lld, %lld, %ld)\n", fd,
577 		   partition->offset, partition->size, partition->block_size));
578 
579 	partition_data *parent = get_parent_partition(partition->id);
580 	if (!parent)
581 		return B_ERROR;
582 	PrimaryPartition *primary = (PrimaryPartition*)partition->cookie;
583 	// fill in the partition_data structure
584 	partition->status = B_PARTITION_VALID;
585 	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM
586 						| B_PARTITION_READ_ONLY;
587 		// TODO: Update when write functionality is implemented.
588 	partition->content_size = partition->size;
589 	// (no content_name and content_parameters)
590 	// (content_type is set by the system)
591 	partition->content_cookie = primary;
592 	// children
593 	status_t error = B_OK;
594 	int32 index = 0;
595 	for (int32 i = 0; i < primary->CountLogicalPartitions(); i++) {
596 		LogicalPartition *logical = primary->LogicalPartitionAt(i);
597 		partition_data *child = create_child_partition(partition->id,
598 													   index, -1);
599 		index++;
600 		if (!child) {
601 			// something went wrong
602 			TRACE(("intel: ep_scan_partition(): failed to create child "
603 				"partition\n"));
604 			error = B_ERROR;
605 			break;
606 		}
607 		child->offset = parent->offset + logical->Offset();
608 		child->size = logical->Size();
609 		child->block_size = partition->block_size;
610 		// (no name)
611 		char type[B_FILE_NAME_LENGTH];
612 		logical->GetTypeString(type);
613 		child->type = strdup(type);
614 
615 		// parameters
616 		char buffer[128];
617 		sprintf(buffer, "type = %u ; active = %d", logical->Type(),
618 				logical->Active());
619 		child->parameters = strdup(buffer);
620 		child->cookie = logical;
621 		// check for allocation problems
622 		if (!child->type || !child->parameters) {
623 			TRACE(("intel: ep_scan_partition(): failed to allocation type "
624 				"or parameters\n"));
625 			error = B_NO_MEMORY;
626 			break;
627 		}
628 	}
629 	// cleanup on error
630 	if (error != B_OK) {
631 		partition->content_cookie = NULL;
632 		for (int32 i = 0; i < partition->child_count; i++) {
633 			if (partition_data *child = get_child_partition(partition->id, i))
634 				child->cookie = NULL;
635 		}
636 	}
637 	return error;
638 }
639 
640 // ep_free_identify_partition_cookie
641 static
642 void
643 ep_free_identify_partition_cookie(partition_data *partition, void *cookie)
644 {
645 	// nothing to do
646 }
647 
648 // ep_free_partition_cookie
649 static
650 void
651 ep_free_partition_cookie(partition_data *partition)
652 {
653 	// the logical partition's cookie belongs to the partition map partition
654 	if (partition)
655 		partition->cookie = NULL;
656 }
657 
658 // ep_free_partition_content_cookie
659 static
660 void
661 ep_free_partition_content_cookie(partition_data *partition)
662 {
663 	// the extended partition's cookie belongs to the partition map partition
664 	if (partition)
665 		partition->content_cookie = NULL;
666 }
667 
668