xref: /haiku/src/system/kernel/disk_device_manager/KPartition.cpp (revision 002f37b0cca92e4cf72857c72ac95db5a8b09615)
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::IsDevice() const
564 {
565 	return (fPartitionData.flags & B_PARTITION_IS_DEVICE) != 0;
566 }
567 
568 
569 status_t
570 KPartition::SetName(const char* name)
571 {
572 	status_t error = set_string(fPartitionData.name, name);
573 	FireNameChanged(fPartitionData.name);
574 	return error;
575 }
576 
577 
578 const char*
579 KPartition::Name() const
580 {
581 	return fPartitionData.name;
582 }
583 
584 
585 status_t
586 KPartition::SetContentName(const char* name)
587 {
588 	status_t error = set_string(fPartitionData.content_name, name);
589 	FireContentNameChanged(fPartitionData.content_name);
590 	return error;
591 }
592 
593 
594 const char*
595 KPartition::ContentName() const
596 {
597 	return fPartitionData.content_name;
598 }
599 
600 
601 status_t
602 KPartition::SetType(const char* type)
603 {
604 	status_t error = set_string(fPartitionData.type, type);
605 	FireTypeChanged(fPartitionData.type);
606 	return error;
607 }
608 
609 
610 const char*
611 KPartition::Type() const
612 {
613 	return fPartitionData.type;
614 }
615 
616 
617 const char*
618 KPartition::ContentType() const
619 {
620 	return fPartitionData.content_type;
621 }
622 
623 
624 partition_data*
625 KPartition::PartitionData()
626 {
627 	return &fPartitionData;
628 }
629 
630 
631 const partition_data*
632 KPartition::PartitionData() const
633 {
634 	return &fPartitionData;
635 }
636 
637 
638 void
639 KPartition::SetID(partition_id id)
640 {
641 	if (fPartitionData.id != id) {
642 		fPartitionData.id = id;
643 		FireIDChanged(id);
644 	}
645 }
646 
647 
648 partition_id
649 KPartition::ID() const
650 {
651 	return fPartitionData.id;
652 }
653 
654 
655 status_t
656 KPartition::GetFileName(char* buffer, size_t size) const
657 {
658 	// If the parent is the device, the name is the index of the partition.
659 	if (Parent() == NULL || Parent()->IsDevice()) {
660 		if (snprintf(buffer, size, "%" B_PRId32, Index()) >= (int)size)
661 			return B_NAME_TOO_LONG;
662 		return B_OK;
663 	}
664 
665 	// The partition has a non-device parent, so we append the index to the
666 	// parent partition's name.
667 	status_t error = Parent()->GetFileName(buffer, size);
668 	if (error != B_OK)
669 		return error;
670 
671 	size_t len = strlen(buffer);
672 	if (snprintf(buffer + len, size - len, "_%" B_PRId32, Index()) >= int(size - len))
673 		return B_NAME_TOO_LONG;
674 	return B_OK;
675 }
676 
677 
678 status_t
679 KPartition::GetPath(KPath* path) const
680 {
681 	// For a KDiskDevice this version is never invoked, so the check for
682 	// Parent() is correct.
683 	if (!path || path->InitCheck() != B_OK || !Parent() || Index() < 0)
684 		return B_BAD_VALUE;
685 
686 	// init the path with the device path
687 	status_t error = path->SetPath(Device()->Path());
688 	if (error != B_OK)
689 		return error;
690 
691 	// replace the leaf name with the partition's file name
692 	char name[B_FILE_NAME_LENGTH];
693 	error = GetFileName(name, sizeof(name));
694 	if (error == B_OK)
695 		error = path->ReplaceLeaf(name);
696 
697 	return error;
698 }
699 
700 
701 void
702 KPartition::SetVolumeID(dev_t volumeID)
703 {
704 	if (fPartitionData.volume == volumeID)
705 		return;
706 
707 	fPartitionData.volume = volumeID;
708 	FireVolumeIDChanged(volumeID);
709 	if (VolumeID() >= 0)
710 		AddFlags(B_PARTITION_MOUNTED);
711 	else
712 		ClearFlags(B_PARTITION_MOUNTED);
713 
714 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
715 
716 	char messageBuffer[512];
717 	KMessage message;
718 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_DEVICE_UPDATE);
719 	message.AddInt32("event", volumeID >= 0
720 		? B_DEVICE_PARTITION_MOUNTED : B_DEVICE_PARTITION_UNMOUNTED);
721 	message.AddInt32("id", ID());
722 	if (volumeID >= 0)
723 		message.AddInt32("volume", volumeID);
724 
725 	manager->Notify(message, B_DEVICE_REQUEST_MOUNTING);
726 }
727 
728 
729 dev_t
730 KPartition::VolumeID() const
731 {
732 	return fPartitionData.volume;
733 }
734 
735 
736 void
737 KPartition::SetMountCookie(void* cookie)
738 {
739 	if (fPartitionData.mount_cookie != cookie) {
740 		fPartitionData.mount_cookie = cookie;
741 		FireMountCookieChanged(cookie);
742 	}
743 }
744 
745 
746 void*
747 KPartition::MountCookie() const
748 {
749 	return fPartitionData.mount_cookie;
750 }
751 
752 
753 status_t
754 KPartition::Mount(uint32 mountFlags, const char* parameters)
755 {
756 	// not implemented
757 	return B_ERROR;
758 }
759 
760 
761 status_t
762 KPartition::Unmount()
763 {
764 	// not implemented
765 	return B_ERROR;
766 }
767 
768 
769 status_t
770 KPartition::SetParameters(const char* parameters)
771 {
772 	status_t error = set_string(fPartitionData.parameters, parameters);
773 	FireParametersChanged(fPartitionData.parameters);
774 	return error;
775 }
776 
777 
778 const char*
779 KPartition::Parameters() const
780 {
781 	return fPartitionData.parameters;
782 }
783 
784 
785 status_t
786 KPartition::SetContentParameters(const char* parameters)
787 {
788 	status_t error = set_string(fPartitionData.content_parameters, parameters);
789 	FireContentParametersChanged(fPartitionData.content_parameters);
790 	return error;
791 }
792 
793 
794 const char*
795 KPartition::ContentParameters() const
796 {
797 	return fPartitionData.content_parameters;
798 }
799 
800 
801 void
802 KPartition::SetDevice(KDiskDevice* device)
803 {
804 	fDevice = device;
805 	if (fDevice != NULL && fDevice->IsReadOnlyMedia())
806 		AddFlags(B_PARTITION_READ_ONLY);
807 }
808 
809 
810 KDiskDevice*
811 KPartition::Device() const
812 {
813 	return fDevice;
814 }
815 
816 
817 void
818 KPartition::SetParent(KPartition* parent)
819 {
820 	// Must be called in a {Add,Remove}Child() only!
821 	fParent = parent;
822 }
823 
824 
825 KPartition*
826 KPartition::Parent() const
827 {
828 	return fParent;
829 }
830 
831 
832 status_t
833 KPartition::AddChild(KPartition* partition, int32 index)
834 {
835 	// check parameters
836 	int32 count = fPartitionData.child_count;
837 	if (index == -1)
838 		index = count;
839 	if (index < 0 || index > count || !partition)
840 		return B_BAD_VALUE;
841 
842 	// add partition
843 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
844 	if (ManagerLocker locker = manager) {
845 		status_t error = fChildren.Insert(partition, index);
846 		if (error != B_OK)
847 			return error;
848 		if (!manager->PartitionAdded(partition)) {
849 			fChildren.Erase(index);
850 			return B_NO_MEMORY;
851 		}
852 		// update siblings index's
853 		partition->SetIndex(index);
854 		_UpdateChildIndices(count, index);
855 		fPartitionData.child_count++;
856 
857 		partition->SetParent(this);
858 		partition->SetDevice(Device());
859 
860 		// publish to devfs
861 		partition->PublishDevice();
862 
863 		// notify listeners
864 		FireChildAdded(partition, index);
865 		return B_OK;
866 	}
867 	return B_ERROR;
868 }
869 
870 
871 status_t
872 KPartition::CreateChild(partition_id id, int32 index, off_t offset, off_t size,
873 	KPartition** _child)
874 {
875 	// check parameters
876 	int32 count = fPartitionData.child_count;
877 	if (index == -1)
878 		index = count;
879 	if (index < 0 || index > count)
880 		return B_BAD_VALUE;
881 
882 	// create and add partition
883 	KPartition* child = new(std::nothrow) KPartition(id);
884 	if (child == NULL)
885 		return B_NO_MEMORY;
886 
887 	child->SetOffset(offset);
888 	child->SetSize(size);
889 
890 	status_t error = AddChild(child, index);
891 
892 	// cleanup / set result
893 	if (error != B_OK)
894 		delete child;
895 	else if (_child)
896 		*_child = child;
897 
898 	return error;
899 }
900 
901 
902 bool
903 KPartition::RemoveChild(int32 index)
904 {
905 	if (index < 0 || index >= fPartitionData.child_count)
906 		return false;
907 
908 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
909 	if (ManagerLocker locker = manager) {
910 		KPartition* partition = fChildren.ElementAt(index);
911 		PartitionRegistrar _(partition);
912 		if (!partition || !manager->PartitionRemoved(partition)
913 			|| !fChildren.Erase(index)) {
914 			return false;
915 		}
916 		_UpdateChildIndices(index, fChildren.Count());
917 		partition->SetIndex(-1);
918 		fPartitionData.child_count--;
919 		partition->SetParent(NULL);
920 		partition->SetDevice(NULL);
921 		// notify listeners
922 		FireChildRemoved(partition, index);
923 		return true;
924 	}
925 	return false;
926 }
927 
928 
929 bool
930 KPartition::RemoveChild(KPartition* child)
931 {
932 	if (child) {
933 		int32 index = fChildren.IndexOf(child);
934 		if (index >= 0)
935 			return RemoveChild(index);
936 	}
937 	return false;
938 }
939 
940 
941 bool
942 KPartition::RemoveAllChildren()
943 {
944 	int32 count = CountChildren();
945 	for (int32 i = count - 1; i >= 0; i--) {
946 		if (!RemoveChild(i))
947 			return false;
948 	}
949 	return true;
950 }
951 
952 
953 KPartition*
954 KPartition::ChildAt(int32 index) const
955 {
956 	return index >= 0 && index < fChildren.Count()
957 		? fChildren.ElementAt(index) : NULL;
958 }
959 
960 
961 int32
962 KPartition::CountChildren() const
963 {
964 	return fPartitionData.child_count;
965 }
966 
967 
968 int32
969 KPartition::CountDescendants() const
970 {
971 	int32 count = 1;
972 	for (int32 i = 0; KPartition* child = ChildAt(i); i++)
973 		count += child->CountDescendants();
974 	return count;
975 }
976 
977 
978 KPartition*
979 KPartition::VisitEachDescendant(KPartitionVisitor* visitor)
980 {
981 	if (!visitor)
982 		return NULL;
983 	if (visitor->VisitPre(this))
984 		return this;
985 	for (int32 i = 0; KPartition* child = ChildAt(i); i++) {
986 		if (KPartition* result = child->VisitEachDescendant(visitor))
987 			return result;
988 	}
989 	if (visitor->VisitPost(this))
990 		return this;
991 	return NULL;
992 }
993 
994 
995 void
996 KPartition::SetDiskSystem(KDiskSystem* diskSystem, float priority)
997 {
998 	// unload former disk system
999 	if (fDiskSystem) {
1000 		fPartitionData.content_type = NULL;
1001 		fDiskSystem->Unload();
1002 		fDiskSystem = NULL;
1003 		fDiskSystemPriority = -1;
1004 	}
1005 	// set and load new one
1006 	fDiskSystem = diskSystem;
1007 	if (fDiskSystem) {
1008 		fDiskSystem->Load();
1009 			// can't fail, since it's already loaded
1010 	}
1011 	// update concerned partition flags
1012 	if (fDiskSystem) {
1013 		fPartitionData.content_type = fDiskSystem->PrettyName();
1014 		fDiskSystemPriority = priority;
1015 		if (fDiskSystem->IsFileSystem())
1016 			AddFlags(B_PARTITION_FILE_SYSTEM);
1017 		else
1018 			AddFlags(B_PARTITION_PARTITIONING_SYSTEM);
1019 	}
1020 	// notify listeners
1021 	FireDiskSystemChanged(fDiskSystem);
1022 
1023 	KDiskDeviceManager* manager = KDiskDeviceManager::Default();
1024 
1025 	char messageBuffer[512];
1026 	KMessage message;
1027 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_DEVICE_UPDATE);
1028 	message.AddInt32("event", B_DEVICE_PARTITION_INITIALIZED);
1029 	message.AddInt32("id", ID());
1030 
1031 	manager->Notify(message, B_DEVICE_REQUEST_PARTITION);
1032 }
1033 
1034 
1035 KDiskSystem*
1036 KPartition::DiskSystem() const
1037 {
1038 	return fDiskSystem;
1039 }
1040 
1041 
1042 float
1043 KPartition::DiskSystemPriority() const
1044 {
1045 	return fDiskSystemPriority;
1046 }
1047 
1048 
1049 KDiskSystem*
1050 KPartition::ParentDiskSystem() const
1051 {
1052 	return Parent() ? Parent()->DiskSystem() : NULL;
1053 }
1054 
1055 
1056 void
1057 KPartition::SetCookie(void* cookie)
1058 {
1059 	if (fPartitionData.cookie != cookie) {
1060 		fPartitionData.cookie = cookie;
1061 		FireCookieChanged(cookie);
1062 	}
1063 }
1064 
1065 
1066 void*
1067 KPartition::Cookie() const
1068 {
1069 	return fPartitionData.cookie;
1070 }
1071 
1072 
1073 void
1074 KPartition::SetContentCookie(void* cookie)
1075 {
1076 	if (fPartitionData.content_cookie != cookie) {
1077 		fPartitionData.content_cookie = cookie;
1078 		FireContentCookieChanged(cookie);
1079 	}
1080 }
1081 
1082 
1083 void*
1084 KPartition::ContentCookie() const
1085 {
1086 	return fPartitionData.content_cookie;
1087 }
1088 
1089 
1090 bool
1091 KPartition::AddListener(KPartitionListener* listener)
1092 {
1093 	if (!listener)
1094 		return false;
1095 	// lazy create listeners
1096 	if (!fListeners) {
1097 		fListeners = new(nothrow) ListenerSet;
1098 		if (!fListeners)
1099 			return false;
1100 	}
1101 	// add listener
1102 	return fListeners->Insert(listener) == B_OK;
1103 }
1104 
1105 
1106 bool
1107 KPartition::RemoveListener(KPartitionListener* listener)
1108 {
1109 	if (!listener || !fListeners)
1110 		return false;
1111 	// remove listener and delete the set, if empty now
1112 	bool result = (fListeners->Remove(listener) > 0);
1113 	if (fListeners->IsEmpty()) {
1114 		delete fListeners;
1115 		fListeners = NULL;
1116 	}
1117 	return result;
1118 }
1119 
1120 
1121 void
1122 KPartition::Changed(uint32 flags, uint32 clearFlags)
1123 {
1124 	fChangeFlags &= ~clearFlags;
1125 	fChangeFlags |= flags;
1126 	fChangeCounter++;
1127 	if (Parent())
1128 		Parent()->Changed(B_PARTITION_CHANGED_DESCENDANTS);
1129 }
1130 
1131 
1132 void
1133 KPartition::SetChangeFlags(uint32 flags)
1134 {
1135 	fChangeFlags = flags;
1136 }
1137 
1138 
1139 uint32
1140 KPartition::ChangeFlags() const
1141 {
1142 	return fChangeFlags;
1143 }
1144 
1145 
1146 int32
1147 KPartition::ChangeCounter() const
1148 {
1149 	return fChangeCounter;
1150 }
1151 
1152 
1153 status_t
1154 KPartition::UninitializeContents(bool logChanges)
1155 {
1156 	if (DiskSystem()) {
1157 		uint32 flags = B_PARTITION_CHANGED_INITIALIZATION
1158 			| B_PARTITION_CHANGED_CONTENT_TYPE
1159 			| B_PARTITION_CHANGED_STATUS
1160 			| B_PARTITION_CHANGED_FLAGS;
1161 
1162 		// children
1163 		if (CountChildren() > 0) {
1164 			if (!RemoveAllChildren())
1165 				return B_ERROR;
1166 			flags |= B_PARTITION_CHANGED_CHILDREN;
1167 		}
1168 
1169 		// volume
1170 		if (VolumeID() >= 0) {
1171 			status_t error = vfs_unmount(VolumeID(),
1172 				B_FORCE_UNMOUNT | B_UNMOUNT_BUSY_PARTITION);
1173 			if (error != B_OK) {
1174 				dprintf("KPartition::UninitializeContents(): Failed to unmount "
1175 					"device %" B_PRIdDEV ": %s\n", VolumeID(), strerror(error));
1176 			}
1177 
1178 			SetVolumeID(-1);
1179 			flags |= B_PARTITION_CHANGED_VOLUME;
1180 		}
1181 
1182 		// content name
1183 		if (ContentName()) {
1184 			SetContentName(NULL);
1185 			flags |= B_PARTITION_CHANGED_CONTENT_NAME;
1186 		}
1187 
1188 		// content parameters
1189 		if (ContentParameters()) {
1190 			SetContentParameters(NULL);
1191 			flags |= B_PARTITION_CHANGED_CONTENT_PARAMETERS;
1192 		}
1193 
1194 		// content size
1195 		if (ContentSize() > 0) {
1196 			SetContentSize(0);
1197 			flags |= B_PARTITION_CHANGED_CONTENT_SIZE;
1198 		}
1199 
1200 		// block size
1201 		if (Parent() && Parent()->BlockSize() != BlockSize()) {
1202 			SetBlockSize(Parent()->BlockSize());
1203 			flags |= B_PARTITION_CHANGED_BLOCK_SIZE;
1204 		}
1205 
1206 		// disk system
1207 		DiskSystem()->FreeContentCookie(this);
1208 		SetDiskSystem(NULL);
1209 
1210 		// status
1211 		SetStatus(B_PARTITION_UNINITIALIZED);
1212 
1213 		// flags
1214 		ClearFlags(B_PARTITION_FILE_SYSTEM | B_PARTITION_PARTITIONING_SYSTEM);
1215 		if (!Device()->IsReadOnlyMedia())
1216 			ClearFlags(B_PARTITION_READ_ONLY);
1217 
1218 		// log changes
1219 		if (logChanges) {
1220 			Changed(flags, B_PARTITION_CHANGED_DEFRAGMENTATION
1221 				| B_PARTITION_CHANGED_CHECK | B_PARTITION_CHANGED_REPAIR);
1222 		}
1223 	}
1224 
1225 	return B_OK;
1226 }
1227 
1228 
1229 void
1230 KPartition::SetAlgorithmData(uint32 data)
1231 {
1232 	fAlgorithmData = data;
1233 }
1234 
1235 
1236 uint32
1237 KPartition::AlgorithmData() const
1238 {
1239 	return fAlgorithmData;
1240 }
1241 
1242 
1243 void
1244 KPartition::WriteUserData(UserDataWriter& writer, user_partition_data* data)
1245 {
1246 	// allocate
1247 	char* name = writer.PlaceString(Name());
1248 	char* contentName = writer.PlaceString(ContentName());
1249 	char* type = writer.PlaceString(Type());
1250 	char* contentType = writer.PlaceString(ContentType());
1251 	char* parameters = writer.PlaceString(Parameters());
1252 	char* contentParameters = writer.PlaceString(ContentParameters());
1253 	// fill in data
1254 	if (data) {
1255 		data->id = ID();
1256 		data->offset = Offset();
1257 		data->size = Size();
1258 		data->content_size = ContentSize();
1259 		data->block_size = BlockSize();
1260 		data->status = Status();
1261 		data->flags = Flags();
1262 		data->volume = VolumeID();
1263 		data->index = Index();
1264 		data->change_counter = ChangeCounter();
1265 		data->disk_system = (DiskSystem() ? DiskSystem()->ID() : -1);
1266 		data->name = name;
1267 		data->content_name = contentName;
1268 		data->type = type;
1269 		data->content_type = contentType;
1270 		data->parameters = parameters;
1271 		data->content_parameters = contentParameters;
1272 		data->child_count = CountChildren();
1273 		// make buffer relocatable
1274 		writer.AddRelocationEntry(&data->name);
1275 		writer.AddRelocationEntry(&data->content_name);
1276 		writer.AddRelocationEntry(&data->type);
1277 		writer.AddRelocationEntry(&data->content_type);
1278 		writer.AddRelocationEntry(&data->parameters);
1279 		writer.AddRelocationEntry(&data->content_parameters);
1280 	}
1281 	// children
1282 	for (int32 i = 0; KPartition* child = ChildAt(i); i++) {
1283 		user_partition_data* childData
1284 			= writer.AllocatePartitionData(child->CountChildren());
1285 		if (data) {
1286 			data->children[i] = childData;
1287 			writer.AddRelocationEntry(&data->children[i]);
1288 		}
1289 		child->WriteUserData(writer, childData);
1290 	}
1291 }
1292 
1293 
1294 void
1295 KPartition::Dump(bool deep, int32 level)
1296 {
1297 	if (level < 0 || level > 255)
1298 		return;
1299 
1300 	char prefix[256];
1301 	sprintf(prefix, "%*s%*s", (int)level, "", (int)level, "");
1302 	KPath path;
1303 	GetPath(&path);
1304 	if (level > 0)
1305 		OUT("%spartition %" B_PRId32 ": %s\n", prefix, ID(), path.Path());
1306 	OUT("%s  offset:            %" B_PRIdOFF "\n", prefix, Offset());
1307 	OUT("%s  size:              %" B_PRIdOFF " (%.2f MB)\n", prefix, Size(),
1308 		Size() / (1024.0*1024));
1309 	OUT("%s  content size:      %" B_PRIdOFF "\n", prefix, ContentSize());
1310 	OUT("%s  block size:        %" B_PRIu32 "\n", prefix, BlockSize());
1311 	OUT("%s  child count:       %" B_PRId32 "\n", prefix, CountChildren());
1312 	OUT("%s  index:             %" B_PRId32 "\n", prefix, Index());
1313 	OUT("%s  status:            %" B_PRIu32 "\n", prefix, Status());
1314 	OUT("%s  flags:             %" B_PRIx32 "\n", prefix, Flags());
1315 	OUT("%s  volume:            %" B_PRIdDEV "\n", prefix, VolumeID());
1316 	OUT("%s  disk system:       %s\n", prefix,
1317 		(DiskSystem() ? DiskSystem()->Name() : NULL));
1318 	OUT("%s  name:              %s\n", prefix, Name());
1319 	OUT("%s  content name:      %s\n", prefix, ContentName());
1320 	OUT("%s  type:              %s\n", prefix, Type());
1321 	OUT("%s  content type:      %s\n", prefix, ContentType());
1322 	OUT("%s  params:            %s\n", prefix, Parameters());
1323 	OUT("%s  content params:    %s\n", prefix, ContentParameters());
1324 	if (deep) {
1325 		for (int32 i = 0; KPartition* child = ChildAt(i); i++)
1326 			child->Dump(true, level + 1);
1327 	}
1328 }
1329 
1330 
1331 void
1332 KPartition::FireOffsetChanged(off_t offset)
1333 {
1334 	if (fListeners) {
1335 		for (ListenerSet::Iterator it = fListeners->Begin();
1336 			 it != fListeners->End(); ++it) {
1337 			(*it)->OffsetChanged(this, offset);
1338 		}
1339 	}
1340 }
1341 
1342 
1343 void
1344 KPartition::FireSizeChanged(off_t size)
1345 {
1346 	if (fListeners) {
1347 		for (ListenerSet::Iterator it = fListeners->Begin();
1348 			 it != fListeners->End(); ++it) {
1349 			(*it)->SizeChanged(this, size);
1350 		}
1351 	}
1352 }
1353 
1354 
1355 void
1356 KPartition::FireContentSizeChanged(off_t size)
1357 {
1358 	if (fListeners) {
1359 		for (ListenerSet::Iterator it = fListeners->Begin();
1360 			 it != fListeners->End(); ++it) {
1361 			(*it)->ContentSizeChanged(this, size);
1362 		}
1363 	}
1364 }
1365 
1366 
1367 void
1368 KPartition::FireBlockSizeChanged(uint32 blockSize)
1369 {
1370 	if (fListeners) {
1371 		for (ListenerSet::Iterator it = fListeners->Begin();
1372 			 it != fListeners->End(); ++it) {
1373 			(*it)->BlockSizeChanged(this, blockSize);
1374 		}
1375 	}
1376 }
1377 
1378 
1379 void
1380 KPartition::FireIndexChanged(int32 index)
1381 {
1382 	if (fListeners) {
1383 		for (ListenerSet::Iterator it = fListeners->Begin();
1384 			 it != fListeners->End(); ++it) {
1385 			(*it)->IndexChanged(this, index);
1386 		}
1387 	}
1388 }
1389 
1390 
1391 void
1392 KPartition::FireStatusChanged(uint32 status)
1393 {
1394 	if (fListeners) {
1395 		for (ListenerSet::Iterator it = fListeners->Begin();
1396 			 it != fListeners->End(); ++it) {
1397 			(*it)->StatusChanged(this, status);
1398 		}
1399 	}
1400 }
1401 
1402 
1403 void
1404 KPartition::FireFlagsChanged(uint32 flags)
1405 {
1406 	if (fListeners) {
1407 		for (ListenerSet::Iterator it = fListeners->Begin();
1408 			 it != fListeners->End(); ++it) {
1409 			(*it)->FlagsChanged(this, flags);
1410 		}
1411 	}
1412 }
1413 
1414 
1415 void
1416 KPartition::FireNameChanged(const char* name)
1417 {
1418 	if (fListeners) {
1419 		for (ListenerSet::Iterator it = fListeners->Begin();
1420 			 it != fListeners->End(); ++it) {
1421 			(*it)->NameChanged(this, name);
1422 		}
1423 	}
1424 }
1425 
1426 
1427 void
1428 KPartition::FireContentNameChanged(const char* name)
1429 {
1430 	if (fListeners) {
1431 		for (ListenerSet::Iterator it = fListeners->Begin();
1432 			 it != fListeners->End(); ++it) {
1433 			(*it)->ContentNameChanged(this, name);
1434 		}
1435 	}
1436 }
1437 
1438 
1439 void
1440 KPartition::FireTypeChanged(const char* type)
1441 {
1442 	if (fListeners) {
1443 		for (ListenerSet::Iterator it = fListeners->Begin();
1444 			 it != fListeners->End(); ++it) {
1445 			(*it)->TypeChanged(this, type);
1446 		}
1447 	}
1448 }
1449 
1450 
1451 void
1452 KPartition::FireIDChanged(partition_id id)
1453 {
1454 	if (fListeners) {
1455 		for (ListenerSet::Iterator it = fListeners->Begin();
1456 			 it != fListeners->End(); ++it) {
1457 			(*it)->IDChanged(this, id);
1458 		}
1459 	}
1460 }
1461 
1462 
1463 void
1464 KPartition::FireVolumeIDChanged(dev_t volumeID)
1465 {
1466 	if (fListeners) {
1467 		for (ListenerSet::Iterator it = fListeners->Begin();
1468 			 it != fListeners->End(); ++it) {
1469 			(*it)->VolumeIDChanged(this, volumeID);
1470 		}
1471 	}
1472 }
1473 
1474 
1475 void
1476 KPartition::FireMountCookieChanged(void* cookie)
1477 {
1478 	if (fListeners) {
1479 		for (ListenerSet::Iterator it = fListeners->Begin();
1480 			 it != fListeners->End(); ++it) {
1481 			(*it)->MountCookieChanged(this, cookie);
1482 		}
1483 	}
1484 }
1485 
1486 
1487 void
1488 KPartition::FireParametersChanged(const char* parameters)
1489 {
1490 	if (fListeners) {
1491 		for (ListenerSet::Iterator it = fListeners->Begin();
1492 			 it != fListeners->End(); ++it) {
1493 			(*it)->ParametersChanged(this, parameters);
1494 		}
1495 	}
1496 }
1497 
1498 
1499 void
1500 KPartition::FireContentParametersChanged(const char* parameters)
1501 {
1502 	if (fListeners) {
1503 		for (ListenerSet::Iterator it = fListeners->Begin();
1504 			 it != fListeners->End(); ++it) {
1505 			(*it)->ContentParametersChanged(this, parameters);
1506 		}
1507 	}
1508 }
1509 
1510 
1511 void
1512 KPartition::FireChildAdded(KPartition* child, int32 index)
1513 {
1514 	if (fListeners) {
1515 		for (ListenerSet::Iterator it = fListeners->Begin();
1516 			 it != fListeners->End(); ++it) {
1517 			(*it)->ChildAdded(this, child, index);
1518 		}
1519 	}
1520 }
1521 
1522 
1523 void
1524 KPartition::FireChildRemoved(KPartition* child, int32 index)
1525 {
1526 	if (fListeners) {
1527 		for (ListenerSet::Iterator it = fListeners->Begin();
1528 			 it != fListeners->End(); ++it) {
1529 			(*it)->ChildRemoved(this, child, index);
1530 		}
1531 	}
1532 }
1533 
1534 
1535 void
1536 KPartition::FireDiskSystemChanged(KDiskSystem* diskSystem)
1537 {
1538 	if (fListeners) {
1539 		for (ListenerSet::Iterator it = fListeners->Begin();
1540 			 it != fListeners->End(); ++it) {
1541 			(*it)->DiskSystemChanged(this, diskSystem);
1542 		}
1543 	}
1544 }
1545 
1546 
1547 void
1548 KPartition::FireCookieChanged(void* cookie)
1549 {
1550 	if (fListeners) {
1551 		for (ListenerSet::Iterator it = fListeners->Begin();
1552 			 it != fListeners->End(); ++it) {
1553 			(*it)->CookieChanged(this, cookie);
1554 		}
1555 	}
1556 }
1557 
1558 
1559 void
1560 KPartition::FireContentCookieChanged(void* cookie)
1561 {
1562 	if (fListeners) {
1563 		for (ListenerSet::Iterator it = fListeners->Begin();
1564 			 it != fListeners->End(); ++it) {
1565 			(*it)->ContentCookieChanged(this, cookie);
1566 		}
1567 	}
1568 }
1569 
1570 
1571 void
1572 KPartition::_UpdateChildIndices(int32 start, int32 end)
1573 {
1574 	if (start < end) {
1575 		for (int32 i = start; i < end; i++) {
1576 			fChildren.ElementAt(i)->SetIndex(i);
1577 			fChildren.ElementAt(i)->RepublishDevice();
1578 		}
1579 	} else {
1580 		for (int32 i = start; i > end; i--) {
1581 			fChildren.ElementAt(i)->SetIndex(i);
1582 			fChildren.ElementAt(i)->RepublishDevice();
1583 		}
1584 	}
1585 }
1586 
1587 
1588 int32
1589 KPartition::_NextID()
1590 {
1591 	return atomic_add(&sNextID, 1);
1592 }
1593