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