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