1 /*
2 * Copyright 2004-2018, Haiku, Inc.
3 * Copyright 2003-2004, Ingo Weinhold, bonefish@cs.tu-berlin.de.
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6
7
8 #include "KDiskDevice.h"
9 #include "KDiskDeviceManager.h"
10 #include "KDiskDeviceUtils.h"
11 #include "KDiskSystem.h"
12 #include "KFileDiskDevice.h"
13 #include "KFileSystem.h"
14 #include "KPartition.h"
15 #include "KPartitioningSystem.h"
16 #include "KPartitionVisitor.h"
17 #include "KPath.h"
18
19 #include <VectorMap.h>
20 #include <VectorSet.h>
21
22 #include <DiskDeviceRoster.h>
23 #include <KernelExport.h>
24 #include <NodeMonitor.h>
25
26 #include <boot_device.h>
27 #include <kmodule.h>
28 #include <node_monitor.h>
29 #include <Notifications.h>
30 #include <util/kernel_cpp.h>
31 #include <vfs.h>
32
33 #include <dirent.h>
34 #include <errno.h>
35 #include <module.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40
41
42 //#define TRACE_KDISK_DEVICE_MANAGER
43 #ifdef TRACE_KDISK_DEVICE_MANAGER
44 # define TRACE TRACE_ALWAYS
45 #else
46 # define TRACE(x...) do { } while (false)
47 #endif
48 #define TRACE_ALWAYS(x...) dprintf("disk_device_manager: " x)
49 #define TRACE_ERROR(x...) dprintf("disk_device_manager: error: " x)
50
51
52 // directories for partitioning and file system modules
53 static const char* kPartitioningSystemPrefix = "partitioning_systems";
54 static const char* kFileSystemPrefix = "file_systems";
55
56
57 // singleton instance
58 KDiskDeviceManager* KDiskDeviceManager::sDefaultManager = NULL;
59
60
61 struct device_event {
62 int32 opcode;
63 const char* name;
64 dev_t device;
65 ino_t directory;
66 ino_t node;
67 };
68
69
70 struct GetPartitionID {
operator ()GetPartitionID71 inline partition_id operator()(const KPartition* partition) const
72 {
73 return partition->ID();
74 }
75 };
76
77
78 struct GetDiskSystemID {
operator ()GetDiskSystemID79 inline disk_system_id operator()(const KDiskSystem* system) const
80 {
81 return system->ID();
82 }
83 };
84
85
86 struct KDiskDeviceManager::PartitionMap : VectorMap<partition_id, KPartition*,
87 VectorMapEntryStrategy::ImplicitKey<partition_id, KPartition*,
88 GetPartitionID> > {
89 };
90
91
92 struct KDiskDeviceManager::DeviceMap : VectorMap<partition_id, KDiskDevice*,
93 VectorMapEntryStrategy::ImplicitKey<partition_id, KDiskDevice*,
94 GetPartitionID> > {
95 };
96
97
98 struct KDiskDeviceManager::DiskSystemMap : VectorMap<disk_system_id,
99 KDiskSystem*,
100 VectorMapEntryStrategy::ImplicitKey<disk_system_id, KDiskSystem*,
101 GetDiskSystemID> > {
102 };
103
104
105 struct KDiskDeviceManager::PartitionSet : VectorSet<KPartition*> {
106 };
107
108
109 class KDiskDeviceManager::DiskSystemWatcher : public NotificationListener {
110 public:
DiskSystemWatcher(KDiskDeviceManager * manager)111 DiskSystemWatcher(KDiskDeviceManager* manager)
112 :
113 fManager(manager)
114 {
115 }
116
~DiskSystemWatcher()117 virtual ~DiskSystemWatcher()
118 {
119 }
120
EventOccurred(NotificationService & service,const KMessage * event)121 virtual void EventOccurred(NotificationService& service,
122 const KMessage* event)
123 {
124 if (event->GetInt32("opcode", -1) != B_ENTRY_REMOVED)
125 fManager->RescanDiskSystems();
126 }
127
128 private:
129 KDiskDeviceManager* fManager;
130 };
131
132
133 class KDiskDeviceManager::DeviceWatcher : public NotificationListener {
134 public:
DeviceWatcher()135 DeviceWatcher()
136 {
137 }
138
~DeviceWatcher()139 virtual ~DeviceWatcher()
140 {
141 }
142
EventOccurred(NotificationService & service,const KMessage * event)143 virtual void EventOccurred(NotificationService& service,
144 const KMessage* event)
145 {
146 int32 opcode = event->GetInt32("opcode", -1);
147 switch (opcode) {
148 case B_ENTRY_CREATED:
149 case B_ENTRY_REMOVED:
150 {
151 device_event* deviceEvent = new(std::nothrow) device_event;
152 if (deviceEvent == NULL)
153 break;
154
155 const char* name = event->GetString("name", NULL);
156 if (name != NULL)
157 deviceEvent->name = strdup(name);
158 else
159 deviceEvent->name = NULL;
160
161 deviceEvent->opcode = opcode;
162 deviceEvent->device = event->GetInt32("device", -1);
163 deviceEvent->directory = event->GetInt64("directory", -1);
164 deviceEvent->node = event->GetInt64("node", -1);
165
166 struct stat stat;
167 if (vfs_stat_node_ref(deviceEvent->device, deviceEvent->node,
168 &stat) != 0) {
169 delete deviceEvent;
170 break;
171 }
172 if (S_ISDIR(stat.st_mode)) {
173 if (opcode == B_ENTRY_CREATED) {
174 add_node_listener(deviceEvent->device,
175 deviceEvent->node, B_WATCH_DIRECTORY, *this);
176 } else {
177 remove_node_listener(deviceEvent->device,
178 deviceEvent->node, *this);
179 }
180 delete deviceEvent;
181 break;
182 }
183
184 // TODO: a real in-kernel DPC mechanism would be preferred...
185 thread_id thread = spawn_kernel_thread(_HandleDeviceEvent,
186 "device event", B_NORMAL_PRIORITY, deviceEvent);
187 if (thread < 0)
188 delete deviceEvent;
189 else
190 resume_thread(thread);
191 break;
192 }
193
194 default:
195 break;
196 }
197 }
198
_HandleDeviceEvent(void * _event)199 static status_t _HandleDeviceEvent(void* _event)
200 {
201 device_event* event = (device_event*)_event;
202
203 if (strcmp(event->name, "raw") == 0) {
204 // a new raw device was added/removed
205 KPath path;
206 if (path.InitCheck() != B_OK
207 || vfs_entry_ref_to_path(event->device, event->directory,
208 event->name, true, path.LockBuffer(),
209 path.BufferSize()) != B_OK) {
210 delete event;
211 return B_ERROR;
212 }
213
214 path.UnlockBuffer();
215 if (event->opcode == B_ENTRY_CREATED)
216 KDiskDeviceManager::Default()->CreateDevice(path.Path());
217 else
218 KDiskDeviceManager::Default()->DeleteDevice(path.Path());
219 }
220
221 delete event;
222 return B_OK;
223 }
224 };
225
226
227 class KDiskDeviceManager::DiskNotifications
228 : public DefaultUserNotificationService {
229 public:
DiskNotifications()230 DiskNotifications()
231 : DefaultUserNotificationService("disk devices")
232 {
233 }
234
~DiskNotifications()235 virtual ~DiskNotifications()
236 {
237 }
238 };
239
240
241 // #pragma mark -
242
243
KDiskDeviceManager()244 KDiskDeviceManager::KDiskDeviceManager()
245 :
246 fDevices(new(nothrow) DeviceMap),
247 fPartitions(new(nothrow) PartitionMap),
248 fDiskSystems(new(nothrow) DiskSystemMap),
249 fObsoletePartitions(new(nothrow) PartitionSet),
250 fMediaChecker(-1),
251 fTerminating(false),
252 fDiskSystemWatcher(NULL),
253 fDeviceWatcher(new(nothrow) DeviceWatcher()),
254 fNotifications(new(nothrow) DiskNotifications)
255 {
256 recursive_lock_init(&fLock, "disk device manager");
257
258 if (InitCheck() != B_OK)
259 return;
260
261 fNotifications->Register();
262
263 RescanDiskSystems();
264
265 fMediaChecker = spawn_kernel_thread(_CheckMediaStatusDaemon,
266 "media checker", B_NORMAL_PRIORITY, this);
267 if (fMediaChecker >= 0)
268 resume_thread(fMediaChecker);
269
270 TRACE("number of disk systems: %" B_PRId32 "\n", CountDiskSystems());
271 // TODO: Watch the disk systems and the relevant directories.
272 }
273
274
~KDiskDeviceManager()275 KDiskDeviceManager::~KDiskDeviceManager()
276 {
277 fTerminating = true;
278
279 status_t result;
280 wait_for_thread(fMediaChecker, &result);
281
282 // stop all node monitoring
283 _AddRemoveMonitoring("/dev/disk", false);
284 delete fDeviceWatcher;
285
286 // remove all devices
287 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie);) {
288 PartitionRegistrar _(device);
289 _RemoveDevice(device);
290 }
291
292 // some sanity checks
293 if (fPartitions->Count() > 0) {
294 TRACE_ALWAYS("WARNING: There are still %" B_PRId32 " unremoved partitions!\n",
295 fPartitions->Count());
296 for (PartitionMap::Iterator it = fPartitions->Begin();
297 it != fPartitions->End(); ++it) {
298 TRACE(" partition: %" B_PRId32 "\n", it->Value()->ID());
299 }
300 }
301 if (fObsoletePartitions->Count() > 0) {
302 TRACE_ALWAYS("WARNING: There are still %" B_PRId32 " obsolete partitions!\n",
303 fObsoletePartitions->Count());
304 for (PartitionSet::Iterator it = fObsoletePartitions->Begin();
305 it != fObsoletePartitions->End(); ++it) {
306 TRACE(" partition: %" B_PRId32 "\n", (*it)->ID());
307 }
308 }
309 // remove all disk systems
310 for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) {
311 fDiskSystems->Remove(diskSystem->ID());
312 if (diskSystem->IsLoaded()) {
313 TRACE_ALWAYS("WARNING: Disk system `%s' (%" B_PRId32 ") is still loaded!\n",
314 diskSystem->Name(), diskSystem->ID());
315 } else
316 delete diskSystem;
317 }
318
319 fNotifications->Unregister();
320
321 // delete the containers
322 delete fPartitions;
323 delete fDevices;
324 delete fDiskSystems;
325 delete fObsoletePartitions;
326 }
327
328
329 status_t
InitCheck() const330 KDiskDeviceManager::InitCheck() const
331 {
332 if (fPartitions == NULL || fDevices == NULL || fDiskSystems == NULL
333 || fObsoletePartitions == NULL || fNotifications == NULL)
334 return B_NO_MEMORY;
335
336 return B_OK;
337 }
338
339
340 /*! This creates the system's default DiskDeviceManager.
341 The creation is not thread-safe, and shouldn't be done more than once.
342 */
343 status_t
CreateDefault()344 KDiskDeviceManager::CreateDefault()
345 {
346 if (sDefaultManager != NULL)
347 return B_OK;
348
349 sDefaultManager = new(nothrow) KDiskDeviceManager;
350 if (sDefaultManager == NULL)
351 return B_NO_MEMORY;
352
353 return sDefaultManager->InitCheck();
354 }
355
356
357 /*! This deletes the default DiskDeviceManager. The deletion is not
358 thread-safe either, you should make sure that it's called only once.
359 */
360 void
DeleteDefault()361 KDiskDeviceManager::DeleteDefault()
362 {
363 delete sDefaultManager;
364 sDefaultManager = NULL;
365 }
366
367
368 KDiskDeviceManager*
Default()369 KDiskDeviceManager::Default()
370 {
371 return sDefaultManager;
372 }
373
374
375 bool
Lock()376 KDiskDeviceManager::Lock()
377 {
378 return recursive_lock_lock(&fLock) == B_OK;
379 }
380
381
382 void
Unlock()383 KDiskDeviceManager::Unlock()
384 {
385 recursive_lock_unlock(&fLock);
386 }
387
388
389 DefaultUserNotificationService&
Notifications()390 KDiskDeviceManager::Notifications()
391 {
392 return *fNotifications;
393 }
394
395
396 void
Notify(const KMessage & event,uint32 eventMask)397 KDiskDeviceManager::Notify(const KMessage& event, uint32 eventMask)
398 {
399 fNotifications->Notify(event, eventMask);
400 }
401
402
403 KDiskDevice*
FindDevice(const char * path)404 KDiskDeviceManager::FindDevice(const char* path)
405 {
406 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) {
407 if (device->Path() && !strcmp(path, device->Path()))
408 return device;
409 }
410 return NULL;
411 }
412
413
414 KDiskDevice*
FindDevice(partition_id id,bool deviceOnly)415 KDiskDeviceManager::FindDevice(partition_id id, bool deviceOnly)
416 {
417 if (KPartition* partition = FindPartition(id)) {
418 KDiskDevice* device = partition->Device();
419 if (!deviceOnly || id == device->ID())
420 return device;
421 }
422 return NULL;
423 }
424
425
426 KPartition*
FindPartition(const char * path)427 KDiskDeviceManager::FindPartition(const char* path)
428 {
429 // TODO: Optimize!
430 KPath partitionPath;
431 if (partitionPath.InitCheck() != B_OK)
432 return NULL;
433
434 for (PartitionMap::Iterator iterator = fPartitions->Begin();
435 iterator != fPartitions->End(); ++iterator) {
436 KPartition* partition = iterator->Value();
437 if (partition->GetPath(&partitionPath) == B_OK
438 && partitionPath == path) {
439 return partition;
440 }
441 }
442
443 return NULL;
444 }
445
446
447 KPartition*
FindPartition(partition_id id)448 KDiskDeviceManager::FindPartition(partition_id id)
449 {
450 PartitionMap::Iterator iterator = fPartitions->Find(id);
451 if (iterator != fPartitions->End())
452 return iterator->Value();
453
454 return NULL;
455 }
456
457
458 KFileDiskDevice*
FindFileDevice(const char * filePath)459 KDiskDeviceManager::FindFileDevice(const char* filePath)
460 {
461 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) {
462 KFileDiskDevice* fileDevice = dynamic_cast<KFileDiskDevice*>(device);
463 if (fileDevice && fileDevice->FilePath()
464 && !strcmp(filePath, fileDevice->FilePath())) {
465 return fileDevice;
466 }
467 }
468 return NULL;
469 }
470
471
472 KDiskDevice*
RegisterDevice(const char * path)473 KDiskDeviceManager::RegisterDevice(const char* path)
474 {
475 if (ManagerLocker locker = this) {
476 for (int32 i = 0; i < 2; i++) {
477 if (KDiskDevice* device = FindDevice(path)) {
478 device->Register();
479 return device;
480 }
481
482 // if the device is not known yet, create it and try again
483 const char* leaf = strrchr(path, '/');
484 if (i == 0 && !strncmp(path, "/dev/disk", 9)
485 && !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK)
486 break;
487 }
488 }
489 return NULL;
490 }
491
492
493 KDiskDevice*
RegisterDevice(partition_id id,bool deviceOnly)494 KDiskDeviceManager::RegisterDevice(partition_id id, bool deviceOnly)
495 {
496 if (ManagerLocker locker = this) {
497 if (KDiskDevice* device = FindDevice(id, deviceOnly)) {
498 device->Register();
499 return device;
500 }
501 }
502 return NULL;
503 }
504
505
506 KDiskDevice*
RegisterNextDevice(int32 * cookie)507 KDiskDeviceManager::RegisterNextDevice(int32* cookie)
508 {
509 if (!cookie)
510 return NULL;
511
512 if (ManagerLocker locker = this) {
513 if (KDiskDevice* device = NextDevice(cookie)) {
514 device->Register();
515 return device;
516 }
517 }
518 return NULL;
519 }
520
521
522 KPartition*
RegisterPartition(const char * path)523 KDiskDeviceManager::RegisterPartition(const char* path)
524 {
525 if (ManagerLocker locker = this) {
526 for (int32 i = 0; i < 2; i++) {
527 if (KPartition* partition = FindPartition(path)) {
528 partition->Register();
529 return partition;
530 }
531
532 // if the device is not known yet, create it and try again
533 const char* leaf = strrchr(path, '/');
534 if (i == 0 && !strncmp(path, "/dev/disk", 9)
535 && !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK)
536 break;
537 }
538 }
539 return NULL;
540 }
541
542
543 KPartition*
RegisterPartition(partition_id id)544 KDiskDeviceManager::RegisterPartition(partition_id id)
545 {
546 if (ManagerLocker locker = this) {
547 if (KPartition* partition = FindPartition(id)) {
548 partition->Register();
549 return partition;
550 }
551 }
552 return NULL;
553 }
554
555
556 KFileDiskDevice*
RegisterFileDevice(const char * filePath)557 KDiskDeviceManager::RegisterFileDevice(const char* filePath)
558 {
559 if (ManagerLocker locker = this) {
560 if (KFileDiskDevice* device = FindFileDevice(filePath)) {
561 device->Register();
562 return device;
563 }
564 }
565 return NULL;
566 }
567
568
569 KDiskDevice*
ReadLockDevice(partition_id id,bool deviceOnly)570 KDiskDeviceManager::ReadLockDevice(partition_id id, bool deviceOnly)
571 {
572 // register device
573 KDiskDevice* device = RegisterDevice(id, deviceOnly);
574 if (!device)
575 return NULL;
576 // lock device
577 if (device->ReadLock())
578 return device;
579 device->Unregister();
580 return NULL;
581 }
582
583
584 KDiskDevice*
WriteLockDevice(partition_id id,bool deviceOnly)585 KDiskDeviceManager::WriteLockDevice(partition_id id, bool deviceOnly)
586 {
587 // register device
588 KDiskDevice* device = RegisterDevice(id, deviceOnly);
589 if (!device)
590 return NULL;
591 // lock device
592 if (device->WriteLock())
593 return device;
594 device->Unregister();
595 return NULL;
596 }
597
598
599 KPartition*
ReadLockPartition(partition_id id)600 KDiskDeviceManager::ReadLockPartition(partition_id id)
601 {
602 // register partition
603 KPartition* partition = RegisterPartition(id);
604 if (!partition)
605 return NULL;
606 // get and register the device
607 KDiskDevice* device = NULL;
608 if (ManagerLocker locker = this) {
609 device = partition->Device();
610 if (device)
611 device->Register();
612 }
613 // lock the device
614 if (device && device->ReadLock()) {
615 // final check, if the partition still belongs to the device
616 if (partition->Device() == device)
617 return partition;
618 device->ReadUnlock();
619 }
620 // cleanup on failure
621 if (device)
622 device->Unregister();
623 partition->Unregister();
624 return NULL;
625 }
626
627
628 KPartition*
WriteLockPartition(partition_id id)629 KDiskDeviceManager::WriteLockPartition(partition_id id)
630 {
631 // register partition
632 KPartition* partition = RegisterPartition(id);
633 if (!partition)
634 return NULL;
635 // get and register the device
636 KDiskDevice* device = NULL;
637 if (ManagerLocker locker = this) {
638 device = partition->Device();
639 if (device)
640 device->Register();
641 }
642 // lock the device
643 if (device && device->WriteLock()) {
644 // final check, if the partition still belongs to the device
645 if (partition->Device() == device)
646 return partition;
647 device->WriteUnlock();
648 }
649 // cleanup on failure
650 if (device)
651 device->Unregister();
652 partition->Unregister();
653 return NULL;
654 }
655
656
657 status_t
ScanPartition(KPartition * partition)658 KDiskDeviceManager::ScanPartition(KPartition* partition)
659 {
660 // TODO: This won't do. Locking the DDM while scanning the partition is not a
661 // good idea. Even locking the device doesn't feel right. Marking the partition
662 // busy and passing the disk system a temporary clone of the partition_data
663 // should work as well.
664 if (DeviceWriteLocker deviceLocker = partition->Device()) {
665 if (ManagerLocker locker = this)
666 return _ScanPartition(partition, false);
667 }
668
669 return B_ERROR;
670 }
671
672
673 partition_id
CreateDevice(const char * path,bool * newlyCreated)674 KDiskDeviceManager::CreateDevice(const char* path, bool* newlyCreated)
675 {
676 if (!path)
677 return B_BAD_VALUE;
678
679 status_t error = B_ERROR;
680 if (ManagerLocker locker = this) {
681 KDiskDevice* device = FindDevice(path);
682 if (device != NULL) {
683 // we already know this device
684 if (newlyCreated)
685 *newlyCreated = false;
686
687 return device->ID();
688 }
689
690 // create a KDiskDevice for it
691 device = new(nothrow) KDiskDevice;
692 if (!device)
693 return B_NO_MEMORY;
694
695 // initialize and add the device
696 error = device->SetTo(path);
697
698 // Note: Here we are allowed to lock a device although already having
699 // the manager locked, since it is not yet added to the manager.
700 DeviceWriteLocker deviceLocker(device);
701 if (error == B_OK && !deviceLocker.IsLocked())
702 error = B_ERROR;
703 if (error == B_OK && !_AddDevice(device))
704 error = B_NO_MEMORY;
705
706 // cleanup on error
707 if (error != B_OK) {
708 deviceLocker.Unlock();
709 delete device;
710 return error;
711 }
712
713 // scan for partitions
714 _ScanPartition(device, false);
715 device->UnmarkBusy(true);
716
717 _NotifyDeviceEvent(device, B_DEVICE_ADDED,
718 B_DEVICE_REQUEST_DEVICE_LIST);
719
720 if (newlyCreated)
721 *newlyCreated = true;
722
723 return device->ID();
724 }
725
726 return error;
727 }
728
729
730 status_t
DeleteDevice(const char * path)731 KDiskDeviceManager::DeleteDevice(const char* path)
732 {
733 KDiskDevice* device = FindDevice(path);
734 if (device == NULL)
735 return B_ENTRY_NOT_FOUND;
736
737 PartitionRegistrar _(device, false);
738 if (DeviceWriteLocker locker = device) {
739 if (_RemoveDevice(device))
740 return B_OK;
741 }
742
743 return B_ERROR;
744 }
745
746
747 partition_id
CreateFileDevice(const char * filePath,bool * newlyCreated)748 KDiskDeviceManager::CreateFileDevice(const char* filePath, bool* newlyCreated)
749 {
750 if (!filePath)
751 return B_BAD_VALUE;
752
753 // normalize the file path
754 KPath normalizedFilePath;
755 status_t error = normalizedFilePath.SetTo(filePath, KPath::NORMALIZE);
756 if (error != B_OK)
757 return error;
758 filePath = normalizedFilePath.Path();
759
760 KFileDiskDevice* device = NULL;
761 if (ManagerLocker locker = this) {
762 // check, if the device does already exist
763 if ((device = FindFileDevice(filePath))) {
764 if (newlyCreated)
765 *newlyCreated = false;
766
767 return device->ID();
768 }
769
770 // allocate a KFileDiskDevice
771 device = new(nothrow) KFileDiskDevice;
772 if (!device)
773 return B_NO_MEMORY;
774
775 // initialize and add the device
776 error = device->SetTo(filePath);
777
778 // Note: Here we are allowed to lock a device although already having
779 // the manager locked, since it is not yet added to the manager.
780 DeviceWriteLocker deviceLocker(device);
781 if (error == B_OK && !deviceLocker.IsLocked())
782 error = B_ERROR;
783 if (error == B_OK && !_AddDevice(device))
784 error = B_NO_MEMORY;
785
786 // scan device
787 if (error == B_OK) {
788 _ScanPartition(device, false);
789 device->UnmarkBusy(true);
790
791 _NotifyDeviceEvent(device, B_DEVICE_ADDED,
792 B_DEVICE_REQUEST_DEVICE_LIST);
793
794 if (newlyCreated)
795 *newlyCreated = true;
796
797 return device->ID();
798 }
799
800 // cleanup on failure
801 deviceLocker.Unlock();
802 delete device;
803 } else
804 error = B_ERROR;
805 return error;
806 }
807
808
809 status_t
DeleteFileDevice(const char * filePath)810 KDiskDeviceManager::DeleteFileDevice(const char* filePath)
811 {
812 if (KFileDiskDevice* device = RegisterFileDevice(filePath)) {
813 PartitionRegistrar _(device, true);
814 if (DeviceWriteLocker locker = device) {
815 if (_RemoveDevice(device))
816 return B_OK;
817 }
818 }
819 return B_ERROR;
820 }
821
822
823 status_t
DeleteFileDevice(partition_id id)824 KDiskDeviceManager::DeleteFileDevice(partition_id id)
825 {
826 if (KDiskDevice* device = RegisterDevice(id)) {
827 PartitionRegistrar _(device, true);
828 if (!dynamic_cast<KFileDiskDevice*>(device) || id != device->ID())
829 return B_ENTRY_NOT_FOUND;
830 if (DeviceWriteLocker locker = device) {
831 if (_RemoveDevice(device))
832 return B_OK;
833 }
834 }
835 return B_ERROR;
836 }
837
838
839 int32
CountDevices()840 KDiskDeviceManager::CountDevices()
841 {
842 return fDevices->Count();
843 }
844
845
846 KDiskDevice*
NextDevice(int32 * cookie)847 KDiskDeviceManager::NextDevice(int32* cookie)
848 {
849 if (!cookie)
850 return NULL;
851
852 DeviceMap::Iterator it = fDevices->FindClose(*cookie, false);
853 if (it != fDevices->End()) {
854 KDiskDevice* device = it->Value();
855 *cookie = device->ID() + 1;
856 return device;
857 }
858 return NULL;
859 }
860
861
862 bool
PartitionAdded(KPartition * partition)863 KDiskDeviceManager::PartitionAdded(KPartition* partition)
864 {
865 return partition && fPartitions->Put(partition->ID(), partition) == B_OK;
866 }
867
868
869 bool
PartitionRemoved(KPartition * partition)870 KDiskDeviceManager::PartitionRemoved(KPartition* partition)
871 {
872 if (partition && partition->PrepareForRemoval()
873 && fPartitions->Remove(partition->ID())) {
874 // TODO: If adding the partition to the obsolete list fails (due to lack
875 // of memory), we can't do anything about it. We will leak memory then.
876 fObsoletePartitions->Insert(partition);
877 partition->MarkObsolete();
878 return true;
879 }
880 return false;
881 }
882
883
884 bool
DeletePartition(KPartition * partition)885 KDiskDeviceManager::DeletePartition(KPartition* partition)
886 {
887 if (partition && partition->IsObsolete()
888 && partition->CountReferences() == 0
889 && partition->PrepareForDeletion()
890 && fObsoletePartitions->Remove(partition)) {
891 delete partition;
892 return true;
893 }
894 return false;
895 }
896
897
898 KDiskSystem*
FindDiskSystem(const char * name,bool byPrettyName)899 KDiskDeviceManager::FindDiskSystem(const char* name, bool byPrettyName)
900 {
901 for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) {
902 if (byPrettyName) {
903 if (strcmp(name, diskSystem->PrettyName()) == 0)
904 return diskSystem;
905 } else {
906 if (strcmp(name, diskSystem->Name()) == 0)
907 return diskSystem;
908 }
909 }
910 return NULL;
911 }
912
913
914 KDiskSystem*
FindDiskSystem(disk_system_id id)915 KDiskDeviceManager::FindDiskSystem(disk_system_id id)
916 {
917 DiskSystemMap::Iterator it = fDiskSystems->Find(id);
918 if (it != fDiskSystems->End())
919 return it->Value();
920 return NULL;
921 }
922
923
924 int32
CountDiskSystems()925 KDiskDeviceManager::CountDiskSystems()
926 {
927 return fDiskSystems->Count();
928 }
929
930
931 KDiskSystem*
NextDiskSystem(int32 * cookie)932 KDiskDeviceManager::NextDiskSystem(int32* cookie)
933 {
934 if (!cookie)
935 return NULL;
936
937 DiskSystemMap::Iterator it = fDiskSystems->FindClose(*cookie, false);
938 if (it != fDiskSystems->End()) {
939 KDiskSystem* diskSystem = it->Value();
940 *cookie = diskSystem->ID() + 1;
941 return diskSystem;
942 }
943 return NULL;
944 }
945
946
947 KDiskSystem*
LoadDiskSystem(const char * name,bool byPrettyName)948 KDiskDeviceManager::LoadDiskSystem(const char* name, bool byPrettyName)
949 {
950 KDiskSystem* diskSystem = NULL;
951 if (ManagerLocker locker = this) {
952 diskSystem = FindDiskSystem(name, byPrettyName);
953 if (diskSystem && diskSystem->Load() != B_OK)
954 diskSystem = NULL;
955 }
956 return diskSystem;
957 }
958
959
960 KDiskSystem*
LoadDiskSystem(disk_system_id id)961 KDiskDeviceManager::LoadDiskSystem(disk_system_id id)
962 {
963 KDiskSystem* diskSystem = NULL;
964 if (ManagerLocker locker = this) {
965 diskSystem = FindDiskSystem(id);
966 if (diskSystem && diskSystem->Load() != B_OK)
967 diskSystem = NULL;
968 }
969 return diskSystem;
970 }
971
972
973 KDiskSystem*
LoadNextDiskSystem(int32 * cookie)974 KDiskDeviceManager::LoadNextDiskSystem(int32* cookie)
975 {
976 if (!cookie)
977 return NULL;
978
979 if (ManagerLocker locker = this) {
980 if (KDiskSystem* diskSystem = NextDiskSystem(cookie)) {
981 if (diskSystem->Load() == B_OK) {
982 *cookie = diskSystem->ID() + 1;
983 return diskSystem;
984 }
985 }
986 }
987 return NULL;
988 }
989
990
991 status_t
InitialDeviceScan()992 KDiskDeviceManager::InitialDeviceScan()
993 {
994 // scan for devices
995 if (ManagerLocker locker = this) {
996 status_t error = _Scan("/dev/disk");
997 if (error != B_OK)
998 return error;
999 }
1000
1001 // scan the devices for partitions
1002 int32 cookie = 0;
1003 status_t status = B_OK;
1004 while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1005 PartitionRegistrar _(device, true);
1006 if (DeviceWriteLocker deviceLocker = device) {
1007 if (ManagerLocker locker = this) {
1008 status_t error = _ScanPartition(device, false);
1009 device->UnmarkBusy(true);
1010 if (error != B_OK)
1011 status = error;
1012 // Even if we could not scan this partition, we want to try
1013 // and scan the rest. Just because one partition is invalid
1014 // or unscannable does not mean the ones after it are.
1015 } else
1016 return B_ERROR;
1017 } else
1018 return B_ERROR;
1019 }
1020 return status;
1021 }
1022
1023
1024 status_t
StartMonitoring()1025 KDiskDeviceManager::StartMonitoring()
1026 {
1027 // do another scan, this will populate the devfs directories
1028 InitialDeviceScan();
1029
1030 // start monitoring the disk systems
1031 fDiskSystemWatcher = new(std::nothrow) DiskSystemWatcher(this);
1032 if (fDiskSystemWatcher != NULL) {
1033 start_watching_modules(kFileSystemPrefix, *fDiskSystemWatcher);
1034 start_watching_modules(kPartitioningSystemPrefix,
1035 *fDiskSystemWatcher);
1036 }
1037
1038 // start monitoring all dirs under /dev/disk
1039 return _AddRemoveMonitoring("/dev/disk", true);
1040 }
1041
1042
1043 status_t
_RescanDiskSystems(DiskSystemMap & addedSystems,bool fileSystems)1044 KDiskDeviceManager::_RescanDiskSystems(DiskSystemMap& addedSystems,
1045 bool fileSystems)
1046 {
1047 void* cookie = open_module_list(fileSystems
1048 ? kFileSystemPrefix : kPartitioningSystemPrefix);
1049 if (cookie == NULL)
1050 return B_NO_MEMORY;
1051
1052 while (true) {
1053 KPath name;
1054 if (name.InitCheck() != B_OK)
1055 break;
1056 size_t nameLength = name.BufferSize();
1057 if (read_next_module_name(cookie, name.LockBuffer(),
1058 &nameLength) != B_OK) {
1059 break;
1060 }
1061 name.UnlockBuffer();
1062
1063 if (FindDiskSystem(name.Path()))
1064 continue;
1065
1066 if (fileSystems) {
1067 TRACE("file system: %s\n", name.Path());
1068 _AddFileSystem(name.Path());
1069 } else {
1070 TRACE("partitioning system: %s\n", name.Path());
1071 _AddPartitioningSystem(name.Path());
1072 }
1073
1074 if (KDiskSystem* system = FindDiskSystem(name.Path()))
1075 addedSystems.Put(system->ID(), system);
1076 }
1077
1078 close_module_list(cookie);
1079 return B_OK;
1080 }
1081
1082
1083 /*! Rescan the existing disk systems. This is called after the boot device
1084 has become available.
1085 */
1086 status_t
RescanDiskSystems()1087 KDiskDeviceManager::RescanDiskSystems()
1088 {
1089 DiskSystemMap addedSystems;
1090
1091 Lock();
1092
1093 // rescan for partitioning and file systems
1094 _RescanDiskSystems(addedSystems, false);
1095 _RescanDiskSystems(addedSystems, true);
1096
1097 Unlock();
1098
1099 // rescan existing devices with the new disk systems
1100 int32 cookie = 0;
1101 status_t status = B_OK;
1102 while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1103 PartitionRegistrar _(device, true);
1104 if (DeviceWriteLocker deviceLocker = device) {
1105 if (ManagerLocker locker = this) {
1106 status_t error = _ScanPartition(device, false, &addedSystems);
1107 device->UnmarkBusy(true);
1108 if (error != B_OK)
1109 status = error;
1110 // See comment in InitialDeviceScan().
1111 } else
1112 return B_ERROR;
1113 } else
1114 return B_ERROR;
1115 }
1116
1117 return status;
1118 }
1119
1120
1121 status_t
_AddPartitioningSystem(const char * name)1122 KDiskDeviceManager::_AddPartitioningSystem(const char* name)
1123 {
1124 if (!name)
1125 return B_BAD_VALUE;
1126
1127 KDiskSystem* diskSystem = new(nothrow) KPartitioningSystem(name);
1128 if (!diskSystem)
1129 return B_NO_MEMORY;
1130 return _AddDiskSystem(diskSystem);
1131 }
1132
1133
1134 status_t
_AddFileSystem(const char * name)1135 KDiskDeviceManager::_AddFileSystem(const char* name)
1136 {
1137 if (!name)
1138 return B_BAD_VALUE;
1139
1140 KDiskSystem* diskSystem = new(nothrow) KFileSystem(name);
1141 if (!diskSystem)
1142 return B_NO_MEMORY;
1143
1144 return _AddDiskSystem(diskSystem);
1145 }
1146
1147
1148 status_t
_AddDiskSystem(KDiskSystem * diskSystem)1149 KDiskDeviceManager::_AddDiskSystem(KDiskSystem* diskSystem)
1150 {
1151 if (!diskSystem)
1152 return B_BAD_VALUE;
1153 TRACE("KDiskDeviceManager::_AddDiskSystem(%s)\n", diskSystem->Name());
1154 status_t error = diskSystem->Init();
1155 if (error != B_OK) {
1156 TRACE(" initialization failed: %s\n", strerror(error));
1157 }
1158 if (error == B_OK)
1159 error = fDiskSystems->Put(diskSystem->ID(), diskSystem);
1160 if (error != B_OK)
1161 delete diskSystem;
1162 TRACE("KDiskDeviceManager::_AddDiskSystem() done: %s\n", strerror(error));
1163 return error;
1164 }
1165
1166
1167 bool
_AddDevice(KDiskDevice * device)1168 KDiskDeviceManager::_AddDevice(KDiskDevice* device)
1169 {
1170 if (!device || !PartitionAdded(device))
1171 return false;
1172 if (fDevices->Put(device->ID(), device) == B_OK)
1173 return true;
1174 PartitionRemoved(device);
1175 return false;
1176 }
1177
1178
1179 bool
_RemoveDevice(KDiskDevice * device)1180 KDiskDeviceManager::_RemoveDevice(KDiskDevice* device)
1181 {
1182 if (device != NULL && fDevices->Remove(device->ID())
1183 && PartitionRemoved(device)) {
1184 _NotifyDeviceEvent(device, B_DEVICE_REMOVED,
1185 B_DEVICE_REQUEST_DEVICE_LIST);
1186 return true;
1187 }
1188
1189 return false;
1190 }
1191
1192
1193 #if 0
1194 /*!
1195 The device must be write locked, the manager must be locked.
1196 */
1197 status_t
1198 KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice *device)
1199 {
1200 if (!device)
1201 return B_BAD_VALUE;
1202 // mark all partitions un-busy
1203 struct UnmarkBusyVisitor : KPartitionVisitor {
1204 virtual bool VisitPre(KPartition *partition)
1205 {
1206 partition->ClearFlags(B_PARTITION_BUSY
1207 | B_PARTITION_DESCENDANT_BUSY);
1208 return false;
1209 }
1210 } visitor;
1211 device->VisitEachDescendant(&visitor);
1212 // Iterate through all job queues and all jobs scheduled or in
1213 // progress and mark their scope busy.
1214 for (int32 cookie = 0;
1215 KDiskDeviceJobQueue *jobQueue = NextJobQueue(&cookie); ) {
1216 if (jobQueue->Device() != device)
1217 continue;
1218 for (int32 i = jobQueue->ActiveJobIndex();
1219 KDiskDeviceJob *job = jobQueue->JobAt(i); i++) {
1220 if (job->Status() != B_DISK_DEVICE_JOB_IN_PROGRESS
1221 && job->Status() != B_DISK_DEVICE_JOB_SCHEDULED) {
1222 continue;
1223 }
1224 KPartition *partition = FindPartition(job->ScopeID());
1225 if (!partition || partition->Device() != device)
1226 continue;
1227 partition->AddFlags(B_PARTITION_BUSY);
1228 }
1229 }
1230 // mark all anscestors of busy partitions descendant busy and all
1231 // descendants busy
1232 struct MarkBusyVisitor : KPartitionVisitor {
1233 virtual bool VisitPre(KPartition *partition)
1234 {
1235 // parent busy => child busy
1236 if (partition->Parent() && partition->Parent()->IsBusy())
1237 partition->AddFlags(B_PARTITION_BUSY);
1238 return false;
1239 }
1240
1241 virtual bool VisitPost(KPartition *partition)
1242 {
1243 // child [descendant] busy => parent descendant busy
1244 if ((partition->IsBusy() || partition->IsDescendantBusy())
1245 && partition->Parent()) {
1246 partition->Parent()->AddFlags(B_PARTITION_DESCENDANT_BUSY);
1247 }
1248 return false;
1249 }
1250 } visitor2;
1251 device->VisitEachDescendant(&visitor2);
1252 return B_OK;
1253 }
1254 #endif
1255
1256
1257 status_t
_Scan(const char * path)1258 KDiskDeviceManager::_Scan(const char* path)
1259 {
1260 TRACE("KDiskDeviceManager::_Scan(%s)\n", path);
1261 status_t error = B_ENTRY_NOT_FOUND;
1262 struct stat st;
1263 if (lstat(path, &st) < 0) {
1264 return errno;
1265 }
1266 if (S_ISDIR(st.st_mode)) {
1267 // a directory: iterate through its contents
1268 DIR* dir = opendir(path);
1269 if (!dir)
1270 return errno;
1271 while (dirent* entry = readdir(dir)) {
1272 // skip "." and ".."
1273 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
1274 continue;
1275 KPath entryPath;
1276 if (entryPath.SetPath(path) != B_OK
1277 || entryPath.Append(entry->d_name) != B_OK) {
1278 continue;
1279 }
1280 if (_Scan(entryPath.Path()) == B_OK)
1281 error = B_OK;
1282 }
1283 closedir(dir);
1284 } else {
1285 // not a directory
1286 // check, if it is named "raw"
1287 int32 len = strlen(path);
1288 int32 leafLen = strlen("/raw");
1289 if (len <= leafLen || strcmp(path + len - leafLen, "/raw"))
1290 return B_ERROR;
1291 if (FindDevice(path) != NULL) {
1292 // we already know this device
1293 return B_OK;
1294 }
1295
1296 TRACE(" found device: %s\n", path);
1297 // create a KDiskDevice for it
1298 KDiskDevice* device = new(nothrow) KDiskDevice;
1299 if (!device)
1300 return B_NO_MEMORY;
1301
1302 // init the KDiskDevice
1303 error = device->SetTo(path);
1304 // add the device
1305 if (error == B_OK && !_AddDevice(device))
1306 error = B_NO_MEMORY;
1307 // cleanup on error
1308 if (error != B_OK)
1309 delete device;
1310 }
1311 return error;
1312 }
1313
1314
1315 /*!
1316 The device must be write locked, the manager must be locked.
1317 */
1318 status_t
_ScanPartition(KPartition * partition,bool async,DiskSystemMap * restrictScan)1319 KDiskDeviceManager::_ScanPartition(KPartition* partition, bool async,
1320 DiskSystemMap* restrictScan)
1321 {
1322 // TODO: There's no reason why the manager needs to be locked anymore.
1323 if (!partition)
1324 return B_BAD_VALUE;
1325
1326 // TODO: Reimplement asynchronous scanning, if we really need it.
1327 #if 0
1328 if (async) {
1329 // create a new job queue for the device
1330 KDiskDeviceJobQueue *jobQueue = new(nothrow) KDiskDeviceJobQueue;
1331 if (!jobQueue)
1332 return B_NO_MEMORY;
1333 jobQueue->SetDevice(partition->Device());
1334
1335 // create a job for scanning the device and add it to the job queue
1336 KDiskDeviceJob *job = fJobFactory->CreateScanPartitionJob(partition->ID());
1337 if (!job) {
1338 delete jobQueue;
1339 return B_NO_MEMORY;
1340 }
1341
1342 if (!jobQueue->AddJob(job)) {
1343 delete jobQueue;
1344 delete job;
1345 return B_NO_MEMORY;
1346 }
1347
1348 // add the job queue
1349 status_t error = AddJobQueue(jobQueue);
1350 if (error != B_OK)
1351 delete jobQueue;
1352
1353 return error;
1354 }
1355 #endif
1356
1357 // scan synchronously
1358
1359 return _ScanPartition(partition, restrictScan);
1360 }
1361
1362
1363 status_t
_ScanPartition(KPartition * partition,DiskSystemMap * restrictScan)1364 KDiskDeviceManager::_ScanPartition(KPartition* partition,
1365 DiskSystemMap* restrictScan)
1366 {
1367 // the partition's device must be write-locked
1368 if (partition == NULL)
1369 return B_BAD_VALUE;
1370 if (!partition->Device()->HasMedia() || partition->IsMounted())
1371 return B_OK;
1372
1373 if (partition->CountChildren() > 0) {
1374 // Since this partition has already children, we don't scan it
1375 // again, but only its children.
1376 for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++) {
1377 _ScanPartition(child, restrictScan);
1378 }
1379 return B_OK;
1380 }
1381
1382 KPath partitionPath;
1383 partition->GetPath(&partitionPath);
1384
1385 // This happens with some copy protected CDs or eventually other issues.
1386 // Just ignore the partition...
1387 if (partition->Offset() < 0 || partition->BlockSize() == 0
1388 || partition->Size() <= 0) {
1389 TRACE_ALWAYS("Partition %s has invalid parameters, ignoring it.\n",
1390 partitionPath.Path());
1391 return B_BAD_DATA;
1392 }
1393
1394 TRACE("KDiskDeviceManager::_ScanPartition(%s)\n", partitionPath.Path());
1395
1396 // publish the partition
1397 status_t error = B_OK;
1398 if (!partition->IsPublished()) {
1399 error = partition->PublishDevice();
1400 if (error != B_OK)
1401 return error;
1402 }
1403
1404 DiskSystemMap* diskSystems = restrictScan;
1405 if (diskSystems == NULL)
1406 diskSystems = fDiskSystems;
1407
1408 // find the disk system that returns the best priority for this partition
1409 float bestPriority = partition->DiskSystemPriority();
1410 KDiskSystem* bestDiskSystem = NULL;
1411 void* bestCookie = NULL;
1412 for (DiskSystemMap::Iterator iterator = diskSystems->Begin();
1413 iterator != diskSystems->End(); iterator++) {
1414 KDiskSystem* diskSystem = iterator->Value();
1415 if (diskSystem->Load() != B_OK)
1416 continue;
1417
1418 TRACE(" trying: %s\n", diskSystem->Name());
1419
1420 void* cookie = NULL;
1421 float priority = diskSystem->Identify(partition, &cookie);
1422
1423 TRACE(" returned: %g\n", priority);
1424
1425 if (priority >= 0 && priority > bestPriority) {
1426 // new best disk system
1427 if (bestDiskSystem != NULL) {
1428 bestDiskSystem->FreeIdentifyCookie(partition, bestCookie);
1429 bestDiskSystem->Unload();
1430 }
1431 bestPriority = priority;
1432 bestDiskSystem = diskSystem;
1433 bestCookie = cookie;
1434 } else {
1435 // disk system doesn't identify the partition or worse than our
1436 // current favorite
1437 if (priority >= 0)
1438 diskSystem->FreeIdentifyCookie(partition, cookie);
1439 diskSystem->Unload();
1440 }
1441 }
1442
1443 // now, if we have found a disk system, let it scan the partition
1444 if (bestDiskSystem != NULL) {
1445 TRACE(" scanning with: %s\n", bestDiskSystem->Name());
1446 error = bestDiskSystem->Scan(partition, bestCookie);
1447 bestDiskSystem->FreeIdentifyCookie(partition, bestCookie);
1448 if (error == B_OK) {
1449 partition->SetDiskSystem(bestDiskSystem, bestPriority);
1450 for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++)
1451 _ScanPartition(child, restrictScan);
1452 } else {
1453 // TODO: Handle the error.
1454 TRACE_ERROR("scanning failed: %s\n", strerror(error));
1455 }
1456
1457 // now we can safely unload the disk system -- it has been loaded by
1458 // the partition(s) and thus will not really be unloaded
1459 bestDiskSystem->Unload();
1460 } else {
1461 // contents not recognized
1462 // nothing to be done -- partitions are created as unrecognized
1463 }
1464
1465 return error;
1466 }
1467
1468
1469 status_t
_AddRemoveMonitoring(const char * path,bool add)1470 KDiskDeviceManager::_AddRemoveMonitoring(const char* path, bool add)
1471 {
1472 struct stat st;
1473 if (lstat(path, &st) < 0)
1474 return errno;
1475
1476 status_t error = B_ENTRY_NOT_FOUND;
1477 if (S_ISDIR(st.st_mode)) {
1478 if (add) {
1479 error = add_node_listener(st.st_dev, st.st_ino, B_WATCH_DIRECTORY,
1480 *fDeviceWatcher);
1481 } else {
1482 error = remove_node_listener(st.st_dev, st.st_ino,
1483 *fDeviceWatcher);
1484 }
1485 if (error != B_OK)
1486 return error;
1487
1488 DIR* dir = opendir(path);
1489 if (!dir)
1490 return errno;
1491
1492 while (dirent* entry = readdir(dir)) {
1493 // skip "." and ".."
1494 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
1495 continue;
1496
1497 KPath entryPath;
1498 if (entryPath.SetPath(path) != B_OK
1499 || entryPath.Append(entry->d_name) != B_OK) {
1500 continue;
1501 }
1502
1503 if (_AddRemoveMonitoring(entryPath.Path(), add) == B_OK)
1504 error = B_OK;
1505 }
1506 closedir(dir);
1507 }
1508
1509 return error;
1510 }
1511
1512
1513 status_t
_CheckMediaStatus()1514 KDiskDeviceManager::_CheckMediaStatus()
1515 {
1516 while (!fTerminating) {
1517 int32 cookie = 0;
1518 while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1519 PartitionRegistrar _(device, true);
1520 DeviceWriteLocker locker(device);
1521
1522 if (device->IsBusy(true))
1523 continue;
1524
1525 bool hadMedia = device->HasMedia();
1526 bool changedMedia = device->MediaChanged();
1527 device->UpdateMediaStatusIfNeeded();
1528
1529 // Detect if there was any status change since last check.
1530 if ((!device->MediaChanged() && (device->HasMedia() || !hadMedia))
1531 || !(hadMedia != device->HasMedia()
1532 || changedMedia != device->MediaChanged()))
1533 continue;
1534
1535 device->MarkBusy(true);
1536 device->UninitializeMedia();
1537
1538 if (device->MediaChanged()) {
1539 dprintf("Media changed from %s\n", device->Path());
1540 device->UpdateGeometry();
1541 _ScanPartition(device, false);
1542 _NotifyDeviceEvent(device, B_DEVICE_MEDIA_CHANGED,
1543 B_DEVICE_REQUEST_DEVICE);
1544 } else if (!device->HasMedia() && hadMedia) {
1545 dprintf("Media removed from %s\n", device->Path());
1546 }
1547
1548 device->UnmarkBusy(true);
1549 }
1550
1551 snooze(1000000);
1552 }
1553
1554 return 0;
1555 }
1556
1557
1558 status_t
_CheckMediaStatusDaemon(void * self)1559 KDiskDeviceManager::_CheckMediaStatusDaemon(void* self)
1560 {
1561 return ((KDiskDeviceManager*)self)->_CheckMediaStatus();
1562 }
1563
1564
1565 void
_NotifyDeviceEvent(KDiskDevice * device,int32 event,uint32 mask)1566 KDiskDeviceManager::_NotifyDeviceEvent(KDiskDevice* device, int32 event,
1567 uint32 mask)
1568 {
1569 char messageBuffer[512];
1570 KMessage message;
1571 message.SetTo(messageBuffer, sizeof(messageBuffer), B_DEVICE_UPDATE);
1572 message.AddInt32("event", event);
1573 message.AddInt32("id", device->ID());
1574 message.AddString("device", device->Path());
1575
1576 fNotifications->Notify(message, mask);
1577 }
1578
1579