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*/
523status_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		return 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	return fDelegate->Uninitialize();
1153}
1154
1155
1156bool
1157BPartition::CanCreateChild() const
1158{
1159	return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD);
1160}
1161
1162
1163status_t
1164BPartition::ValidateCreateChild(off_t* offset, off_t* size, const char* type,
1165	BString* name, const char* parameters) const
1166{
1167	if (fDelegate == NULL)
1168		return B_NO_INIT;
1169
1170	return fDelegate->ValidateCreateChild(offset, size, type, name, parameters);
1171}
1172
1173
1174status_t
1175BPartition::CreateChild(off_t offset, off_t size, const char* type,
1176	const char* name, const char* parameters, BPartition** child)
1177{
1178	if (fDelegate == NULL)
1179		return B_NO_INIT;
1180
1181	return fDelegate->CreateChild(offset, size, type, name, parameters, child);
1182}
1183
1184
1185bool
1186BPartition::CanDeleteChild(int32 index) const
1187{
1188	BPartition* child = ChildAt(index);
1189	if (fDelegate == NULL || child == NULL)
1190		return false;
1191
1192	return _SupportsChildOperation(child,
1193		B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD);
1194}
1195
1196
1197status_t
1198BPartition::DeleteChild(int32 index)
1199{
1200	if (fDelegate == NULL)
1201		return B_NO_INIT;
1202
1203	BPartition* child = ChildAt(index);
1204	if (child == NULL || child->Parent() != this)
1205		return B_BAD_VALUE;
1206
1207	return fDelegate->DeleteChild(child->fDelegate);
1208}
1209
1210
1211/*!	\brief Privatized copy constructor to avoid usage.
1212*/
1213BPartition::BPartition(const BPartition &)
1214{
1215}
1216
1217
1218/*!	\brief Privatized assignment operator to avoid usage.
1219*/
1220BPartition &
1221BPartition::operator=(const BPartition &)
1222{
1223	return *this;
1224}
1225
1226
1227status_t
1228BPartition::_SetTo(BDiskDevice* device, BPartition* parent,
1229	user_partition_data* data)
1230{
1231	_Unset();
1232	if (device == NULL || data == NULL)
1233		return B_BAD_VALUE;
1234
1235	fPartitionData = data;
1236	fDevice = device;
1237	fParent = parent;
1238	fPartitionData->user_data = this;
1239
1240	// create and init children
1241	status_t error = B_OK;
1242	for (int32 i = 0; error == B_OK && i < fPartitionData->child_count; i++) {
1243		BPartition* child = new(nothrow) BPartition;
1244		if (child) {
1245			error = child->_SetTo(fDevice, this, fPartitionData->children[i]);
1246			if (error != B_OK)
1247				delete child;
1248		} else
1249			error = B_NO_MEMORY;
1250	}
1251
1252	// cleanup on error
1253	if (error != B_OK)
1254		_Unset();
1255	return error;
1256}
1257
1258
1259void
1260BPartition::_Unset()
1261{
1262	// delete children
1263	if (fPartitionData != NULL) {
1264		for (int32 i = 0; i < fPartitionData->child_count; i++) {
1265			if (BPartition* child = ChildAt(i))
1266				delete child;
1267		}
1268		fPartitionData->user_data = NULL;
1269	}
1270
1271	fDevice = NULL;
1272	fParent = NULL;
1273	fPartitionData = NULL;
1274	fDelegate = NULL;
1275}
1276
1277
1278status_t
1279BPartition::_RemoveObsoleteDescendants(user_partition_data* data, bool* updated)
1280{
1281	// remove all children not longer persistent
1282	// Not exactly efficient: O(n^2), considering BList::RemoveItem()
1283	// O(1). We could do better (O(n*log(n))), when sorting the arrays before,
1284	// but then the list access is more random and we had to find the
1285	// BPartition to remove, which makes the list operation definitely O(n).
1286	int32 count = CountChildren();
1287	for (int32 i = count - 1; i >= 0; i--) {
1288		BPartition* child = ChildAt(i);
1289		bool found = false;
1290		for (int32 k = data->child_count - 1; k >= 0; k--) {
1291			if (data->children[k]->id == child->ID()) {
1292				// found partition: ask it to remove its obsolete descendants
1293				found = true;
1294				status_t error = child->_RemoveObsoleteDescendants(
1295					data->children[k], updated);
1296				if (error != B_OK)
1297					return error;
1298
1299				// set the user data to the BPartition object to find it
1300				// quicker later
1301				data->children[k]->user_data = child;
1302				break;
1303			}
1304		}
1305
1306		// if partition is obsolete, remove it
1307		if (!found) {
1308			*updated = true;
1309			_RemoveChild(i);
1310		}
1311	}
1312	return B_OK;
1313}
1314
1315
1316status_t
1317BPartition::_Update(user_partition_data* data, bool* updated)
1318{
1319	user_partition_data* oldData = fPartitionData;
1320	fPartitionData = data;
1321	// check for changes
1322	if (data->offset != oldData->offset
1323		|| data->size != oldData->size
1324		|| data->block_size != oldData->block_size
1325		|| data->status != oldData->status
1326		|| data->flags != oldData->flags
1327		|| data->volume != oldData->volume
1328		|| data->disk_system != oldData->disk_system	// not needed
1329		|| compare_string(data->name, oldData->name)
1330		|| compare_string(data->content_name, oldData->content_name)
1331		|| compare_string(data->type, oldData->type)
1332		|| compare_string(data->content_type, oldData->content_type)
1333		|| compare_string(data->parameters, oldData->parameters)
1334		|| compare_string(data->content_parameters,
1335				oldData->content_parameters)) {
1336		*updated = true;
1337	}
1338
1339	// add new children and update existing ones
1340	status_t error = B_OK;
1341	for (int32 i = 0; i < data->child_count; i++) {
1342		user_partition_data* childData = data->children[i];
1343		BPartition* child = (BPartition*)childData->user_data;
1344		if (child) {
1345			// old partition
1346			error = child->_Update(childData, updated);
1347			if (error != B_OK)
1348				return error;
1349		} else {
1350			// new partition
1351			*updated = true;
1352			child = new(nothrow) BPartition;
1353			if (!child)
1354				return B_NO_MEMORY;
1355
1356			error = child->_SetTo(fDevice, this, childData);
1357			if (error != B_OK) {
1358				delete child;
1359				return error;
1360			}
1361
1362			childData->user_data = child;
1363		}
1364	}
1365	return error;
1366}
1367
1368
1369void
1370BPartition::_RemoveChild(int32 index)
1371{
1372	int32 count = CountChildren();
1373	if (!fPartitionData || index < 0 || index >= count)
1374		return;
1375
1376	// delete the BPartition and its children
1377	delete ChildAt(index);
1378
1379	// compact the children array
1380	for (int32 i = index + 1; i < count; i++)
1381		fPartitionData->children[i - 1] = fPartitionData->children[i];
1382	fPartitionData->child_count--;
1383}
1384
1385
1386BPartition*
1387BPartition::_ChildAt(int32 index) const
1388{
1389	if (index < 0 || index >= fPartitionData->child_count)
1390		return NULL;
1391	return (BPartition*)fPartitionData->children[index]->user_data;
1392}
1393
1394
1395int32
1396BPartition::_CountChildren() const
1397{
1398	return fPartitionData->child_count;
1399}
1400
1401
1402int32
1403BPartition::_CountDescendants() const
1404{
1405	int32 count = 1;
1406	for (int32 i = 0; BPartition* child = _ChildAt(i); i++)
1407		count += child->_CountDescendants();
1408	return count;
1409}
1410
1411
1412int32
1413BPartition::_Level() const
1414{
1415	int32 level = 0;
1416	const BPartition* ancestor = this;
1417	while ((ancestor = ancestor->Parent()))
1418		level++;
1419	return level;
1420}
1421
1422
1423bool
1424BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level)
1425{
1426	return visitor->Visit(this, level);
1427}
1428
1429
1430BPartition*
1431BPartition::_VisitEachDescendant(BDiskDeviceVisitor* visitor, int32 level)
1432{
1433	if (level < 0)
1434		level = _Level();
1435	if (_AcceptVisitor(visitor, level))
1436		return this;
1437	for (int32 i = 0; BPartition* child = ChildAt(i); i++) {
1438		if (BPartition* result = child->_VisitEachDescendant(visitor,
1439				level + 1)) {
1440			return result;
1441		}
1442	}
1443	return NULL;
1444}
1445
1446
1447const user_partition_data*
1448BPartition::_PartitionData() const
1449{
1450	return fDelegate ? fDelegate->PartitionData() : fPartitionData;
1451}
1452
1453
1454bool
1455BPartition::_HasContent() const
1456{
1457	return ContentType() != NULL;
1458}
1459
1460
1461bool
1462BPartition::_SupportsOperation(uint32 flag, uint32 whileMountedFlag,
1463	bool* whileMounted) const
1464{
1465	if (fDelegate == NULL)
1466		return false;
1467
1468	uint32 supported = fDelegate->SupportedOperations(flag | whileMountedFlag);
1469
1470	if (whileMounted)
1471		*whileMounted = supported & whileMountedFlag;
1472
1473	return (supported & flag) != 0;
1474}
1475
1476
1477bool
1478BPartition::_SupportsChildOperation(const BPartition* child, uint32 flag) const
1479{
1480	if (fDelegate == NULL || (child != NULL && child->fDelegate == NULL))
1481		return false;
1482
1483	uint32 supported = fDelegate->SupportedChildOperations(
1484		child != NULL ? child->fDelegate : NULL, flag);
1485
1486	return (supported & flag) != 0;
1487}
1488
1489
1490status_t
1491BPartition::_CreateDelegates()
1492{
1493	if (fDelegate != NULL || fPartitionData == NULL)
1494		return B_NO_INIT;
1495
1496	// create and init delegate
1497	fDelegate = new(nothrow) Delegate(this);
1498	if (fDelegate == NULL)
1499		return B_NO_MEMORY;
1500
1501	status_t error = fDelegate->InitHierarchy(fPartitionData,
1502		fParent != NULL ? fParent->fDelegate : NULL);
1503	if (error != B_OK)
1504		return error;
1505
1506	// create child delegates
1507	int32 count = _CountChildren();
1508	for (int32 i = 0; i < count; i++) {
1509		BPartition* child = _ChildAt(i);
1510		error = child->_CreateDelegates();
1511		if (error != B_OK)
1512			return error;
1513	}
1514
1515	return B_OK;
1516}
1517
1518
1519status_t
1520BPartition::_InitDelegates()
1521{
1522	// init delegate
1523	status_t error = fDelegate->InitAfterHierarchy();
1524	if (error != B_OK)
1525		return error;
1526
1527	// recursively init child delegates
1528	int32 count = CountChildren();
1529	for (int32 i = 0; i < count; i++) {
1530		error = ChildAt(i)->_InitDelegates();
1531		if (error != B_OK)
1532			return error;
1533	}
1534
1535	return B_OK;
1536}
1537
1538
1539void
1540BPartition::_DeleteDelegates()
1541{
1542	// recursively delete child delegates
1543	int32 count = CountChildren();
1544	for (int32 i = count - 1; i >= 0; i--)
1545		ChildAt(i)->_DeleteDelegates();
1546
1547	// delete delegate
1548	delete fDelegate;
1549	fDelegate = NULL;
1550
1551	// Commit suicide, if the delegate was our only link to reality (i.e.
1552	// there's no physically existing partition we represent).
1553	if (fPartitionData == NULL)
1554		delete this;
1555}
1556
1557
1558bool
1559BPartition::_IsModified() const
1560{
1561	if (fDelegate == NULL)
1562		return false;
1563
1564	return fDelegate->IsModified();
1565}
1566