1/*
2 * Copyright 2003-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ingo Weinhold, ingo_weinhold@gmx.de
7 *		Tomas Kucera, kucerat@centrum.cz
8 */
9
10
11#include "write_support.h"
12
13#include <errno.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18#include <new>
19
20#include <DiskDeviceTypes.h>
21#include <KernelExport.h>
22
23#include <AutoDeleter.h>
24
25#include "intel.h"
26#include "PartitionLocker.h"
27#include "PartitionMap.h"
28#include "PartitionMapParser.h"
29#include "PartitionMapWriter.h"
30
31
32//#define TRACE(x) ;
33#define TRACE(x) dprintf x
34
35// Maximal size of move buffer (in sectors).
36static const int32 MAX_MOVE_BUFFER = 2 * 1024 * 4;
37
38// for logical partitions in Intel Extended Partition
39// Count of free sectors after Partition Table Sector (at logical partition).
40static const uint32 FREE_SECTORS_AFTER_PTS = 63;
41// Count of free sectors after Master Boot Record.
42static const uint32 FREE_SECTORS_AFTER_MBR = 63;
43// size of logical partition header in blocks
44static const uint32 PTS_OFFSET = FREE_SECTORS_AFTER_PTS + 1;
45static const uint32 MBR_OFFSET = FREE_SECTORS_AFTER_MBR + 1;
46
47
48typedef partitionable_space_data PartitionPosition;
49
50typedef void (*fc_get_sibling_partitions)(partition_data* partition,
51		partition_data* child, off_t childOffset, partition_data** prec,
52		partition_data** follow, off_t* prec_offset, off_t* prec_size,
53		off_t* follow_offset, off_t* follow_size);
54
55typedef int32 (*fc_fill_partitionable_spaces_buffer)(partition_data* partition,
56		PartitionPosition* positions);
57
58
59status_t pm_get_partitionable_spaces(partition_data* partition,
60	partitionable_space_data* buffer, int32 count, int32* actualCount);
61status_t ep_get_partitionable_spaces(partition_data* partition,
62	partitionable_space_data* buffer, int32 count, int32* actualCount);
63
64
65// #pragma mark - Intel Partition Map - support functions
66
67
68// pm_get_supported_operations
69uint32
70pm_get_supported_operations(partition_data* partition, uint32 mask)
71{
72	uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
73		| B_DISK_SYSTEM_SUPPORTS_MOVING
74		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
75		| B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
76
77	// creating child
78	int32 countSpaces = 0;
79	if (partition->child_count < 4
80		// free space check
81		&& pm_get_partitionable_spaces(partition, NULL, 0, &countSpaces)
82			== B_BUFFER_OVERFLOW
83		&& countSpaces > 0) {
84		flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
85	}
86
87	return flags;
88}
89
90
91// pm_get_supported_child_operations
92uint32
93pm_get_supported_child_operations(partition_data* partition,
94	partition_data* child, uint32 mask)
95{
96	return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
97		| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
98		| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
99		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
100}
101
102
103// pm_is_sub_system_for
104bool
105pm_is_sub_system_for(partition_data* partition)
106{
107	// primary partition map doesn't naturally live in any other child partition
108	return false;
109}
110
111bool
112get_partition_from_offset_ep(partition_data* partition, off_t offset,
113	partition_data** nextPartition)
114{
115	for (int32 i = 0; i < partition->child_count; i++) {
116		partition_data* sibling = get_child_partition(partition->id, i);
117		if (sibling != NULL && sibling->offset == offset) {
118			*nextPartition = sibling;
119			return true;
120		}
121	}
122
123	return false;
124}
125
126
127// #pragma mark - Intel Partition Map - validate functions
128
129
130// sector_align (auxiliary function)
131static inline off_t
132sector_align(off_t offset, int32 blockSize)
133{
134	return offset / blockSize * blockSize;
135}
136
137
138// sector_align_up (auxiliary function)
139static inline off_t
140sector_align_up(off_t offset, int32 blockSize)
141{
142	return (offset + blockSize - 1) / blockSize * blockSize;
143}
144
145
146// validate_resize (auxiliary function)
147static bool
148validate_resize(partition_data* partition, off_t* size)
149{
150	off_t newSize = *size;
151	// size remains the same?
152	if (newSize == partition->size)
153		return true;
154
155	if (newSize < 0)
156		newSize = 0;
157	else
158		newSize = sector_align(newSize, partition->block_size);
159
160	// grow partition?
161	if (newSize > partition->size) {
162		*size = newSize;
163		return true;
164	}
165
166	// shrink partition
167	// no child has to be over the new size of the parent partition
168	// TODO: shouldn't be just: off_t currentEnd = newSize; ??? probably not
169	// If child->offset is relative to parent, then yes!
170	off_t currentEnd = partition->offset + newSize;
171	for (int32 i = 0; i < partition->child_count; i++) {
172		partition_data* child = get_child_partition(partition->id, i);
173		if (child && child->offset + child->size > currentEnd)
174			currentEnd = child->offset + child->size;
175	}
176	newSize = currentEnd - partition->offset;
177	// make the size a multiple of the block size (greater one)
178	newSize = sector_align_up(newSize, partition->block_size);
179	*size = newSize;
180	return true;
181}
182
183
184// pm_validate_resize
185bool
186pm_validate_resize(partition_data* partition, off_t* size)
187{
188	TRACE(("intel: pm_validate_resize\n"));
189
190	if (!partition || !size)
191		return false;
192
193	return validate_resize(partition, size);
194}
195
196
197// get_sibling_partitions_pm (auxiliary function)
198/*!
199	according to childOffset returns previous and next sibling or NULL
200	precious, next output parameters
201	partition - Intel Partition Map
202*/
203static void
204get_sibling_partitions_pm(partition_data* partition,
205	partition_data* child, off_t childOffset, partition_data** previous,
206	partition_data** next, off_t* previousOffset, off_t* previousSize,
207	off_t* nextOffset, off_t* nextSize)
208{
209	// finding out sibling partitions
210	partition_data* previousSibling = NULL;
211	partition_data* nextSibling = NULL;
212	for (int32 i = 0; i < partition->child_count; i++) {
213		partition_data* sibling = get_child_partition(partition->id, i);
214		if (sibling && sibling != child) {
215			if (sibling->offset <= childOffset) {
216				if (!previousSibling || previousSibling->offset < sibling->offset)
217					previousSibling = sibling;
218			} else {
219				// sibling->offset > childOffset
220				if (!nextSibling || nextSibling->offset > sibling->offset)
221					nextSibling = sibling;
222			}
223		}
224	}
225	*previous = previousSibling;
226	*next = nextSibling;
227	if (previousSibling) {
228		*previousOffset = previousSibling->offset;
229		*previousSize = previousSibling->size;
230	}
231	if (nextSibling) {
232		*nextOffset = nextSibling->offset;
233		*nextSize = nextSibling->size;
234	}
235}
236
237
238// get_sibling_partitions_ep (auxiliary function)
239/*!
240	according to childOffset returns previous and next sibling or NULL
241	previous, next output parameters
242	partition - Intel Extended Partition
243*/
244static void
245get_sibling_partitions_ep(partition_data* partition,
246	partition_data* child, off_t childOffset, partition_data** previous,
247	partition_data** next, off_t* previousOffset, off_t* previousSize,
248	off_t* nextOffset, off_t* nextSize)
249{
250	// finding out sibling partitions
251	partition_data* previousSibling = NULL;
252	partition_data* nextSibling = NULL;
253	for (int32 i = 0; i < partition->child_count; i++) {
254		partition_data* sibling = get_child_partition(partition->id, i);
255		if (sibling && sibling != child) {
256			if (sibling->offset <= childOffset) {
257				if (!previousSibling || previousSibling->offset < sibling->offset)
258					previousSibling = sibling;
259			} else {
260				// get_offset_ep(sibling) > childOffset
261				if (!nextSibling || nextSibling->offset > sibling->offset)
262					nextSibling = sibling;
263			}
264		}
265	}
266	*previous = previousSibling;
267	*next = nextSibling;
268	if (previousSibling) {
269		*previousOffset = previousSibling->offset;
270		*previousSize = previousSibling->size;
271	}
272	if (nextSibling) {
273		*nextOffset = nextSibling->offset;
274		*nextSize = nextSibling->size;
275	}
276}
277
278
279// validate_resize_child (auxiliary function)
280static bool
281validate_resize_child(partition_data* partition, partition_data* child,
282	off_t childOffset, off_t childSize, off_t* size,
283	fc_get_sibling_partitions getSiblingPartitions)
284{
285	// size remains the same?
286	if (*size == childSize)
287		return true;
288	// shrink partition?
289	if (*size < childSize) {
290		if (*size < 0)
291			*size = 0;
292		// make the size a multiple of the block size
293		*size = sector_align(*size, partition->block_size);
294		return true;
295	}
296	// grow partition
297	// child must completely lie within the parent partition
298	if (childOffset + *size > partition->offset + partition->size)
299		*size = partition->offset + partition->size - childOffset;
300
301	// child must not intersect with sibling partitions
302	// finding out sibling partitions
303	partition_data* previousSibling = NULL;
304	partition_data* nextSibling = NULL;
305	off_t previousOffset = 0, previousSize = 0, nextOffset = 0, nextSize = 0;
306
307	getSiblingPartitions(partition, child, childOffset, &previousSibling,
308		&nextSibling, &previousOffset, &previousSize, &nextOffset, &nextSize);
309
310	if (nextSibling && (nextOffset < childOffset + *size))
311		*size = nextOffset - childOffset;
312	*size = sector_align(*size, partition->block_size);
313	return true;
314}
315
316
317// pm_validate_resize_child
318bool
319pm_validate_resize_child(partition_data* partition, partition_data* child,
320	off_t* size)
321{
322	TRACE(("intel: pm_validate_resize_child\n"));
323
324	if (!partition || !child || !size)
325		return false;
326
327	return validate_resize_child(partition, child, child->offset,
328		child->size, size, get_sibling_partitions_pm);
329}
330
331
332// pm_validate_move
333bool
334pm_validate_move(partition_data* partition, off_t* start)
335{
336	TRACE(("intel: pm_validate_move\n"));
337
338	if (!partition || !start)
339		return false;
340	// nothing to do here
341	return true;
342}
343
344
345// validate_move_child (auxiliary function)
346static bool
347validate_move_child(partition_data* partition, partition_data* child,
348	off_t childOffset, off_t childSize, off_t* _start,
349	fc_get_sibling_partitions getSiblingPartitions)
350{
351	off_t start = *_start;
352
353	if (start < 0)
354		start = 0;
355	else if (start + childSize > partition->size)
356		start = partition->size - childSize;
357
358	start = sector_align(start, partition->block_size);
359
360	// finding out sibling partitions
361	partition_data* previousSibling = NULL;
362	partition_data* nextSibling = NULL;
363	off_t previousOffset = 0, previousSize = 0, nextOffset = 0, nextSize = 0;
364
365	getSiblingPartitions(partition, child, childOffset, &previousSibling,
366		&nextSibling, &previousOffset, &previousSize, &nextOffset, &nextSize);
367
368	// we cannot move child over sibling partition
369	if (start < childOffset) {
370		// moving left
371		if (previousSibling && previousOffset + previousSize > start) {
372			start = previousOffset + previousSize;
373			start = sector_align_up(start, partition->block_size);
374		}
375	} else {
376		// moving right
377		if (nextSibling && nextOffset < start + childSize) {
378			start = nextOffset - childSize;
379			start = sector_align(start, partition->block_size);
380		}
381	}
382	*_start = start;
383	return true;
384}
385
386
387// pm_validate_move_child
388bool
389pm_validate_move_child(partition_data* partition, partition_data* child,
390	off_t* start)
391{
392	TRACE(("intel: pm_validate_move_child\n"));
393
394	if (!partition || !child || !start)
395		return false;
396	if (*start == child->offset)
397		return true;
398
399	return validate_move_child(partition, child, child->offset,
400		child->size, start, get_sibling_partitions_pm);
401}
402
403// is_type_valid_pm (auxiliary function)
404/*!
405	type has to be known, only one extended partition is allowed
406	partition - intel partition map
407	child can be NULL
408*/
409static bool
410is_type_valid_pm(const char* type, partition_data* partition,
411	PrimaryPartition* child = NULL)
412{
413	// validity check of the type
414	PartitionType ptype;
415	ptype.SetType(type);
416	if (!ptype.IsValid() || ptype.IsEmpty())
417		return false;
418
419	// only one extended partition is allowed
420	if (ptype.IsExtended()) {
421		PartitionMap* map = (PartitionMap*)partition->content_cookie;
422		if (!map)
423			return false;
424		for (int32 i = 0; i < partition->child_count; i++) {
425			PrimaryPartition* primary = map->PrimaryPartitionAt(i);
426			if (primary && primary->IsExtended() && primary != child)
427				return false;
428		}
429	}
430	return true;
431}
432
433
434// pm_validate_set_type
435bool
436pm_validate_set_type(partition_data* partition, const char* type)
437{
438	TRACE(("intel: pm_validate_set_type\n"));
439
440	if (!partition || !type)
441		return false;
442
443	partition_data* parent = get_parent_partition(partition->id);
444	if (!parent)
445		return false;
446	PrimaryPartition* child = (PrimaryPartition*)partition->cookie;
447	if (!child)
448		return false;
449
450	// validity check of the type
451	return is_type_valid_pm(type, parent, child);
452}
453
454// pm_validate_initialize
455bool
456pm_validate_initialize(partition_data* partition, char* name,
457	const char* parameters)
458{
459	TRACE(("intel: pm_validate_initialize\n"));
460
461	if (!partition || !(pm_get_supported_operations(partition)
462			& B_DISK_SYSTEM_SUPPORTS_INITIALIZING)) {
463		return false;
464	}
465
466	// name is ignored
467	if (name)
468		name[0] = '\0';
469
470	// parameters are ignored, too
471
472	return true;
473}
474
475
476// validate_create_child_partition (auxiliary function)
477static bool
478validate_create_child_partition(partition_data* partition, off_t* start,
479	off_t* size, fc_get_sibling_partitions getSiblingPartitions)
480{
481	// make the start and size a multiple of the block size
482	*start = sector_align(*start, partition->block_size);
483	if (*size < 0)
484		*size = 0;
485	else
486		*size = sector_align(*size, partition->block_size);
487
488	// child must completely lie within the parent partition
489	if (*start >= partition->offset + partition->size)
490		return false;
491	if (*start + *size > partition->offset + partition->size)
492		*size = partition->offset + partition->size - *start;
493
494	// new child must not intersect with sibling partitions
495	// finding out sibling partitions
496	partition_data* previousSibling = NULL;
497	partition_data* nextSibling = NULL;
498	off_t previousOffset = 0, previousSize = 0, nextOffset = 0, nextSize = 0;
499
500	getSiblingPartitions(partition, NULL, *start, &previousSibling,
501		&nextSibling, &previousOffset, &previousSize, &nextOffset, &nextSize);
502
503	// position check of the new partition
504	if (previousSibling && (previousOffset + previousSize > *start)) {
505		*start = previousOffset + previousSize;
506		*start = sector_align_up(*start, partition->block_size);
507	}
508
509	if (nextSibling && (nextOffset < *start + *size))
510		*size = nextOffset - *start;
511	*size = sector_align(*size, partition->block_size);
512	if (*size == 0)
513		return false;
514
515	return true;
516}
517
518
519// pm_validate_create_child
520/*!
521	index - returns position of the new partition (first free record in MBR)
522*/
523bool
524pm_validate_create_child(partition_data* partition, off_t* start, off_t* size,
525	const char* type, const char* name, const char* parameters, int32* index)
526{
527	TRACE(("intel: pm_validate_create_child\n"));
528
529	if (!partition || !(pm_get_supported_operations(partition)
530			& B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD)
531		|| !start || !size || !type || !index) {
532		return false;
533	}
534
535	// TODO: check name
536	// TODO: check parameters
537	// type check
538	if (!is_type_valid_pm(type, partition))
539		return false;
540
541	// finding out index of the new partition (first free record in MBR)
542	// at least one record has to be free
543	PartitionMap* map = (PartitionMap*)partition->content_cookie;
544	if (!map)
545		return false;
546	int32 newIndex = -1;
547	for (int32 i = 0; i < 4; i++) {
548		PrimaryPartition* primary = map->PrimaryPartitionAt(i);
549		if (primary->IsEmpty()) {
550			newIndex = i;
551			break;
552		}
553	}
554	// this cannot happen
555	if (newIndex < 0)
556		return false;
557	*index = newIndex;
558
559	if (*start < partition->offset + MBR_OFFSET * partition->block_size) {
560		*start = partition->offset + MBR_OFFSET * partition->block_size;
561		*start = sector_align_up(*start, partition->block_size);
562	}
563
564	return validate_create_child_partition(partition, start, size,
565		get_sibling_partitions_pm);
566}
567
568
569// cmp_partition_position
570static int
571cmp_partition_position(const void* o1, const void* o2)
572{
573	off_t offset1 = ((PartitionPosition*)o1)->offset;
574	off_t offset2 = ((PartitionPosition*)o2)->offset;
575
576	if (offset1 < offset2)
577		return -1;
578	if (offset1 > offset2)
579		return 1;
580
581	return 0;
582}
583
584
585// fill_partitionable_spaces_buffer_pm
586/*!
587	positions - output buffer with sufficient size
588	returns partition count
589*/
590static int32
591fill_partitionable_spaces_buffer_pm(partition_data* partition,
592	PartitionPosition* positions)
593{
594	int32 partition_count = 0;
595	for (int32 i = 0; i < partition->child_count; i++) {
596		const partition_data* child = get_child_partition(partition->id, i);
597		if (child) {
598			positions[partition_count].offset = child->offset;
599			positions[partition_count].size = child->size;
600			partition_count++;
601		}
602	}
603	return partition_count;
604}
605
606
607// fill_partitionable_spaces_buffer_ep
608/*!
609	positions - output buffer with sufficient size
610	returns partition count
611*/
612static int32
613fill_partitionable_spaces_buffer_ep(partition_data* partition,
614	PartitionPosition* positions)
615{
616	int32 partition_count = 0;
617	for (int32 i = 0; i < partition->child_count; i++) {
618		const partition_data* child = get_child_partition(partition->id, i);
619		if (child) {
620			positions[partition_count].offset = child->offset;
621			positions[partition_count].size = child->size;
622			partition_count++;
623		}
624	}
625	return partition_count;
626}
627
628
629// get_partitionable_spaces (auxiliary function)
630static status_t
631get_partitionable_spaces(partition_data* partition,
632	partitionable_space_data* buffer, int32 count, int32* _actualCount,
633	fc_fill_partitionable_spaces_buffer fillBuffer, off_t startOffset,
634	off_t limitSize = 0, off_t headerSize = 0)
635{
636	PartitionPosition* positions
637		= new(nothrow) PartitionPosition[partition->child_count];
638	if (!positions)
639		return B_NO_MEMORY;
640	// fill the array
641	int32 partition_count = fillBuffer(partition, positions);
642	// sort the array
643	qsort(positions, partition_count, sizeof(PartitionPosition),
644		cmp_partition_position);
645
646	// first sektor is MBR or EBR
647	off_t offset = startOffset + headerSize;
648	off_t size = 0;
649	int32 actualCount = 0;
650
651	// offset alignment (to upper bound)
652	offset = sector_align_up(offset, partition->block_size);
653	// finding out all partitionable spaces
654	for (int32 i = 0; i < partition_count; i++) {
655		size = positions[i].offset - offset;
656		size = sector_align(size, partition->block_size);
657		if (size >= limitSize) {
658			if (actualCount < count) {
659				buffer[actualCount].offset = offset;
660				buffer[actualCount].size = size;
661			}
662			actualCount++;
663		}
664		offset = positions[i].offset + positions[i].size + headerSize;
665		offset = sector_align_up(offset, partition->block_size);
666	}
667	// space in the end of partition
668	size = partition->offset + partition->size - offset;
669	size = sector_align(size, partition->block_size);
670	if (size > 0) {
671		if (actualCount < count) {
672			buffer[actualCount].offset = offset;
673			buffer[actualCount].size = size;
674		}
675		actualCount++;
676	}
677
678	// cleanup
679	if (positions)
680		delete[] positions;
681
682	TRACE(("intel: get_partitionable_spaces - found: %" B_PRId32 "\n",
683		actualCount));
684
685	*_actualCount = actualCount;
686
687	if (count < actualCount)
688		return B_BUFFER_OVERFLOW;
689	return B_OK;
690}
691
692
693// pm_get_partitionable_spaces
694status_t
695pm_get_partitionable_spaces(partition_data* partition,
696	partitionable_space_data* buffer, int32 count, int32* actualCount)
697{
698	TRACE(("intel: pm_get_partitionable_spaces\n"));
699
700	if (!partition || !partition->content_type
701		|| strcmp(partition->content_type, kPartitionTypeIntel)
702		|| !actualCount) {
703		return B_BAD_VALUE;
704	}
705	if (count > 0 && !buffer)
706		return B_BAD_VALUE;
707
708	return get_partitionable_spaces(partition, buffer, count, actualCount,
709		fill_partitionable_spaces_buffer_pm, MBR_OFFSET * partition->block_size,
710		0, 0);
711}
712
713
714// pm_get_next_supported_type
715status_t
716pm_get_next_supported_type(partition_data* partition, int32* cookie,
717	char* _type)
718{
719	TRACE(("intel: pm_get_next_supported_type\n"));
720
721	if (!partition || !partition->content_type
722		|| strcmp(partition->content_type, kPartitionTypeIntel)
723		|| !cookie || !_type) {
724		return B_BAD_VALUE;
725	}
726
727	if (*cookie > 255)
728		return B_ENTRY_NOT_FOUND;
729	if (*cookie < 1)
730		*cookie = 1;
731
732	uint8 type = *cookie;
733
734	// get type
735	PartitionType ptype;
736	ptype.SetType(type);
737	if (!ptype.IsValid())
738		return B_ENTRY_NOT_FOUND;
739
740	ptype.GetTypeString(_type);
741
742	// find next type
743	if (ptype.FindNext())
744		*cookie = ptype.Type();
745	else
746		*cookie = 256;
747
748	return B_OK;
749}
750
751// pm_shadow_changed
752status_t
753pm_shadow_changed(partition_data* partition, partition_data* child,
754	uint32 operation)
755{
756	TRACE(("intel: pm_shadow_changed(%p, %p, %" B_PRIu32 ")\n", partition,
757		child, operation));
758
759	switch (operation) {
760		case B_PARTITION_SHADOW:
761		{
762			// get the physical partition
763			partition_data* physicalPartition = get_partition(
764				partition->id);
765			if (!physicalPartition) {
766				dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW): no "
767					"physical partition with ID %" B_PRId32 "\n",
768					partition->id);
769				return B_ERROR;
770			}
771
772			// clone the map
773			if (!physicalPartition->content_cookie) {
774				dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW): no "
775					"content cookie, physical partition: %" B_PRId32 "\n",
776					partition->id);
777				return B_ERROR;
778			}
779
780			PartitionMapCookie* map = new(nothrow) PartitionMapCookie;
781			if (!map)
782				return B_NO_MEMORY;
783
784			status_t error = map->Assign(
785				*(PartitionMapCookie*)physicalPartition->content_cookie);
786			if (error != B_OK) {
787				delete map;
788				return error;
789			}
790
791			partition->content_cookie = map;
792
793			return B_OK;
794		}
795
796		case B_PARTITION_SHADOW_CHILD:
797		{
798			// get the physical child partition
799			partition_data* physical = get_partition(child->id);
800			if (!physical) {
801				dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
802					"no physical partition with ID %" B_PRId32 "\n", child->id);
803				return B_ERROR;
804			}
805
806			if (!physical->cookie) {
807				dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
808					"no cookie, physical partition: %" B_PRId32 "\n",
809					child->id);
810				return B_ERROR;
811			}
812
813			// primary partition index
814			int32 index = ((PrimaryPartition*)physical->cookie)->Index();
815
816			if (!partition->content_cookie) {
817				dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
818					"no content cookie, physical partition: %" B_PRId32 "\n",
819					partition->id);
820				return B_ERROR;
821			}
822
823			// get the primary partition
824			PartitionMapCookie* map
825				= ((PartitionMapCookie*)partition->content_cookie);
826			PrimaryPartition* primary = map->PrimaryPartitionAt(index);
827
828			if (!primary || primary->IsEmpty()) {
829				dprintf("intel: pm_shadow_changed(B_PARTITION_SHADOW_CHILD): "
830					"partition %" B_PRId32 " is empty, primary index: "
831					"%" B_PRId32 "\n", child->id, index);
832				return B_BAD_VALUE;
833			}
834
835			child->cookie = primary;
836
837			return B_OK;
838		}
839
840		case B_PARTITION_INITIALIZE:
841		{
842			// create an empty partition map
843			PartitionMapCookie* map = new(nothrow) PartitionMapCookie;
844			if (!map)
845				return B_NO_MEMORY;
846
847			partition->content_cookie = map;
848
849			return B_OK;
850		}
851
852		case B_PARTITION_CREATE_CHILD:
853		{
854			if (!partition->content_cookie) {
855				dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): "
856					"no content cookie, partition: %" B_PRId32 "\n",
857					partition->id);
858				return B_ERROR;
859			}
860
861			PartitionMapCookie* map
862				= ((PartitionMapCookie*)partition->content_cookie);
863
864			// find an empty primary partition slot
865			PrimaryPartition* primary = NULL;
866			for (int32 i = 0; i < 4; i++) {
867				if (map->PrimaryPartitionAt(i)->IsEmpty()) {
868					primary = map->PrimaryPartitionAt(i);
869					break;
870				}
871			}
872
873			if (!primary) {
874				dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): "
875					"no empty primary slot, partition: %" B_PRId32 "\n",
876					partition->id);
877				return B_ERROR;
878			}
879
880			// apply type
881			PartitionType type;
882			type.SetType(child->type);
883			if (!type.IsValid()) {
884				dprintf("intel: pm_shadow_changed(B_PARTITION_CREATE_CHILD): "
885					"invalid partition type, partition: %" B_PRId32 "\n",
886					partition->id);
887				return B_ERROR;
888			}
889
890			primary->SetType(type.Type());
891
892			// TODO: Apply parameters!
893
894			child->cookie = primary;
895
896			return B_OK;
897		}
898
899		case B_PARTITION_DEFRAGMENT:
900		case B_PARTITION_REPAIR:
901		case B_PARTITION_RESIZE:
902		case B_PARTITION_RESIZE_CHILD:
903		case B_PARTITION_MOVE:
904		case B_PARTITION_MOVE_CHILD:
905		case B_PARTITION_SET_NAME:
906		case B_PARTITION_SET_CONTENT_NAME:
907		case B_PARTITION_SET_TYPE:
908		case B_PARTITION_SET_PARAMETERS:
909		case B_PARTITION_SET_CONTENT_PARAMETERS:
910		case B_PARTITION_DELETE_CHILD:
911			break;
912	}
913
914	return B_ERROR;
915}
916
917
918// #pragma mark - Intel Partition Map - writing functions
919
920
921// pm_resize
922status_t
923pm_resize(int fd, partition_id partitionID, off_t size, disk_job_id job)
924{
925	TRACE(("intel: pm_resize\n"));
926
927	if (fd < 0)
928		return B_ERROR;
929
930	PartitionWriteLocker locker(partitionID);
931	if (!locker.IsLocked())
932		return B_ERROR;
933
934	// get out partition
935	partition_data* partition = get_partition(partitionID);
936	if (!partition)
937		return B_BAD_VALUE;
938
939	// validate the new size
940// TODO: The parameter has already been checked and must not be altered!
941	off_t validatedSize = size;
942	if (!pm_validate_resize(partition, &validatedSize))
943		return B_BAD_VALUE;
944
945	// update data stuctures
946	update_disk_device_job_progress(job, 0.0);
947
948// TODO: partition->size is not supposed to be touched.
949	partition->size = validatedSize;
950	partition->content_size = validatedSize;
951
952	// all changes applied
953	update_disk_device_job_progress(job, 1.0);
954	partition_modified(partitionID);
955	return B_OK;
956}
957
958
959// pm_resize_child
960status_t
961pm_resize_child(int fd, partition_id partitionID, off_t size, disk_job_id job)
962{
963	TRACE(("intel: pm_resize_child\n"));
964
965	if (fd < 0)
966		return B_ERROR;
967
968	PartitionWriteLocker locker(partitionID);
969	if (!locker.IsLocked())
970		return B_ERROR;
971
972	// get out partition, child and partition map structure
973	partition_data* partition = get_parent_partition(partitionID);
974	partition_data* child = get_partition(partitionID);
975	if (!partition || !child)
976		return B_BAD_VALUE;
977	PartitionMap* map = (PartitionMap*)partition->content_cookie;
978	PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
979	if (!map || !primary)
980		return B_BAD_VALUE;
981
982	// validate the new size
983// TODO: The parameter has already been checked and must not be altered!
984	off_t validatedSize = size;
985	if (!pm_validate_resize_child(partition, child, &validatedSize))
986		return B_BAD_VALUE;
987	if (child->size == validatedSize)
988		return B_OK;
989
990	// update data stuctures and write changes
991	update_disk_device_job_progress(job, 0.0);
992	primary->SetSize(validatedSize);
993
994// TODO: The partition is not supposed to be locked here!
995	PartitionMapWriter writer(fd, primary->BlockSize());
996		// TODO: disk size?
997	status_t error = writer.WriteMBR(map, false);
998	if (error != B_OK) {
999		// putting into previous state
1000		primary->SetSize(child->size);
1001		return error;
1002	}
1003
1004	child->size = validatedSize;
1005
1006	// all changes applied
1007	update_disk_device_job_progress(job, 1.0);
1008	partition_modified(partitionID);
1009	return B_OK;
1010}
1011
1012
1013// pm_move
1014status_t
1015pm_move(int fd, partition_id partitionID, off_t offset, disk_job_id job)
1016{
1017	TRACE(("intel: pm_move\n"));
1018
1019	if (fd < 0)
1020		return B_ERROR;
1021
1022// TODO: Should be a no-op!
1023
1024	PartitionWriteLocker locker(partitionID);
1025	if (!locker.IsLocked())
1026		return B_ERROR;
1027
1028	// get out partition
1029	partition_data* partition = get_partition(partitionID);
1030	if (!partition)
1031		return B_BAD_VALUE;
1032
1033	// validate the new start
1034	if (!pm_validate_move(partition, &offset))
1035		return B_BAD_VALUE;
1036
1037	// nothing to do here
1038	return B_OK;
1039}
1040
1041
1042// allocate_buffer (auxiliary function)
1043/*!
1044	tries to allocate buffer with the size: blockSize * tryAlloc
1045	if it's not possible, tries smaller buffer (until the size: blockSize * 1)
1046	returns pointer to the buffer (it's size is: blockSize * allocated)
1047	or returns NULL - B_NO_MEMORY
1048*/
1049static uint8*
1050allocate_buffer(uint32 blockSize, int32 tryAlloc, int32* allocated)
1051{
1052	uint8* buffer = NULL;
1053	for (int32 i = tryAlloc; i > 1; i /= 2) {
1054		buffer = new(nothrow) uint8[i * blockSize];
1055		if (buffer) {
1056			*allocated = i;
1057			return buffer;
1058		}
1059	}
1060	*allocated = 0;
1061	return NULL;
1062}
1063
1064
1065// move_block (auxiliary function)
1066static status_t
1067move_block(int fd, off_t fromOffset, off_t toOffset, uint8* buffer, int32 size)
1068{
1069	status_t error = B_OK;
1070	// read block to buffer
1071	if (read_pos(fd, fromOffset, buffer, size) != size) {
1072		error = errno;
1073		if (error == B_OK)
1074			error = B_IO_ERROR;
1075		TRACE(("intel: move_block(): reading failed: %" B_PRIx32 "\n", error));
1076		return error;
1077	}
1078
1079	// write block from buffer
1080	if (write_pos(fd, toOffset, buffer, size) != size) {
1081		error = errno;
1082		if (error == B_OK)
1083			error = B_IO_ERROR;
1084		TRACE(("intel: move_block(): writing failed: %" B_PRIx32 "\n", error));
1085	}
1086
1087	return error;
1088}
1089
1090
1091// move_partition (auxiliary function)
1092static status_t
1093move_partition(int fd, off_t fromOffset, off_t toOffset, off_t size,
1094	uint8* buffer, int32 buffer_size, disk_job_id job)
1095{
1096	// TODO: This should be a service function of the DDM!
1097	// TODO: This seems to be broken if source and destination overlap.
1098	status_t error = B_OK;
1099	off_t cycleCount = size / buffer_size;
1100	int32 remainingSize = size - cycleCount * buffer_size;
1101	update_disk_device_job_progress(job, 0.0);
1102	for (off_t i = 0; i < cycleCount; i++) {
1103		error = move_block(fd, fromOffset, toOffset, buffer, buffer_size);
1104		if (error != B_OK)
1105			return error;
1106		fromOffset += buffer_size;
1107		toOffset += buffer_size;
1108		update_disk_device_job_progress(job, (float)i / cycleCount);
1109	}
1110	if (remainingSize)
1111		error = move_block(fd, fromOffset, toOffset, buffer, remainingSize);
1112	update_disk_device_job_progress(job, 1.0);
1113	return error;
1114}
1115
1116
1117// pm_move_child
1118status_t
1119pm_move_child(int fd, partition_id partitionID, partition_id childID,
1120	off_t offset, disk_job_id job)
1121{
1122	TRACE(("intel: pm_move_child\n"));
1123
1124	if (fd < 0)
1125		return B_ERROR;
1126
1127	PartitionWriteLocker locker(partitionID);
1128	if (!locker.IsLocked())
1129		return B_ERROR;
1130
1131	// get partition, child and partition map structure
1132	partition_data* partition = get_partition(partitionID);
1133	partition_data* child = get_partition(childID);
1134	if (!partition || !child)
1135		return B_BAD_VALUE;
1136	PartitionMap* map = (PartitionMap*)partition->content_cookie;
1137	PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
1138	if (!map || !primary)
1139		return B_BAD_VALUE;
1140
1141	// TODO: The parameter has already been checked and must not be altered!
1142	off_t validatedOffset = offset;
1143	if (!pm_validate_move_child(partition, child, &validatedOffset))
1144		return B_BAD_VALUE;
1145
1146	// if the old offset is the same, there is nothing to do
1147	if (child->offset == validatedOffset)
1148		return B_OK;
1149
1150	// buffer allocation
1151	int32 allocated;
1152	uint8* buffer = allocate_buffer(partition->block_size, MAX_MOVE_BUFFER,
1153		&allocated);
1154	if (!buffer)
1155		return B_NO_MEMORY;
1156
1157	// partition moving
1158	// TODO: The partition is not supposed to be locked at this point!
1159	update_disk_device_job_progress(job, 0.0);
1160	status_t error = B_OK;
1161	error = move_partition(fd, child->offset, validatedOffset, child->size,
1162		buffer, allocated * partition->block_size, job);
1163	delete[] buffer;
1164	if (error != B_OK)
1165		return error;
1166
1167	// partition moved
1168	// updating data structure
1169	child->offset = validatedOffset;
1170	primary->SetOffset(validatedOffset);
1171
1172	PartitionMapWriter writer(fd, partition->block_size);
1173		// TODO: disk size?
1174	error = writer.WriteMBR(map, false);
1175	if (error != B_OK)
1176		// something went wrong - this is fatal (partition has been moved)
1177		// but MBR is not updated
1178		return error;
1179
1180	// all changes applied
1181	update_disk_device_job_progress(job, 1.0);
1182	partition_modified(childID);
1183	return B_OK;
1184}
1185
1186
1187// pm_set_type
1188status_t
1189pm_set_type(int fd, partition_id partitionID, const char* type, disk_job_id job)
1190{
1191	TRACE(("intel: pm_set_type\n"));
1192
1193	if (fd < 0 || !type)
1194		return B_BAD_VALUE;
1195
1196	PartitionWriteLocker locker(partitionID);
1197	if (!locker.IsLocked())
1198		return B_ERROR;
1199
1200	// get parent partition, child and partition map structure
1201	partition_data* partition = get_parent_partition(partitionID);
1202	partition_data* child = get_partition(partitionID);
1203	if (!partition || !child)
1204		return B_BAD_VALUE;
1205	PartitionMap* map = (PartitionMap*)partition->content_cookie;
1206	PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
1207	if (!map || !primary)
1208		return B_BAD_VALUE;
1209
1210// TODO: The parameter has already been checked and must not be altered!
1211	if (!pm_validate_set_type(child, type))
1212		return B_BAD_VALUE;
1213
1214	// if the old type is the same, there is nothing to do
1215	if (child->type && !strcmp(type, child->type))
1216		return B_OK;
1217
1218	PartitionType ptype;
1219	ptype.SetType(type);
1220	// this is impossible
1221	if (!ptype.IsValid() || ptype.IsEmpty())
1222		return false;
1223	// TODO: Incompatible return value!
1224
1225	// setting type to the partition
1226	update_disk_device_job_progress(job, 0.0);
1227	uint8 oldType = primary->Type();
1228	primary->SetType(ptype.Type());
1229
1230	// TODO: The partition is not supposed to be locked at this point!
1231	PartitionMapWriter writer(fd, primary->BlockSize());
1232		// TODO: disk size?
1233	status_t error = writer.WriteMBR(map, false);
1234	if (error != B_OK) {
1235		// something went wrong - putting into previous state
1236		primary->SetType(oldType);
1237		return error;
1238	}
1239
1240	free(child->type);
1241	child->type = strdup(type);
1242	if (!child->type)
1243		return B_NO_MEMORY;
1244
1245	// all changes applied
1246	update_disk_device_job_progress(job, 1.0);
1247	partition_modified(partitionID);
1248	return B_OK;
1249}
1250
1251
1252// pm_initialize
1253status_t
1254pm_initialize(int fd, partition_id partitionID, const char* name,
1255	const char* parameters, off_t partitionSize, disk_job_id job)
1256{
1257	TRACE(("intel: pm_initialize\n"));
1258
1259	if (fd < 0)
1260		return B_ERROR;
1261
1262	PartitionWriteLocker locker(partitionID);
1263	if (!locker.IsLocked())
1264		return B_ERROR;
1265
1266	// get partition and partition map structure
1267	partition_data* partition = get_partition(partitionID);
1268	if (!partition)
1269		return B_BAD_VALUE;
1270	update_disk_device_job_progress(job, 0.0);
1271
1272	// we will write an empty partition map
1273	PartitionMap map;
1274
1275	// write the sector to disk
1276	PartitionMapWriter writer(fd, partition->block_size);
1277		// TODO: disk size or 2 * SECTOR_SIZE?
1278	status_t error = writer.WriteMBR(&map, true);
1279	if (error != B_OK)
1280		return error;
1281
1282	// rescan partition
1283	error = scan_partition(partitionID);
1284	if (error != B_OK)
1285		return error;
1286
1287	// all changes applied
1288	update_disk_device_job_progress(job, 1.0);
1289	partition_modified(partitionID);
1290
1291	return B_OK;
1292}
1293
1294
1295status_t
1296pm_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
1297	uint32 blockSize, disk_job_id job)
1298{
1299	if (blockSize == 0)
1300		return B_BAD_VALUE;
1301
1302	// We overwrite the first block, which contains the partition table.
1303	// Allocate a buffer, we can clear and write.
1304	void* block = malloc(blockSize);
1305	if (block == NULL)
1306		return B_NO_MEMORY;
1307	MemoryDeleter blockDeleter(block);
1308
1309	memset(block, 0, blockSize);
1310
1311	if (write_pos(fd, 0, block, blockSize) < 0)
1312		return errno;
1313
1314	update_disk_device_job_progress(job, 1.0);
1315
1316	return B_OK;
1317}
1318
1319
1320// pm_create_child
1321/*!	childID is used for the return value, but is also an optional input
1322	parameter -- -1 to be ignored
1323*/
1324status_t
1325pm_create_child(int fd, partition_id partitionID, off_t offset, off_t size,
1326	const char* type, const char* name, const char* parameters,
1327	disk_job_id job, partition_id* childID)
1328{
1329	TRACE(("intel: pm_create_child\n"));
1330
1331	if (fd < 0 || !childID)
1332		return B_BAD_VALUE;
1333
1334	PartitionWriteLocker locker(partitionID);
1335	if (!locker.IsLocked())
1336		return B_ERROR;
1337
1338	// get partition and partition map structure
1339	partition_data* partition = get_partition(partitionID);
1340	if (!partition)
1341		return B_BAD_VALUE;
1342	PartitionMap* map = (PartitionMap*)partition->content_cookie;
1343	if (!map)
1344		return B_BAD_VALUE;
1345
1346	// validate the offset, size and get index of the new partition
1347	// TODO: The parameters have already been checked and must not be altered!
1348	off_t validatedOffset = offset;
1349	off_t validatedSize = size;
1350	int32 index = 0;
1351
1352	if (!pm_validate_create_child(partition, &validatedOffset, &validatedSize,
1353			type, name, parameters, &index)) {
1354		return B_BAD_VALUE;
1355	}
1356
1357	// finding out free primary partition in the map (index from
1358	// pm_validate_create_child)
1359	PrimaryPartition* primary = map->PrimaryPartitionAt(index);
1360	if (!primary->IsEmpty())
1361		return B_BAD_DATA;
1362
1363	// creating partition
1364	update_disk_device_job_progress(job, 0.0);
1365	partition_data* child = create_child_partition(partition->id, index,
1366		validatedOffset, validatedSize, *childID);
1367	if (!child)
1368		return B_ERROR;
1369
1370	PartitionType ptype;
1371	ptype.SetType(type);
1372
1373	// check parameters
1374	void* handle = parse_driver_settings_string(parameters);
1375	if (handle == NULL)
1376		return B_ERROR;
1377
1378	bool active = get_driver_boolean_parameter(handle, "active", false, true);
1379	delete_driver_settings(handle);
1380
1381	// set the active flags to false
1382	if (active) {
1383		for (int i = 0; i < 4; i++) {
1384			PrimaryPartition* partition = map->PrimaryPartitionAt(i);
1385			partition->SetActive(false);
1386		}
1387	}
1388
1389	primary->SetPartitionTableOffset(0);
1390	primary->SetOffset(validatedOffset);
1391	primary->SetSize(validatedSize);
1392	primary->SetType(ptype.Type());
1393	primary->SetActive(active);
1394
1395	// write changes to disk
1396	PartitionMapWriter writer(fd, primary->BlockSize());
1397
1398	// TODO: The partition is not supposed to be locked at this point!
1399	status_t error = writer.WriteMBR(map, false);
1400	if (error != B_OK) {
1401		// putting into previous state
1402		primary->Unset();
1403		delete_partition(child->id);
1404		return error;
1405	}
1406
1407	*childID = child->id;
1408
1409	child->block_size = primary->BlockSize();
1410	// (no name)
1411	child->type = strdup(type);
1412	// parameters
1413	child->parameters = strdup(parameters);
1414	child->cookie = primary;
1415	// check for allocation problems
1416	if (!child->type || !child->parameters)
1417		return B_NO_MEMORY;
1418
1419	// rescan partition if needed
1420	if (strcmp(type, INTEL_EXTENDED_PARTITION_NAME) == 0) {
1421		writer.ClearExtendedHead(primary);
1422		error = scan_partition(partitionID);
1423		if (error != B_OK)
1424			return error;
1425	}
1426
1427	// all changes applied
1428	update_disk_device_job_progress(job, 1.0);
1429	partition_modified(partitionID);
1430	return B_OK;
1431}
1432
1433
1434// pm_delete_child
1435status_t
1436pm_delete_child(int fd, partition_id partitionID, partition_id childID,
1437	disk_job_id job)
1438{
1439	TRACE(("intel: pm_delete_child\n"));
1440
1441	if (fd < 0)
1442		return B_ERROR;
1443
1444	PartitionWriteLocker locker(partitionID);
1445	if (!locker.IsLocked())
1446		return B_ERROR;
1447
1448	partition_data* partition = get_partition(partitionID);
1449	partition_data* child = get_partition(childID);
1450	if (!partition || !child)
1451		return B_BAD_VALUE;
1452
1453	PartitionMap* map = (PartitionMap*)partition->content_cookie;
1454	PrimaryPartition* primary = (PrimaryPartition*)child->cookie;
1455	if (!map || !primary)
1456		return B_BAD_VALUE;
1457
1458	// deleting child
1459	update_disk_device_job_progress(job, 0.0);
1460	if (!delete_partition(childID))
1461		return B_ERROR;
1462	primary->Unset();
1463
1464	// write changes to disk
1465	PartitionMapWriter writer(fd, primary->BlockSize());
1466		// TODO: disk size or 2 * SECTOR_SIZE?
1467	// TODO: The partition is not supposed to be locked at this point!
1468	status_t error = writer.WriteMBR(map, false);
1469	if (error != B_OK)
1470		return error;
1471
1472	// all changes applied
1473	update_disk_device_job_progress(job, 1.0);
1474	partition_modified(partitionID);
1475	return B_OK;
1476}
1477
1478
1479// #pragma mark - Intel Extended Partition - support functions
1480
1481
1482// ep_get_supported_operations
1483uint32
1484ep_get_supported_operations(partition_data* partition, uint32 mask)
1485{
1486	uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING
1487		| B_DISK_SYSTEM_SUPPORTS_MOVING
1488		| B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS;
1489
1490	// initializing
1491	if (partition_data* parent = get_parent_partition(partition->id)) {
1492		if (partition->type
1493			&& strcmp(partition->type, kPartitionTypeIntelExtended) == 0
1494			&& strcmp(parent->content_type, kPartitionTypeIntel) == 0) {
1495			flags |= B_DISK_SYSTEM_SUPPORTS_INITIALIZING;
1496		}
1497	}
1498
1499	// creating child
1500	int32 countSpaces = 0;
1501	if (ep_get_partitionable_spaces(partition, NULL, 0, &countSpaces)
1502			== B_BUFFER_OVERFLOW
1503		&& countSpaces > 0) {
1504		flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD;
1505	}
1506
1507	return flags;
1508}
1509
1510
1511// ep_get_supported_child_operations
1512uint32
1513ep_get_supported_child_operations(partition_data* partition,
1514	partition_data* child, uint32 mask)
1515{
1516	return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD
1517		| B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD
1518		| B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE
1519		| B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD;
1520}
1521
1522
1523// ep_is_sub_system_for
1524bool
1525ep_is_sub_system_for(partition_data* partition)
1526{
1527	if (partition == NULL)
1528		return false;
1529
1530	TRACE(("intel: ep_is_sub_system_for(%" B_PRId32 ": %" B_PRId64 ", "
1531		"%" B_PRId64 ", %" B_PRId32 ", %s)\n", partition->id, partition->offset,
1532		partition->size, partition->block_size, partition->content_type));
1533
1534	// Intel Extended Partition can live in child partition of Intel Partition
1535	// Map
1536	return partition->content_type
1537		&& !strcmp(partition->content_type, kPartitionTypeIntel);
1538}
1539
1540
1541// #pragma mark - Intel Extended Partition - validate functions
1542
1543
1544// ep_validate_resize
1545bool
1546ep_validate_resize(partition_data* partition, off_t* size)
1547{
1548	TRACE(("intel: ep_validate_resize\n"));
1549
1550	if (!partition || !size)
1551		return false;
1552
1553	return validate_resize(partition, size);
1554}
1555
1556
1557// ep_validate_resize_child
1558bool
1559ep_validate_resize_child(partition_data* partition, partition_data* child,
1560	off_t* _size)
1561{
1562	TRACE(("intel: ep_validate_resize_child\n"));
1563
1564	if (!partition || !child || !_size)
1565		return false;
1566
1567	// validate position
1568	off_t size = *_size;
1569	if (!validate_resize_child(partition, child, child->offset,
1570		 child->size, &size, get_sibling_partitions_ep))
1571		return false;
1572	*_size = size;
1573	return true;
1574}
1575
1576
1577// ep_validate_move
1578bool
1579ep_validate_move(partition_data* partition, off_t* start)
1580{
1581	TRACE(("intel: ep_validate_move\n"));
1582
1583	if (!partition || !start)
1584		return false;
1585	// nothing to do here
1586	return true;
1587}
1588
1589
1590// ep_validate_move_child
1591bool
1592ep_validate_move_child(partition_data* partition, partition_data* child,
1593	off_t* _start)
1594{
1595	TRACE(("intel: ep_validate_move_child\n"));
1596
1597	if (!partition || !child || !_start)
1598		return false;
1599	if (*_start == child->offset)
1600		return true;
1601
1602	// validate position
1603	off_t start = *_start;
1604	if (!validate_move_child(partition, child, child->offset,
1605		child->size, &start, get_sibling_partitions_ep))
1606		return false;
1607	*_start = start;
1608	return true;
1609}
1610
1611
1612// is_type_valid_ep (auxiliary function)
1613static inline bool
1614is_type_valid_ep(const char* type)
1615{
1616	// validity check of the type - it has to be known
1617	PartitionType ptype;
1618	ptype.SetType(type);
1619	return (ptype.IsValid() && !ptype.IsEmpty() && !ptype.IsExtended());
1620}
1621
1622
1623// ep_validate_set_type
1624bool
1625ep_validate_set_type(partition_data* partition, const char* type)
1626{
1627	TRACE(("intel: ep_validate_set_type\n"));
1628
1629	if (!partition || !type)
1630		return false;
1631
1632	// validity check of the type
1633	return is_type_valid_ep(type);
1634}
1635
1636
1637// ep_validate_initialize
1638bool
1639ep_validate_initialize(partition_data* partition, char* name,
1640	const char* parameters)
1641{
1642	TRACE(("intel: ep_validate_initialize\n"));
1643
1644	if (!partition || !(ep_get_supported_operations(partition)
1645			& B_DISK_SYSTEM_SUPPORTS_INITIALIZING)) {
1646		return false;
1647	}
1648	// name is ignored - we cannot set it to the Intel Extended Partition
1649	// TODO: check parameters - don't know whether any parameters could be set
1650	//		 to the Intel Extended Partition
1651	return true;
1652}
1653
1654
1655// ep_validate_create_child
1656bool
1657ep_validate_create_child(partition_data* partition, off_t* offset, off_t* size,
1658	const char* type, const char* name, const char* parameters, int32* index)
1659	// index - returns position of the new partition (the last one)
1660{
1661	return false;
1662}
1663
1664
1665// ep_get_partitionable_spaces
1666status_t
1667ep_get_partitionable_spaces(partition_data* partition,
1668	partitionable_space_data* buffer, int32 count, int32* actualCount)
1669{
1670	TRACE(("intel: ep_get_partitionable_spaces\n"));
1671
1672	if (!partition || !partition->content_type
1673		|| strcmp(partition->content_type, kPartitionTypeIntelExtended)
1674		|| !actualCount) {
1675		return B_BAD_VALUE;
1676	}
1677	if (count > 0 && !buffer)
1678		return B_BAD_VALUE;
1679
1680	return get_partitionable_spaces(partition, buffer, count, actualCount,
1681		fill_partitionable_spaces_buffer_ep,
1682		partition->offset + PTS_OFFSET * partition->block_size,
1683		PTS_OFFSET * partition->block_size,
1684		PTS_OFFSET * partition->block_size);
1685}
1686
1687
1688// ep_get_next_supported_type
1689status_t
1690ep_get_next_supported_type(partition_data* partition, int32* cookie,
1691	char* _type)
1692{
1693	TRACE(("intel: ep_get_next_supported_type\n"));
1694
1695	if (!partition || !partition->content_type
1696		|| strcmp(partition->content_type, kPartitionTypeIntelExtended)
1697		|| !cookie || !_type) {
1698		return B_BAD_VALUE;
1699	}
1700
1701	if (*cookie > 255)
1702		return B_ENTRY_NOT_FOUND;
1703	if (*cookie < 1)
1704		*cookie = 1;
1705
1706	uint8 type = *cookie;
1707
1708	// get type
1709	PartitionType ptype;
1710	ptype.SetType(type);
1711	while (ptype.IsValid() && !ptype.IsExtended())
1712		ptype.FindNext();
1713
1714	if (!ptype.IsValid())
1715		return B_ENTRY_NOT_FOUND;
1716
1717	ptype.GetTypeString(_type);
1718
1719	// find next type
1720	if (ptype.FindNext())
1721		*cookie = ptype.Type();
1722	else
1723		*cookie = 256;
1724
1725	return B_OK;
1726}
1727
1728
1729// ep_shadow_changed
1730status_t
1731ep_shadow_changed(partition_data* partition, partition_data* child,
1732	uint32 operation)
1733{
1734	TRACE(("intel: ep_shadow_changed\n"));
1735
1736	if (!partition)
1737		return B_BAD_VALUE;
1738
1739	// nothing to do here
1740	return B_OK;
1741}
1742
1743
1744bool
1745check_partition_location_ep(partition_data* partition, off_t offset,
1746	off_t size, off_t ptsOffset)
1747{
1748	if (!partition)
1749		return false;
1750
1751	// make sure we are sector aligned
1752	off_t alignedOffset = sector_align(offset, partition->block_size);
1753	if (alignedOffset != offset)
1754		return false;
1755
1756	// partition does not lie within extended partition
1757	if (offset < partition->offset
1758		|| (offset > partition->offset + partition->size
1759		&& offset + size <= partition->offset + partition->size))
1760		return false;
1761
1762	// check if the new partition table is within an existing partition
1763	// or that the new partition does not overwrite an existing partition
1764	// table.
1765	for (int32 i = 0; i < partition->child_count; i++) {
1766		partition_data* sibling = get_child_partition(partition->id, i);
1767		LogicalPartition* logical = (LogicalPartition*)sibling->cookie;
1768		if (logical == NULL)
1769			return false;
1770		if (ptsOffset > logical->Offset()
1771			&& ptsOffset < logical->Offset() + logical->Size())
1772			return false;
1773		if ((logical->PartitionTableOffset() >= offset
1774			&& logical->PartitionTableOffset() < offset + size)
1775			|| logical->PartitionTableOffset() == ptsOffset)
1776			return false;
1777	}
1778
1779	return true;
1780}
1781
1782
1783// #pragma mark - Intel Extended Partition - write functions
1784
1785
1786// ep_resize
1787status_t
1788ep_resize(int fd, partition_id partitionID, off_t size, disk_job_id job)
1789{
1790	TRACE(("intel: ep_resize\n"));
1791
1792	if (fd < 0)
1793		return B_ERROR;
1794
1795	PartitionWriteLocker locker(partitionID);
1796	if (!locker.IsLocked())
1797		return B_ERROR;
1798
1799	// get out partition
1800	partition_data* partition = get_partition(partitionID);
1801	if (!partition)
1802		return B_BAD_VALUE;
1803
1804	// validate the new size
1805	// TODO: The parameter has already been checked and must not be altered!
1806	off_t validatedSize = size;
1807	if (!ep_validate_resize(partition, &validatedSize))
1808		return B_BAD_VALUE;
1809
1810	// update data stuctures
1811	update_disk_device_job_progress(job, 0.0);
1812
1813	// TODO: partition->size is not supposed to be touched.
1814	partition->size = validatedSize;
1815	partition->content_size = validatedSize;
1816
1817	// all changes applied
1818	update_disk_device_job_progress(job, 1.0);
1819	partition_modified(partitionID);
1820	return B_OK;
1821}
1822
1823
1824// ep_resize_child
1825status_t
1826ep_resize_child(int fd, partition_id partitionID, off_t size, disk_job_id job)
1827{
1828	TRACE(("intel: ep_resize_child\n"));
1829
1830	if (fd < 0)
1831		return B_ERROR;
1832
1833	PartitionWriteLocker locker(partitionID);
1834	if (!locker.IsLocked())
1835		return B_ERROR;
1836
1837	// get out partition, child and LogicalPartition structure
1838	partition_data* partition = get_parent_partition(partitionID);
1839	partition_data* child = get_partition(partitionID);
1840	if (!partition || !child)
1841		return B_BAD_VALUE;
1842	LogicalPartition* logical = (LogicalPartition*)child->cookie;
1843	PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
1844	if (!logical || !primary)
1845		return B_BAD_VALUE;
1846
1847	// validate the new size
1848	// TODO: The parameter has already been checked and must not be altered!
1849	off_t validatedSize = size;
1850	if (!ep_validate_resize_child(partition, child, &validatedSize))
1851		return B_BAD_VALUE;
1852	if (child->size == validatedSize)
1853		return B_OK;
1854
1855	// update data stuctures and write changes
1856	update_disk_device_job_progress(job, 0.0);
1857	logical->SetSize(validatedSize);
1858
1859	PartitionMapWriter writer(fd, partition->block_size);
1860	// TODO: The partition is not supposed to be locked here!
1861	status_t error = writer.WriteLogical(logical, primary, false);
1862	if (error != B_OK) {
1863		// putting into previous state
1864		logical->SetSize(child->size);
1865		return error;
1866	}
1867	LogicalPartition* prev = logical->Previous();
1868	error = prev ? writer.WriteLogical(prev, primary, false)
1869				 : writer.WriteLogical(logical, primary, false);
1870	if (error != B_OK)
1871		// this should be not so fatal
1872		return error;
1873
1874	child->size = validatedSize;
1875
1876	// all changes applied
1877	update_disk_device_job_progress(job, 1.0);
1878	partition_modified(partitionID);
1879	return B_OK;
1880}
1881
1882
1883// ep_move
1884status_t
1885ep_move(int fd, partition_id partitionID, off_t offset, disk_job_id job)
1886{
1887	TRACE(("intel: ep_move\n"));
1888
1889	if (fd < 0)
1890		return B_ERROR;
1891
1892	PartitionWriteLocker locker(partitionID);
1893	if (!locker.IsLocked())
1894		return B_ERROR;
1895
1896	// get out partition
1897	partition_data* partition = get_partition(partitionID);
1898	if (!partition)
1899		return B_BAD_VALUE;
1900
1901	// validate the new start
1902	// TODO: The parameter has already been checked and must not be altered!
1903	if (!ep_validate_move(partition, &offset))
1904		return B_BAD_VALUE;
1905
1906	// nothing to do here
1907	return B_OK;
1908}
1909
1910
1911// ep_move_child
1912status_t
1913ep_move_child(int fd, partition_id partitionID, partition_id childID,
1914	off_t offset, disk_job_id job)
1915{
1916	TRACE(("intel: ep_move_child\n"));
1917
1918	if (fd < 0)
1919		return B_ERROR;
1920
1921	PartitionWriteLocker locker(partitionID);
1922	if (!locker.IsLocked())
1923		return B_ERROR;
1924
1925	// get partition, child and LogicalPartition structure
1926	partition_data* partition = get_partition(partitionID);
1927	partition_data* child = get_partition(childID);
1928	if (!partition || !child)
1929		return B_BAD_VALUE;
1930	LogicalPartition* logical = (LogicalPartition*)child->cookie;
1931	PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
1932	if (!logical || !primary)
1933		return B_BAD_VALUE;
1934
1935	// TODO: The parameter has already been checked and must not be altered!
1936	off_t validatedOffset = offset;
1937	if (!ep_validate_move_child(partition, child, &validatedOffset))
1938		return B_BAD_VALUE;
1939
1940	// if the old offset is the same, there is nothing to do
1941	if (child->offset == validatedOffset)
1942		return B_OK;
1943
1944	off_t diffOffset = validatedOffset - child->offset;
1945
1946	// buffer allocation
1947	int32 allocated;
1948	uint8* buffer = allocate_buffer(partition->block_size, MAX_MOVE_BUFFER,
1949		&allocated);
1950	if (!buffer)
1951		return B_NO_MEMORY;
1952
1953	// partition moving
1954	update_disk_device_job_progress(job, 0.0);
1955	status_t error = B_OK;
1956	// move partition with its header (partition table)
1957	off_t pts_offset = logical->Offset() - logical->PartitionTableOffset();
1958	error = move_partition(fd, child->offset - pts_offset,
1959		validatedOffset - pts_offset, child->size + pts_offset, buffer,
1960		allocated * partition->block_size, job);
1961	delete[] buffer;
1962	if (error != B_OK)
1963		return error;
1964
1965	// partition moved
1966	// updating data structure
1967	child->offset = validatedOffset;
1968	logical->SetOffset(logical->Offset() + diffOffset);
1969	logical->SetPartitionTableOffset(logical->PartitionTableOffset() + diffOffset);
1970
1971	PartitionMapWriter writer(fd, partition->block_size);
1972		// TODO: If partition->offset is > prev->offset, then writing
1973		// the previous logical partition table will fail!
1974	// TODO: The partition is not supposed to be locked here!
1975	error = writer.WriteLogical(logical, primary, false);
1976	if (error != B_OK)
1977		// something went wrong - this is fatal (partition has been moved)
1978		// but EBR is not updated
1979		return error;
1980	LogicalPartition* prev = logical->Previous();
1981	error = prev ? writer.WriteLogical(prev, primary, false)
1982				 : writer.WriteLogical(logical, primary, false);
1983	if (error != B_OK)
1984		// this is fatal - linked list is not updated
1985		return error;
1986
1987	// all changes applied
1988	update_disk_device_job_progress(job, 1.0);
1989	partition_modified(childID);
1990	return B_OK;
1991}
1992
1993
1994// ep_set_type
1995status_t
1996ep_set_type(int fd, partition_id partitionID, const char* type, disk_job_id job)
1997{
1998	TRACE(("intel: ep_set_type\n"));
1999
2000	if (fd < 0 || !type)
2001		return B_BAD_VALUE;
2002
2003	PartitionWriteLocker locker(partitionID);
2004	if (!locker.IsLocked())
2005		return B_ERROR;
2006
2007	// get partition, child and LogicalPartition structure
2008	partition_data* partition = get_parent_partition(partitionID);
2009	partition_data* child = get_partition(partitionID);
2010	if (!partition || !child)
2011		return B_BAD_VALUE;
2012	LogicalPartition* logical = (LogicalPartition*)child->cookie;
2013	PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
2014	if (!logical || !primary)
2015		return B_BAD_VALUE;
2016
2017	// TODO: The parameter has already been checked and must not be altered!
2018	if (!ep_validate_set_type(child, type))
2019		return B_BAD_VALUE;
2020
2021	// if the old type is the same, there is nothing to do
2022	if (child->type && !strcmp(type, child->type))
2023		return B_OK;
2024
2025	PartitionType ptype;
2026	ptype.SetType(type);
2027	// this is impossible
2028	if (!ptype.IsValid() || ptype.IsEmpty() || ptype.IsExtended())
2029		return false;
2030
2031	// setting type to the partition
2032	update_disk_device_job_progress(job, 0.0);
2033	uint8 oldType = logical->Type();
2034	logical->SetType(ptype.Type());
2035
2036	PartitionMapWriter writer(fd, partition->block_size);
2037	// TODO: The partition is not supposed to be locked here!
2038	status_t error = writer.WriteLogical(logical, primary, false);
2039	if (error != B_OK) {
2040		// something went wrong - putting into previous state
2041		logical->SetType(oldType);
2042		return error;
2043	}
2044
2045	free(child->type);
2046	child->type = strdup(type);
2047	if (!child->type)
2048		return B_NO_MEMORY;
2049
2050	// all changes applied
2051	update_disk_device_job_progress(job, 1.0);
2052	partition_modified(partitionID);
2053	return B_OK;
2054}
2055
2056
2057// ep_initialize
2058status_t
2059ep_initialize(int fd, partition_id partitionID, const char* name,
2060	const char* parameters, off_t partitionSize, disk_job_id job)
2061{
2062	TRACE(("intel: ep_initialize\n"));
2063
2064	if (fd < 0)
2065		return B_ERROR;
2066
2067	PartitionWriteLocker locker(partitionID);
2068	if (!locker.IsLocked())
2069		return B_ERROR;
2070
2071	// get partition
2072	partition_data* partition = get_partition(partitionID);
2073	if (!partition)
2074		return B_BAD_VALUE;
2075
2076	PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
2077	if (!primary)
2078		return B_BAD_VALUE;
2079
2080	// name is ignored - we cannot set it to the Intel Extended Partition
2081// TODO: The parameter has already been checked and must not be altered!
2082	if (!ep_validate_initialize(partition, NULL, parameters))
2083		return B_BAD_VALUE;
2084
2085	// partition init (we have no child partition)
2086	update_disk_device_job_progress(job, 0.0);
2087	// fill in the partition_data structure
2088	partition->status = B_PARTITION_VALID;
2089	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
2090	partition->content_size = partition->size;
2091	// (no content_name and content_parameters)
2092	// (content_type is set by the system)
2093	partition->content_cookie = primary;
2094
2095	// we delete code area in EBR - nothing should be there
2096	partition_table table;
2097	table.clear_code_area();
2098
2099	PartitionMapWriter writer(fd, partition->block_size);
2100	// TODO: The partition is not supposed to be locked here!
2101	status_t error = writer.ClearExtendedHead(primary);
2102	if (error != B_OK)
2103		return error;
2104
2105	// all changes applied
2106	update_disk_device_job_progress(job, 1.0);
2107	partition_modified(partitionID);
2108	return B_OK;
2109}
2110
2111
2112// ep_create_child
2113/*!
2114	childID is used for the return value, but is also an optional input
2115	parameter -- -1 to be ignored
2116*/
2117status_t
2118ep_create_child(int fd, partition_id partitionID, off_t offset, off_t size,
2119	const char* type, const char* name, const char* parameters, disk_job_id job,
2120	partition_id* childID)
2121{
2122	TRACE(("intel: ep_create_child\n"));
2123
2124	if (fd < 0 || !childID)
2125		return B_BAD_VALUE;
2126
2127	// aquire lock
2128	PartitionWriteLocker locker(partitionID);
2129	if (!locker.IsLocked())
2130		return B_ERROR;
2131
2132	// get partition data
2133	partition_data* partition = get_partition(partitionID);
2134	partition_data* parent = get_parent_partition(partitionID);
2135	if (partition == NULL || parent == NULL)
2136		return B_BAD_VALUE;
2137
2138	PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
2139	if (!primary)
2140		return B_BAD_VALUE;
2141
2142	// parse parameters
2143	void* handle = parse_driver_settings_string(parameters);
2144	if (handle == NULL)
2145		return B_ERROR;
2146
2147	bool active = get_driver_boolean_parameter(handle, "active", false, true);
2148
2149	off_t ptsOffset = 0;
2150	const char* buffer = get_driver_parameter(
2151		handle, "partition_table_offset", NULL, NULL);
2152	if (buffer != NULL)
2153		ptsOffset = strtoull(buffer, NULL, 10);
2154	else {
2155		delete_driver_settings(handle);
2156		return B_BAD_VALUE;
2157	}
2158	delete_driver_settings(handle);
2159
2160	// check the partition location
2161	if (!check_partition_location_ep(partition, offset, size, ptsOffset))
2162		return B_BAD_VALUE;
2163
2164	// creating partition
2165	update_disk_device_job_progress(job, 0.0);
2166	partition_data* child = create_child_partition(partition->id,
2167		partition->child_count, offset, size, *childID);
2168	if (!child)
2169		return B_ERROR;
2170
2171	// setup logical partition
2172	LogicalPartition* logical = new(nothrow) LogicalPartition;
2173	if (!logical)
2174		return B_NO_MEMORY;
2175
2176	PartitionType ptype;
2177	ptype.SetType(type);
2178	logical->SetPartitionTableOffset(ptsOffset - parent->offset);
2179	logical->SetOffset(offset);
2180	logical->SetSize(size);
2181	logical->SetType(ptype.Type());
2182	logical->SetActive(active);
2183	logical->SetPrimaryPartition(primary);
2184	logical->SetBlockSize(partition->block_size);
2185	primary->AddLogicalPartition(logical);
2186
2187	int parentFD = open_partition(parent->id, O_RDWR);
2188	if (parentFD < 0) {
2189		primary->RemoveLogicalPartition(logical);
2190		delete logical;
2191		return B_IO_ERROR;
2192	}
2193
2194	// write changes to disk
2195	PartitionMapWriter writer(parentFD, primary->BlockSize());
2196
2197	// Write the logical partition's EBR first in case of failure.
2198	// This way we will not add a partition to the previous logical
2199	// partition. If there is no previous logical partition then write
2200	// the current partition's EBR to the first sector of the primary partition
2201	status_t error = writer.WriteLogical(logical, primary, true);
2202	if (error != B_OK) {
2203		primary->RemoveLogicalPartition(logical);
2204		delete logical;
2205		return error;
2206	}
2207
2208	LogicalPartition* previous = logical->Previous();
2209	if (previous != NULL) {
2210		error = writer.WriteLogical(previous, primary, true);
2211		if (error != B_OK) {
2212			primary->RemoveLogicalPartition(logical);
2213			delete logical;
2214			return error;
2215		}
2216	}
2217	*childID = child->id;
2218
2219	child->block_size = logical->BlockSize();
2220	child->type = strdup(type);
2221	child->parameters = strdup(parameters);
2222	child->cookie = logical;
2223	// check for allocation problems
2224	if (!child->type || !child->parameters)
2225		error = B_NO_MEMORY;
2226
2227	// all changes applied
2228	update_disk_device_job_progress(job, 1.0);
2229	partition_modified(partitionID);
2230	return B_OK;
2231}
2232
2233
2234// ep_delete_child
2235status_t
2236ep_delete_child(int fd, partition_id partitionID, partition_id childID,
2237	disk_job_id job)
2238{
2239	TRACE(("intel: ep_delete_child\n"));
2240
2241	if (fd < 0)
2242		return B_ERROR;
2243
2244	PartitionWriteLocker locker(partitionID);
2245	if (!locker.IsLocked())
2246		return B_ERROR;
2247
2248	partition_data* partition = get_partition(partitionID);
2249	partition_data* parent = get_parent_partition(partitionID);
2250	partition_data* child = get_partition(childID);
2251	if (partition == NULL || parent == NULL || child == NULL)
2252		return B_BAD_VALUE;
2253
2254	PrimaryPartition* primary = (PrimaryPartition*)partition->cookie;
2255	LogicalPartition* logical = (LogicalPartition*)child->cookie;
2256	if (primary == NULL || logical == NULL)
2257		return B_BAD_VALUE;
2258
2259	// deleting child
2260	update_disk_device_job_progress(job, 0.0);
2261	if (!delete_partition(childID))
2262		return B_ERROR;
2263
2264	LogicalPartition* previous = logical->Previous();
2265	LogicalPartition* next = logical->Next();
2266
2267	primary->RemoveLogicalPartition(logical);
2268	delete logical;
2269
2270	int parentFD = open_partition(parent->id, O_RDWR);
2271	if (parentFD < 0)
2272		return B_IO_ERROR;
2273
2274	// write changes to disk
2275	PartitionMapWriter writer(parentFD, primary->BlockSize());
2276
2277	status_t error;
2278	if (previous != NULL) {
2279		error = writer.WriteLogical(previous, primary, true);
2280	} else {
2281		error = writer.WriteExtendedHead(next, primary, true);
2282
2283		if (next != NULL) {
2284			next->SetPartitionTableOffset(primary->Offset());
2285
2286			partition_data* nextSibling = NULL;
2287			if (get_partition_from_offset_ep(partition, next->Offset(),
2288				&nextSibling)) {
2289				char buffer[128];
2290				sprintf(buffer, "active %s ;\npartition_table_offset %" B_PRId64
2291					" ;\n", next->Active() ? "true" : "false",
2292					next->PartitionTableOffset());
2293				nextSibling->parameters = strdup(buffer);
2294			}
2295		}
2296	}
2297
2298	close(parentFD);
2299
2300	if (error != B_OK)
2301		return error;
2302
2303	// all changes applied
2304	update_disk_device_job_progress(job, 1.0);
2305	partition_modified(partitionID);
2306	return B_OK;
2307}
2308
2309