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