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