vfs: check userland buffers before reading them.
[haiku.git] / src / kits / storage / disk_device / DiskDeviceList.cpp
blob3b9c99d7500b538aa746bc230a728bc74ed0ed31
1 /*
2 * Copyright 2003-2006, Haiku Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ingo Weinhold, bonefish@users.sf.net
7 */
9 #include <DiskDeviceList.h>
11 #include <AutoLocker.h>
12 #include <DiskDevice.h>
13 #include <DiskDevicePrivate.h>
14 #include <DiskDeviceRoster.h>
15 #include <Locker.h>
16 #include <Looper.h>
17 #include <Partition.h>
19 #include <new>
20 using namespace std;
22 // constructor
23 /*! \brief Creates an empty BDiskDeviceList object.
25 BDiskDeviceList::BDiskDeviceList(bool useOwnLocker)
26 : fLocker(NULL),
27 fDevices(20, true),
28 fSubscribed(false)
30 if (useOwnLocker)
31 fLocker = new(nothrow) BLocker("BDiskDeviceList_fLocker");
34 // destructor
35 /*! \brief Frees all resources associated with the object.
37 BDiskDeviceList::~BDiskDeviceList()
39 delete fLocker;
42 // MessageReceived
43 /*! \brief Implemented to handle notification messages.
45 void
46 BDiskDeviceList::MessageReceived(BMessage *message)
48 AutoLocker<BDiskDeviceList> _(this);
49 switch (message->what) {
50 case B_DEVICE_UPDATE:
52 uint32 event;
53 if (message->FindInt32("event", (int32*)&event) == B_OK) {
54 switch (event) {
55 case B_DEVICE_MOUNT_POINT_MOVED:
56 _MountPointMoved(message);
57 break;
58 case B_DEVICE_PARTITION_MOUNTED:
59 _PartitionMounted(message);
60 break;
61 case B_DEVICE_PARTITION_UNMOUNTED:
62 _PartitionUnmounted(message);
63 break;
64 case B_DEVICE_PARTITION_INITIALIZED:
65 _PartitionInitialized(message);
66 break;
67 case B_DEVICE_PARTITION_RESIZED:
68 _PartitionResized(message);
69 break;
70 case B_DEVICE_PARTITION_MOVED:
71 _PartitionMoved(message);
72 break;
73 case B_DEVICE_PARTITION_CREATED:
74 _PartitionCreated(message);
75 break;
76 case B_DEVICE_PARTITION_DELETED:
77 _PartitionDeleted(message);
78 break;
79 case B_DEVICE_PARTITION_DEFRAGMENTED:
80 _PartitionDefragmented(message);
81 break;
82 case B_DEVICE_PARTITION_REPAIRED:
83 _PartitionRepaired(message);
84 break;
85 case B_DEVICE_MEDIA_CHANGED:
86 _MediaChanged(message);
87 break;
88 case B_DEVICE_ADDED:
89 _DeviceAdded(message);
90 break;
91 case B_DEVICE_REMOVED:
92 _DeviceRemoved(message);
93 break;
97 default:
98 BHandler::MessageReceived(message);
102 // SetNextHandler
103 /*! \brief Implemented to unsubscribe from notification services when going
104 to be detached from looper.
106 void
107 BDiskDeviceList::SetNextHandler(BHandler *handler)
109 if (!handler) {
110 AutoLocker<BDiskDeviceList> _(this);
111 if (fSubscribed)
112 _StopWatching();
114 BHandler::SetNextHandler(handler);
117 // Fetch
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.
132 status_t
133 BDiskDeviceList::Fetch()
135 Unset();
136 AutoLocker<BDiskDeviceList> _(this);
137 // register for notifications
138 status_t error = B_OK;
139 if (Looper())
140 error = _StartWatching();
141 // get the devices
142 BDiskDeviceRoster roster;
143 while (error == B_OK) {
144 if (BDiskDevice *device = new(nothrow) BDiskDevice) {
145 status_t status = roster.GetNextDevice(device);
146 if (status == B_OK)
147 fDevices.AddItem(device);
148 else if (status == B_ENTRY_NOT_FOUND)
149 break;
150 else
151 error = status;
152 } else
153 error = B_NO_MEMORY;
155 // cleanup on error
156 if (error != B_OK)
157 Unset();
158 return error;
161 // Unset
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.
169 void
170 BDiskDeviceList::Unset()
172 AutoLocker<BDiskDeviceList> _(this);
173 // unsubscribe from notification services
174 _StopWatching();
175 // empty the list
176 fDevices.MakeEmpty();
179 // Lock
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
184 invoked.
186 \return \c true, if the list could be locked successfully, \c false
187 otherwise.
189 bool
190 BDiskDeviceList::Lock()
192 if (fLocker)
193 return fLocker->Lock();
194 return LockLooper();
197 // Unlock
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
202 invoked.
204 void
205 BDiskDeviceList::Unlock()
207 if (fLocker)
208 return fLocker->Unlock();
209 return UnlockLooper();
212 // CountDevices
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.
219 int32
220 BDiskDeviceList::CountDevices() const
222 return fDevices.CountItems();
225 // DeviceAt
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.
234 BDiskDevice *
235 BDiskDeviceList::DeviceAt(int32 index) const
237 return fDevices.ItemAt(index);
240 // VisitEachDevice
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,
251 \c NULL otherwise.
253 BDiskDevice *
254 BDiskDeviceList::VisitEachDevice(BDiskDeviceVisitor *visitor)
256 if (visitor) {
257 for (int32 i = 0; BDiskDevice *device = DeviceAt(i); i++) {
258 if (visitor->Visit(device))
259 return device;
262 return NULL;
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,
276 \c NULL otherwise.
278 BPartition *
279 BDiskDeviceList::VisitEachPartition(BDiskDeviceVisitor *visitor)
281 if (visitor) {
282 for (int32 i = 0; BDiskDevice *device = DeviceAt(i); i++) {
283 if (BPartition *partition = device->VisitEachDescendant(visitor))
284 return partition;
287 return NULL;
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
294 partition.
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,
302 \c NULL otherwise.
304 BPartition *
305 BDiskDeviceList::VisitEachMountedPartition(BDiskDeviceVisitor *visitor)
307 BPartition *partition = NULL;
308 if (visitor) {
309 struct MountedPartitionFilter : public PartitionFilter {
310 virtual ~MountedPartitionFilter() {};
311 virtual bool Filter(BPartition *partition, int32 level)
312 { return partition->IsMounted(); }
313 } filter;
314 PartitionFilterVisitor filterVisitor(visitor, &filter);
315 partition = VisitEachPartition(&filterVisitor);
317 return partition;
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
324 partition.
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,
332 \c NULL otherwise.
334 BPartition *
335 BDiskDeviceList::VisitEachMountablePartition(BDiskDeviceVisitor *visitor)
337 BPartition *partition = NULL;
338 if (visitor) {
339 struct MountablePartitionFilter : public PartitionFilter {
340 virtual ~MountablePartitionFilter() {};
341 virtual bool Filter(BPartition *partition, int32 level)
342 { return partition->ContainsFileSystem(); }
343 } filter;
344 PartitionFilterVisitor filterVisitor(visitor, &filter);
345 partition = VisitEachPartition(&filterVisitor);
347 return partition;
350 // DeviceWithID
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.
359 BDiskDevice *
360 BDiskDeviceList::DeviceWithID(int32 id) const
362 IDFinderVisitor visitor(id);
363 return const_cast<BDiskDeviceList*>(this)->VisitEachDevice(&visitor);
366 // PartitionWithID
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.
375 BPartition *
376 BDiskDeviceList::PartitionWithID(int32 id) const
378 IDFinderVisitor visitor(id);
379 return const_cast<BDiskDeviceList*>(this)->VisitEachPartition(&visitor);
382 // MountPointMoved
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.
389 void
390 BDiskDeviceList::MountPointMoved(BPartition *partition)
392 PartitionChanged(partition, B_DEVICE_MOUNT_POINT_MOVED);
395 // PartitionMounted
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.
402 void
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.
415 void
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.
428 void
429 BDiskDeviceList::PartitionInitialized(BPartition *partition)
431 PartitionChanged(partition, B_DEVICE_PARTITION_INITIALIZED);
434 // PartitionResized
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.
441 void
442 BDiskDeviceList::PartitionResized(BPartition *partition)
444 PartitionChanged(partition, B_DEVICE_PARTITION_RESIZED);
447 // PartitionMoved
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.
454 void
455 BDiskDeviceList::PartitionMoved(BPartition *partition)
457 PartitionChanged(partition, B_DEVICE_PARTITION_MOVED);
460 // PartitionCreated
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.
467 void
468 BDiskDeviceList::PartitionCreated(BPartition *partition)
472 // PartitionDeleted
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
480 \c NULL then.
482 The list is locked, when this method is invoked.
484 \param partition The concerned partition. Only non- \c NULL on the first
485 invocation.
486 \param partitionID The ID of the concerned partition.
488 void
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.
501 void
502 BDiskDeviceList::PartitionDefragmented(BPartition *partition)
504 PartitionChanged(partition, B_DEVICE_PARTITION_DEFRAGMENTED);
507 // PartitionRepaired
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.
514 void
515 BDiskDeviceList::PartitionRepaired(BPartition *partition)
517 PartitionChanged(partition, B_DEVICE_PARTITION_REPAIRED);
520 // PartitionChanged
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.
531 void
532 BDiskDeviceList::PartitionChanged(BPartition *partition, uint32 event)
536 // MediaChanged
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.
543 void
544 BDiskDeviceList::MediaChanged(BDiskDevice *device)
548 // DeviceAdded
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.
555 void
556 BDiskDeviceList::DeviceAdded(BDiskDevice *device)
560 // DeviceRemoved
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.
570 void
571 BDiskDeviceList::DeviceRemoved(BDiskDevice *device)
575 // _StartWatching
576 /*! \brief Starts watching for disk device notifications.
578 The object must be locked (if possible at all), when this method is
579 invoked.
581 \return \c B_OK, if everything went fine, another error code otherwise.
583 status_t
584 BDiskDeviceList::_StartWatching()
586 if (!Looper() || fSubscribed)
587 return B_BAD_VALUE;
589 status_t error = BDiskDeviceRoster().StartWatching(BMessenger(this));
590 fSubscribed = (error == B_OK);
591 return error;
594 // _StopWatching
595 /*! \brief Stop watching for disk device notifications.
597 The object must be locked (if possible at all), when this method is
598 invoked.
600 void
601 BDiskDeviceList::_StopWatching()
603 if (fSubscribed) {
604 BDiskDeviceRoster().StopWatching(BMessenger(this));
605 fSubscribed = false;
609 // _MountPointMoved
610 /*! \brief Handles a "mount point moved" message.
611 \param message The respective notification message.
613 void
614 BDiskDeviceList::_MountPointMoved(BMessage *message)
616 if (_UpdateDevice(message) != NULL) {
617 if (BPartition *partition = _FindPartition(message))
618 MountPointMoved(partition);
622 // _PartitionMounted
623 /*! \brief Handles a "partition mounted" message.
624 \param message The respective notification message.
626 void
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.
639 void
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.
652 void
653 BDiskDeviceList::_PartitionInitialized(BMessage *message)
655 if (_UpdateDevice(message) != NULL) {
656 if (BPartition *partition = _FindPartition(message))
657 PartitionInitialized(partition);
661 // _PartitionResized
662 /*! \brief Handles a "partition resized" message.
663 \param message The respective notification message.
665 void
666 BDiskDeviceList::_PartitionResized(BMessage *message)
668 if (_UpdateDevice(message) != NULL) {
669 if (BPartition *partition = _FindPartition(message))
670 PartitionResized(partition);
674 // _PartitionMoved
675 /*! \brief Handles a "partition moved" message.
676 \param message The respective notification message.
678 void
679 BDiskDeviceList::_PartitionMoved(BMessage *message)
681 if (_UpdateDevice(message) != NULL) {
682 if (BPartition *partition = _FindPartition(message))
683 PartitionMoved(partition);
687 // _PartitionCreated
688 /*! \brief Handles a "partition created" message.
689 \param message The respective notification message.
691 void
692 BDiskDeviceList::_PartitionCreated(BMessage *message)
694 if (_UpdateDevice(message) != NULL) {
695 if (BPartition *partition = _FindPartition(message))
696 PartitionCreated(partition);
700 // _PartitionDeleted
701 /*! \brief Handles a "partition deleted" message.
702 \param message The respective notification message.
704 void
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.
719 void
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.
732 void
733 BDiskDeviceList::_PartitionRepaired(BMessage *message)
735 if (_UpdateDevice(message) != NULL) {
736 if (BPartition *partition = _FindPartition(message))
737 PartitionRepaired(partition);
741 // _MediaChanged
742 /*! \brief Handles a "media changed" message.
743 \param message The respective notification message.
745 void
746 BDiskDeviceList::_MediaChanged(BMessage *message)
748 if (BDiskDevice *device = _UpdateDevice(message))
749 MediaChanged(device);
752 // _DeviceAdded
753 /*! \brief Handles a "device added" message.
754 \param message The respective notification message.
756 void
757 BDiskDeviceList::_DeviceAdded(BMessage *message)
759 int32 id;
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);
764 DeviceAdded(device);
765 } else
766 delete device;
770 // _DeviceRemoved
771 /*! \brief Handles a "device removed" message.
772 \param message The respective notification message.
774 void
775 BDiskDeviceList::_DeviceRemoved(BMessage *message)
777 if (BDiskDevice *device = _FindDevice(message)) {
778 fDevices.RemoveItem(device, false);
779 DeviceRemoved(device);
780 delete device;
784 // _FindDevice
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
788 not be found.
790 BDiskDevice *
791 BDiskDeviceList::_FindDevice(BMessage *message)
793 BDiskDevice *device = NULL;
794 int32 id;
795 if (message->FindInt32("device_id", &id) == B_OK)
796 device = DeviceWithID(id);
797 return device;
800 // _FindPartition
801 /*! \brief Returns the partition for the ID contained in a motification
802 message.
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.*/
806 BPartition *
807 BDiskDeviceList::_FindPartition(BMessage *message)
809 BPartition *partition = NULL;
810 int32 id;
811 if (message->FindInt32("partition_id", &id) == B_OK)
812 partition = PartitionWithID(id);
813 return partition;
816 // _UpdateDevice
817 /*! \brief Finds the device for the ID contained in a motification message
818 and updates it.
819 \param message The notification message.
820 \return The device with the ID, or \c NULL, if the ID or the device could
821 not be found.
823 BDiskDevice *
824 BDiskDeviceList::_UpdateDevice(BMessage *message)
826 BDiskDevice *device = _FindDevice(message);
827 if (device) {
828 if (device->Update() != B_OK) {
829 fDevices.RemoveItem(device);
830 device = NULL;
833 return device;