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