2 * Copyright 2003-2006, Haiku Inc.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold, bonefish@users.sf.net
9 #include <DiskDeviceList.h>
11 #include <AutoLocker.h>
12 #include <DiskDevice.h>
13 #include <DiskDevicePrivate.h>
14 #include <DiskDeviceRoster.h>
17 #include <Partition.h>
23 /*! \brief Creates an empty BDiskDeviceList object.
25 BDiskDeviceList::BDiskDeviceList(bool useOwnLocker
)
31 fLocker
= new(nothrow
) BLocker("BDiskDeviceList_fLocker");
35 /*! \brief Frees all resources associated with the object.
37 BDiskDeviceList::~BDiskDeviceList()
43 /*! \brief Implemented to handle notification messages.
46 BDiskDeviceList::MessageReceived(BMessage
*message
)
48 AutoLocker
<BDiskDeviceList
> _(this);
49 switch (message
->what
) {
53 if (message
->FindInt32("event", (int32
*)&event
) == B_OK
) {
55 case B_DEVICE_MOUNT_POINT_MOVED
:
56 _MountPointMoved(message
);
58 case B_DEVICE_PARTITION_MOUNTED
:
59 _PartitionMounted(message
);
61 case B_DEVICE_PARTITION_UNMOUNTED
:
62 _PartitionUnmounted(message
);
64 case B_DEVICE_PARTITION_INITIALIZED
:
65 _PartitionInitialized(message
);
67 case B_DEVICE_PARTITION_RESIZED
:
68 _PartitionResized(message
);
70 case B_DEVICE_PARTITION_MOVED
:
71 _PartitionMoved(message
);
73 case B_DEVICE_PARTITION_CREATED
:
74 _PartitionCreated(message
);
76 case B_DEVICE_PARTITION_DELETED
:
77 _PartitionDeleted(message
);
79 case B_DEVICE_PARTITION_DEFRAGMENTED
:
80 _PartitionDefragmented(message
);
82 case B_DEVICE_PARTITION_REPAIRED
:
83 _PartitionRepaired(message
);
85 case B_DEVICE_MEDIA_CHANGED
:
86 _MediaChanged(message
);
89 _DeviceAdded(message
);
91 case B_DEVICE_REMOVED
:
92 _DeviceRemoved(message
);
98 BHandler::MessageReceived(message
);
103 /*! \brief Implemented to unsubscribe from notification services when going
104 to be detached from looper.
107 BDiskDeviceList::SetNextHandler(BHandler
*handler
)
110 AutoLocker
<BDiskDeviceList
> _(this);
114 BHandler::SetNextHandler(handler
);
118 /*! \brief Empties the list and refills it according to the current state.
120 Furthermore, if added to a looper, the list subscribes to notification
121 services needed to keep the list up-to-date.
123 If an error occurs, the list Unset()s itself.
125 The object doesn't need to be locked, when this method is invoked. The
126 method does itself try to lock the list, but doesn't fail, if that
127 doesn't succeed. That way an object can be used without locking in a
128 single threaded environment.
130 \return \c B_OK, if everything went fine, another error code otherwise.
133 BDiskDeviceList::Fetch()
136 AutoLocker
<BDiskDeviceList
> _(this);
137 // register for notifications
138 status_t error
= B_OK
;
140 error
= _StartWatching();
142 BDiskDeviceRoster roster
;
143 while (error
== B_OK
) {
144 if (BDiskDevice
*device
= new(nothrow
) BDiskDevice
) {
145 status_t status
= roster
.GetNextDevice(device
);
147 fDevices
.AddItem(device
);
148 else if (status
== B_ENTRY_NOT_FOUND
)
162 /*! \brief Empties the list and unsubscribes from all notification services.
164 The object doesn't need to be locked, when this method is invoked. The
165 method does itself try to lock the list, but doesn't fail, if that
166 doesn't succeed. That way an object can be used without locking in a
167 single threaded environment.
170 BDiskDeviceList::Unset()
172 AutoLocker
<BDiskDeviceList
> _(this);
173 // unsubscribe from notification services
176 fDevices
.MakeEmpty();
180 /*! \brief Locks the list.
182 If on construction it had been specified, that the list shall use an
183 own BLocker, then this locker is locked, otherwise LockLooper() is
186 \return \c true, if the list could be locked successfully, \c false
190 BDiskDeviceList::Lock()
193 return fLocker
->Lock();
198 /*! \brief Unlocks the list.
200 If on construction it had been specified, that the list shall use an
201 own BLocker, then this locker is unlocked, otherwise UnlockLooper() is
205 BDiskDeviceList::Unlock()
208 return fLocker
->Unlock();
209 return UnlockLooper();
213 /*! \brief Returns the number of devices in the list.
215 The list must be locked.
217 \return The number of devices in the list.
220 BDiskDeviceList::CountDevices() const
222 return fDevices
.CountItems();
226 /*! \brief Retrieves a device by index.
228 The list must be locked.
230 \param index The list index of the device to be returned.
231 \return The device with index \a index, or \c NULL, if the list is not
232 locked or \a index is out of range.
235 BDiskDeviceList::DeviceAt(int32 index
) const
237 return fDevices
.ItemAt(index
);
241 /*! \brief Iterates through the all devices in the list.
243 The supplied visitor's Visit(BDiskDevice*) is invoked for each device.
244 If Visit() returns \c true, the iteration is terminated and this method
245 returns the respective device.
247 The list must be locked.
249 \param visitor The visitor.
250 \return The respective device, if the iteration was terminated early,
254 BDiskDeviceList::VisitEachDevice(BDiskDeviceVisitor
*visitor
)
257 for (int32 i
= 0; BDiskDevice
*device
= DeviceAt(i
); i
++) {
258 if (visitor
->Visit(device
))
265 // VisitEachPartition
266 /*! \brief Iterates through the all devices' partitions.
268 The supplied visitor's Visit(BPartition*) is invoked for each partition.
269 If Visit() returns \c true, the iteration is terminated and this method
270 returns the respective partition.
272 The list must be locked.
274 \param visitor The visitor.
275 \return The respective partition, if the iteration was terminated early,
279 BDiskDeviceList::VisitEachPartition(BDiskDeviceVisitor
*visitor
)
282 for (int32 i
= 0; BDiskDevice
*device
= DeviceAt(i
); i
++) {
283 if (BPartition
*partition
= device
->VisitEachDescendant(visitor
))
290 // VisitEachMountedPartition
291 /*! \brief Iterates through the all devices' partitions that are mounted.
293 The supplied visitor's Visit(BPartition*) is invoked for each mounted
295 If Visit() returns \c true, the iteration is terminated and this method
296 returns the respective partition.
298 The list must be locked.
300 \param visitor The visitor.
301 \return The respective partition, if the iteration was terminated early,
305 BDiskDeviceList::VisitEachMountedPartition(BDiskDeviceVisitor
*visitor
)
307 BPartition
*partition
= NULL
;
309 struct MountedPartitionFilter
: public PartitionFilter
{
310 virtual ~MountedPartitionFilter() {};
311 virtual bool Filter(BPartition
*partition
, int32 level
)
312 { return partition
->IsMounted(); }
314 PartitionFilterVisitor
filterVisitor(visitor
, &filter
);
315 partition
= VisitEachPartition(&filterVisitor
);
320 // VisitEachMountablePartition
321 /*! \brief Iterates through the all devices' partitions that are mountable.
323 The supplied visitor's Visit(BPartition*) is invoked for each mountable
325 If Visit() returns \c true, the iteration is terminated and this method
326 returns the respective partition.
328 The list must be locked.
330 \param visitor The visitor.
331 \return The respective partition, if the iteration was terminated early,
335 BDiskDeviceList::VisitEachMountablePartition(BDiskDeviceVisitor
*visitor
)
337 BPartition
*partition
= NULL
;
339 struct MountablePartitionFilter
: public PartitionFilter
{
340 virtual ~MountablePartitionFilter() {};
341 virtual bool Filter(BPartition
*partition
, int32 level
)
342 { return partition
->ContainsFileSystem(); }
344 PartitionFilterVisitor
filterVisitor(visitor
, &filter
);
345 partition
= VisitEachPartition(&filterVisitor
);
351 /*! \brief Retrieves a device by ID.
353 The list must be locked.
355 \param id The ID of the device to be returned.
356 \return The device with ID \a id, or \c NULL, if the list is not
357 locked or no device with ID \a id is in the list.
360 BDiskDeviceList::DeviceWithID(int32 id
) const
362 IDFinderVisitor
visitor(id
);
363 return const_cast<BDiskDeviceList
*>(this)->VisitEachDevice(&visitor
);
367 /*! \brief Retrieves a partition by ID.
369 The list must be locked.
371 \param id The ID of the partition to be returned.
372 \return The partition with ID \a id, or \c NULL, if the list is not
373 locked or no partition with ID \a id is in the list.
376 BDiskDeviceList::PartitionWithID(int32 id
) const
378 IDFinderVisitor
visitor(id
);
379 return const_cast<BDiskDeviceList
*>(this)->VisitEachPartition(&visitor
);
383 /*! \brief Invoked, when the mount point of a partition has been moved.
385 The list is locked, when this method is invoked.
387 \param partition The concerned partition.
390 BDiskDeviceList::MountPointMoved(BPartition
*partition
)
392 PartitionChanged(partition
, B_DEVICE_MOUNT_POINT_MOVED
);
396 /*! \brief Invoked, when a partition has been mounted.
398 The list is locked, when this method is invoked.
400 \param partition The concerned partition.
403 BDiskDeviceList::PartitionMounted(BPartition
*partition
)
405 PartitionChanged(partition
, B_DEVICE_PARTITION_MOUNTED
);
408 // PartitionUnmounted
409 /*! \brief Invoked, when a partition has been unmounted.
411 The list is locked, when this method is invoked.
413 \param partition The concerned partition.
416 BDiskDeviceList::PartitionUnmounted(BPartition
*partition
)
418 PartitionChanged(partition
, B_DEVICE_PARTITION_UNMOUNTED
);
421 // PartitionInitialized
422 /*! \brief Invoked, when a partition has been initialized.
424 The list is locked, when this method is invoked.
426 \param partition The concerned partition.
429 BDiskDeviceList::PartitionInitialized(BPartition
*partition
)
431 PartitionChanged(partition
, B_DEVICE_PARTITION_INITIALIZED
);
435 /*! \brief Invoked, when a partition has been resized.
437 The list is locked, when this method is invoked.
439 \param partition The concerned partition.
442 BDiskDeviceList::PartitionResized(BPartition
*partition
)
444 PartitionChanged(partition
, B_DEVICE_PARTITION_RESIZED
);
448 /*! \brief Invoked, when a partition has been moved.
450 The list is locked, when this method is invoked.
452 \param partition The concerned partition.
455 BDiskDeviceList::PartitionMoved(BPartition
*partition
)
457 PartitionChanged(partition
, B_DEVICE_PARTITION_MOVED
);
461 /*! \brief Invoked, when a partition has been created.
463 The list is locked, when this method is invoked.
465 \param partition The concerned partition.
468 BDiskDeviceList::PartitionCreated(BPartition
*partition
)
473 /*! \brief Invoked, when a partition has been deleted.
475 The method is called twice for a deleted partition. The first time
476 before the BDiskDevice the partition belongs to has been updated. The
477 \a partition parameter will point to a still valid BPartition object.
478 On the second invocation the device object will have been updated and
479 the partition object will have been deleted -- \a partition will be
482 The list is locked, when this method is invoked.
484 \param partition The concerned partition. Only non- \c NULL on the first
486 \param partitionID The ID of the concerned partition.
489 BDiskDeviceList::PartitionDeleted(BPartition
*partition
,
490 partition_id partitionID
)
494 // PartitionDefragmented
495 /*! \brief Invoked, when a partition has been defragmented.
497 The list is locked, when this method is invoked.
499 \param partition The concerned partition.
502 BDiskDeviceList::PartitionDefragmented(BPartition
*partition
)
504 PartitionChanged(partition
, B_DEVICE_PARTITION_DEFRAGMENTED
);
508 /*! \brief Invoked, when a partition has been repaired.
510 The list is locked, when this method is invoked.
512 \param partition The concerned partition.
515 BDiskDeviceList::PartitionRepaired(BPartition
*partition
)
517 PartitionChanged(partition
, B_DEVICE_PARTITION_REPAIRED
);
521 /*! \brief Catch-all method invoked by the \c Partition*() hooks, save by
522 PartitionCreated() and PartitionDeleted().
524 If you're interested only in the fact, that something about the partition
525 changed, you can just override this hook instead of the ones telling you
526 exactly what happened.
528 \param partition The concerned partition.
529 \param event The event that occurred, if you are interested in it after all.
532 BDiskDeviceList::PartitionChanged(BPartition
*partition
, uint32 event
)
537 /*! \brief Invoked, when the media of a device has been changed.
539 The list is locked, when this method is invoked.
541 \param device The concerned device.
544 BDiskDeviceList::MediaChanged(BDiskDevice
*device
)
549 /*! \brief Invoked, when a device has been added.
551 The list is locked, when this method is invoked.
553 \param device The concerned device.
556 BDiskDeviceList::DeviceAdded(BDiskDevice
*device
)
561 /*! \brief Invoked, when a device has been removed.
563 The supplied object is already removed from the list and is going to be
564 deleted after the hook returns.
566 The list is locked, when this method is invoked.
568 \param device The concerned device.
571 BDiskDeviceList::DeviceRemoved(BDiskDevice
*device
)
576 /*! \brief Starts watching for disk device notifications.
578 The object must be locked (if possible at all), when this method is
581 \return \c B_OK, if everything went fine, another error code otherwise.
584 BDiskDeviceList::_StartWatching()
586 if (!Looper() || fSubscribed
)
589 status_t error
= BDiskDeviceRoster().StartWatching(BMessenger(this));
590 fSubscribed
= (error
== B_OK
);
595 /*! \brief Stop watching for disk device notifications.
597 The object must be locked (if possible at all), when this method is
601 BDiskDeviceList::_StopWatching()
604 BDiskDeviceRoster().StopWatching(BMessenger(this));
610 /*! \brief Handles a "mount point moved" message.
611 \param message The respective notification message.
614 BDiskDeviceList::_MountPointMoved(BMessage
*message
)
616 if (_UpdateDevice(message
) != NULL
) {
617 if (BPartition
*partition
= _FindPartition(message
))
618 MountPointMoved(partition
);
623 /*! \brief Handles a "partition mounted" message.
624 \param message The respective notification message.
627 BDiskDeviceList::_PartitionMounted(BMessage
*message
)
629 if (_UpdateDevice(message
) != NULL
) {
630 if (BPartition
*partition
= _FindPartition(message
))
631 PartitionMounted(partition
);
635 // _PartitionUnmounted
636 /*! \brief Handles a "partition unmounted" message.
637 \param message The respective notification message.
640 BDiskDeviceList::_PartitionUnmounted(BMessage
*message
)
642 if (_UpdateDevice(message
) != NULL
) {
643 if (BPartition
*partition
= _FindPartition(message
))
644 PartitionUnmounted(partition
);
648 // _PartitionInitialized
649 /*! \brief Handles a "partition initialized" message.
650 \param message The respective notification message.
653 BDiskDeviceList::_PartitionInitialized(BMessage
*message
)
655 if (_UpdateDevice(message
) != NULL
) {
656 if (BPartition
*partition
= _FindPartition(message
))
657 PartitionInitialized(partition
);
662 /*! \brief Handles a "partition resized" message.
663 \param message The respective notification message.
666 BDiskDeviceList::_PartitionResized(BMessage
*message
)
668 if (_UpdateDevice(message
) != NULL
) {
669 if (BPartition
*partition
= _FindPartition(message
))
670 PartitionResized(partition
);
675 /*! \brief Handles a "partition moved" message.
676 \param message The respective notification message.
679 BDiskDeviceList::_PartitionMoved(BMessage
*message
)
681 if (_UpdateDevice(message
) != NULL
) {
682 if (BPartition
*partition
= _FindPartition(message
))
683 PartitionMoved(partition
);
688 /*! \brief Handles a "partition created" message.
689 \param message The respective notification message.
692 BDiskDeviceList::_PartitionCreated(BMessage
*message
)
694 if (_UpdateDevice(message
) != NULL
) {
695 if (BPartition
*partition
= _FindPartition(message
))
696 PartitionCreated(partition
);
701 /*! \brief Handles a "partition deleted" message.
702 \param message The respective notification message.
705 BDiskDeviceList::_PartitionDeleted(BMessage
*message
)
707 if (BPartition
*partition
= _FindPartition(message
)) {
708 partition_id id
= partition
->ID();
709 PartitionDeleted(partition
, id
);
710 if (_UpdateDevice(message
))
711 PartitionDeleted(NULL
, id
);
715 // _PartitionDefragmented
716 /*! \brief Handles a "partition defragmented" message.
717 \param message The respective notification message.
720 BDiskDeviceList::_PartitionDefragmented(BMessage
*message
)
722 if (_UpdateDevice(message
) != NULL
) {
723 if (BPartition
*partition
= _FindPartition(message
))
724 PartitionDefragmented(partition
);
728 // _PartitionRepaired
729 /*! \brief Handles a "partition repaired" message.
730 \param message The respective notification message.
733 BDiskDeviceList::_PartitionRepaired(BMessage
*message
)
735 if (_UpdateDevice(message
) != NULL
) {
736 if (BPartition
*partition
= _FindPartition(message
))
737 PartitionRepaired(partition
);
742 /*! \brief Handles a "media changed" message.
743 \param message The respective notification message.
746 BDiskDeviceList::_MediaChanged(BMessage
*message
)
748 if (BDiskDevice
*device
= _UpdateDevice(message
))
749 MediaChanged(device
);
753 /*! \brief Handles a "device added" message.
754 \param message The respective notification message.
757 BDiskDeviceList::_DeviceAdded(BMessage
*message
)
760 if (message
->FindInt32("device_id", &id
) == B_OK
&& !DeviceWithID(id
)) {
761 BDiskDevice
*device
= new(nothrow
) BDiskDevice
;
762 if (BDiskDeviceRoster().GetDeviceWithID(id
, device
) == B_OK
) {
763 fDevices
.AddItem(device
);
771 /*! \brief Handles a "device removed" message.
772 \param message The respective notification message.
775 BDiskDeviceList::_DeviceRemoved(BMessage
*message
)
777 if (BDiskDevice
*device
= _FindDevice(message
)) {
778 fDevices
.RemoveItem(device
, false);
779 DeviceRemoved(device
);
785 /*! \brief Returns the device for the ID contained in a motification message.
786 \param message The notification message.
787 \return The device with the ID, or \c NULL, if the ID or the device could
791 BDiskDeviceList::_FindDevice(BMessage
*message
)
793 BDiskDevice
*device
= NULL
;
795 if (message
->FindInt32("device_id", &id
) == B_OK
)
796 device
= DeviceWithID(id
);
801 /*! \brief Returns the partition for the ID contained in a motification
803 \param message The notification message.
804 \return The partition with the ID, or \c NULL, if the ID or the partition
805 could not be found.*/
807 BDiskDeviceList::_FindPartition(BMessage
*message
)
809 BPartition
*partition
= NULL
;
811 if (message
->FindInt32("partition_id", &id
) == B_OK
)
812 partition
= PartitionWithID(id
);
817 /*! \brief Finds the device for the ID contained in a motification message
819 \param message The notification message.
820 \return The device with the ID, or \c NULL, if the ID or the device could
824 BDiskDeviceList::_UpdateDevice(BMessage
*message
)
826 BDiskDevice
*device
= _FindDevice(message
);
828 if (device
->Update() != B_OK
) {
829 fDevices
.RemoveItem(device
);