xref: /haiku/src/kits/storage/disk_device/Partition.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
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 <disk_device_manager/ddm_userland_interface.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 /*
388 	status_t error = (icon ? B_OK : B_BAD_VALUE);
389 	if (error == B_OK) {
390 		if (IsMounted()) {
391 			// mounted: get the icon from the volume
392 			BVolume volume;
393 			error = GetVolume(&volume);
394 			if (error == B_OK)
395 				error = volume.GetIcon(icon, which);
396 		} else {
397 			// not mounted: retrieve the icon ourselves
398 			if (BDiskDevice* device = Device()) {
399 				// get the icon
400 				if (error == B_OK)
401 					error = get_device_icon(device->Path(), icon, which);
402 			} else
403 				error = B_ERROR;
404 		}
405 	}
406 	return error;
407 */
408 	// not implemented
409 	return B_ERROR;
410 }
411 
412 
413 // GetMountPoint
414 /*!	\brief Returns the mount point for the partition.
415 
416 	If the partition is mounted this is the actual mount point. If it is not
417 	mounted, but contains a file system, derived from the partition name
418 	the name for a not yet existing directory in the root directory is
419 	constructed and the path to it returned.
420 
421 	For partitions not containing a file system the method returns an error.
422 
423 	\param mountPoint Pointer to the path to be set to refer the mount point
424 		   (respectively potential mount point) of the partition.
425 	\return \c B_OK, if everything went fine, an error code otherwise.
426 */
427 status_t
428 BPartition::GetMountPoint(BPath* mountPoint) const
429 {
430 	if (!mountPoint || !ContainsFileSystem())
431 		return B_BAD_VALUE;
432 
433 	// if the partition is mounted, return the actual mount point
434 	BVolume volume;
435 	if (GetVolume(&volume) == B_OK) {
436 		BDirectory dir;
437 		status_t error = volume.GetRootDirectory(&dir);
438 		if (error == B_OK)
439 			error = mountPoint->SetTo(&dir, NULL);
440 		return error;
441 	}
442 
443 	// partition not mounted
444 	// get the volume name
445 	const char* volumeName = ContentName();
446 	if (!volumeName || strlen(volumeName) == 0)
447 		volumeName = Name();
448 	if (!volumeName || strlen(volumeName) == 0)
449 		volumeName = "unnamed volume";
450 
451 	// construct a path name from the volume name
452 	// replace '/'s and prepend a '/'
453 	BString mountPointPath(volumeName);
454 	mountPointPath.ReplaceAll('/', '-');
455 	mountPointPath.Insert("/", 0);
456 
457 	// make the name unique
458 	BString basePath(mountPointPath);
459 	int counter = 1;
460 	while (true) {
461 		BEntry entry;
462 		status_t error = entry.SetTo(mountPointPath.String());
463 		if (error != B_OK)
464 			return error;
465 
466 		if (!entry.Exists())
467 			break;
468 		mountPointPath = basePath;
469 		mountPointPath << counter;
470 		counter++;
471 	}
472 
473 	return mountPoint->SetTo(mountPointPath.String());
474 }
475 
476 
477 // Mount
478 /*!	\brief Mounts the volume.
479 
480 	The volume can only be mounted, if the partition contains a recognized
481 	file system (\see ContainsFileSystem()) and it is not already mounted.
482 
483 	If no mount point is given, one will be created automatically under the
484 	root directory (derived from the volume name). If one is given, the
485 	directory must already exist.
486 
487 	\param mountPoint The directory where to mount the file system. May be
488 		   \c NULL, in which case a mount point in the root directory will be
489 		   created automatically.
490 	\param mountFlags Currently only \c B_MOUNT_READ_ONLY is defined, which
491 		   forces the volume to be mounted read-only.
492 	\param parameters File system specific mount parameters.
493 	\return \c B_OK, if everything went fine, another error code otherwise.
494 */
495 dev_t
496 BPartition::Mount(const char* mountPoint, uint32 mountFlags,
497 	const char* parameters)
498 {
499 	if (IsMounted() || !ContainsFileSystem())
500 		return B_BAD_VALUE;
501 
502 	// get the partition path
503 	BPath partitionPath;
504 	status_t error = GetPath(&partitionPath);
505 	if (error != B_OK)
506 		return error;
507 
508 	// create a mount point, if none is given
509 	bool deleteMountPoint = false;
510 	BPath mountPointPath;
511 	if (!mountPoint) {
512 		// get a unique mount point
513 		error = GetMountPoint(&mountPointPath);
514 		if (error != B_OK)
515 			return error;
516 
517 		mountPoint = mountPointPath.Path();
518 
519 		// create the directory
520 		if (mkdir(mountPoint, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
521 			return errno;
522 
523 		deleteMountPoint = true;
524 	}
525 
526 	// mount the partition
527 	dev_t device = fs_mount_volume(mountPoint, partitionPath.Path(), NULL,
528 		mountFlags, parameters);
529 
530 	// delete the mount point on error, if we created it
531 	if (device < B_OK && deleteMountPoint)
532 		rmdir(mountPoint);
533 
534 	// update object, if successful
535 	if (device >= 0)
536 		error = Device()->Update();
537 
538 	return device;
539 }
540 
541 
542 // Unmount
543 /*!	\brief Unmounts the volume.
544 
545 	The volume can of course only be unmounted, if it currently is mounted.
546 
547 	\param unmountFlags Currently only \c B_FORCE_UNMOUNT is defined, which
548 		   forces the partition to be unmounted, even if there are still
549 		   open FDs. Be careful using this flag -- you risk the user's data.
550 
551 	\return \c B_OK, if everything went fine, another error code otherwise.
552 */
553 status_t
554 BPartition::Unmount(uint32 unmountFlags)
555 {
556 	if (!IsMounted())
557 		return B_BAD_VALUE;
558 
559 	// get the partition path
560 	BPath path;
561 	status_t status = GetMountPoint(&path);
562 	if (status != B_OK)
563 		return status;
564 
565 	// unmount
566 	status = fs_unmount_volume(path.Path(), unmountFlags);
567 
568 	// update object, if successful
569 	if (status == B_OK)
570 		status = Device()->Update();
571 
572 	return status;
573 }
574 
575 
576 // Device
577 /*!	\brief Returns the device this partition resides on.
578 	\return The device this partition resides on.
579 */
580 BDiskDevice*
581 BPartition::Device() const
582 {
583 	return fDevice;
584 }
585 
586 
587 // Parent
588 BPartition*
589 BPartition::Parent() const
590 {
591 	return fParent;
592 }
593 
594 
595 // ChildAt
596 BPartition*
597 BPartition::ChildAt(int32 index) const
598 {
599 	if (fDelegate) {
600 		Delegate* child = fDelegate->ChildAt(index);
601 		return child ? child->Partition() : NULL;
602 	}
603 
604 	return _ChildAt(index);
605 }
606 
607 
608 // CountChildren
609 int32
610 BPartition::CountChildren() const
611 {
612 	if (fDelegate)
613 		return fDelegate->CountChildren();
614 
615 	return _CountChildren();
616 }
617 
618 
619 // CountDescendants
620 int32
621 BPartition::CountDescendants() const
622 {
623 	int32 count = 1;
624 	for (int32 i = 0; BPartition* child = ChildAt(i); i++)
625 		count += child->CountDescendants();
626 	return count;
627 }
628 
629 
630 // FindDescendant
631 BPartition*
632 BPartition::FindDescendant(partition_id id) const
633 {
634 	IDFinderVisitor visitor(id);
635 	return VisitEachDescendant(&visitor);
636 }
637 
638 
639 // GetPartitioningInfo
640 status_t
641 BPartition::GetPartitioningInfo(BPartitioningInfo* info) const
642 {
643 	if (!info)
644 		return B_BAD_VALUE;
645 	if (!fDelegate)
646 		return B_NO_INIT;
647 
648 	return fDelegate->GetPartitioningInfo(info);
649 }
650 
651 
652 // VisitEachChild
653 BPartition*
654 BPartition::VisitEachChild(BDiskDeviceVisitor* visitor) const
655 {
656 	if (visitor) {
657 		int32 level = _Level();
658 		for (int32 i = 0; BPartition* child = ChildAt(i); i++) {
659 			if (child->_AcceptVisitor(visitor, level))
660 				return child;
661 		}
662 	}
663 	return NULL;
664 }
665 
666 
667 // VisitEachDescendant
668 BPartition*
669 BPartition::VisitEachDescendant(BDiskDeviceVisitor* visitor) const
670 {
671 	if (visitor)
672 		return const_cast<BPartition*>(this)->_VisitEachDescendant(visitor);
673 	return NULL;
674 }
675 
676 
677 // CanDefragment
678 bool
679 BPartition::CanDefragment(bool* whileMounted) const
680 {
681 	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING,
682 		B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED, whileMounted);
683 }
684 
685 
686 // Defragment
687 status_t
688 BPartition::Defragment() const
689 {
690 	if (!fDelegate)
691 		return B_NO_INIT;
692 
693 	return fDelegate->Defragment();
694 }
695 
696 
697 // CanRepair
698 bool
699 BPartition::CanRepair(bool checkOnly, bool* whileMounted) const
700 {
701 	uint32 flag;
702 	uint32 whileMountedFlag;
703 	if (checkOnly) {
704 		flag = B_DISK_SYSTEM_SUPPORTS_CHECKING;
705 		whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED;
706 	} else {
707 		flag = B_DISK_SYSTEM_SUPPORTS_REPAIRING;
708 		whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED;
709 	}
710 
711 	return _SupportsOperation(flag, whileMountedFlag, whileMounted);
712 }
713 
714 
715 // Repair
716 status_t
717 BPartition::Repair(bool checkOnly) const
718 {
719 	if (!fDelegate)
720 		return B_NO_INIT;
721 
722 	return fDelegate->Repair(checkOnly);
723 }
724 
725 
726 // CanResize
727 bool
728 BPartition::CanResize(bool* canResizeContents, bool* whileMounted) const
729 {
730 	BPartition* parent = Parent();
731 	if (!parent)
732 		return false;
733 
734 	if (!parent->_SupportsChildOperation(this,
735 			B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD)) {
736 		return false;
737 	}
738 
739 	if (!_HasContent())
740 		return true;
741 
742 	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_RESIZING,
743 		B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED, whileMounted);
744 }
745 
746 
747 // ValidateResize
748 status_t
749 BPartition::ValidateResize(off_t* size) const
750 {
751 	BPartition* parent = Parent();
752 	if (!parent || !fDelegate)
753 		return B_NO_INIT;
754 
755 	status_t error = parent->fDelegate->ValidateResizeChild(fDelegate, size);
756 	if (error != B_OK)
757 		return error;
758 
759 	if (_HasContent()) {
760 // TODO: We would actually need the parameter for the content size.
761 		off_t contentSize = *size;
762 		error = fDelegate->ValidateResize(&contentSize);
763 		if (error != B_OK)
764 			return error;
765 
766 		if (contentSize > *size)
767 			return B_BAD_VALUE;
768 	}
769 
770 	return B_OK;
771 }
772 
773 
774 // Resize
775 status_t
776 BPartition::Resize(off_t size)
777 {
778 	BPartition* parent = Parent();
779 	if (!parent || !fDelegate)
780 		return B_NO_INIT;
781 
782 	status_t error;
783 	off_t contentSize = size;
784 	if (_HasContent()) {
785 		error = fDelegate->ValidateResize(&contentSize);
786 		if (error != B_OK)
787 			return error;
788 
789 		if (contentSize > size)
790 			return B_BAD_VALUE;
791 	}
792 
793 	// If shrinking the partition, resize content first, otherwise last.
794 	bool shrink = (Size() > size);
795 
796 	if (shrink && ContentType() != NULL) {
797 		error = fDelegate->Resize(contentSize);
798 		if (error != B_OK)
799 			return error;
800 	}
801 
802 	error = parent->fDelegate->ResizeChild(fDelegate, size);
803 	if (error != B_OK)
804 		return error;
805 
806 	if (!shrink && ContentType() != NULL) {
807 		error = fDelegate->Resize(contentSize);
808 		if (error != B_OK)
809 			return error;
810 	}
811 
812 	return B_OK;
813 }
814 
815 
816 // CanMove
817 bool
818 BPartition::CanMove(BObjectList<BPartition>* unmovableDescendants,
819 	BObjectList<BPartition>* movableOnlyIfUnmounted) const
820 {
821 	BPartition* parent = Parent();
822 	if (!parent || !fDelegate)
823 		return false;
824 
825 	if (!parent->_SupportsChildOperation(this,
826 			B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD)) {
827 		return false;
828 	}
829 
830 	bool whileMounted;
831 	bool movable = _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_MOVING,
832 		B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED, &whileMounted);
833 	if (!movable)
834 		return false;
835 
836 	if (!whileMounted)
837 		movableOnlyIfUnmounted->AddItem(const_cast<BPartition*>(this));
838 
839 	// collect descendent partitions
840 	// TODO: ...
841 // TODO: Currently there's no interface for asking descendents. They'll still
842 // have the same offset (relative to their parent) after moving. The only thing
843 // we really have to ask is whether they need to be unmounted.
844 
845 	return true;
846 }
847 
848 
849 // ValidateMove
850 status_t
851 BPartition::ValidateMove(off_t* offset) const
852 {
853 	BPartition* parent = Parent();
854 	if (!parent || !fDelegate)
855 		return B_NO_INIT;
856 
857 	status_t error = parent->fDelegate->ValidateMoveChild(fDelegate, offset);
858 	if (error != B_OK)
859 		return error;
860 
861 	if (_HasContent()) {
862 		off_t contentOffset = *offset;
863 		error = fDelegate->ValidateMove(&contentOffset);
864 		if (error != B_OK)
865 			return error;
866 
867 		if (contentOffset != *offset)
868 			return B_BAD_VALUE;
869 	}
870 
871 	return B_OK;
872 }
873 
874 
875 // Move
876 status_t
877 BPartition::Move(off_t offset)
878 {
879 	BPartition* parent = Parent();
880 	if (!parent || !fDelegate)
881 		return B_NO_INIT;
882 
883 	status_t error = parent->fDelegate->MoveChild(fDelegate, offset);
884 	if (error != B_OK)
885 		return error;
886 
887 	if (_HasContent()) {
888 		error = fDelegate->Move(offset);
889 		if (error != B_OK)
890 			return error;
891 	}
892 
893 	return B_OK;
894 }
895 
896 
897 // CanSetName
898 bool
899 BPartition::CanSetName() const
900 {
901 	BPartition* parent = Parent();
902 	if (!parent || !fDelegate)
903 		return false;
904 
905 	return parent->_SupportsChildOperation(this,
906 		B_DISK_SYSTEM_SUPPORTS_SETTING_NAME);
907 }
908 
909 
910 // ValidateSetName
911 status_t
912 BPartition::ValidateSetName(BString* name) const
913 {
914 	BPartition* parent = Parent();
915 	if (!parent || !fDelegate)
916 		return B_NO_INIT;
917 
918 	return parent->fDelegate->ValidateSetName(fDelegate, name);
919 }
920 
921 
922 // SetName
923 status_t
924 BPartition::SetName(const char* name)
925 {
926 	BPartition* parent = Parent();
927 	if (!parent || !fDelegate)
928 		return B_NO_INIT;
929 
930 	return parent->fDelegate->SetName(fDelegate, name);
931 }
932 
933 
934 // CanSetContentName
935 bool
936 BPartition::CanSetContentName(bool* whileMounted) const
937 {
938 	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME,
939 		B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED,
940 		whileMounted);
941 }
942 
943 
944 // ValidateSetContentName
945 status_t
946 BPartition::ValidateSetContentName(BString* name) const
947 {
948 	if (!fDelegate)
949 		return B_NO_INIT;
950 
951 	return fDelegate->ValidateSetContentName(name);
952 }
953 
954 
955 // SetContentName
956 status_t
957 BPartition::SetContentName(const char* name)
958 {
959 	if (!fDelegate)
960 		return B_NO_INIT;
961 
962 	return fDelegate->SetContentName(name);
963 }
964 
965 
966 // CanSetType
967 bool
968 BPartition::CanSetType() const
969 {
970 	BPartition* parent = Parent();
971 	if (!parent)
972 		return false;
973 
974 	return parent->_SupportsChildOperation(this,
975 		B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE);
976 }
977 
978 
979 // ValidateSetType
980 status_t
981 BPartition::ValidateSetType(const char* type) const
982 {
983 	BPartition* parent = Parent();
984 	if (!parent || !fDelegate)
985 		return B_NO_INIT;
986 
987 	return parent->fDelegate->ValidateSetType(fDelegate, type);
988 }
989 
990 
991 // SetType
992 status_t
993 BPartition::SetType(const char* type)
994 {
995 	BPartition* parent = Parent();
996 	if (!parent || !fDelegate)
997 		return B_NO_INIT;
998 
999 	return parent->fDelegate->SetType(fDelegate, type);
1000 }
1001 
1002 
1003 // CanEditParameters
1004 bool
1005 BPartition::CanEditParameters() const
1006 {
1007 	BPartition* parent = Parent();
1008 	if (!parent)
1009 		return false;
1010 
1011 	return parent->_SupportsChildOperation(this,
1012 		B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS);
1013 }
1014 
1015 
1016 // GetParameterEditor
1017 status_t
1018 BPartition::GetParameterEditor(BDiskDeviceParameterEditor** editor)
1019 {
1020 	BPartition* parent = Parent();
1021 	if (!parent || !fDelegate)
1022 		return B_NO_INIT;
1023 
1024 	return parent->fDelegate->GetParameterEditor(fDelegate, editor);
1025 }
1026 
1027 
1028 // SetParameters
1029 status_t
1030 BPartition::SetParameters(const char* parameters)
1031 {
1032 	BPartition* parent = Parent();
1033 	if (!parent || !fDelegate)
1034 		return B_NO_INIT;
1035 
1036 	return parent->fDelegate->SetParameters(fDelegate, parameters);
1037 }
1038 
1039 
1040 // CanEditContentParameters
1041 bool
1042 BPartition::CanEditContentParameters(bool* whileMounted) const
1043 {
1044 	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS,
1045 		B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED,
1046 		whileMounted);
1047 }
1048 
1049 
1050 // GetContentParameterEditor
1051 status_t
1052 BPartition::GetContentParameterEditor(BDiskDeviceParameterEditor** editor)
1053 {
1054 	if (!fDelegate)
1055 		return B_NO_INIT;
1056 
1057 	return fDelegate->GetContentParameterEditor(editor);
1058 }
1059 
1060 
1061 // SetContentParameters
1062 status_t
1063 BPartition::SetContentParameters(const char* parameters)
1064 {
1065 	if (!fDelegate)
1066 		return B_NO_INIT;
1067 
1068 	return fDelegate->SetContentParameters(parameters);
1069 }
1070 
1071 
1072 // GetNextSupportedType
1073 status_t
1074 BPartition::GetNextSupportedType(int32 *cookie, BString* type) const
1075 {
1076 	BPartition* parent = Parent();
1077 	if (!parent || !fDelegate)
1078 		return B_NO_INIT;
1079 
1080 	return parent->fDelegate->GetNextSupportedChildType(fDelegate, cookie,
1081 		type);
1082 }
1083 
1084 
1085 // GetNextSupportedChildType
1086 status_t
1087 BPartition::GetNextSupportedChildType(int32 *cookie, BString* type) const
1088 {
1089 	if (!fDelegate)
1090 		return B_NO_INIT;
1091 
1092 	return fDelegate->GetNextSupportedChildType(NULL, cookie, type);
1093 }
1094 
1095 
1096 // IsSubSystem
1097 bool
1098 BPartition::BPartition::IsSubSystem(const char* diskSystem) const
1099 {
1100 	BPartition* parent = Parent();
1101 	if (!parent || !fDelegate)
1102 		return false;
1103 
1104 	return parent->fDelegate->IsSubSystem(fDelegate, diskSystem);
1105 }
1106 
1107 
1108 // CanInitialize
1109 bool
1110 BPartition::CanInitialize(const char* diskSystem) const
1111 {
1112 	return fDelegate && fDelegate->CanInitialize(diskSystem);
1113 }
1114 
1115 
1116 // GetInitializationParameterEditor
1117 status_t
1118 BPartition::GetInitializationParameterEditor(const char* diskSystem,
1119 	BDiskDeviceParameterEditor** editor) const
1120 {
1121 	if (!fDelegate)
1122 		return B_NO_INIT;
1123 
1124 	return fDelegate->GetInitializationParameterEditor(diskSystem, editor);
1125 }
1126 
1127 
1128 // ValidateInitialize
1129 status_t
1130 BPartition::ValidateInitialize(const char* diskSystem, BString* name,
1131 	const char* parameters)
1132 {
1133 	if (!fDelegate)
1134 		return B_NO_INIT;
1135 
1136 	return fDelegate->ValidateInitialize(diskSystem, name, parameters);
1137 }
1138 
1139 
1140 // Initialize
1141 status_t
1142 BPartition::Initialize(const char* diskSystem, const char* name,
1143 	const char* parameters)
1144 {
1145 	if (!fDelegate)
1146 		return B_NO_INIT;
1147 
1148 	return fDelegate->Initialize(diskSystem, name, parameters);
1149 }
1150 
1151 
1152 // Uninitialize
1153 status_t
1154 BPartition::Uninitialize()
1155 {
1156 	// TODO: Implement!
1157 	return B_NOT_SUPPORTED;
1158 }
1159 
1160 
1161 // CanCreateChild
1162 bool
1163 BPartition::CanCreateChild() const
1164 {
1165 	return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD);
1166 }
1167 
1168 
1169 // GetChildCreationParameterEditor
1170 status_t
1171 BPartition::GetChildCreationParameterEditor(const char* type,
1172 	BDiskDeviceParameterEditor** editor) const
1173 {
1174 	if (!fDelegate)
1175 		return B_NO_INIT;
1176 
1177 	return fDelegate->GetChildCreationParameterEditor(type, editor);
1178 }
1179 
1180 
1181 // ValidateCreateChild
1182 status_t
1183 BPartition::ValidateCreateChild(off_t* offset, off_t* size, const char* type,
1184 	BString* name, const char* parameters) const
1185 {
1186 	if (!fDelegate)
1187 		return B_NO_INIT;
1188 
1189 	return fDelegate->ValidateCreateChild(offset, size, type, name, parameters);
1190 }
1191 
1192 
1193 // CreateChild
1194 status_t
1195 BPartition::CreateChild(off_t offset, off_t size, const char* type,
1196 	const char* name, const char* parameters, BPartition** child)
1197 {
1198 	if (!fDelegate)
1199 		return B_NO_INIT;
1200 
1201 	return fDelegate->CreateChild(offset, size, type, name, parameters, child);
1202 }
1203 
1204 
1205 // CanDeleteChild
1206 bool
1207 BPartition::CanDeleteChild(int32 index) const
1208 {
1209 	BPartition* child = ChildAt(index);
1210 	if (!fDelegate || !child)
1211 		return false;
1212 
1213 	return _SupportsChildOperation(child,
1214 		B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD);
1215 }
1216 
1217 
1218 // DeleteChild
1219 status_t
1220 BPartition::DeleteChild(int32 index)
1221 {
1222 	if (!fDelegate)
1223 		return B_NO_INIT;
1224 	BPartition* child = ChildAt(index);
1225 	if (!child || child->Parent() != this)
1226 		return B_BAD_VALUE;
1227 
1228 	return fDelegate->DeleteChild(child->fDelegate);
1229 }
1230 
1231 
1232 // constructor
1233 /*!	\brief Privatized copy constructor to avoid usage.
1234 */
1235 BPartition::BPartition(const BPartition &)
1236 {
1237 }
1238 
1239 
1240 // =
1241 /*!	\brief Privatized assignment operator to avoid usage.
1242 */
1243 BPartition &
1244 BPartition::operator=(const BPartition &)
1245 {
1246 	return *this;
1247 }
1248 
1249 
1250 // _SetTo
1251 status_t
1252 BPartition::_SetTo(BDiskDevice* device, BPartition* parent,
1253 	user_partition_data* data)
1254 {
1255 	_Unset();
1256 	if (!device || !data)
1257 		return B_BAD_VALUE;
1258 	fPartitionData = data;
1259 	fDevice = device;
1260 	fParent = parent;
1261 	fPartitionData->user_data = this;
1262 
1263 	// create and init children
1264 	status_t error = B_OK;
1265 	for (int32 i = 0; error == B_OK && i < fPartitionData->child_count; i++) {
1266 		BPartition* child = new(nothrow) BPartition;
1267 		if (child) {
1268 			error = child->_SetTo(fDevice, this, fPartitionData->children[i]);
1269 			if (error != B_OK)
1270 				delete child;
1271 		} else
1272 			error = B_NO_MEMORY;
1273 	}
1274 
1275 	// cleanup on error
1276 	if (error != B_OK)
1277 		_Unset();
1278 	return error;
1279 }
1280 
1281 
1282 // _Unset
1283 void
1284 BPartition::_Unset()
1285 {
1286 	// delete children
1287 	if (fPartitionData) {
1288 		for (int32 i = 0; i < fPartitionData->child_count; i++) {
1289 			if (BPartition* child = ChildAt(i))
1290 				delete child;
1291 		}
1292 		fPartitionData->user_data = NULL;
1293 	}
1294 
1295 	fDevice = NULL;
1296 	fParent = NULL;
1297 	fPartitionData = NULL;
1298 	fDelegate = NULL;
1299 }
1300 
1301 
1302 // _RemoveObsoleteDescendants
1303 status_t
1304 BPartition::_RemoveObsoleteDescendants(user_partition_data* data, bool* updated)
1305 {
1306 	// remove all children not longer persistent
1307 	// Not exactly efficient: O(n^2), considering BList::RemoveItem()
1308 	// O(1). We could do better (O(n*log(n))), when sorting the arrays before,
1309 	// but then the list access is more random and we had to find the
1310 	// BPartition to remove, which makes the list operation definitely O(n).
1311 	int32 count = CountChildren();
1312 	for (int32 i = count - 1; i >= 0; i--) {
1313 		BPartition* child = ChildAt(i);
1314 		bool found = false;
1315 		for (int32 k = data->child_count - 1; k >= 0; k--) {
1316 			if (data->children[k]->id == child->ID()) {
1317 				// found partition: ask it to remove its obsolete descendants
1318 				found = true;
1319 				status_t error = child->_RemoveObsoleteDescendants(
1320 					data->children[k], updated);
1321 				if (error != B_OK)
1322 					return error;
1323 
1324 				// set the user data to the BPartition object to find it
1325 				// quicker later
1326 				data->children[k]->user_data = child;
1327 				break;
1328 			}
1329 		}
1330 
1331 		// if partition is obsolete, remove it
1332 		if (!found) {
1333 			*updated = true;
1334 			_RemoveChild(i);
1335 		}
1336 	}
1337 	return B_OK;
1338 }
1339 
1340 
1341 // _Update
1342 status_t
1343 BPartition::_Update(user_partition_data* data, bool* updated)
1344 {
1345 	user_partition_data* oldData = fPartitionData;
1346 	fPartitionData = data;
1347 	// check for changes
1348 	if (data->offset != oldData->offset
1349 		|| data->size != oldData->size
1350 		|| data->block_size != oldData->block_size
1351 		|| data->status != oldData->status
1352 		|| data->flags != oldData->flags
1353 		|| data->volume != oldData->volume
1354 		|| data->disk_system != oldData->disk_system	// not needed
1355 		|| compare_string(data->name, oldData->name)
1356 		|| compare_string(data->content_name, oldData->content_name)
1357 		|| compare_string(data->type, oldData->type)
1358 		|| compare_string(data->content_type, oldData->content_type)
1359 		|| compare_string(data->parameters, oldData->parameters)
1360 		|| compare_string(data->content_parameters,
1361 						  oldData->content_parameters)) {
1362 		*updated = true;
1363 	}
1364 
1365 	// add new children and update existing ones
1366 	status_t error = B_OK;
1367 	for (int32 i = 0; i < data->child_count; i++) {
1368 		user_partition_data* childData = data->children[i];
1369 		BPartition* child = (BPartition*)childData->user_data;
1370 		if (child) {
1371 			// old partition
1372 			error = child->_Update(childData, updated);
1373 			if (error != B_OK)
1374 				return error;
1375 		} else {
1376 			// new partition
1377 			*updated = true;
1378 			child = new(nothrow) BPartition;
1379 			if (!child)
1380 				return B_NO_MEMORY;
1381 
1382 			error = child->_SetTo(fDevice, this, data->children[i]);
1383 			if (error != B_OK) {
1384 				delete child;
1385 				return error;
1386 			}
1387 
1388 			childData->user_data = child;
1389 		}
1390 	}
1391 	return error;
1392 }
1393 
1394 
1395 // _RemoveChild
1396 void
1397 BPartition::_RemoveChild(int32 index)
1398 {
1399 	int32 count = CountChildren();
1400 	if (!fPartitionData || index < 0 || index >= count)
1401 		return;
1402 
1403 	// delete the BPartition and its children
1404 	delete ChildAt(index);
1405 
1406 	// compact the children array
1407 	for (int32 i = index + 1; i < count; i++)
1408 		fPartitionData->children[i - 1] = fPartitionData->children[i];
1409 	fPartitionData->child_count--;
1410 }
1411 
1412 
1413 // _ChildAt
1414 BPartition*
1415 BPartition::_ChildAt(int32 index) const
1416 {
1417 	if (index < 0 || index >= fPartitionData->child_count)
1418 		return NULL;
1419 	return (BPartition*)fPartitionData->children[index]->user_data;
1420 }
1421 
1422 
1423 // _CountChildren
1424 int32
1425 BPartition::_CountChildren() const
1426 {
1427 	return fPartitionData->child_count;
1428 }
1429 
1430 
1431 // _CountDescendants
1432 int32
1433 BPartition::_CountDescendants() const
1434 {
1435 	int32 count = 1;
1436 	for (int32 i = 0; BPartition* child = _ChildAt(i); i++)
1437 		count += child->_CountDescendants();
1438 	return count;
1439 }
1440 
1441 
1442 // _Level
1443 int32
1444 BPartition::_Level() const
1445 {
1446 	int32 level = 0;
1447 	const BPartition* ancestor = this;
1448 	while ((ancestor = ancestor->Parent()))
1449 		level++;
1450 	return level;
1451 }
1452 
1453 
1454 // _AcceptVisitor
1455 bool
1456 BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level)
1457 {
1458 	return visitor->Visit(this, level);
1459 }
1460 
1461 
1462 // _VisitEachDescendant
1463 BPartition*
1464 BPartition::_VisitEachDescendant(BDiskDeviceVisitor* visitor, int32 level)
1465 {
1466 	if (level < 0)
1467 		level = _Level();
1468 	if (_AcceptVisitor(visitor, level))
1469 		return this;
1470 	for (int32 i = 0; BPartition* child = ChildAt(i); i++) {
1471 		if (BPartition* result = child->_VisitEachDescendant(visitor,
1472 				level + 1)) {
1473 			return result;
1474 		}
1475 	}
1476 	return NULL;
1477 }
1478 
1479 
1480 // _PartitionData
1481 const user_partition_data*
1482 BPartition::_PartitionData() const
1483 {
1484 	return fDelegate ? fDelegate->PartitionData() : fPartitionData;
1485 }
1486 
1487 
1488 // _HasContent
1489 bool
1490 BPartition::_HasContent() const
1491 {
1492 	return (ContentType() != NULL);
1493 }
1494 
1495 
1496 // _SupportsOperation
1497 bool
1498 BPartition::_SupportsOperation(uint32 flag, uint32 whileMountedFlag,
1499 	bool* whileMounted) const
1500 {
1501 	if (!fDelegate)
1502 		return false;
1503 
1504 	uint32 supported = fDelegate->SupportedOperations(flag | whileMountedFlag);
1505 
1506 	if (whileMounted)
1507 		*whileMounted = supported & whileMountedFlag;
1508 
1509 	return supported & flag;
1510 }
1511 
1512 
1513 // _SupportsChildOperation
1514 bool
1515 BPartition::_SupportsChildOperation(const BPartition* child, uint32 flag) const
1516 {
1517 	if (!fDelegate || child && !child->fDelegate)
1518 		return false;
1519 
1520 	uint32 supported = fDelegate->SupportedChildOperations(
1521 		child ? child->fDelegate : NULL, flag);
1522 
1523 	return supported & flag;
1524 }
1525 
1526 
1527 // _CreateDelegates
1528 status_t
1529 BPartition::_CreateDelegates()
1530 {
1531 	if (fDelegate || !fPartitionData)
1532 		return B_NO_INIT;
1533 
1534 	// create and init delegate
1535 	fDelegate = new(nothrow) Delegate(this);
1536 	if (!fDelegate)
1537 		return B_NO_MEMORY;
1538 
1539 	status_t error = fDelegate->InitHierarchy(fPartitionData,
1540 		fParent ? fParent->fDelegate : NULL);
1541 	if (error != B_OK)
1542 		return error;
1543 
1544 	// create child delegates
1545 	int32 count = _CountChildren();
1546 	for (int32 i = 0; i < count; i++) {
1547 		BPartition* child = _ChildAt(i);
1548 		error = child->_CreateDelegates();
1549 		if (error != B_OK)
1550 			return error;
1551 	}
1552 
1553 	return B_OK;
1554 }
1555 
1556 
1557 // _InitDelegates
1558 status_t
1559 BPartition::_InitDelegates()
1560 {
1561 	// init delegate
1562 	status_t error = fDelegate->InitAfterHierarchy();
1563 	if (error != B_OK)
1564 		return error;
1565 
1566 	// recursively init child delegates
1567 	int32 count = CountChildren();
1568 	for (int32 i = 0; i < count; i++) {
1569 		error = ChildAt(i)->_InitDelegates();
1570 		if (error != B_OK)
1571 			return error;
1572 	}
1573 
1574 	return B_OK;
1575 }
1576 
1577 
1578 // _DeleteDelegates
1579 void
1580 BPartition::_DeleteDelegates()
1581 {
1582 	// recursively delete child delegates
1583 	int32 count = CountChildren();
1584 	for (int32 i = count - 1; i >= 0; i--)
1585 		ChildAt(i)->_DeleteDelegates();
1586 
1587 	// delete delegate
1588 	delete fDelegate;
1589 	fDelegate = NULL;
1590 
1591 	// Commit suicide, if the delegate was our only link to reality (i.e.
1592 	// there's no physically existing partition we represent).
1593 	if (fPartitionData == NULL)
1594 		delete this;
1595 }
1596 
1597 
1598 // _IsModified
1599 bool
1600 BPartition::_IsModified() const
1601 {
1602 	if (!fDelegate)
1603 		return false;
1604 
1605 	return fDelegate->IsModified();
1606 }
1607