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