xref: /haiku/src/kits/storage/disk_device/Partition.cpp (revision 4a850ca730d8282b5b924e49e09b4ba4d6db7f54)
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 device;
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 || parent->fDelegate == 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 || parent->fDelegate == 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 	// When creating a new partition, this will be called for parent inside
1039 	// which we are creating a partition.
1040 	// When modifying an existing partition, this will be called for the
1041 	// partition itself, but the parameters are in fact managed by the parent
1042 	// (see SetParameters)
1043 	if (type == B_CREATE_PARAMETER_EDITOR) {
1044 		if (fDelegate == NULL)
1045 			return B_NO_INIT;
1046 		return fDelegate->GetParameterEditor(type, editor);
1047 	} else {
1048 		BPartition* parent = Parent();
1049 		if (parent == NULL || parent->fDelegate == NULL)
1050 			return B_NO_INIT;
1051 
1052 		return parent->fDelegate->GetParameterEditor(type, editor);
1053 	}
1054 }
1055 
1056 
1057 status_t
1058 BPartition::SetParameters(const char* parameters)
1059 {
1060 	BPartition* parent = Parent();
1061 	if (parent == NULL || parent->fDelegate == NULL || fDelegate == NULL)
1062 		return B_NO_INIT;
1063 
1064 	return parent->fDelegate->SetParameters(fDelegate, parameters);
1065 }
1066 
1067 
1068 bool
1069 BPartition::CanEditContentParameters(bool* whileMounted) const
1070 {
1071 	return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS,
1072 		B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED,
1073 		whileMounted);
1074 }
1075 
1076 
1077 status_t
1078 BPartition::SetContentParameters(const char* parameters)
1079 {
1080 	if (fDelegate == NULL)
1081 		return B_NO_INIT;
1082 
1083 	return fDelegate->SetContentParameters(parameters);
1084 }
1085 
1086 
1087 status_t
1088 BPartition::GetNextSupportedType(int32* cookie, BString* type) const
1089 {
1090 	TRACE("%p->BPartition::GetNextSupportedType(%ld)\n", this, *cookie);
1091 
1092 	BPartition* parent = Parent();
1093 	if (parent == NULL || fDelegate == NULL) {
1094 		TRACE("  not prepared (parent: %p, fDelegate: %p)!\n", parent,
1095 			fDelegate);
1096 		return B_NO_INIT;
1097 	}
1098 
1099 	return parent->fDelegate->GetNextSupportedChildType(fDelegate, cookie,
1100 		type);
1101 }
1102 
1103 
1104 status_t
1105 BPartition::GetNextSupportedChildType(int32* cookie, BString* type) const
1106 {
1107 	TRACE("%p->BPartition::GetNextSupportedChildType(%ld)\n", this, *cookie);
1108 
1109 	if (fDelegate == NULL) {
1110 		TRACE("  not prepared!\n");
1111 		return B_NO_INIT;
1112 	}
1113 
1114 	return fDelegate->GetNextSupportedChildType(NULL, cookie, type);
1115 }
1116 
1117 
1118 bool
1119 BPartition::BPartition::IsSubSystem(const char* diskSystem) const
1120 {
1121 	BPartition* parent = Parent();
1122 	if (parent == NULL || fDelegate == NULL)
1123 		return false;
1124 
1125 	return parent->fDelegate->IsSubSystem(fDelegate, diskSystem);
1126 }
1127 
1128 
1129 bool
1130 BPartition::CanInitialize(const char* diskSystem) const
1131 {
1132 	if (Size() == 0 || BlockSize() == 0 || fDelegate == NULL)
1133 		return false;
1134 
1135 	return fDelegate->CanInitialize(diskSystem);
1136 }
1137 
1138 
1139 status_t
1140 BPartition::ValidateInitialize(const char* diskSystem, BString* name,
1141 	const char* parameters)
1142 {
1143 	if (fDelegate == NULL)
1144 		return B_NO_INIT;
1145 
1146 	return fDelegate->ValidateInitialize(diskSystem, name, parameters);
1147 }
1148 
1149 
1150 status_t
1151 BPartition::Initialize(const char* diskSystem, const char* name,
1152 	const char* parameters)
1153 {
1154 	if (fDelegate == NULL)
1155 		return B_NO_INIT;
1156 
1157 	return fDelegate->Initialize(diskSystem, name, parameters);
1158 }
1159 
1160 
1161 status_t
1162 BPartition::Uninitialize()
1163 {
1164 	return fDelegate->Uninitialize();
1165 }
1166 
1167 
1168 bool
1169 BPartition::CanCreateChild() const
1170 {
1171 	return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD);
1172 }
1173 
1174 
1175 status_t
1176 BPartition::ValidateCreateChild(off_t* offset, off_t* size, const char* type,
1177 	BString* name, const char* parameters) const
1178 {
1179 	if (fDelegate == NULL)
1180 		return B_NO_INIT;
1181 
1182 	return fDelegate->ValidateCreateChild(offset, size, type, name, parameters);
1183 }
1184 
1185 
1186 status_t
1187 BPartition::CreateChild(off_t offset, off_t size, const char* type,
1188 	const char* name, const char* parameters, BPartition** child)
1189 {
1190 	if (fDelegate == NULL)
1191 		return B_NO_INIT;
1192 
1193 	return fDelegate->CreateChild(offset, size, type, name, parameters, child);
1194 }
1195 
1196 
1197 bool
1198 BPartition::CanDeleteChild(int32 index) const
1199 {
1200 	BPartition* child = ChildAt(index);
1201 	if (fDelegate == NULL || child == NULL)
1202 		return false;
1203 
1204 	return _SupportsChildOperation(child,
1205 		B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD);
1206 }
1207 
1208 
1209 status_t
1210 BPartition::DeleteChild(int32 index)
1211 {
1212 	if (fDelegate == NULL)
1213 		return B_NO_INIT;
1214 
1215 	BPartition* child = ChildAt(index);
1216 	if (child == NULL || child->Parent() != this)
1217 		return B_BAD_VALUE;
1218 
1219 	return fDelegate->DeleteChild(child->fDelegate);
1220 }
1221 
1222 
1223 /*!	\brief Privatized copy constructor to avoid usage.
1224 */
1225 BPartition::BPartition(const BPartition &)
1226 {
1227 }
1228 
1229 
1230 /*!	\brief Privatized assignment operator to avoid usage.
1231 */
1232 BPartition &
1233 BPartition::operator=(const BPartition &)
1234 {
1235 	return *this;
1236 }
1237 
1238 
1239 status_t
1240 BPartition::_SetTo(BDiskDevice* device, BPartition* parent,
1241 	user_partition_data* data)
1242 {
1243 	_Unset();
1244 	if (device == NULL || data == NULL)
1245 		return B_BAD_VALUE;
1246 
1247 	fPartitionData = data;
1248 	fDevice = device;
1249 	fParent = parent;
1250 	fPartitionData->user_data = this;
1251 
1252 	// create and init children
1253 	status_t error = B_OK;
1254 	for (int32 i = 0; error == B_OK && i < fPartitionData->child_count; i++) {
1255 		BPartition* child = new(nothrow) BPartition;
1256 		if (child) {
1257 			error = child->_SetTo(fDevice, this, fPartitionData->children[i]);
1258 			if (error != B_OK)
1259 				delete child;
1260 		} else
1261 			error = B_NO_MEMORY;
1262 	}
1263 
1264 	// cleanup on error
1265 	if (error != B_OK)
1266 		_Unset();
1267 	return error;
1268 }
1269 
1270 
1271 void
1272 BPartition::_Unset()
1273 {
1274 	// delete children
1275 	if (fPartitionData != NULL) {
1276 		for (int32 i = 0; i < fPartitionData->child_count; i++) {
1277 			if (BPartition* child = ChildAt(i))
1278 				delete child;
1279 		}
1280 		fPartitionData->user_data = NULL;
1281 	}
1282 
1283 	fDevice = NULL;
1284 	fParent = NULL;
1285 	fPartitionData = NULL;
1286 	fDelegate = NULL;
1287 }
1288 
1289 
1290 status_t
1291 BPartition::_RemoveObsoleteDescendants(user_partition_data* data, bool* updated)
1292 {
1293 	// remove all children not longer persistent
1294 	// Not exactly efficient: O(n^2), considering BList::RemoveItem()
1295 	// O(1). We could do better (O(n*log(n))), when sorting the arrays before,
1296 	// but then the list access is more random and we had to find the
1297 	// BPartition to remove, which makes the list operation definitely O(n).
1298 	int32 count = CountChildren();
1299 	for (int32 i = count - 1; i >= 0; i--) {
1300 		BPartition* child = ChildAt(i);
1301 		bool found = false;
1302 		for (int32 k = data->child_count - 1; k >= 0; k--) {
1303 			if (data->children[k]->id == child->ID()) {
1304 				// found partition: ask it to remove its obsolete descendants
1305 				found = true;
1306 				status_t error = child->_RemoveObsoleteDescendants(
1307 					data->children[k], updated);
1308 				if (error != B_OK)
1309 					return error;
1310 
1311 				// set the user data to the BPartition object to find it
1312 				// quicker later
1313 				data->children[k]->user_data = child;
1314 				break;
1315 			}
1316 		}
1317 
1318 		// if partition is obsolete, remove it
1319 		if (!found) {
1320 			*updated = true;
1321 			_RemoveChild(i);
1322 		}
1323 	}
1324 	return B_OK;
1325 }
1326 
1327 
1328 status_t
1329 BPartition::_Update(user_partition_data* data, bool* updated)
1330 {
1331 	user_partition_data* oldData = fPartitionData;
1332 	fPartitionData = data;
1333 	// check for changes
1334 	if (data->offset != oldData->offset
1335 		|| data->size != oldData->size
1336 		|| data->block_size != oldData->block_size
1337 		|| data->status != oldData->status
1338 		|| data->flags != oldData->flags
1339 		|| data->volume != oldData->volume
1340 		|| data->disk_system != oldData->disk_system	// not needed
1341 		|| compare_string(data->name, oldData->name)
1342 		|| compare_string(data->content_name, oldData->content_name)
1343 		|| compare_string(data->type, oldData->type)
1344 		|| compare_string(data->content_type, oldData->content_type)
1345 		|| compare_string(data->parameters, oldData->parameters)
1346 		|| compare_string(data->content_parameters,
1347 				oldData->content_parameters)) {
1348 		*updated = true;
1349 	}
1350 
1351 	// add new children and update existing ones
1352 	status_t error = B_OK;
1353 	for (int32 i = 0; i < data->child_count; i++) {
1354 		user_partition_data* childData = data->children[i];
1355 		BPartition* child = (BPartition*)childData->user_data;
1356 		if (child) {
1357 			// old partition
1358 			error = child->_Update(childData, updated);
1359 			if (error != B_OK)
1360 				return error;
1361 		} else {
1362 			// new partition
1363 			*updated = true;
1364 			child = new(nothrow) BPartition;
1365 			if (!child)
1366 				return B_NO_MEMORY;
1367 
1368 			error = child->_SetTo(fDevice, this, childData);
1369 			if (error != B_OK) {
1370 				delete child;
1371 				return error;
1372 			}
1373 
1374 			childData->user_data = child;
1375 		}
1376 	}
1377 	return error;
1378 }
1379 
1380 
1381 void
1382 BPartition::_RemoveChild(int32 index)
1383 {
1384 	int32 count = CountChildren();
1385 	if (!fPartitionData || index < 0 || index >= count)
1386 		return;
1387 
1388 	// delete the BPartition and its children
1389 	delete ChildAt(index);
1390 
1391 	// compact the children array
1392 	for (int32 i = index + 1; i < count; i++)
1393 		fPartitionData->children[i - 1] = fPartitionData->children[i];
1394 	fPartitionData->child_count--;
1395 }
1396 
1397 
1398 BPartition*
1399 BPartition::_ChildAt(int32 index) const
1400 {
1401 	if (index < 0 || index >= fPartitionData->child_count)
1402 		return NULL;
1403 	return (BPartition*)fPartitionData->children[index]->user_data;
1404 }
1405 
1406 
1407 int32
1408 BPartition::_CountChildren() const
1409 {
1410 	return fPartitionData->child_count;
1411 }
1412 
1413 
1414 int32
1415 BPartition::_CountDescendants() const
1416 {
1417 	int32 count = 1;
1418 	for (int32 i = 0; BPartition* child = _ChildAt(i); i++)
1419 		count += child->_CountDescendants();
1420 	return count;
1421 }
1422 
1423 
1424 int32
1425 BPartition::_Level() const
1426 {
1427 	int32 level = 0;
1428 	const BPartition* ancestor = this;
1429 	while ((ancestor = ancestor->Parent()))
1430 		level++;
1431 	return level;
1432 }
1433 
1434 
1435 bool
1436 BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level)
1437 {
1438 	return visitor->Visit(this, level);
1439 }
1440 
1441 
1442 BPartition*
1443 BPartition::_VisitEachDescendant(BDiskDeviceVisitor* visitor, int32 level)
1444 {
1445 	if (level < 0)
1446 		level = _Level();
1447 	if (_AcceptVisitor(visitor, level))
1448 		return this;
1449 	for (int32 i = 0; BPartition* child = ChildAt(i); i++) {
1450 		if (BPartition* result = child->_VisitEachDescendant(visitor,
1451 				level + 1)) {
1452 			return result;
1453 		}
1454 	}
1455 	return NULL;
1456 }
1457 
1458 
1459 const user_partition_data*
1460 BPartition::_PartitionData() const
1461 {
1462 	return fDelegate ? fDelegate->PartitionData() : fPartitionData;
1463 }
1464 
1465 
1466 bool
1467 BPartition::_HasContent() const
1468 {
1469 	return ContentType() != NULL;
1470 }
1471 
1472 
1473 bool
1474 BPartition::_SupportsOperation(uint32 flag, uint32 whileMountedFlag,
1475 	bool* whileMounted) const
1476 {
1477 	if (fDelegate == NULL)
1478 		return false;
1479 
1480 	uint32 supported = fDelegate->SupportedOperations(flag | whileMountedFlag);
1481 
1482 	if (whileMounted)
1483 		*whileMounted = supported & whileMountedFlag;
1484 
1485 	return (supported & flag) != 0;
1486 }
1487 
1488 
1489 bool
1490 BPartition::_SupportsChildOperation(const BPartition* child, uint32 flag) const
1491 {
1492 	if (fDelegate == NULL || (child != NULL && child->fDelegate == NULL))
1493 		return false;
1494 
1495 	uint32 supported = fDelegate->SupportedChildOperations(
1496 		child != NULL ? child->fDelegate : NULL, flag);
1497 
1498 	return (supported & flag) != 0;
1499 }
1500 
1501 
1502 status_t
1503 BPartition::_CreateDelegates()
1504 {
1505 	if (fDelegate != NULL || fPartitionData == NULL)
1506 		return B_NO_INIT;
1507 
1508 	// create and init delegate
1509 	fDelegate = new(nothrow) Delegate(this);
1510 	if (fDelegate == NULL)
1511 		return B_NO_MEMORY;
1512 
1513 	status_t error = fDelegate->InitHierarchy(fPartitionData,
1514 		fParent != NULL ? fParent->fDelegate : NULL);
1515 	if (error != B_OK)
1516 		return error;
1517 
1518 	// create child delegates
1519 	int32 count = _CountChildren();
1520 	for (int32 i = 0; i < count; i++) {
1521 		BPartition* child = _ChildAt(i);
1522 		error = child->_CreateDelegates();
1523 		if (error != B_OK)
1524 			return error;
1525 	}
1526 
1527 	return B_OK;
1528 }
1529 
1530 
1531 status_t
1532 BPartition::_InitDelegates()
1533 {
1534 	// init delegate
1535 	status_t error = fDelegate->InitAfterHierarchy();
1536 	if (error != B_OK)
1537 		return error;
1538 
1539 	// recursively init child delegates
1540 	int32 count = CountChildren();
1541 	for (int32 i = 0; i < count; i++) {
1542 		error = ChildAt(i)->_InitDelegates();
1543 		if (error != B_OK)
1544 			return error;
1545 	}
1546 
1547 	return B_OK;
1548 }
1549 
1550 
1551 void
1552 BPartition::_DeleteDelegates()
1553 {
1554 	// recursively delete child delegates
1555 	int32 count = CountChildren();
1556 	for (int32 i = count - 1; i >= 0; i--)
1557 		ChildAt(i)->_DeleteDelegates();
1558 
1559 	// delete delegate
1560 	delete fDelegate;
1561 	fDelegate = NULL;
1562 
1563 	// Commit suicide, if the delegate was our only link to reality (i.e.
1564 	// there's no physically existing partition we represent).
1565 	if (fPartitionData == NULL)
1566 		delete this;
1567 }
1568 
1569 
1570 bool
1571 BPartition::_IsModified() const
1572 {
1573 	if (fDelegate == NULL)
1574 		return false;
1575 
1576 	return fDelegate->IsModified();
1577 }
1578