xref: /haiku/src/system/kernel/disk_device_manager/KPartition.cpp (revision 44cceee67e056d8e34cb017d5f5c07b9fac874c0)
1 /*
2  * Copyright 2009, Bryce Groff, bgroff@hawaii.edu.
3  * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2003-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
5  *
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include <KPartition.h>
11 
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 
18 #include <DiskDeviceRoster.h>
19 #include <Drivers.h>
20 #include <Errors.h>
21 #include <fs_volume.h>
22 #include <KernelExport.h>
23 
24 #include <ddm_userland_interface.h>
25 #include <fs/devfs.h>
26 #include <KDiskDevice.h>
27 #include <KDiskDeviceManager.h>
28 #include <KDiskDeviceUtils.h>
29 #include <KDiskSystem.h>
30 #include <KPartitionListener.h>
31 #include <KPartitionVisitor.h>
32 #include <KPath.h>
33 #include <util/kernel_cpp.h>
34 #include <VectorSet.h>
35 #include <vfs.h>
36 
37 #include "UserDataWriter.h"
38 
39 
40 using namespace std;
41 
42 
43 // debugging
44 //#define DBG(x)
45 #define DBG(x) x
46 #define OUT dprintf
47 
48 
49 struct KPartition::ListenerSet : VectorSet<KPartitionListener*> {};
50 
51 
52 int32 KPartition::sNextID = 0;
53 
54 
55 KPartition::KPartition(partition_id id)
56 	:
57 	fPartitionData(),
58 	fChildren(),
59 	fDevice(NULL),
60 	fParent(NULL),
61 	fDiskSystem(NULL),
62 	fDiskSystemPriority(-1),
63 	fListeners(NULL),
64 	fChangeFlags(0),
65 	fChangeCounter(0),
66 	fAlgorithmData(0),
67 	fReferenceCount(0),
68 	fObsolete(false),
69 	fPublishedName(NULL)
70 {
71 	fPartitionData.id = id >= 0 ? id : _NextID();
72 	fPartitionData.offset = 0;
73 	fPartitionData.size = 0;
74 	fPartitionData.content_size = 0;
75 	fPartitionData.block_size = 0;
76 	fPartitionData.physical_block_size = 0;
77 	fPartitionData.child_count = 0;
78 	fPartitionData.index = -1;
79 	fPartitionData.status = B_PARTITION_UNRECOGNIZED;
80 	fPartitionData.flags = B_PARTITION_BUSY;
81 	fPartitionData.volume = -1;
82 	fPartitionData.mount_cookie = NULL;
83 	fPartitionData.name = NULL;
84 	fPartitionData.content_name = NULL;
85 	fPartitionData.type = NULL;
86 	fPartitionData.content_type = NULL;
87 	fPartitionData.parameters = NULL;
88 	fPartitionData.content_parameters = NULL;
89 	fPartitionData.cookie = NULL;
90 	fPartitionData.content_cookie = NULL;
91 }
92 
93 
94 KPartition::~KPartition()
95 {
96 	delete fListeners;
97 	SetDiskSystem(NULL);
98 	free(fPartitionData.name);
99 	free(fPartitionData.content_name);
100 	free(fPartitionData.type);
101 	free(fPartitionData.parameters);
102 	free(fPartitionData.content_parameters);
103 }
104 
105 
106 void
107 KPartition::Register()
108 {
109 	fReferenceCount++;
110 }
111 
112 
113 void
114 KPartition::Unregister()
115 {
116 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
117 	ManagerLocker locker(manager);
118 	fReferenceCount--;
119 	if (IsObsolete() && fReferenceCount == 0) {
120 		// let the manager delete object
121 		manager->DeletePartition(this);
122 	}
123 }
124 
125 
126 int32
127 KPartition::CountReferences() const
128 {
129 	return fReferenceCount;
130 }
131 
132 
133 void
134 KPartition::MarkObsolete()
135 {
136 	fObsolete = true;
137 }
138 
139 
140 bool
141 KPartition::IsObsolete() const
142 {
143 	return fObsolete;
144 }
145 
146 
147 bool
148 KPartition::PrepareForRemoval()
149 {
150 	bool result = RemoveAllChildren();
151 	UninitializeContents();
152 	UnpublishDevice();
153 	if (ParentDiskSystem())
154 		ParentDiskSystem()->FreeCookie(this);
155 	if (DiskSystem())
156 		DiskSystem()->FreeContentCookie(this);
157 	return result;
158 }
159 
160 
161 bool
162 KPartition::PrepareForDeletion()
163 {
164 	return true;
165 }
166 
167 
168 status_t
169 KPartition::Open(int flags, int* fd)
170 {
171 	if (!fd)
172 		return B_BAD_VALUE;
173 
174 	// get the path
175 	KPath path;
176 	status_t error = GetPath(&path);
177 	if (error != B_OK)
178 		return error;
179 
180 	// open the device
181 	*fd = open(path.Path(), flags);
182 	if (*fd < 0)
183 		return errno;
184 
185 	return B_OK;
186 }
187 
188 
189 status_t
190 KPartition::PublishDevice()
191 {
192 	if (fPublishedName)
193 		return B_OK;
194 
195 	// get the name to publish
196 	char buffer[B_FILE_NAME_LENGTH];
197 	status_t error = GetFileName(buffer, B_FILE_NAME_LENGTH);
198 	if (error != B_OK)
199 		return error;
200 
201 	// prepare a partition_info
202 	partition_info info;
203 	info.offset = Offset();
204 	info.size = Size();
205 	info.logical_block_size = BlockSize();
206 	info.physical_block_size = PhysicalBlockSize();
207 	info.session = 0;
208 	info.partition = ID();
209 	if (strlcpy(info.device, Device()->Path(), sizeof(info.device))
210 			>= sizeof(info.device)) {
211 		return B_NAME_TOO_LONG;
212 	}
213 
214 	fPublishedName = strdup(buffer);
215 	if (!fPublishedName)
216 		return B_NO_MEMORY;
217 
218 	error = devfs_publish_partition(buffer, &info);
219 	if (error != B_OK) {
220 		dprintf("KPartition::PublishDevice(): Failed to publish partition "
221 			"%" B_PRId32 ": %s\n", ID(), strerror(error));
222 		free(fPublishedName);
223 		fPublishedName = NULL;
224 		return error;
225 	}
226 
227 	return B_OK;
228 }
229 
230 
231 status_t
232 KPartition::UnpublishDevice()
233 {
234 	if (!fPublishedName)
235 		return B_OK;
236 
237 	// get the path
238 	KPath path;
239 	status_t error = GetPath(&path);
240 	if (error != B_OK) {
241 		dprintf("KPartition::UnpublishDevice(): Failed to get path for "
242 			"partition %" B_PRId32 ": %s\n", ID(), strerror(error));
243 		return error;
244 	}
245 
246 	error = devfs_unpublish_partition(path.Path());
247 	if (error != B_OK) {
248 		dprintf("KPartition::UnpublishDevice(): Failed to unpublish partition "
249 			"%" B_PRId32 ": %s\n", ID(), strerror(error));
250 	}
251 
252 	free(fPublishedName);
253 	fPublishedName = NULL;
254 
255 	return error;
256 }
257 
258 
259 status_t
260 KPartition::RepublishDevice()
261 {
262 	if (!fPublishedName)
263 		return B_OK;
264 
265 	char newNameBuffer[B_FILE_NAME_LENGTH];
266 	status_t error = GetFileName(newNameBuffer, B_FILE_NAME_LENGTH);
267 	if (error != B_OK) {
268 		UnpublishDevice();
269 		return error;
270 	}
271 
272 	if (strcmp(fPublishedName, newNameBuffer) == 0)
273 		return B_OK;
274 
275 	for (int i = 0; i < CountChildren(); i++)
276 		ChildAt(i)->RepublishDevice();
277 
278 	char* newName = strdup(newNameBuffer);
279 	if (!newName) {
280 		UnpublishDevice();
281 		return B_NO_MEMORY;
282 	}
283 
284 	error = devfs_rename_partition(Device()->Path(), fPublishedName, newName);
285 
286 	if (error != B_OK) {
287 		free(newName);
288 		UnpublishDevice();
289 		dprintf("KPartition::RepublishDevice(): Failed to republish partition "
290 			"%" B_PRId32 ": %s\n", ID(), strerror(error));
291 		return error;
292 	}
293 
294 	free(fPublishedName);
295 	fPublishedName = newName;
296 
297 	return B_OK;
298 }
299 
300 
301 bool
302 KPartition::IsPublished() const
303 {
304 	return fPublishedName != NULL;
305 }
306 
307 
308 void
309 KPartition::SetBusy(bool busy)
310 {
311 	if (busy)
312 		AddFlags(B_PARTITION_BUSY);
313 	else
314 		ClearFlags(B_PARTITION_BUSY);
315 }
316 
317 
318 bool
319 KPartition::IsBusy() const
320 {
321 	return (fPartitionData.flags & B_PARTITION_BUSY) != 0;
322 }
323 
324 
325 bool
326 KPartition::IsBusy(bool includeDescendants)
327 {
328 	if (!includeDescendants)
329 		return IsBusy();
330 
331 	struct IsBusyVisitor : KPartitionVisitor {
332 		virtual bool VisitPre(KPartition* partition)
333 		{
334 			return partition->IsBusy();
335 		}
336 	} checkVisitor;
337 
338 	return VisitEachDescendant(&checkVisitor) != NULL;
339 }
340 
341 
342 bool
343 KPartition::CheckAndMarkBusy(bool includeDescendants)
344 {
345 	if (IsBusy(includeDescendants))
346 		return false;
347 
348 	MarkBusy(includeDescendants);
349 
350 	return true;
351 }
352 
353 
354 void
355 KPartition::MarkBusy(bool includeDescendants)
356 {
357 	if (includeDescendants) {
358 		struct MarkBusyVisitor : KPartitionVisitor {
359 			virtual bool VisitPre(KPartition* partition)
360 			{
361 				partition->AddFlags(B_PARTITION_BUSY);
362 				return false;
363 			}
364 		} markVisitor;
365 
366 		VisitEachDescendant(&markVisitor);
367 	} else
368 		SetBusy(true);
369 }
370 
371 
372 void
373 KPartition::UnmarkBusy(bool includeDescendants)
374 {
375 	if (includeDescendants) {
376 		struct UnmarkBusyVisitor : KPartitionVisitor {
377 			virtual bool VisitPre(KPartition* partition)
378 			{
379 				partition->ClearFlags(B_PARTITION_BUSY);
380 				return false;
381 			}
382 		} visitor;
383 
384 		VisitEachDescendant(&visitor);
385 	} else
386 		SetBusy(false);
387 }
388 
389 
390 void
391 KPartition::SetOffset(off_t offset)
392 {
393 	if (fPartitionData.offset != offset) {
394 		fPartitionData.offset = offset;
395 		FireOffsetChanged(offset);
396 	}
397 }
398 
399 
400 off_t
401 KPartition::Offset() const
402 {
403 	return fPartitionData.offset;
404 }
405 
406 
407 void
408 KPartition::SetSize(off_t size)
409 {
410 	if (fPartitionData.size != size) {
411 		fPartitionData.size = size;
412 		FireSizeChanged(size);
413 	}
414 }
415 
416 
417 off_t
418 KPartition::Size() const
419 {
420 	return fPartitionData.size;
421 }
422 
423 
424 void
425 KPartition::SetContentSize(off_t size)
426 {
427 	if (fPartitionData.content_size != size) {
428 		fPartitionData.content_size = size;
429 		FireContentSizeChanged(size);
430 	}
431 }
432 
433 
434 off_t
435 KPartition::ContentSize() const
436 {
437 	return fPartitionData.content_size;
438 }
439 
440 
441 void
442 KPartition::SetBlockSize(uint32 blockSize)
443 {
444 	if (fPartitionData.block_size != blockSize) {
445 		fPartitionData.block_size = blockSize;
446 		FireBlockSizeChanged(blockSize);
447 	}
448 }
449 
450 
451 uint32
452 KPartition::BlockSize() const
453 {
454 	return fPartitionData.block_size;
455 }
456 
457 
458 uint32
459 KPartition::PhysicalBlockSize() const
460 {
461 	return fPartitionData.physical_block_size;
462 }
463 
464 
465 void
466 KPartition::SetPhysicalBlockSize(uint32 blockSize)
467 {
468 	if (fPartitionData.physical_block_size != blockSize)
469 		fPartitionData.physical_block_size = blockSize;
470 }
471 
472 
473 void
474 KPartition::SetIndex(int32 index)
475 {
476 	if (fPartitionData.index != index) {
477 		fPartitionData.index = index;
478 		FireIndexChanged(index);
479 	}
480 }
481 
482 
483 int32
484 KPartition::Index() const
485 {
486 	return fPartitionData.index;
487 }
488 
489 
490 void
491 KPartition::SetStatus(uint32 status)
492 {
493 	if (fPartitionData.status != status) {
494 		fPartitionData.status = status;
495 		FireStatusChanged(status);
496 	}
497 }
498 
499 
500 uint32
501 KPartition::Status() const
502 {
503 	return fPartitionData.status;
504 }
505 
506 
507 bool
508 KPartition::IsUninitialized() const
509 {
510 	return Status() == B_PARTITION_UNINITIALIZED;
511 }
512 
513 
514 void
515 KPartition::SetFlags(uint32 flags)
516 {
517 	if (fPartitionData.flags != flags) {
518 		fPartitionData.flags = flags;
519 		FireFlagsChanged(flags);
520 	}
521 }
522 
523 
524 void
525 KPartition::AddFlags(uint32 flags)
526 {
527 	if (~fPartitionData.flags & flags) {
528 		fPartitionData.flags |= flags;
529 		FireFlagsChanged(fPartitionData.flags);
530 	}
531 }
532 
533 
534 void
535 KPartition::ClearFlags(uint32 flags)
536 {
537 	if (fPartitionData.flags & flags) {
538 		fPartitionData.flags &= ~flags;
539 		FireFlagsChanged(fPartitionData.flags);
540 	}
541 }
542 
543 
544 uint32
545 KPartition::Flags() const
546 {
547 	return fPartitionData.flags;
548 }
549 
550 
551 bool
552 KPartition::ContainsFileSystem() const
553 {
554 	return (fPartitionData.flags & B_PARTITION_FILE_SYSTEM) != 0;
555 }
556 
557 
558 bool
559 KPartition::ContainsPartitioningSystem() const
560 {
561 	return (fPartitionData.flags & B_PARTITION_PARTITIONING_SYSTEM) != 0;
562 }
563 
564 
565 bool
566 KPartition::IsReadOnly() const
567 {
568 	return (fPartitionData.flags & B_PARTITION_READ_ONLY) != 0;
569 }
570 
571 
572 bool
573 KPartition::IsMounted() const
574 {
575 	return (fPartitionData.flags & B_PARTITION_MOUNTED) != 0;
576 }
577 
578 
579 bool
580 KPartition::IsChildMounted()
581 {
582 	struct IsMountedVisitor : KPartitionVisitor {
583 		virtual bool VisitPre(KPartition* partition)
584 		{
585 			return partition->IsMounted();
586 		}
587 	} checkVisitor;
588 
589 	return VisitEachDescendant(&checkVisitor) != NULL;
590 }
591 
592 
593 bool
594 KPartition::IsDevice() const
595 {
596 	return (fPartitionData.flags & B_PARTITION_IS_DEVICE) != 0;
597 }
598 
599 
600 status_t
601 KPartition::SetName(const char* name)
602 {
603 	status_t error = set_string(fPartitionData.name, name);
604 	FireNameChanged(fPartitionData.name);
605 	return error;
606 }
607 
608 
609 const char*
610 KPartition::Name() const
611 {
612 	return fPartitionData.name;
613 }
614 
615 
616 status_t
617 KPartition::SetContentName(const char* name)
618 {
619 	status_t error = set_string(fPartitionData.content_name, name);
620 	FireContentNameChanged(fPartitionData.content_name);
621 	return error;
622 }
623 
624 
625 const char*
626 KPartition::ContentName() const
627 {
628 	return fPartitionData.content_name;
629 }
630 
631 
632 status_t
633 KPartition::SetType(const char* type)
634 {
635 	status_t error = set_string(fPartitionData.type, type);
636 	FireTypeChanged(fPartitionData.type);
637 	return error;
638 }
639 
640 
641 const char*
642 KPartition::Type() const
643 {
644 	return fPartitionData.type;
645 }
646 
647 
648 const char*
649 KPartition::ContentType() const
650 {
651 	return fPartitionData.content_type;
652 }
653 
654 
655 partition_data*
656 KPartition::PartitionData()
657 {
658 	return &fPartitionData;
659 }
660 
661 
662 const partition_data*
663 KPartition::PartitionData() const
664 {
665 	return &fPartitionData;
666 }
667 
668 
669 void
670 KPartition::SetID(partition_id id)
671 {
672 	if (fPartitionData.id != id) {
673 		fPartitionData.id = id;
674 		FireIDChanged(id);
675 	}
676 }
677 
678 
679 partition_id
680 KPartition::ID() const
681 {
682 	return fPartitionData.id;
683 }
684 
685 
686 status_t
687 KPartition::GetFileName(char* buffer, size_t size) const
688 {
689 	// If the parent is the device, the name is the index of the partition.
690 	if (Parent() == NULL || Parent()->IsDevice()) {
691 		if (snprintf(buffer, size, "%" B_PRId32, Index()) >= (int)size)
692 			return B_NAME_TOO_LONG;
693 		return B_OK;
694 	}
695 
696 	// The partition has a non-device parent, so we append the index to the
697 	// parent partition's name.
698 	status_t error = Parent()->GetFileName(buffer, size);
699 	if (error != B_OK)
700 		return error;
701 
702 	size_t len = strlen(buffer);
703 	if (snprintf(buffer + len, size - len, "_%" B_PRId32, Index()) >= int(size - len))
704 		return B_NAME_TOO_LONG;
705 	return B_OK;
706 }
707 
708 
709 status_t
710 KPartition::GetPath(KPath* path) const
711 {
712 	// For a KDiskDevice this version is never invoked, so the check for
713 	// Parent() is correct.
714 	if (!path || path->InitCheck() != B_OK || !Parent() || Index() < 0)
715 		return B_BAD_VALUE;
716 
717 	// init the path with the device path
718 	status_t error = path->SetPath(Device()->Path());
719 	if (error != B_OK)
720 		return error;
721 
722 	// replace the leaf name with the partition's file name
723 	char name[B_FILE_NAME_LENGTH];
724 	error = GetFileName(name, sizeof(name));
725 	if (error == B_OK)
726 		error = path->ReplaceLeaf(name);
727 
728 	return error;
729 }
730 
731 
732 void
733 KPartition::SetVolumeID(dev_t volumeID)
734 {
735 	if (fPartitionData.volume == volumeID)
736 		return;
737 
738 	fPartitionData.volume = volumeID;
739 	FireVolumeIDChanged(volumeID);
740 	if (VolumeID() >= 0)
741 		AddFlags(B_PARTITION_MOUNTED);
742 	else
743 		ClearFlags(B_PARTITION_MOUNTED);
744 
745 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
746 
747 	char messageBuffer[512];
748 	KMessage message;
749 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_DEVICE_UPDATE);
750 	message.AddInt32("event", volumeID >= 0
751 		? B_DEVICE_PARTITION_MOUNTED : B_DEVICE_PARTITION_UNMOUNTED);
752 	message.AddInt32("id", ID());
753 	if (volumeID >= 0)
754 		message.AddInt32("volume", volumeID);
755 
756 	manager->Notify(message, B_DEVICE_REQUEST_MOUNTING);
757 }
758 
759 
760 dev_t
761 KPartition::VolumeID() const
762 {
763 	return fPartitionData.volume;
764 }
765 
766 
767 void
768 KPartition::SetMountCookie(void* cookie)
769 {
770 	if (fPartitionData.mount_cookie != cookie) {
771 		fPartitionData.mount_cookie = cookie;
772 		FireMountCookieChanged(cookie);
773 	}
774 }
775 
776 
777 void*
778 KPartition::MountCookie() const
779 {
780 	return fPartitionData.mount_cookie;
781 }
782 
783 
784 status_t
785 KPartition::Mount(uint32 mountFlags, const char* parameters)
786 {
787 	// not implemented
788 	return B_ERROR;
789 }
790 
791 
792 status_t
793 KPartition::Unmount()
794 {
795 	// not implemented
796 	return B_ERROR;
797 }
798 
799 
800 status_t
801 KPartition::SetParameters(const char* parameters)
802 {
803 	status_t error = set_string(fPartitionData.parameters, parameters);
804 	FireParametersChanged(fPartitionData.parameters);
805 	return error;
806 }
807 
808 
809 const char*
810 KPartition::Parameters() const
811 {
812 	return fPartitionData.parameters;
813 }
814 
815 
816 status_t
817 KPartition::SetContentParameters(const char* parameters)
818 {
819 	status_t error = set_string(fPartitionData.content_parameters, parameters);
820 	FireContentParametersChanged(fPartitionData.content_parameters);
821 	return error;
822 }
823 
824 
825 const char*
826 KPartition::ContentParameters() const
827 {
828 	return fPartitionData.content_parameters;
829 }
830 
831 
832 void
833 KPartition::SetDevice(KDiskDevice* device)
834 {
835 	fDevice = device;
836 	if (fDevice != NULL && fDevice->IsReadOnlyMedia())
837 		AddFlags(B_PARTITION_READ_ONLY);
838 }
839 
840 
841 KDiskDevice*
842 KPartition::Device() const
843 {
844 	return fDevice;
845 }
846 
847 
848 void
849 KPartition::SetParent(KPartition* parent)
850 {
851 	// Must be called in a {Add,Remove}Child() only!
852 	fParent = parent;
853 }
854 
855 
856 KPartition*
857 KPartition::Parent() const
858 {
859 	return fParent;
860 }
861 
862 
863 status_t
864 KPartition::AddChild(KPartition* partition, int32 index)
865 {
866 	// check parameters
867 	int32 count = fPartitionData.child_count;
868 	if (index == -1)
869 		index = count;
870 	if (index < 0 || index > count || !partition)
871 		return B_BAD_VALUE;
872 
873 	// add partition
874 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
875 	if (ManagerLocker locker = manager) {
876 		status_t error = fChildren.Insert(partition, index);
877 		if (error != B_OK)
878 			return error;
879 		if (!manager->PartitionAdded(partition)) {
880 			fChildren.Erase(index);
881 			return B_NO_MEMORY;
882 		}
883 		// update siblings index's
884 		partition->SetIndex(index);
885 		_UpdateChildIndices(count, index);
886 		fPartitionData.child_count++;
887 
888 		partition->SetParent(this);
889 		partition->SetDevice(Device());
890 		partition->SetPhysicalBlockSize(PhysicalBlockSize());
891 
892 		// publish to devfs
893 		partition->PublishDevice();
894 
895 		// notify listeners
896 		FireChildAdded(partition, index);
897 		return B_OK;
898 	}
899 	return B_ERROR;
900 }
901 
902 
903 status_t
904 KPartition::CreateChild(partition_id id, int32 index, off_t offset, off_t size,
905 	KPartition** _child)
906 {
907 	// check parameters
908 	int32 count = fPartitionData.child_count;
909 	if (index == -1)
910 		index = count;
911 	if (index < 0 || index > count)
912 		return B_BAD_VALUE;
913 
914 	// create and add partition
915 	KPartition* child = new(std::nothrow) KPartition(id);
916 	if (child == NULL)
917 		return B_NO_MEMORY;
918 
919 	child->SetOffset(offset);
920 	child->SetSize(size);
921 
922 	status_t error = AddChild(child, index);
923 
924 	// cleanup / set result
925 	if (error != B_OK)
926 		delete child;
927 	else if (_child)
928 		*_child = child;
929 
930 	return error;
931 }
932 
933 
934 bool
935 KPartition::RemoveChild(int32 index)
936 {
937 	if (index < 0 || index >= fPartitionData.child_count)
938 		return false;
939 
940 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
941 	if (ManagerLocker locker = manager) {
942 		KPartition* partition = fChildren.ElementAt(index);
943 		PartitionRegistrar _(partition);
944 		if (!partition || !manager->PartitionRemoved(partition)
945 			|| !fChildren.Erase(index)) {
946 			return false;
947 		}
948 		_UpdateChildIndices(index, fChildren.Count());
949 		partition->SetIndex(-1);
950 		fPartitionData.child_count--;
951 		partition->SetParent(NULL);
952 		partition->SetDevice(NULL);
953 		// notify listeners
954 		FireChildRemoved(partition, index);
955 		return true;
956 	}
957 	return false;
958 }
959 
960 
961 bool
962 KPartition::RemoveChild(KPartition* child)
963 {
964 	if (child) {
965 		int32 index = fChildren.IndexOf(child);
966 		if (index >= 0)
967 			return RemoveChild(index);
968 	}
969 	return false;
970 }
971 
972 
973 bool
974 KPartition::RemoveAllChildren()
975 {
976 	int32 count = CountChildren();
977 	for (int32 i = count - 1; i >= 0; i--) {
978 		if (!RemoveChild(i))
979 			return false;
980 	}
981 	return true;
982 }
983 
984 
985 KPartition*
986 KPartition::ChildAt(int32 index) const
987 {
988 	return index >= 0 && index < fChildren.Count()
989 		? fChildren.ElementAt(index) : NULL;
990 }
991 
992 
993 int32
994 KPartition::CountChildren() const
995 {
996 	return fPartitionData.child_count;
997 }
998 
999 
1000 int32
1001 KPartition::CountDescendants() const
1002 {
1003 	int32 count = 1;
1004 	for (int32 i = 0; KPartition* child = ChildAt(i); i++)
1005 		count += child->CountDescendants();
1006 	return count;
1007 }
1008 
1009 
1010 KPartition*
1011 KPartition::VisitEachDescendant(KPartitionVisitor* visitor)
1012 {
1013 	if (!visitor)
1014 		return NULL;
1015 	if (visitor->VisitPre(this))
1016 		return this;
1017 	for (int32 i = 0; KPartition* child = ChildAt(i); i++) {
1018 		if (KPartition* result = child->VisitEachDescendant(visitor))
1019 			return result;
1020 	}
1021 	if (visitor->VisitPost(this))
1022 		return this;
1023 	return NULL;
1024 }
1025 
1026 
1027 void
1028 KPartition::SetDiskSystem(KDiskSystem* diskSystem, float priority)
1029 {
1030 	// unload former disk system
1031 	if (fDiskSystem) {
1032 		fPartitionData.content_type = NULL;
1033 		fDiskSystem->Unload();
1034 		fDiskSystem = NULL;
1035 		fDiskSystemPriority = -1;
1036 	}
1037 	// set and load new one
1038 	fDiskSystem = diskSystem;
1039 	if (fDiskSystem) {
1040 		fDiskSystem->Load();
1041 			// can't fail, since it's already loaded
1042 	}
1043 	// update concerned partition flags
1044 	if (fDiskSystem) {
1045 		fPartitionData.content_type = fDiskSystem->PrettyName();
1046 		fDiskSystemPriority = priority;
1047 		if (fDiskSystem->IsFileSystem())
1048 			AddFlags(B_PARTITION_FILE_SYSTEM);
1049 		else
1050 			AddFlags(B_PARTITION_PARTITIONING_SYSTEM);
1051 	}
1052 	// notify listeners
1053 	FireDiskSystemChanged(fDiskSystem);
1054 
1055 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1056 
1057 	char messageBuffer[512];
1058 	KMessage message;
1059 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_DEVICE_UPDATE);
1060 	message.AddInt32("event", B_DEVICE_PARTITION_INITIALIZED);
1061 	message.AddInt32("id", ID());
1062 
1063 	manager->Notify(message, B_DEVICE_REQUEST_PARTITION);
1064 }
1065 
1066 
1067 KDiskSystem*
1068 KPartition::DiskSystem() const
1069 {
1070 	return fDiskSystem;
1071 }
1072 
1073 
1074 float
1075 KPartition::DiskSystemPriority() const
1076 {
1077 	return fDiskSystemPriority;
1078 }
1079 
1080 
1081 KDiskSystem*
1082 KPartition::ParentDiskSystem() const
1083 {
1084 	return Parent() ? Parent()->DiskSystem() : NULL;
1085 }
1086 
1087 
1088 void
1089 KPartition::SetCookie(void* cookie)
1090 {
1091 	if (fPartitionData.cookie != cookie) {
1092 		fPartitionData.cookie = cookie;
1093 		FireCookieChanged(cookie);
1094 	}
1095 }
1096 
1097 
1098 void*
1099 KPartition::Cookie() const
1100 {
1101 	return fPartitionData.cookie;
1102 }
1103 
1104 
1105 void
1106 KPartition::SetContentCookie(void* cookie)
1107 {
1108 	if (fPartitionData.content_cookie != cookie) {
1109 		fPartitionData.content_cookie = cookie;
1110 		FireContentCookieChanged(cookie);
1111 	}
1112 }
1113 
1114 
1115 void*
1116 KPartition::ContentCookie() const
1117 {
1118 	return fPartitionData.content_cookie;
1119 }
1120 
1121 
1122 bool
1123 KPartition::AddListener(KPartitionListener* listener)
1124 {
1125 	if (!listener)
1126 		return false;
1127 	// lazy create listeners
1128 	if (!fListeners) {
1129 		fListeners = new(nothrow) ListenerSet;
1130 		if (!fListeners)
1131 			return false;
1132 	}
1133 	// add listener
1134 	return fListeners->Insert(listener) == B_OK;
1135 }
1136 
1137 
1138 bool
1139 KPartition::RemoveListener(KPartitionListener* listener)
1140 {
1141 	if (!listener || !fListeners)
1142 		return false;
1143 	// remove listener and delete the set, if empty now
1144 	bool result = (fListeners->Remove(listener) > 0);
1145 	if (fListeners->IsEmpty()) {
1146 		delete fListeners;
1147 		fListeners = NULL;
1148 	}
1149 	return result;
1150 }
1151 
1152 
1153 void
1154 KPartition::Changed(uint32 flags, uint32 clearFlags)
1155 {
1156 	fChangeFlags &= ~clearFlags;
1157 	fChangeFlags |= flags;
1158 	fChangeCounter++;
1159 	if (Parent())
1160 		Parent()->Changed(B_PARTITION_CHANGED_DESCENDANTS);
1161 }
1162 
1163 
1164 void
1165 KPartition::SetChangeFlags(uint32 flags)
1166 {
1167 	fChangeFlags = flags;
1168 }
1169 
1170 
1171 uint32
1172 KPartition::ChangeFlags() const
1173 {
1174 	return fChangeFlags;
1175 }
1176 
1177 
1178 int32
1179 KPartition::ChangeCounter() const
1180 {
1181 	return fChangeCounter;
1182 }
1183 
1184 
1185 status_t
1186 KPartition::UninitializeContents(bool logChanges)
1187 {
1188 	if (DiskSystem()) {
1189 		uint32 flags = B_PARTITION_CHANGED_INITIALIZATION
1190 			| B_PARTITION_CHANGED_CONTENT_TYPE
1191 			| B_PARTITION_CHANGED_STATUS
1192 			| B_PARTITION_CHANGED_FLAGS;
1193 
1194 		// children
1195 		if (CountChildren() > 0) {
1196 			if (!RemoveAllChildren())
1197 				return B_ERROR;
1198 			flags |= B_PARTITION_CHANGED_CHILDREN;
1199 		}
1200 
1201 		// volume
1202 		if (VolumeID() >= 0) {
1203 			status_t error = vfs_unmount(VolumeID(),
1204 				B_FORCE_UNMOUNT | B_UNMOUNT_BUSY_PARTITION);
1205 			if (error != B_OK) {
1206 				dprintf("KPartition::UninitializeContents(): Failed to unmount "
1207 					"device %" B_PRIdDEV ": %s\n", VolumeID(), strerror(error));
1208 			}
1209 
1210 			SetVolumeID(-1);
1211 			flags |= B_PARTITION_CHANGED_VOLUME;
1212 		}
1213 
1214 		// content name
1215 		if (ContentName()) {
1216 			SetContentName(NULL);
1217 			flags |= B_PARTITION_CHANGED_CONTENT_NAME;
1218 		}
1219 
1220 		// content parameters
1221 		if (ContentParameters()) {
1222 			SetContentParameters(NULL);
1223 			flags |= B_PARTITION_CHANGED_CONTENT_PARAMETERS;
1224 		}
1225 
1226 		// content size
1227 		if (ContentSize() > 0) {
1228 			SetContentSize(0);
1229 			flags |= B_PARTITION_CHANGED_CONTENT_SIZE;
1230 		}
1231 
1232 		// block size
1233 		if (Parent() && Parent()->BlockSize() != BlockSize()) {
1234 			SetBlockSize(Parent()->BlockSize());
1235 			flags |= B_PARTITION_CHANGED_BLOCK_SIZE;
1236 		}
1237 
1238 		// disk system
1239 		DiskSystem()->FreeContentCookie(this);
1240 		SetDiskSystem(NULL);
1241 
1242 		// status
1243 		SetStatus(B_PARTITION_UNINITIALIZED);
1244 
1245 		// flags
1246 		ClearFlags(B_PARTITION_FILE_SYSTEM | B_PARTITION_PARTITIONING_SYSTEM);
1247 		if (!Device()->IsReadOnlyMedia())
1248 			ClearFlags(B_PARTITION_READ_ONLY);
1249 
1250 		// log changes
1251 		if (logChanges) {
1252 			Changed(flags, B_PARTITION_CHANGED_DEFRAGMENTATION
1253 				| B_PARTITION_CHANGED_CHECK | B_PARTITION_CHANGED_REPAIR);
1254 		}
1255 	}
1256 
1257 	return B_OK;
1258 }
1259 
1260 
1261 void
1262 KPartition::SetAlgorithmData(uint32 data)
1263 {
1264 	fAlgorithmData = data;
1265 }
1266 
1267 
1268 uint32
1269 KPartition::AlgorithmData() const
1270 {
1271 	return fAlgorithmData;
1272 }
1273 
1274 
1275 void
1276 KPartition::WriteUserData(UserDataWriter& writer, user_partition_data* data)
1277 {
1278 	// allocate
1279 	char* name = writer.PlaceString(Name());
1280 	char* contentName = writer.PlaceString(ContentName());
1281 	char* type = writer.PlaceString(Type());
1282 	char* contentType = writer.PlaceString(ContentType());
1283 	char* parameters = writer.PlaceString(Parameters());
1284 	char* contentParameters = writer.PlaceString(ContentParameters());
1285 	// fill in data
1286 	if (data) {
1287 		data->id = ID();
1288 		data->offset = Offset();
1289 		data->size = Size();
1290 		data->content_size = ContentSize();
1291 		data->block_size = BlockSize();
1292 		data->physical_block_size = PhysicalBlockSize();
1293 		data->status = Status();
1294 		data->flags = Flags();
1295 		data->volume = VolumeID();
1296 		data->index = Index();
1297 		data->change_counter = ChangeCounter();
1298 		data->disk_system = (DiskSystem() ? DiskSystem()->ID() : -1);
1299 		data->name = name;
1300 		data->content_name = contentName;
1301 		data->type = type;
1302 		data->content_type = contentType;
1303 		data->parameters = parameters;
1304 		data->content_parameters = contentParameters;
1305 		data->child_count = CountChildren();
1306 		// make buffer relocatable
1307 		writer.AddRelocationEntry(&data->name);
1308 		writer.AddRelocationEntry(&data->content_name);
1309 		writer.AddRelocationEntry(&data->type);
1310 		writer.AddRelocationEntry(&data->content_type);
1311 		writer.AddRelocationEntry(&data->parameters);
1312 		writer.AddRelocationEntry(&data->content_parameters);
1313 	}
1314 	// children
1315 	for (int32 i = 0; KPartition* child = ChildAt(i); i++) {
1316 		user_partition_data* childData
1317 			= writer.AllocatePartitionData(child->CountChildren());
1318 		if (data) {
1319 			data->children[i] = childData;
1320 			writer.AddRelocationEntry(&data->children[i]);
1321 		}
1322 		child->WriteUserData(writer, childData);
1323 	}
1324 }
1325 
1326 
1327 void
1328 KPartition::Dump(bool deep, int32 level)
1329 {
1330 	if (level < 0 || level > 255)
1331 		return;
1332 
1333 	char prefix[256];
1334 	sprintf(prefix, "%*s%*s", (int)level, "", (int)level, "");
1335 	KPath path;
1336 	GetPath(&path);
1337 	if (level > 0)
1338 		OUT("%spartition %" B_PRId32 ": %s\n", prefix, ID(), path.Path());
1339 	OUT("%s  offset:            %" B_PRIdOFF "\n", prefix, Offset());
1340 	OUT("%s  size:              %" B_PRIdOFF " (%.2f MB)\n", prefix, Size(),
1341 		Size() / (1024.0*1024));
1342 	OUT("%s  content size:      %" B_PRIdOFF "\n", prefix, ContentSize());
1343 	OUT("%s  block size:        %" B_PRIu32 "\n", prefix, BlockSize());
1344 	OUT("%s  physical block size: %" B_PRIu32 "\n", prefix, PhysicalBlockSize());
1345 	OUT("%s  child count:       %" B_PRId32 "\n", prefix, CountChildren());
1346 	OUT("%s  index:             %" B_PRId32 "\n", prefix, Index());
1347 	OUT("%s  status:            %" B_PRIu32 "\n", prefix, Status());
1348 	OUT("%s  flags:             %" B_PRIx32 "\n", prefix, Flags());
1349 	OUT("%s  volume:            %" B_PRIdDEV "\n", prefix, VolumeID());
1350 	OUT("%s  disk system:       %s\n", prefix,
1351 		(DiskSystem() ? DiskSystem()->Name() : NULL));
1352 	OUT("%s  name:              %s\n", prefix, Name());
1353 	OUT("%s  content name:      %s\n", prefix, ContentName());
1354 	OUT("%s  type:              %s\n", prefix, Type());
1355 	OUT("%s  content type:      %s\n", prefix, ContentType());
1356 	OUT("%s  params:            %s\n", prefix, Parameters());
1357 	OUT("%s  content params:    %s\n", prefix, ContentParameters());
1358 	if (deep) {
1359 		for (int32 i = 0; KPartition* child = ChildAt(i); i++)
1360 			child->Dump(true, level + 1);
1361 	}
1362 }
1363 
1364 
1365 void
1366 KPartition::FireOffsetChanged(off_t offset)
1367 {
1368 	if (fListeners) {
1369 		for (ListenerSet::Iterator it = fListeners->Begin();
1370 			 it != fListeners->End(); ++it) {
1371 			(*it)->OffsetChanged(this, offset);
1372 		}
1373 	}
1374 }
1375 
1376 
1377 void
1378 KPartition::FireSizeChanged(off_t size)
1379 {
1380 	if (fListeners) {
1381 		for (ListenerSet::Iterator it = fListeners->Begin();
1382 			 it != fListeners->End(); ++it) {
1383 			(*it)->SizeChanged(this, size);
1384 		}
1385 	}
1386 }
1387 
1388 
1389 void
1390 KPartition::FireContentSizeChanged(off_t size)
1391 {
1392 	if (fListeners) {
1393 		for (ListenerSet::Iterator it = fListeners->Begin();
1394 			 it != fListeners->End(); ++it) {
1395 			(*it)->ContentSizeChanged(this, size);
1396 		}
1397 	}
1398 }
1399 
1400 
1401 void
1402 KPartition::FireBlockSizeChanged(uint32 blockSize)
1403 {
1404 	if (fListeners) {
1405 		for (ListenerSet::Iterator it = fListeners->Begin();
1406 			 it != fListeners->End(); ++it) {
1407 			(*it)->BlockSizeChanged(this, blockSize);
1408 		}
1409 	}
1410 }
1411 
1412 
1413 void
1414 KPartition::FireIndexChanged(int32 index)
1415 {
1416 	if (fListeners) {
1417 		for (ListenerSet::Iterator it = fListeners->Begin();
1418 			 it != fListeners->End(); ++it) {
1419 			(*it)->IndexChanged(this, index);
1420 		}
1421 	}
1422 }
1423 
1424 
1425 void
1426 KPartition::FireStatusChanged(uint32 status)
1427 {
1428 	if (fListeners) {
1429 		for (ListenerSet::Iterator it = fListeners->Begin();
1430 			 it != fListeners->End(); ++it) {
1431 			(*it)->StatusChanged(this, status);
1432 		}
1433 	}
1434 }
1435 
1436 
1437 void
1438 KPartition::FireFlagsChanged(uint32 flags)
1439 {
1440 	if (fListeners) {
1441 		for (ListenerSet::Iterator it = fListeners->Begin();
1442 			 it != fListeners->End(); ++it) {
1443 			(*it)->FlagsChanged(this, flags);
1444 		}
1445 	}
1446 }
1447 
1448 
1449 void
1450 KPartition::FireNameChanged(const char* name)
1451 {
1452 	if (fListeners) {
1453 		for (ListenerSet::Iterator it = fListeners->Begin();
1454 			 it != fListeners->End(); ++it) {
1455 			(*it)->NameChanged(this, name);
1456 		}
1457 	}
1458 }
1459 
1460 
1461 void
1462 KPartition::FireContentNameChanged(const char* name)
1463 {
1464 	if (fListeners) {
1465 		for (ListenerSet::Iterator it = fListeners->Begin();
1466 			 it != fListeners->End(); ++it) {
1467 			(*it)->ContentNameChanged(this, name);
1468 		}
1469 	}
1470 }
1471 
1472 
1473 void
1474 KPartition::FireTypeChanged(const char* type)
1475 {
1476 	if (fListeners) {
1477 		for (ListenerSet::Iterator it = fListeners->Begin();
1478 			 it != fListeners->End(); ++it) {
1479 			(*it)->TypeChanged(this, type);
1480 		}
1481 	}
1482 }
1483 
1484 
1485 void
1486 KPartition::FireIDChanged(partition_id id)
1487 {
1488 	if (fListeners) {
1489 		for (ListenerSet::Iterator it = fListeners->Begin();
1490 			 it != fListeners->End(); ++it) {
1491 			(*it)->IDChanged(this, id);
1492 		}
1493 	}
1494 }
1495 
1496 
1497 void
1498 KPartition::FireVolumeIDChanged(dev_t volumeID)
1499 {
1500 	if (fListeners) {
1501 		for (ListenerSet::Iterator it = fListeners->Begin();
1502 			 it != fListeners->End(); ++it) {
1503 			(*it)->VolumeIDChanged(this, volumeID);
1504 		}
1505 	}
1506 }
1507 
1508 
1509 void
1510 KPartition::FireMountCookieChanged(void* cookie)
1511 {
1512 	if (fListeners) {
1513 		for (ListenerSet::Iterator it = fListeners->Begin();
1514 			 it != fListeners->End(); ++it) {
1515 			(*it)->MountCookieChanged(this, cookie);
1516 		}
1517 	}
1518 }
1519 
1520 
1521 void
1522 KPartition::FireParametersChanged(const char* parameters)
1523 {
1524 	if (fListeners) {
1525 		for (ListenerSet::Iterator it = fListeners->Begin();
1526 			 it != fListeners->End(); ++it) {
1527 			(*it)->ParametersChanged(this, parameters);
1528 		}
1529 	}
1530 }
1531 
1532 
1533 void
1534 KPartition::FireContentParametersChanged(const char* parameters)
1535 {
1536 	if (fListeners) {
1537 		for (ListenerSet::Iterator it = fListeners->Begin();
1538 			 it != fListeners->End(); ++it) {
1539 			(*it)->ContentParametersChanged(this, parameters);
1540 		}
1541 	}
1542 }
1543 
1544 
1545 void
1546 KPartition::FireChildAdded(KPartition* child, int32 index)
1547 {
1548 	if (fListeners) {
1549 		for (ListenerSet::Iterator it = fListeners->Begin();
1550 			 it != fListeners->End(); ++it) {
1551 			(*it)->ChildAdded(this, child, index);
1552 		}
1553 	}
1554 }
1555 
1556 
1557 void
1558 KPartition::FireChildRemoved(KPartition* child, int32 index)
1559 {
1560 	if (fListeners) {
1561 		for (ListenerSet::Iterator it = fListeners->Begin();
1562 			 it != fListeners->End(); ++it) {
1563 			(*it)->ChildRemoved(this, child, index);
1564 		}
1565 	}
1566 }
1567 
1568 
1569 void
1570 KPartition::FireDiskSystemChanged(KDiskSystem* diskSystem)
1571 {
1572 	if (fListeners) {
1573 		for (ListenerSet::Iterator it = fListeners->Begin();
1574 			 it != fListeners->End(); ++it) {
1575 			(*it)->DiskSystemChanged(this, diskSystem);
1576 		}
1577 	}
1578 }
1579 
1580 
1581 void
1582 KPartition::FireCookieChanged(void* cookie)
1583 {
1584 	if (fListeners) {
1585 		for (ListenerSet::Iterator it = fListeners->Begin();
1586 			 it != fListeners->End(); ++it) {
1587 			(*it)->CookieChanged(this, cookie);
1588 		}
1589 	}
1590 }
1591 
1592 
1593 void
1594 KPartition::FireContentCookieChanged(void* cookie)
1595 {
1596 	if (fListeners) {
1597 		for (ListenerSet::Iterator it = fListeners->Begin();
1598 			 it != fListeners->End(); ++it) {
1599 			(*it)->ContentCookieChanged(this, cookie);
1600 		}
1601 	}
1602 }
1603 
1604 
1605 void
1606 KPartition::_UpdateChildIndices(int32 start, int32 end)
1607 {
1608 	if (start < end) {
1609 		for (int32 i = start; i < end; i++) {
1610 			fChildren.ElementAt(i)->SetIndex(i);
1611 			fChildren.ElementAt(i)->RepublishDevice();
1612 		}
1613 	} else {
1614 		for (int32 i = start; i > end; i--) {
1615 			fChildren.ElementAt(i)->SetIndex(i);
1616 			fChildren.ElementAt(i)->RepublishDevice();
1617 		}
1618 	}
1619 }
1620 
1621 
1622 int32
1623 KPartition::_NextID()
1624 {
1625 	return atomic_add(&sNextID, 1);
1626 }
1627