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