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