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