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