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