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