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