1/*
2 * Copyright 2003-2009, 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 *		Tomas Kucera, kucerat@centrum.cz
8 */
9
10/*!
11	\file intel.cpp
12	\brief partitioning system module for "intel" style partitions.
13*/
14
15// TODO: The implementation is very strict right now. It rejects a partition
16// completely, if it finds an error in its partition tables. We should see,
17// what error can be handled gracefully, e.g. by ignoring the partition
18// descriptor or the whole partition table sector.
19
20#include <errno.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include <new>
26
27#include <KernelExport.h>
28
29#include <AutoDeleter.h>
30#include <disk_device_manager/ddm_modules.h>
31
32#include "intel.h"
33#include "PartitionLocker.h"
34#include "PartitionMap.h"
35#include "PartitionMapParser.h"
36
37#ifndef _BOOT_MODE
38#	include <DiskDeviceTypes.h>
39#	include "write_support.h"
40#	define TRACE(x) dprintf x
41#else
42#	include <boot/partitions.h>
43#	include <util/kernel_cpp.h>
44#	define TRACE(x) ;
45#endif
46
47
48// module names
49#define INTEL_PARTITION_MODULE_NAME "partitioning_systems/intel/map/v1"
50#define INTEL_EXTENDED_PARTITION_MODULE_NAME \
51	"partitioning_systems/intel/extended/v1"
52
53
54using std::nothrow;
55
56
57#ifndef _BOOT_MODE
58
59// get_type_for_content_type (for both pm_* and ep_*)
60static status_t
61get_type_for_content_type(const char* contentType, char* type)
62{
63	TRACE(("intel: get_type_for_content_type(%s)\n",
64		   contentType));
65
66	if (!contentType || !type)
67		return B_BAD_VALUE;
68
69	PartitionType ptype;
70	ptype.SetContentType(contentType);
71	if (!ptype.IsValid())
72		return B_NAME_NOT_FOUND;
73
74	ptype.GetTypeString(type);
75	return B_OK;
76}
77
78#endif
79
80
81// #pragma mark - Intel Partition Map Module
82
83
84// pm_std_ops
85static status_t
86pm_std_ops(int32 op, ...)
87{
88	TRACE(("intel: pm_std_ops(0x%" B_PRIx32 ")\n", op));
89	switch(op) {
90		case B_MODULE_INIT:
91		case B_MODULE_UNINIT:
92			return B_OK;
93	}
94	return B_ERROR;
95}
96
97
98// pm_identify_partition
99static float
100pm_identify_partition(int fd, partition_data* partition, void** cookie)
101{
102	// check parameters
103	if (fd < 0 || !partition || !cookie)
104		return -1;
105
106	TRACE(("intel: pm_identify_partition(%d, %" B_PRId32 ": %" B_PRId64 ", "
107		"%" B_PRId64 ", %" B_PRId32 ")\n", fd, partition->id, partition->offset,
108		partition->size, partition->block_size));
109	// reject extended partitions
110	if (partition->type
111		&& !strcmp(partition->type, kPartitionTypeIntelExtended)) {
112		return -1;
113	}
114
115	// allocate a PartitionMap
116	PartitionMapCookie* map = new(nothrow) PartitionMapCookie;
117	if (!map)
118		return -1;
119
120	// read the partition structure
121	PartitionMapParser parser(fd, 0, partition->size, partition->block_size);
122	status_t error = parser.Parse(NULL, map);
123	if (error != B_OK) {
124		// cleanup, if not detected
125		delete map;
126		return -1;
127	}
128
129	*cookie = map;
130
131	// Depending on whether we actually have recognized child partitions and
132	// whether we are installed directly on a device (the by far most common
133	// setup), we determine the priority.
134	bool hasChildren = (map->CountNonEmptyPartitions() > 0);
135	bool hasParent = (get_parent_partition(partition->id) != NULL);
136
137	if (!hasParent) {
138		if (hasChildren) {
139			// This value overrides BFS.
140			return 0.81;
141		}
142
143		// No children -- might be a freshly initialized disk. But it could
144		// also be an image file. So we give BFS a chance to override us.
145		return 0.5;
146	}
147
148// NOTE: It seems supporting nested partition maps makes more trouble than it
149// has useful applications ATM. So it is disabled for the time being.
150#if 0
151	// We have a parent. That's a very unlikely setup.
152	if (hasChildren)
153		return 0.4;
154
155	// No children. Extremely unlikely, that this is desired. But if no one
156	// else claims the partition, we take it anyway.
157	return 0.1;
158#endif
159	return -1;
160}
161
162
163// pm_scan_partition
164static status_t
165pm_scan_partition(int fd, partition_data* partition, void* cookie)
166{
167	// check parameters
168	if (fd < 0 || !partition || !cookie)
169		return B_ERROR;
170
171	TRACE(("intel: pm_scan_partition(%d, %" B_PRId32 ": %" B_PRId64 ", "
172		"%" B_PRId64 ", %" B_PRId32 ")\n", fd, partition->id, partition->offset,
173		partition->size, partition->block_size));
174
175	PartitionMapCookie* map = (PartitionMapCookie*)cookie;
176	// fill in the partition_data structure
177	partition->status = B_PARTITION_VALID;
178	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
179	partition->content_size = partition->size;
180	// (no content_name and content_parameters)
181	// (content_type is set by the system)
182
183	partition->content_cookie = map;
184	// children
185	status_t error = B_OK;
186	int32 index = 0;
187	for (int32 i = 0; i < 4; i++) {
188		PrimaryPartition* primary = map->PrimaryPartitionAt(i);
189		if (!primary->IsEmpty()) {
190			partition_data* child = create_child_partition(partition->id,
191				index, partition->offset + primary->Offset(), primary->Size(),
192				-1);
193			index++;
194			if (!child) {
195				// something went wrong
196				error = B_ERROR;
197				break;
198			}
199
200			child->block_size = partition->block_size;
201
202			// (no name)
203			char type[B_FILE_NAME_LENGTH];
204			primary->GetTypeString(type);
205			child->type = strdup(type);
206			// parameters
207			char buffer[128];
208			sprintf(buffer, "type = %u ; active = %d", primary->Type(),
209				primary->Active());
210			child->parameters = strdup(buffer);
211			child->cookie = primary;
212			// check for allocation problems
213			if (!child->type || !child->parameters) {
214				error = B_NO_MEMORY;
215				break;
216			}
217		}
218	}
219
220	// keep map on success or cleanup on error
221	if (error == B_OK) {
222		atomic_add(&map->ref_count, 1);
223	} else {
224		partition->content_cookie = NULL;
225		for (int32 i = 0; i < partition->child_count; i++) {
226			if (partition_data* child = get_child_partition(partition->id, i))
227				child->cookie = NULL;
228		}
229	}
230
231	return error;
232}
233
234
235// pm_free_identify_partition_cookie
236static void
237pm_free_identify_partition_cookie(partition_data*/* partition*/, void* cookie)
238{
239	if (cookie) {
240		PartitionMapCookie* map = (PartitionMapCookie*)cookie;
241		if (atomic_add(&map->ref_count, -1) == 1)
242			delete map;
243	}
244}
245
246
247// pm_free_partition_cookie
248static void
249pm_free_partition_cookie(partition_data* partition)
250{
251	// called for the primary partitions: the PrimaryPartition is allocated
252	// by the partition containing the partition map
253	if (partition)
254		partition->cookie = NULL;
255}
256
257
258// pm_free_partition_content_cookie
259static void
260pm_free_partition_content_cookie(partition_data* partition)
261{
262	if (partition && partition->content_cookie) {
263		pm_free_identify_partition_cookie(partition, partition->content_cookie);
264		partition->content_cookie = NULL;
265	}
266}
267
268
269// #pragma mark - Intel Extended Partition Module
270
271
272// ep_std_ops
273static status_t
274ep_std_ops(int32 op, ...)
275{
276	TRACE(("intel: ep_std_ops(0x%" B_PRIx32 ")\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
286// ep_identify_partition
287static float
288ep_identify_partition(int fd, partition_data* partition, void** cookie)
289{
290	// check parameters
291	if (fd < 0 || !partition || !cookie || !partition->cookie)
292		return -1;
293
294	TRACE(("intel: ep_identify_partition(%d, %" B_PRId64 ", %" B_PRId64 ", "
295		"%" B_PRId32 ")\n", fd, partition->offset, partition->size,
296		partition->block_size));
297
298	// our parent must be a intel partition map partition and we must have
299	// extended partition type
300	if (!partition->type
301		|| strcmp(partition->type, kPartitionTypeIntelExtended)) {
302		return -1;
303	}
304	partition_data* parent = get_parent_partition(partition->id);
305	if (!parent || !parent->content_type
306		|| strcmp(parent->content_type, kPartitionTypeIntel)) {
307		return -1;
308	}
309
310	// things seem to be in order
311	return 0.95;
312}
313
314
315// ep_scan_partition
316static status_t
317ep_scan_partition(int fd, partition_data* partition, void* cookie)
318{
319	// check parameters
320	if (fd < 0 || !partition || !partition->cookie)
321		return B_ERROR;
322
323	TRACE(("intel: ep_scan_partition(%d, %" B_PRId64 ", %" B_PRId64 ", "
324		"%" B_PRId32 ")\n", fd, partition->offset, partition->size,
325		partition->block_size));
326
327	partition_data* parent = get_parent_partition(partition->id);
328	if (!parent)
329		return B_ERROR;
330
331	PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
332	// fill in the partition_data structure
333	partition->status = B_PARTITION_VALID;
334	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
335	partition->content_size = partition->size;
336	// (no content_name and content_parameters)
337	// (content_type is set by the system)
338
339	partition->content_cookie = primary;
340	// children
341	status_t error = B_OK;
342	int32 index = 0;
343	for (int32 i = 0; i < primary->CountLogicalPartitions(); i++) {
344		LogicalPartition* logical = primary->LogicalPartitionAt(i);
345		partition_data* child = create_child_partition(partition->id, index,
346			parent->offset + logical->Offset(), logical->Size(), -1);
347		index++;
348		if (!child) {
349			// something went wrong
350			TRACE(("intel: ep_scan_partition(): failed to create child "
351				"partition\n"));
352			error = B_ERROR;
353			break;
354		}
355		child->block_size = partition->block_size;
356
357		// (no name)
358		char type[B_FILE_NAME_LENGTH];
359		logical->GetTypeString(type);
360		child->type = strdup(type);
361
362		// parameters
363		char buffer[128];
364		sprintf(buffer, "active %s ;\npartition_table_offset %" B_PRId64 " ;\n",
365			logical->Active() ? "true" : "false",
366			logical->PartitionTableOffset());
367		child->parameters = strdup(buffer);
368		child->cookie = logical;
369		// check for allocation problems
370		if (!child->type || !child->parameters) {
371			TRACE(("intel: ep_scan_partition(): failed to allocation type "
372				"or parameters\n"));
373			error = B_NO_MEMORY;
374			break;
375		}
376	}
377
378	// cleanup on error
379	if (error != B_OK) {
380		partition->content_cookie = NULL;
381		for (int32 i = 0; i < partition->child_count; i++) {
382			if (partition_data* child = get_child_partition(partition->id, i))
383				child->cookie = NULL;
384		}
385	}
386	return error;
387}
388
389
390// ep_free_identify_partition_cookie
391static void
392ep_free_identify_partition_cookie(partition_data* partition, void* cookie)
393{
394	// nothing to do
395}
396
397
398// ep_free_partition_cookie
399static void
400ep_free_partition_cookie(partition_data* partition)
401{
402	// the logical partition's cookie belongs to the partition map partition
403	if (partition)
404		partition->cookie = NULL;
405}
406
407
408// ep_free_partition_content_cookie
409static void
410ep_free_partition_content_cookie(partition_data* partition)
411{
412	// the extended partition's cookie belongs to the partition map partition
413	if (partition)
414		partition->content_cookie = NULL;
415}
416
417
418// #pragma mark - modules
419
420
421#ifdef _BOOT_MODE
422partition_module_info gIntelPartitionMapModule =
423#else
424static partition_module_info intel_partition_map_module =
425#endif
426{
427	{
428		INTEL_PARTITION_MODULE_NAME,
429		0,
430		pm_std_ops
431	},
432	"intel",							// short_name
433	INTEL_PARTITION_NAME,				// pretty_name
434
435	// flags
436	0
437//	| B_DISK_SYSTEM_SUPPORTS_CHECKING
438//	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
439	| B_DISK_SYSTEM_SUPPORTS_RESIZING
440	| B_DISK_SYSTEM_SUPPORTS_MOVING
441//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
442	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
443	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
444//	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
445
446	| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
447	| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
448//	| B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
449	| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
450//	| B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
451	| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
452	| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
453//	| B_DISK_SYSTEM_SUPPORTS_NAME
454	,
455
456	// scanning
457	pm_identify_partition,				// identify_partition
458	pm_scan_partition,					// scan_partition
459	pm_free_identify_partition_cookie,	// free_identify_partition_cookie
460	pm_free_partition_cookie,			// free_partition_cookie
461	pm_free_partition_content_cookie,	// free_partition_content_cookie
462
463#ifndef _BOOT_MODE
464	// querying
465	pm_get_supported_operations,		// get_supported_operations
466	pm_get_supported_child_operations,	// get_supported_child_operations
467	NULL,								// supports_initializing_child
468	pm_is_sub_system_for,				// is_sub_system_for
469
470	pm_validate_resize,					// validate_resize
471	pm_validate_resize_child,			// validate_resize_child
472	pm_validate_move,					// validate_move
473	pm_validate_move_child,				// validate_move_child
474	NULL,								// validate_set_name
475	NULL,								// validate_set_content_name
476	pm_validate_set_type,				// validate_set_type
477	NULL,								// validate_set_parameters
478	NULL,								// validate_set_content_parameters
479	pm_validate_initialize,				// validate_initialize
480	pm_validate_create_child,			// validate_create_child
481	pm_get_partitionable_spaces,		// get_partitionable_spaces
482	pm_get_next_supported_type,			// get_next_supported_type
483	get_type_for_content_type,			// get_type_for_content_type
484
485	// shadow partition modification
486	pm_shadow_changed,					// shadow_changed
487
488	// writing
489	NULL,								// repair
490	pm_resize,							// resize
491	pm_resize_child,					// resize_child
492	pm_move,							// move
493	pm_move_child,						// move_child
494	NULL,								// set_name
495	NULL,								// set_content_name
496	pm_set_type,						// set_type
497	pm_set_parameters,					// set_parameters
498	pm_set_parameters,					// set_content_parameters
499	pm_initialize,						// initialize
500	pm_uninitialize,					// uninitialize
501	pm_create_child,					// create_child
502	pm_delete_child,					// delete_child
503#else
504	NULL
505#endif	// _BOOT_MODE
506};
507
508
509#ifdef _BOOT_MODE
510partition_module_info gIntelExtendedPartitionModule =
511#else
512static partition_module_info intel_extended_partition_module =
513#endif
514{
515	{
516		INTEL_EXTENDED_PARTITION_MODULE_NAME,
517		0,
518		ep_std_ops
519	},
520	"intel_extended",					// short_name
521	INTEL_EXTENDED_PARTITION_NAME,		// pretty_name
522
523	// flags
524	0
525//	| B_DISK_SYSTEM_SUPPORTS_CHECKING
526//	| B_DISK_SYSTEM_SUPPORTS_REPAIRING
527	| B_DISK_SYSTEM_SUPPORTS_RESIZING
528	| B_DISK_SYSTEM_SUPPORTS_MOVING
529//	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
530	| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
531	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
532//	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
533
534	| B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
535	| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
536//	| B_DISK_SYSTEM_SUPPORTS_SETTING_NAME
537	| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
538//	| B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS
539	| B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD
540	| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD
541//	| B_DISK_SYSTEM_SUPPORTS_NAME
542	,
543
544	// scanning
545	ep_identify_partition,				// identify_partition
546	ep_scan_partition,					// scan_partition
547	ep_free_identify_partition_cookie,	// free_identify_partition_cookie
548	ep_free_partition_cookie,			// free_partition_cookie
549	ep_free_partition_content_cookie,	// free_partition_content_cookie
550
551#ifndef _BOOT_MODE
552	// querying
553	ep_get_supported_operations,		// get_supported_operations
554	ep_get_supported_child_operations,	// get_supported_child_operations
555	NULL,								// supports_initializing_child
556	ep_is_sub_system_for,				// is_sub_system_for
557
558	ep_validate_resize,					// validate_resize
559	ep_validate_resize_child,			// validate_resize_child
560	ep_validate_move,					// validate_move
561	ep_validate_move_child,				// validate_move_child
562	NULL,								// validate_set_name
563	NULL,								// validate_set_content_name
564	ep_validate_set_type,				// validate_set_type
565	NULL,								// validate_set_parameters
566	NULL,								// validate_set_content_parameters
567	ep_validate_initialize,				// validate_initialize
568	ep_validate_create_child,			// validate_create_child
569	ep_get_partitionable_spaces,		// get_partitionable_spaces
570	ep_get_next_supported_type,			// get_next_supported_type
571	get_type_for_content_type,			// get_type_for_content_type
572
573	// shadow partition modification
574	ep_shadow_changed,					// shadow_changed
575
576	// writing
577	NULL,								// repair
578	ep_resize,							// resize
579	ep_resize_child,					// resize_child
580	ep_move,							// move
581	ep_move_child,						// move_child
582	NULL,								// set_name
583	NULL,								// set_content_name
584	ep_set_type,						// set_type
585	NULL,								// set_parameters
586	NULL,								// set_content_parameters
587	ep_initialize,						// initialize
588	NULL,								// uninitialize
589	ep_create_child,					// create_child
590	ep_delete_child,					// delete_child
591#else	// _BOOT_MODE
592	NULL
593#endif	// _BOOT_MODE
594};
595
596
597#ifndef _BOOT_MODE
598extern "C" partition_module_info* modules[];
599_EXPORT partition_module_info* modules[] =
600{
601	&intel_partition_map_module,
602	&intel_extended_partition_module,
603	NULL
604};
605#endif
606