1/*
2 * Copyright 2003-2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <errno.h>
8#include <new>
9#include <unistd.h>
10#include <stdio.h>
11#include <sys/stat.h>
12
13#include <Directory.h>
14#include <DiskDevice.h>
15#include <DiskDevicePrivate.h>
16#include <DiskDeviceVisitor.h>
17#include <DiskSystem.h>
18#include <fs_volume.h>
19#include <Message.h>
20#include <ObjectList.h>
21#include <Partition.h>
22#include <PartitioningInfo.h>
23#include <Path.h>
24#include <String.h>
25#include <Volume.h>
26
27#include <AutoDeleter.h>
28
29#include <ddm_userland_interface_defs.h>
30#include <syscalls.h>
31
32#include "PartitionDelegate.h"
33
34
35//#define TRACE_PARTITION
36#undef TRACE
37#ifdef TRACE_PARTITION
38# define TRACE(x...) printf(x)
39#else
40# define TRACE(x...) do {} while (false)
41#endif
42
43
44using std::nothrow;
45
46static const char *skAutoCreatePrefix = "_HaikuAutoCreated";
47
48
49/*!	\class BPartition
50	\brief A BPartition object represent a partition and provides a lot of
51		   methods to retrieve information about it and some to manipulate it.
52
53	Not all BPartitions represent actual on-disk partitions. Some exist only
54	to make all devices fit smoothly into the framework (e.g. for floppies,
55	\see IsVirtual()), others represents merely partition slots
56	(\see IsEmpty()).
57*/
58
59
60/*!	\brief \c NULL aware strcmp().
61
62	\c NULL is considered the least of all strings. \c NULL equals \c NULL.
63
64	\param str1 First string.
65	\param str2 Second string.
66	\return A value less than 0, if \a str1 is less than \a str2,
67			0, if they are equal, or a value greater than 0, if
68			\a str1 is greater \a str2.
69*/
70static inline int
71compare_string(const char* str1, const char* str2)
72{
73	if (str1 == NULL) {
74		if (str2 == NULL)
75			return 0;
76		return 1;
77	}
78	if (str2 == NULL)
79		return -1;
80	return strcmp(str1, str2);
81}
82
83
84// #pragma mark -
85
86
87BPartition::BPartition()
88	:
89	fDevice(NULL),
90	fParent(NULL),
91	fPartitionData(NULL),
92	fDelegate(NULL)
93{
94}
95
96
97/*!	\brief Frees all resources associated with this object.
98*/
99BPartition::~BPartition()
100{
101	_Unset();
102}
103
104
105/*!	\brief Returns the partition's offset relative to the beginning of the
106		   device it resides on.
107	\return The partition's offset in bytes relative to the beginning of the
108			device it resides on.
109*/
110off_t
111BPartition::Offset() const
112{
113	return _PartitionData()->offset;
114}
115
116
117/*!	\brief Returns the size of the partition.
118	\return The size of the partition in bytes.
119*/
120off_t
121BPartition::Size() const
122{
123	return _PartitionData()->size;
124}
125
126
127off_t
128BPartition::ContentSize() const
129{
130	return _PartitionData()->content_size;
131}
132
133
134/*!	\brief Returns the block size of the device.
135	\return The block size of the device in bytes.
136*/
137uint32
138BPartition::BlockSize() const
139{
140	return _PartitionData()->block_size;
141}
142
143
144/*!	\brief Returns the index of the partition in its session's list of
145		   partitions.
146	\return The index of the partition in its session's list of partitions.
147*/
148int32
149BPartition::Index() const
150{
151	return _PartitionData()->index;
152}
153
154
155uint32
156BPartition::Status() const
157{
158	return _PartitionData()->status;
159}
160
161
162bool
163BPartition::ContainsFileSystem() const
164{
165	return _PartitionData()->flags & B_PARTITION_FILE_SYSTEM;
166}
167
168
169bool
170BPartition::ContainsPartitioningSystem() const
171{
172	return _PartitionData()->flags & B_PARTITION_PARTITIONING_SYSTEM;
173}
174
175
176bool
177BPartition::IsDevice() const
178{
179	return _PartitionData()->flags & B_PARTITION_IS_DEVICE;
180}
181
182
183bool
184BPartition::IsReadOnly() const
185{
186	return _PartitionData()->flags & B_PARTITION_READ_ONLY;
187}
188
189
190/*!	\brief Returns whether the volume is mounted.
191	\return \c true, if the volume is mounted, \c false otherwise.
192*/
193bool
194BPartition::IsMounted() const
195{
196	return _PartitionData()->flags & B_PARTITION_MOUNTED;
197	// alternatively:
198	// return _PartitionData()->volume >= 0;
199}
200
201
202bool
203BPartition::IsBusy() const
204{
205	return _PartitionData()->flags & B_PARTITION_BUSY;
206}
207
208
209bool
210BPartition::SupportsChildName() const
211{
212	return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_NAME);
213}
214
215
216/*!	\brief Returns the flags for this partitions.
217
218	The partition flags are a bitwise combination of:
219	- \c B_HIDDEN_PARTITION: The partition can not contain a file system.
220	- \c B_VIRTUAL_PARTITION: There exists no on-disk partition this object
221	  represents. E.g. for floppies there will be a BPartition object spanning
222	  the whole floppy disk.
223	- \c B_EMPTY_PARTITION: The partition represents no physical partition,
224	  but merely an empty slot. This mainly used to keep the indexing of
225	  partitions more persistent. This flag implies also \c B_HIDDEN_PARTITION.
226
227	\return The flags for this partition.
228*/
229uint32
230BPartition::Flags() const
231{
232	return _PartitionData()->flags;
233}
234
235
236/*!	\brief Returns the name of the partition.
237
238	Note, that not all partitioning system support names. The method returns
239	\c NULL, if the partition doesn't have a name.
240
241	\return The name of the partition, or \c NULL, if the partitioning system
242			does not support names.
243*/
244const char*
245BPartition::Name() const
246{
247	return _PartitionData()->name;
248}
249
250
251const char*
252BPartition::ContentName() const
253{
254	return _PartitionData()->content_name;
255}
256
257
258/*!	\brief Returns a human readable string for the type of the partition.
259	\return A human readable string for the type of the partition.
260*/
261const char*
262BPartition::Type() const
263{
264	return _PartitionData()->type;
265}
266
267
268const char*
269BPartition::ContentType() const
270{
271	return _PartitionData()->content_type;
272}
273
274
275/*!	\brief Returns a unique identifier for this partition.
276
277	The ID is not persistent, i.e. in general won't be the same after
278	rebooting.
279
280	\see BDiskDeviceRoster::GetPartitionWithID().
281
282	\return A unique identifier for this partition.
283*/
284int32
285BPartition::ID() const
286{
287	return _PartitionData()->id;
288}
289
290
291const char*
292BPartition::Parameters() const
293{
294	return _PartitionData()->parameters;
295}
296
297
298const char*
299BPartition::ContentParameters() const
300{
301	return _PartitionData()->content_parameters;
302}
303
304
305status_t
306BPartition::GetDiskSystem(BDiskSystem* diskSystem) const
307{
308	const user_partition_data* data = _PartitionData();
309	if (data == NULL || diskSystem == NULL)
310		return B_BAD_VALUE;
311
312	if (data->disk_system < 0)
313		return B_ENTRY_NOT_FOUND;
314
315	return diskSystem->_SetTo(data->disk_system);
316}
317
318
319status_t
320BPartition::GetPath(BPath* path) const
321{
322	// The path is constructed on the fly using our parent
323	if (path == NULL || Parent() == NULL || Index() < 0)
324		return B_BAD_VALUE;
325
326	// get the parent's path
327	status_t error = Parent()->GetPath(path);
328	if (error != B_OK)
329		return error;
330
331	char indexBuffer[24];
332
333	if (Parent()->IsDevice()) {
334		// Our parent is a device, so we replace `raw' by our index.
335		const char* leaf = path->Leaf();
336		if (!leaf || strcmp(leaf, "raw") != B_OK)
337			return B_ERROR;
338
339		snprintf(indexBuffer, sizeof(indexBuffer), "%" B_PRId32, Index());
340	} else {
341		// Our parent is a normal partition, no device: Append our index.
342		snprintf(indexBuffer, sizeof(indexBuffer), "%s_%" B_PRId32,
343			path->Leaf(), Index());
344	}
345
346	error = path->GetParent(path);
347	if (error == B_OK)
348		error = path->Append(indexBuffer);
349
350	return error;
351}
352
353
354/*!	\brief Returns a BVolume for the partition.
355
356	This can only succeed, if the partition is mounted.
357
358	\param volume Pointer to a pre-allocated BVolume, to be initialized to
359		   represent the volume.
360	\return \c B_OK, if the volume is mounted and the parameter could be set
361			accordingly, another error code otherwise.
362*/
363status_t
364BPartition::GetVolume(BVolume* volume) const
365{
366	if (volume == NULL)
367		return B_BAD_VALUE;
368
369	return volume->SetTo(_PartitionData()->volume);
370}
371
372
373/*!	\brief Returns an icon for this partition.
374
375	Note, that currently there are only per-device icons, i.e. the method
376	returns the same icon for each partition of a device. But this may change
377	in the future.
378
379	\param icon Pointer to a pre-allocated BBitmap to be set to the icon of
380		   the partition.
381	\param which Size of the icon to be retrieved. Can be \c B_MINI_ICON or
382		   \c B_LARGE_ICON.
383	\return \c B_OK, if everything went fine, another error code otherwise.
384*/
385status_t
386BPartition::GetIcon(BBitmap* icon, icon_size which) const
387{
388	if (icon == NULL)
389		return B_BAD_VALUE;
390
391	status_t error;
392
393	if (IsMounted()) {
394		// mounted: get the icon from the volume
395		BVolume volume;
396		error = GetVolume(&volume);
397		if (error == B_OK)
398			error = volume.GetIcon(icon, which);
399	} else {
400		// not mounted: retrieve the icon ourselves
401		if (BDiskDevice* device = Device()) {
402			BPath path;
403			error = device->GetPath(&path);
404			// get the icon
405			if (error == B_OK)
406				error = get_device_icon(path.Path(), icon, which);
407		} else
408			error = B_ERROR;
409	}
410	return error;
411}
412
413
414status_t
415BPartition::GetIcon(uint8** _data, size_t* _size, type_code* _type) const
416{
417	if (_data == NULL || _size == NULL || _type == NULL)
418		return B_BAD_VALUE;
419
420	status_t error;
421
422	if (IsMounted()) {
423		// mounted: get the icon from the volume
424		BVolume volume;
425		error = GetVolume(&volume);
426		if (error == B_OK)
427			error = volume.GetIcon(_data, _size, _type);
428	} else {
429		// not mounted: retrieve the icon ourselves
430		if (BDiskDevice* device = Device()) {
431			BPath path;
432			error = device->GetPath(&path);
433			// get the icon
434			if (error == B_OK)
435				error = get_device_icon(path.Path(), _data, _size, _type);
436		} else
437			error = B_ERROR;
438	}
439	return error;
440}
441
442
443/*!	\brief Returns the mount point for the partition.
444
445	If the partition is mounted this is the actual mount point. If it is not
446	mounted, but contains a file system, derived from the partition name
447	the name for a not yet existing directory in the root directory is
448	constructed and the path to it returned.
449
450	For partitions not containing a file system the method returns an error.
451
452	\param mountPoint Pointer to the path to be set to refer the mount point
453		   (respectively potential mount point) of the partition.
454	\return \c B_OK, if everything went fine, an error code otherwise.
455*/
456status_t
457BPartition::GetMountPoint(BPath* mountPoint) const
458{
459	if (mountPoint == NULL || !ContainsFileSystem())
460		return B_BAD_VALUE;
461
462	// if the partition is mounted, return the actual mount point
463	BVolume volume;
464	if (GetVolume(&volume) == B_OK) {
465		BDirectory dir;
466		status_t error = volume.GetRootDirectory(&dir);
467		if (error == B_OK)
468			error = mountPoint->SetTo(&dir, NULL);
469		return error;
470	}
471
472	// partition not mounted
473	// get the volume name
474	const char* volumeName = ContentName();
475	if (volumeName == NULL || strlen(volumeName) == 0)
476		volumeName = Name();
477	if (volumeName == NULL || strlen(volumeName) == 0)
478		volumeName = "unnamed volume";
479
480	// construct a path name from the volume name
481	// replace '/'s and prepend a '/'
482	BString mountPointPath(volumeName);
483	mountPointPath.ReplaceAll('/', '-');
484	mountPointPath.Insert("/", 0);
485
486	// make the name unique
487	BString basePath(mountPointPath);
488	int counter = 1;
489	while (true) {
490		BEntry entry;
491		status_t error = entry.SetTo(mountPointPath.String());
492		if (error != B_OK)
493			return error;
494
495		if (!entry.Exists())
496			break;
497		mountPointPath = basePath;
498		mountPointPath << counter;
499		counter++;
500	}
501
502	return mountPoint->SetTo(mountPointPath.String());
503}
504
505
506/*!	\brief Mounts the volume.
507
508	The volume can only be mounted, if the partition contains a recognized
509	file system (\see ContainsFileSystem()) and it is not already mounted.
510
511	If no mount point is given, one will be created automatically under the
512	root directory (derived from the volume name). If one is given, the
513	directory must already exist.
514
515	\param mountPoint The directory where to mount the file system. May be
516		   \c NULL, in which case a mount point in the root directory will be
517		   created automatically.
518	\param mountFlags Currently only \c B_MOUNT_READ_ONLY is defined, which
519		   forces the volume to be mounted read-only.
520	\param parameters File system specific mount parameters.
521	\return \c B_OK, if everything went fine, another error code otherwise.
522*/
523dev_t
524BPartition::Mount(const char* mountPoint, uint32 mountFlags,
525	const char* parameters)
526{
527	if (IsMounted() || !ContainsFileSystem())
528		return B_BAD_VALUE;
529
530	// get the partition path
531	BPath partitionPath;
532	status_t error = GetPath(&partitionPath);
533	if (error != B_OK)
534		return error;
535
536	// create a mount point, if none is given
537	bool deleteMountPoint = false;
538	BPath mountPointPath, markerPath;
539	if (!mountPoint) {
540		// get a unique mount point
541		error = GetMountPoint(&mountPointPath);
542		if (error != B_OK)
543			return error;
544
545		mountPoint = mountPointPath.Path();
546		markerPath = mountPointPath;
547		markerPath.Append(skAutoCreatePrefix);
548
549		// create the directory
550		if (mkdir(mountPoint, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
551			return errno;
552
553		if (mkdir(markerPath.Path(), S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
554			rmdir(mountPoint);
555			return errno;
556		}
557
558		deleteMountPoint = true;
559	}
560
561	// mount the partition
562	dev_t device = fs_mount_volume(mountPoint, partitionPath.Path(), NULL,
563		mountFlags, parameters);
564
565	// delete the mount point on error, if we created it
566	if (device < B_OK && deleteMountPoint) {
567		rmdir(markerPath.Path());
568		rmdir(mountPoint);
569	}
570
571	// update object, if successful
572	if (device >= 0)
573		error = Device()->Update();
574
575	return device;
576}
577
578
579/*!	\brief Unmounts the volume.
580
581	The volume can of course only be unmounted, if it currently is mounted.
582
583	\param unmountFlags Currently only \c B_FORCE_UNMOUNT is defined, which
584		   forces the partition to be unmounted, even if there are still
585		   open FDs. Be careful using this flag -- you risk the user's data.
586
587	\return \c B_OK, if everything went fine, another error code otherwise.
588*/
589status_t
590BPartition::Unmount(uint32 unmountFlags)
591{
592	if (!IsMounted())
593		return B_BAD_VALUE;
594
595	// get the partition path
596	BPath path;
597	status_t status = GetMountPoint(&path);
598	if (status != B_OK)
599		return status;
600
601	// unmount
602	status = fs_unmount_volume(path.Path(), unmountFlags);
603
604	// update object, if successful
605	if (status == B_OK) {
606		status = Device()->Update();
607
608		// Check if we created this mount point on the fly.
609		// If so, clean it up.
610		BPath markerPath = path;
611		markerPath.Append(skAutoCreatePrefix);
612		BEntry pathEntry (markerPath.Path());
613		if (pathEntry.InitCheck() == B_OK && pathEntry.Exists()) {
614			rmdir(markerPath.Path());
615			rmdir(path.Path());
616		}
617	}
618
619	return status;
620}
621
622
623/*!	\brief Returns the device this partition resides on.
624	\return The device this partition resides on.
625*/
626BDiskDevice*
627BPartition::Device() const
628{
629	return fDevice;
630}
631
632
633BPartition*
634BPartition::Parent() const
635{
636	return fParent;
637}
638
639
640BPartition*
641BPartition::ChildAt(int32 index) const
642{
643	if (fDelegate != NULL) {
644		Delegate* child = fDelegate->ChildAt(index);
645		return child ? child->Partition() : NULL;
646	}
647
648	return _ChildAt(index);
649}
650
651
652int32
653BPartition::CountChildren() const
654{
655	if (fDelegate != NULL)
656		return fDelegate->CountChildren();
657
658	return _CountChildren();
659}
660
661
662int32
663BPartition::CountDescendants() const
664{
665	int32 count = 1;
666	for (int32 i = 0; BPartition* child = ChildAt(i); i++)
667		count += child->CountDescendants();
668	return count;
669}
670
671
672BPartition*
673BPartition::FindDescendant(partition_id id) const
674{
675	IDFinderVisitor visitor(id);
676	return VisitEachDescendant(&visitor);
677}
678
679
680status_t
681BPartition::GetPartitioningInfo(BPartitioningInfo* info) const
682{
683	if (!info)
684		return B_BAD_VALUE;
685	if (fDelegate == NULL)
686		return B_NO_INIT;
687
688	return fDelegate->GetPartitioningInfo(info);
689}
690
691
692BPartition*
693BPartition::VisitEachChild(BDiskDeviceVisitor* visitor) const
694{
695	if (visitor != NULL) {
696		int32 level = _Level();
697		for (int32 i = 0; BPartition* child = ChildAt(i); i++) {
698			if (child->_AcceptVisitor(visitor, level))
699				return child;
700		}
701	}
702	return NULL;
703}
704
705
706BPartition*
707BPartition::VisitEachDescendant(BDiskDeviceVisitor* visitor) const
708{
709	if (visitor != NULL)
710		return const_cast<BPartition*>(this)->_VisitEachDescendant(visitor);
711	return NULL;
712}
713
714
715bool
716BPartition::CanDefragment(bool* whileMounted) const
717{
718	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING,
719		B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED, whileMounted);
720}
721
722
723status_t
724BPartition::Defragment() const
725{
726	if (fDelegate == NULL)
727		return B_NO_INIT;
728
729	return fDelegate->Defragment();
730}
731
732
733bool
734BPartition::CanRepair(bool checkOnly, bool* whileMounted) const
735{
736	uint32 flag;
737	uint32 whileMountedFlag;
738	if (checkOnly) {
739		flag = B_DISK_SYSTEM_SUPPORTS_CHECKING;
740		whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED;
741	} else {
742		flag = B_DISK_SYSTEM_SUPPORTS_REPAIRING;
743		whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED;
744	}
745
746	return _SupportsOperation(flag, whileMountedFlag, whileMounted);
747}
748
749
750status_t
751BPartition::Repair(bool checkOnly) const
752{
753	if (fDelegate == NULL)
754		return B_NO_INIT;
755
756	return fDelegate->Repair(checkOnly);
757}
758
759
760bool
761BPartition::CanResize(bool* canResizeContents, bool* whileMounted) const
762{
763	BPartition* parent = Parent();
764	if (parent == NULL)
765		return false;
766
767	if (!parent->_SupportsChildOperation(this,
768			B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD)) {
769		return false;
770	}
771
772	if (!_HasContent())
773		return true;
774
775	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_RESIZING,
776		B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED, whileMounted);
777}
778
779
780status_t
781BPartition::ValidateResize(off_t* size) const
782{
783	BPartition* parent = Parent();
784	if (parent == NULL || fDelegate == NULL)
785		return B_NO_INIT;
786
787	status_t error = parent->fDelegate->ValidateResizeChild(fDelegate, size);
788	if (error != B_OK)
789		return error;
790
791	if (_HasContent()) {
792		// TODO: We would actually need the parameter for the content size.
793		off_t contentSize = *size;
794		error = fDelegate->ValidateResize(&contentSize);
795		if (error != B_OK)
796			return error;
797
798		if (contentSize > *size)
799			return B_BAD_VALUE;
800	}
801
802	return B_OK;
803}
804
805
806status_t
807BPartition::Resize(off_t size)
808{
809	BPartition* parent = Parent();
810	if (!parent || !fDelegate)
811		return B_NO_INIT;
812
813	status_t error;
814	off_t contentSize = size;
815	if (_HasContent()) {
816		error = fDelegate->ValidateResize(&contentSize);
817		if (error != B_OK)
818			return error;
819
820		if (contentSize > size)
821			return B_BAD_VALUE;
822	}
823
824	// If shrinking the partition, resize content first, otherwise last.
825	bool shrink = Size() > size;
826
827	if (shrink && ContentType() != NULL) {
828		error = fDelegate->Resize(contentSize);
829		if (error != B_OK)
830			return error;
831	}
832
833	error = parent->fDelegate->ResizeChild(fDelegate, size);
834	if (error != B_OK)
835		return error;
836
837	if (!shrink && ContentType() != NULL) {
838		error = fDelegate->Resize(contentSize);
839		if (error != B_OK)
840			return error;
841	}
842
843	return B_OK;
844}
845
846
847bool
848BPartition::CanMove(BObjectList<BPartition>* unmovableDescendants,
849	BObjectList<BPartition>* movableOnlyIfUnmounted) const
850{
851	BPartition* parent = Parent();
852	if (parent == NULL || fDelegate == NULL)
853		return false;
854
855	if (!parent->_SupportsChildOperation(this,
856			B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD)) {
857		return false;
858	}
859
860	bool whileMounted;
861	bool movable = _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_MOVING,
862		B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED, &whileMounted);
863	if (!movable)
864		return false;
865
866	if (!whileMounted)
867		movableOnlyIfUnmounted->AddItem(const_cast<BPartition*>(this));
868
869	// collect descendent partitions
870	// TODO: ...
871// TODO: Currently there's no interface for asking descendents. They'll still
872// have the same offset (relative to their parent) after moving. The only thing
873// we really have to ask is whether they need to be unmounted.
874
875	return true;
876}
877
878
879status_t
880BPartition::ValidateMove(off_t* offset) const
881{
882	BPartition* parent = Parent();
883	if (parent == NULL || fDelegate == NULL)
884		return B_NO_INIT;
885
886	status_t error = parent->fDelegate->ValidateMoveChild(fDelegate, offset);
887	if (error != B_OK)
888		return error;
889
890	if (_HasContent()) {
891		off_t contentOffset = *offset;
892		error = fDelegate->ValidateMove(&contentOffset);
893		if (error != B_OK)
894			return error;
895
896		if (contentOffset != *offset)
897			return B_BAD_VALUE;
898	}
899
900	return B_OK;
901}
902
903
904status_t
905BPartition::Move(off_t offset)
906{
907	BPartition* parent = Parent();
908	if (parent == NULL || fDelegate == NULL)
909		return B_NO_INIT;
910
911	status_t error = parent->fDelegate->MoveChild(fDelegate, offset);
912	if (error != B_OK)
913		return error;
914
915	if (_HasContent()) {
916		error = fDelegate->Move(offset);
917		if (error != B_OK)
918			return error;
919	}
920
921	return B_OK;
922}
923
924
925bool
926BPartition::CanSetName() const
927{
928	BPartition* parent = Parent();
929	if (parent == NULL || fDelegate == NULL)
930		return false;
931
932	return parent->_SupportsChildOperation(this,
933		B_DISK_SYSTEM_SUPPORTS_SETTING_NAME);
934}
935
936
937status_t
938BPartition::ValidateSetName(BString* name) const
939{
940	BPartition* parent = Parent();
941	if (parent == NULL || fDelegate == NULL)
942		return B_NO_INIT;
943
944	return parent->fDelegate->ValidateSetName(fDelegate, name);
945}
946
947
948status_t
949BPartition::SetName(const char* name)
950{
951	BPartition* parent = Parent();
952	if (parent == NULL || fDelegate == NULL)
953		return B_NO_INIT;
954
955	return parent->fDelegate->SetName(fDelegate, name);
956}
957
958
959bool
960BPartition::CanSetContentName(bool* whileMounted) const
961{
962	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME,
963		B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED,
964		whileMounted);
965}
966
967
968status_t
969BPartition::ValidateSetContentName(BString* name) const
970{
971	if (fDelegate == NULL)
972		return B_NO_INIT;
973
974	return fDelegate->ValidateSetContentName(name);
975}
976
977
978status_t
979BPartition::SetContentName(const char* name)
980{
981	if (fDelegate == NULL)
982		return B_NO_INIT;
983
984	return fDelegate->SetContentName(name);
985}
986
987
988bool
989BPartition::CanSetType() const
990{
991	BPartition* parent = Parent();
992	if (parent == NULL)
993		return false;
994
995	return parent->_SupportsChildOperation(this,
996		B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE);
997}
998
999
1000status_t
1001BPartition::ValidateSetType(const char* type) const
1002{
1003	BPartition* parent = Parent();
1004	if (parent == NULL || fDelegate == NULL)
1005		return B_NO_INIT;
1006
1007	return parent->fDelegate->ValidateSetType(fDelegate, type);
1008}
1009
1010
1011status_t
1012BPartition::SetType(const char* type)
1013{
1014	BPartition* parent = Parent();
1015	if (parent == NULL || fDelegate == NULL)
1016		return B_NO_INIT;
1017
1018	return parent->fDelegate->SetType(fDelegate, type);
1019}
1020
1021
1022bool
1023BPartition::CanEditParameters() const
1024{
1025	BPartition* parent = Parent();
1026	if (parent == NULL)
1027		return false;
1028
1029	return parent->_SupportsChildOperation(this,
1030		B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS);
1031}
1032
1033
1034status_t
1035BPartition::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
1036	BPartitionParameterEditor** editor)
1037{
1038	if (fDelegate == NULL)
1039		return B_NO_INIT;
1040
1041	return fDelegate->GetParameterEditor(type, editor);
1042}
1043
1044
1045status_t
1046BPartition::SetParameters(const char* parameters)
1047{
1048	BPartition* parent = Parent();
1049	if (parent == NULL || fDelegate == NULL)
1050		return B_NO_INIT;
1051
1052	return parent->fDelegate->SetParameters(fDelegate, parameters);
1053}
1054
1055
1056bool
1057BPartition::CanEditContentParameters(bool* whileMounted) const
1058{
1059	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS,
1060		B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED,
1061		whileMounted);
1062}
1063
1064
1065status_t
1066BPartition::SetContentParameters(const char* parameters)
1067{
1068	if (fDelegate == NULL)
1069		return B_NO_INIT;
1070
1071	return fDelegate->SetContentParameters(parameters);
1072}
1073
1074
1075status_t
1076BPartition::GetNextSupportedType(int32* cookie, BString* type) const
1077{
1078	TRACE("%p->BPartition::GetNextSupportedType(%ld)\n", this, *cookie);
1079
1080	BPartition* parent = Parent();
1081	if (parent == NULL || fDelegate == NULL) {
1082		TRACE("  not prepared (parent: %p, fDelegate: %p)!\n", parent,
1083			fDelegate);
1084		return B_NO_INIT;
1085	}
1086
1087	return parent->fDelegate->GetNextSupportedChildType(fDelegate, cookie,
1088		type);
1089}
1090
1091
1092status_t
1093BPartition::GetNextSupportedChildType(int32* cookie, BString* type) const
1094{
1095	TRACE("%p->BPartition::GetNextSupportedChildType(%ld)\n", this, *cookie);
1096
1097	if (fDelegate == NULL) {
1098		TRACE("  not prepared!\n");
1099		return B_NO_INIT;
1100	}
1101
1102	return fDelegate->GetNextSupportedChildType(NULL, cookie, type);
1103}
1104
1105
1106bool
1107BPartition::BPartition::IsSubSystem(const char* diskSystem) const
1108{
1109	BPartition* parent = Parent();
1110	if (parent == NULL || fDelegate == NULL)
1111		return false;
1112
1113	return parent->fDelegate->IsSubSystem(fDelegate, diskSystem);
1114}
1115
1116
1117bool
1118BPartition::CanInitialize(const char* diskSystem) const
1119{
1120	if (Size() == 0 || BlockSize() == 0 || fDelegate == NULL)
1121		return false;
1122
1123	return fDelegate->CanInitialize(diskSystem);
1124}
1125
1126
1127status_t
1128BPartition::ValidateInitialize(const char* diskSystem, BString* name,
1129	const char* parameters)
1130{
1131	if (fDelegate == NULL)
1132		return B_NO_INIT;
1133
1134	return fDelegate->ValidateInitialize(diskSystem, name, parameters);
1135}
1136
1137
1138status_t
1139BPartition::Initialize(const char* diskSystem, const char* name,
1140	const char* parameters)
1141{
1142	if (fDelegate == NULL)
1143		return B_NO_INIT;
1144
1145	return fDelegate->Initialize(diskSystem, name, parameters);
1146}
1147
1148
1149status_t
1150BPartition::Uninitialize()
1151{
1152	// TODO: Implement!
1153	return B_NOT_SUPPORTED;
1154}
1155
1156
1157bool
1158BPartition::CanCreateChild() const
1159{
1160	return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD);
1161}
1162
1163
1164status_t
1165BPartition::ValidateCreateChild(off_t* offset, off_t* size, const char* type,
1166	BString* name, const char* parameters) const
1167{
1168	if (fDelegate == NULL)
1169		return B_NO_INIT;
1170
1171	return fDelegate->ValidateCreateChild(offset, size, type, name, parameters);
1172}
1173
1174
1175status_t
1176BPartition::CreateChild(off_t offset, off_t size, const char* type,
1177	const char* name, const char* parameters, BPartition** child)
1178{
1179	if (fDelegate == NULL)
1180		return B_NO_INIT;
1181
1182	return fDelegate->CreateChild(offset, size, type, name, parameters, child);
1183}
1184
1185
1186bool
1187BPartition::CanDeleteChild(int32 index) const
1188{
1189	BPartition* child = ChildAt(index);
1190	if (fDelegate == NULL || child == NULL)
1191		return false;
1192
1193	return _SupportsChildOperation(child,
1194		B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD);
1195}
1196
1197
1198status_t
1199BPartition::DeleteChild(int32 index)
1200{
1201	if (fDelegate == NULL)
1202		return B_NO_INIT;
1203
1204	BPartition* child = ChildAt(index);
1205	if (child == NULL || child->Parent() != this)
1206		return B_BAD_VALUE;
1207
1208	return fDelegate->DeleteChild(child->fDelegate);
1209}
1210
1211
1212/*!	\brief Privatized copy constructor to avoid usage.
1213*/
1214BPartition::BPartition(const BPartition &)
1215{
1216}
1217
1218
1219/*!	\brief Privatized assignment operator to avoid usage.
1220*/
1221BPartition &
1222BPartition::operator=(const BPartition &)
1223{
1224	return *this;
1225}
1226
1227
1228status_t
1229BPartition::_SetTo(BDiskDevice* device, BPartition* parent,
1230	user_partition_data* data)
1231{
1232	_Unset();
1233	if (device == NULL || data == NULL)
1234		return B_BAD_VALUE;
1235
1236	fPartitionData = data;
1237	fDevice = device;
1238	fParent = parent;
1239	fPartitionData->user_data = this;
1240
1241	// create and init children
1242	status_t error = B_OK;
1243	for (int32 i = 0; error == B_OK && i < fPartitionData->child_count; i++) {
1244		BPartition* child = new(nothrow) BPartition;
1245		if (child) {
1246			error = child->_SetTo(fDevice, this, fPartitionData->children[i]);
1247			if (error != B_OK)
1248				delete child;
1249		} else
1250			error = B_NO_MEMORY;
1251	}
1252
1253	// cleanup on error
1254	if (error != B_OK)
1255		_Unset();
1256	return error;
1257}
1258
1259
1260void
1261BPartition::_Unset()
1262{
1263	// delete children
1264	if (fPartitionData != NULL) {
1265		for (int32 i = 0; i < fPartitionData->child_count; i++) {
1266			if (BPartition* child = ChildAt(i))
1267				delete child;
1268		}
1269		fPartitionData->user_data = NULL;
1270	}
1271
1272	fDevice = NULL;
1273	fParent = NULL;
1274	fPartitionData = NULL;
1275	fDelegate = NULL;
1276}
1277
1278
1279status_t
1280BPartition::_RemoveObsoleteDescendants(user_partition_data* data, bool* updated)
1281{
1282	// remove all children not longer persistent
1283	// Not exactly efficient: O(n^2), considering BList::RemoveItem()
1284	// O(1). We could do better (O(n*log(n))), when sorting the arrays before,
1285	// but then the list access is more random and we had to find the
1286	// BPartition to remove, which makes the list operation definitely O(n).
1287	int32 count = CountChildren();
1288	for (int32 i = count - 1; i >= 0; i--) {
1289		BPartition* child = ChildAt(i);
1290		bool found = false;
1291		for (int32 k = data->child_count - 1; k >= 0; k--) {
1292			if (data->children[k]->id == child->ID()) {
1293				// found partition: ask it to remove its obsolete descendants
1294				found = true;
1295				status_t error = child->_RemoveObsoleteDescendants(
1296					data->children[k], updated);
1297				if (error != B_OK)
1298					return error;
1299
1300				// set the user data to the BPartition object to find it
1301				// quicker later
1302				data->children[k]->user_data = child;
1303				break;
1304			}
1305		}
1306
1307		// if partition is obsolete, remove it
1308		if (!found) {
1309			*updated = true;
1310			_RemoveChild(i);
1311		}
1312	}
1313	return B_OK;
1314}
1315
1316
1317status_t
1318BPartition::_Update(user_partition_data* data, bool* updated)
1319{
1320	user_partition_data* oldData = fPartitionData;
1321	fPartitionData = data;
1322	// check for changes
1323	if (data->offset != oldData->offset
1324		|| data->size != oldData->size
1325		|| data->block_size != oldData->block_size
1326		|| data->status != oldData->status
1327		|| data->flags != oldData->flags
1328		|| data->volume != oldData->volume
1329		|| data->disk_system != oldData->disk_system	// not needed
1330		|| compare_string(data->name, oldData->name)
1331		|| compare_string(data->content_name, oldData->content_name)
1332		|| compare_string(data->type, oldData->type)
1333		|| compare_string(data->content_type, oldData->content_type)
1334		|| compare_string(data->parameters, oldData->parameters)
1335		|| compare_string(data->content_parameters,
1336				oldData->content_parameters)) {
1337		*updated = true;
1338	}
1339
1340	// add new children and update existing ones
1341	status_t error = B_OK;
1342	for (int32 i = 0; i < data->child_count; i++) {
1343		user_partition_data* childData = data->children[i];
1344		BPartition* child = (BPartition*)childData->user_data;
1345		if (child) {
1346			// old partition
1347			error = child->_Update(childData, updated);
1348			if (error != B_OK)
1349				return error;
1350		} else {
1351			// new partition
1352			*updated = true;
1353			child = new(nothrow) BPartition;
1354			if (!child)
1355				return B_NO_MEMORY;
1356
1357			error = child->_SetTo(fDevice, this, childData);
1358			if (error != B_OK) {
1359				delete child;
1360				return error;
1361			}
1362
1363			childData->user_data = child;
1364		}
1365	}
1366	return error;
1367}
1368
1369
1370void
1371BPartition::_RemoveChild(int32 index)
1372{
1373	int32 count = CountChildren();
1374	if (!fPartitionData || index < 0 || index >= count)
1375		return;
1376
1377	// delete the BPartition and its children
1378	delete ChildAt(index);
1379
1380	// compact the children array
1381	for (int32 i = index + 1; i < count; i++)
1382		fPartitionData->children[i - 1] = fPartitionData->children[i];
1383	fPartitionData->child_count--;
1384}
1385
1386
1387BPartition*
1388BPartition::_ChildAt(int32 index) const
1389{
1390	if (index < 0 || index >= fPartitionData->child_count)
1391		return NULL;
1392	return (BPartition*)fPartitionData->children[index]->user_data;
1393}
1394
1395
1396int32
1397BPartition::_CountChildren() const
1398{
1399	return fPartitionData->child_count;
1400}
1401
1402
1403int32
1404BPartition::_CountDescendants() const
1405{
1406	int32 count = 1;
1407	for (int32 i = 0; BPartition* child = _ChildAt(i); i++)
1408		count += child->_CountDescendants();
1409	return count;
1410}
1411
1412
1413int32
1414BPartition::_Level() const
1415{
1416	int32 level = 0;
1417	const BPartition* ancestor = this;
1418	while ((ancestor = ancestor->Parent()))
1419		level++;
1420	return level;
1421}
1422
1423
1424bool
1425BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level)
1426{
1427	return visitor->Visit(this, level);
1428}
1429
1430
1431BPartition*
1432BPartition::_VisitEachDescendant(BDiskDeviceVisitor* visitor, int32 level)
1433{
1434	if (level < 0)
1435		level = _Level();
1436	if (_AcceptVisitor(visitor, level))
1437		return this;
1438	for (int32 i = 0; BPartition* child = ChildAt(i); i++) {
1439		if (BPartition* result = child->_VisitEachDescendant(visitor,
1440				level + 1)) {
1441			return result;
1442		}
1443	}
1444	return NULL;
1445}
1446
1447
1448const user_partition_data*
1449BPartition::_PartitionData() const
1450{
1451	return fDelegate ? fDelegate->PartitionData() : fPartitionData;
1452}
1453
1454
1455bool
1456BPartition::_HasContent() const
1457{
1458	return ContentType() != NULL;
1459}
1460
1461
1462bool
1463BPartition::_SupportsOperation(uint32 flag, uint32 whileMountedFlag,
1464	bool* whileMounted) const
1465{
1466	if (fDelegate == NULL)
1467		return false;
1468
1469	uint32 supported = fDelegate->SupportedOperations(flag | whileMountedFlag);
1470
1471	if (whileMounted)
1472		*whileMounted = supported & whileMountedFlag;
1473
1474	return (supported & flag) != 0;
1475}
1476
1477
1478bool
1479BPartition::_SupportsChildOperation(const BPartition* child, uint32 flag) const
1480{
1481	if (fDelegate == NULL || (child != NULL && child->fDelegate == NULL))
1482		return false;
1483
1484	uint32 supported = fDelegate->SupportedChildOperations(
1485		child != NULL ? child->fDelegate : NULL, flag);
1486
1487	return (supported & flag) != 0;
1488}
1489
1490
1491status_t
1492BPartition::_CreateDelegates()
1493{
1494	if (fDelegate != NULL || fPartitionData == NULL)
1495		return B_NO_INIT;
1496
1497	// create and init delegate
1498	fDelegate = new(nothrow) Delegate(this);
1499	if (fDelegate == NULL)
1500		return B_NO_MEMORY;
1501
1502	status_t error = fDelegate->InitHierarchy(fPartitionData,
1503		fParent != NULL ? fParent->fDelegate : NULL);
1504	if (error != B_OK)
1505		return error;
1506
1507	// create child delegates
1508	int32 count = _CountChildren();
1509	for (int32 i = 0; i < count; i++) {
1510		BPartition* child = _ChildAt(i);
1511		error = child->_CreateDelegates();
1512		if (error != B_OK)
1513			return error;
1514	}
1515
1516	return B_OK;
1517}
1518
1519
1520status_t
1521BPartition::_InitDelegates()
1522{
1523	// init delegate
1524	status_t error = fDelegate->InitAfterHierarchy();
1525	if (error != B_OK)
1526		return error;
1527
1528	// recursively init child delegates
1529	int32 count = CountChildren();
1530	for (int32 i = 0; i < count; i++) {
1531		error = ChildAt(i)->_InitDelegates();
1532		if (error != B_OK)
1533			return error;
1534	}
1535
1536	return B_OK;
1537}
1538
1539
1540void
1541BPartition::_DeleteDelegates()
1542{
1543	// recursively delete child delegates
1544	int32 count = CountChildren();
1545	for (int32 i = count - 1; i >= 0; i--)
1546		ChildAt(i)->_DeleteDelegates();
1547
1548	// delete delegate
1549	delete fDelegate;
1550	fDelegate = NULL;
1551
1552	// Commit suicide, if the delegate was our only link to reality (i.e.
1553	// there's no physically existing partition we represent).
1554	if (fPartitionData == NULL)
1555		delete this;
1556}
1557
1558
1559bool
1560BPartition::_IsModified() const
1561{
1562	if (fDelegate == NULL)
1563		return false;
1564
1565	return fDelegate->IsModified();
1566}
1567