1 /* 2 * Copyright 2003-2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold, bonefish@cs.tu-berlin.de 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 #include <DiskDeviceRoster.h> 11 12 #include <new> 13 14 #include <Directory.h> 15 #include <DiskDevice.h> 16 #include <DiskDevicePrivate.h> 17 #include <DiskSystem.h> 18 #include <Entry.h> 19 #include <FindDirectory.h> 20 #include <Message.h> 21 #include <Partition.h> 22 #include <Path.h> 23 #include <Volume.h> 24 25 #include <MessengerPrivate.h> 26 27 #include <syscalls.h> 28 #include <ddm_userland_interface_defs.h> 29 30 31 /*! \class BDiskDeviceRoster 32 \brief An interface for iterating through the disk devices known to the 33 system and for a notification mechanism provided to listen to their 34 changes. 35 */ 36 37 /*! \brief find_directory constants of the add-on dirs to be searched. */ 38 static const directory_which kAddOnDirs[] = { 39 B_USER_NONPACKAGED_ADDONS_DIRECTORY, 40 B_USER_ADDONS_DIRECTORY, 41 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY, 42 B_SYSTEM_ADDONS_DIRECTORY, 43 }; 44 /*! \brief Size of the kAddOnDirs array. */ 45 static const int32 kAddOnDirCount 46 = sizeof(kAddOnDirs) / sizeof(directory_which); 47 48 49 /*! \brief Creates a BDiskDeviceRoster object. 50 51 The object is ready to be used after construction. 52 */ 53 BDiskDeviceRoster::BDiskDeviceRoster() 54 : fDeviceCookie(0), 55 fDiskSystemCookie(0), 56 fJobCookie(0) 57 // fPartitionAddOnDir(NULL), 58 // fFSAddOnDir(NULL), 59 // fPartitionAddOnDirIndex(0), 60 // fFSAddOnDirIndex(0) 61 { 62 } 63 64 65 /*! \brief Frees all resources associated with the object. 66 */ 67 BDiskDeviceRoster::~BDiskDeviceRoster() 68 { 69 // if (fPartitionAddOnDir) 70 // delete fPartitionAddOnDir; 71 // if (fFSAddOnDir) 72 // delete fFSAddOnDir; 73 } 74 75 76 /*! \brief Returns the next BDiskDevice. 77 \param device Pointer to a pre-allocated BDiskDevice to be initialized to 78 represent the next device. 79 \return 80 - \c B_OK: Everything went fine. 81 - \c B_ENTRY_NOT_FOUND: The end of the list of devices had already been 82 reached. 83 - another error code 84 */ 85 status_t 86 BDiskDeviceRoster::GetNextDevice(BDiskDevice* device) 87 { 88 if (!device) 89 return B_BAD_VALUE; 90 91 size_t neededSize = 0; 92 partition_id id = _kern_get_next_disk_device_id(&fDeviceCookie, 93 &neededSize); 94 if (id < 0) 95 return id; 96 97 return device->_SetTo(id, true, neededSize); 98 } 99 100 101 /*! \brief Rewinds the device list iterator. 102 \return \c B_OK, if everything went fine, another error code otherwise. 103 */ 104 status_t 105 BDiskDeviceRoster::RewindDevices() 106 { 107 fDeviceCookie = 0; 108 return B_OK; 109 } 110 111 112 status_t 113 BDiskDeviceRoster::GetNextDiskSystem(BDiskSystem* system) 114 { 115 if (!system) 116 return B_BAD_VALUE; 117 user_disk_system_info info; 118 status_t error = _kern_get_next_disk_system_info(&fDiskSystemCookie, 119 &info); 120 if (error == B_OK) 121 error = system->_SetTo(&info); 122 return error; 123 } 124 125 126 status_t 127 BDiskDeviceRoster::RewindDiskSystems() 128 { 129 fDiskSystemCookie = 0; 130 return B_OK; 131 } 132 133 134 status_t 135 BDiskDeviceRoster::GetDiskSystem(BDiskSystem* system, const char* name) 136 { 137 if (!system) 138 return B_BAD_VALUE; 139 140 int32 cookie = 0; 141 user_disk_system_info info; 142 while (_kern_get_next_disk_system_info(&cookie, &info) == B_OK) { 143 if (!strcmp(name, info.name) 144 || !strcmp(name, info.short_name) 145 || !strcmp(name, info.pretty_name)) 146 return system->_SetTo(&info); 147 } 148 149 return B_ENTRY_NOT_FOUND; 150 } 151 152 153 partition_id 154 BDiskDeviceRoster::RegisterFileDevice(const char* filename) 155 { 156 if (!filename) 157 return B_BAD_VALUE; 158 return _kern_register_file_device(filename); 159 } 160 161 162 status_t 163 BDiskDeviceRoster::UnregisterFileDevice(const char* filename) 164 { 165 if (!filename) 166 return B_BAD_VALUE; 167 return _kern_unregister_file_device(-1, filename); 168 } 169 170 171 status_t 172 BDiskDeviceRoster::UnregisterFileDevice(partition_id device) 173 { 174 if (device < 0) 175 return B_BAD_VALUE; 176 return _kern_unregister_file_device(device, NULL); 177 } 178 179 180 /*! \brief Iterates through the all devices. 181 182 The supplied visitor's Visit(BDiskDevice*) is invoked for each device. 183 If Visit() returns \c true, the iteration is terminated and this method 184 returns \c true. If supplied, \a device is set to the concerned device. 185 186 \param visitor The visitor. 187 \param device Pointer to a pre-allocated BDiskDevice to be initialized 188 to the device at which the iteration was terminated. 189 May be \c NULL. 190 \return \c true, if the iteration was terminated, \c false otherwise. 191 */ 192 bool 193 BDiskDeviceRoster::VisitEachDevice(BDiskDeviceVisitor* visitor, 194 BDiskDevice* device) 195 { 196 bool terminatedEarly = false; 197 if (visitor) { 198 int32 oldCookie = fDeviceCookie; 199 fDeviceCookie = 0; 200 BDiskDevice deviceOnStack; 201 BDiskDevice* useDevice = device ? device : &deviceOnStack; 202 while (!terminatedEarly && GetNextDevice(useDevice) == B_OK) 203 terminatedEarly = visitor->Visit(useDevice); 204 fDeviceCookie = oldCookie; 205 if (!terminatedEarly) 206 useDevice->Unset(); 207 } 208 return terminatedEarly; 209 } 210 211 212 /*! \brief Pre-order traverses the trees spanned by the BDiskDevices and their 213 subobjects. 214 215 The supplied visitor's Visit(BDiskDevice*) method is invoked for each 216 disk device and Visit(BPartition*) for each (non-disk device) partition. 217 If Visit() returns \c true, the iteration is terminated and this method 218 returns \c true. If supplied, \a device is set to the concerned device 219 and in \a partition the pointer to the partition object is returned. 220 221 \param visitor The visitor. 222 \param device Pointer to a pre-allocated BDiskDevice to be initialized 223 to the device at which the iteration was terminated. 224 May be \c NULL. 225 \param partition Pointer to a pre-allocated BPartition pointer to be set 226 to the partition at which the iteration was terminated. 227 May be \c NULL. 228 \return \c true, if the iteration was terminated, \c false otherwise. 229 */ 230 bool 231 BDiskDeviceRoster::VisitEachPartition(BDiskDeviceVisitor* visitor, 232 BDiskDevice* device, BPartition** partition) 233 { 234 bool terminatedEarly = false; 235 if (visitor) { 236 int32 oldCookie = fDeviceCookie; 237 fDeviceCookie = 0; 238 BDiskDevice deviceOnStack; 239 BDiskDevice* useDevice = device ? device : &deviceOnStack; 240 BPartition* foundPartition = NULL; 241 while (GetNextDevice(useDevice) == B_OK) { 242 foundPartition = useDevice->VisitEachDescendant(visitor); 243 if (foundPartition) { 244 terminatedEarly = true; 245 break; 246 } 247 } 248 fDeviceCookie = oldCookie; 249 if (!terminatedEarly) 250 useDevice->Unset(); 251 else if (device && partition) 252 *partition = foundPartition; 253 } 254 return terminatedEarly; 255 } 256 257 258 /*! \brief Iterates through the all devices' partitions that are mounted. 259 260 The supplied visitor's Visit(BPartition*) is invoked for each mounted 261 partition. 262 If Visit() returns \c true, the iteration is terminated and this method 263 returns \c true. If supplied, \a device is set to the concerned device 264 and in \a partition the pointer to the partition object is returned. 265 266 \param visitor The visitor. 267 \param device Pointer to a pre-allocated BDiskDevice to be initialized 268 to the device at which the iteration was terminated. 269 May be \c NULL. 270 \param partition Pointer to a pre-allocated BPartition pointer to be set 271 to the partition at which the iteration was terminated. 272 May be \c NULL. 273 \return \c true, if the iteration was terminated, \c false otherwise. 274 */ 275 bool 276 BDiskDeviceRoster::VisitEachMountedPartition(BDiskDeviceVisitor* visitor, 277 BDiskDevice* device, BPartition** partition) 278 { 279 bool terminatedEarly = false; 280 if (visitor) { 281 struct MountedPartitionFilter : public PartitionFilter { 282 virtual bool Filter(BPartition *partition, int32) 283 { return partition->IsMounted(); } 284 } filter; 285 PartitionFilterVisitor filterVisitor(visitor, &filter); 286 terminatedEarly 287 = VisitEachPartition(&filterVisitor, device, partition); 288 } 289 return terminatedEarly; 290 } 291 292 293 /*! \brief Iterates through the all devices' partitions that are mountable. 294 295 The supplied visitor's Visit(BPartition*) is invoked for each mountable 296 partition. 297 If Visit() returns \c true, the iteration is terminated and this method 298 returns \c true. If supplied, \a device is set to the concerned device 299 and in \a partition the pointer to the partition object is returned. 300 301 \param visitor The visitor. 302 \param device Pointer to a pre-allocated BDiskDevice to be initialized 303 to the device at which the iteration was terminated. 304 May be \c NULL. 305 \param partition Pointer to a pre-allocated BPartition pointer to be set 306 to the partition at which the iteration was terminated. 307 May be \c NULL. 308 \return \c true, if the iteration was terminated, \c false otherwise. 309 */ 310 bool 311 BDiskDeviceRoster::VisitEachMountablePartition(BDiskDeviceVisitor* visitor, 312 BDiskDevice* device, BPartition** partition) 313 { 314 bool terminatedEarly = false; 315 if (visitor) { 316 struct MountablePartitionFilter : public PartitionFilter { 317 virtual bool Filter(BPartition *partition, int32) 318 { return partition->ContainsFileSystem(); } 319 } filter; 320 PartitionFilterVisitor filterVisitor(visitor, &filter); 321 terminatedEarly 322 = VisitEachPartition(&filterVisitor, device, partition); 323 } 324 return terminatedEarly; 325 } 326 327 328 /*! \brief Finds a BPartition by BVolume. 329 */ 330 status_t 331 BDiskDeviceRoster::FindPartitionByVolume(const BVolume& volume, 332 BDiskDevice* device, BPartition** _partition) 333 { 334 class FindPartitionVisitor : public BDiskDeviceVisitor { 335 public: 336 FindPartitionVisitor(dev_t volume) 337 : 338 fVolume(volume) 339 { 340 } 341 342 virtual bool Visit(BDiskDevice* device) 343 { 344 return Visit(device, 0); 345 } 346 347 virtual bool Visit(BPartition* partition, int32 level) 348 { 349 BVolume volume; 350 return partition->GetVolume(&volume) == B_OK 351 && volume.Device() == fVolume; 352 } 353 354 private: 355 dev_t fVolume; 356 } visitor(volume.Device()); 357 358 if (VisitEachMountedPartition(&visitor, device, _partition)) 359 return B_OK; 360 361 return B_ENTRY_NOT_FOUND; 362 } 363 364 365 /*! \brief Finds a BPartition by mount path. 366 */ 367 status_t 368 BDiskDeviceRoster::FindPartitionByMountPoint(const char* mountPoint, 369 BDiskDevice* device, BPartition** _partition) 370 { 371 BVolume volume(dev_for_path(mountPoint)); 372 if (volume.InitCheck() == B_OK 373 && FindPartitionByVolume(volume, device, _partition)) 374 return B_OK; 375 376 return B_ENTRY_NOT_FOUND; 377 } 378 379 380 /*! \brief Returns a BDiskDevice for a given ID. 381 382 The supplied \a device is initialized to the device identified by \a id. 383 384 \param id The ID of the device to be retrieved. 385 \param device Pointer to a pre-allocated BDiskDevice to be initialized 386 to the device identified by \a id. 387 \return 388 - \c B_OK: Everything went fine. 389 - \c B_ENTRY_NOT_FOUND: A device with ID \a id could not be found. 390 - other error codes 391 */ 392 status_t 393 BDiskDeviceRoster::GetDeviceWithID(int32 id, BDiskDevice* device) const 394 { 395 if (!device) 396 return B_BAD_VALUE; 397 return device->_SetTo(id, true, 0); 398 } 399 400 401 /*! \brief Returns a BPartition for a given ID. 402 403 The supplied \a device is initialized to the device the partition 404 identified by \a id resides on, and \a partition is set to point to the 405 respective BPartition. 406 407 \param id The ID of the partition to be retrieved. 408 \param device Pointer to a pre-allocated BDiskDevice to be initialized 409 to the device the partition identified by \a id resides on. 410 \param partition Pointer to a pre-allocated BPartition pointer to be set 411 to the partition identified by \a id. 412 \return 413 - \c B_OK: Everything went fine. 414 - \c B_ENTRY_NOT_FOUND: A partition with ID \a id could not be found. 415 - other error codes 416 */ 417 status_t 418 BDiskDeviceRoster::GetPartitionWithID(int32 id, BDiskDevice* device, 419 BPartition** partition) const 420 { 421 if (!device || !partition) 422 return B_BAD_VALUE; 423 424 // retrieve the device data 425 status_t error = device->_SetTo(id, false, 0); 426 if (error != B_OK) 427 return error; 428 429 // find the partition object 430 *partition = device->FindDescendant(id); 431 if (!*partition) // should never happen! 432 return B_ENTRY_NOT_FOUND; 433 434 return B_OK; 435 } 436 437 438 status_t 439 BDiskDeviceRoster::GetDeviceForPath(const char* filename, BDiskDevice* device) 440 { 441 if (!filename || !device) 442 return B_BAD_VALUE; 443 444 // get the device ID 445 size_t neededSize = 0; 446 partition_id id = _kern_find_disk_device(filename, &neededSize); 447 if (id < 0) 448 return id; 449 450 // retrieve the device data 451 return device->_SetTo(id, true, neededSize); 452 } 453 454 455 status_t 456 BDiskDeviceRoster::GetPartitionForPath(const char* filename, 457 BDiskDevice* device, BPartition** partition) 458 { 459 if (!filename || !device || !partition) 460 return B_BAD_VALUE; 461 462 // get the partition ID 463 size_t neededSize = 0; 464 partition_id id = _kern_find_partition(filename, &neededSize); 465 if (id < 0) 466 return id; 467 468 // retrieve the device data 469 status_t error = device->_SetTo(id, false, neededSize); 470 if (error != B_OK) 471 return error; 472 473 // find the partition object 474 *partition = device->FindDescendant(id); 475 if (!*partition) // should never happen! 476 return B_ENTRY_NOT_FOUND; 477 return B_OK; 478 } 479 480 481 status_t 482 BDiskDeviceRoster::GetFileDeviceForPath(const char* filename, 483 BDiskDevice* device) 484 { 485 if (!filename || !device) 486 return B_BAD_VALUE; 487 488 // get the device ID 489 size_t neededSize = 0; 490 partition_id id = _kern_find_file_disk_device(filename, &neededSize); 491 if (id < 0) 492 return id; 493 494 // retrieve the device data 495 return device->_SetTo(id, true, neededSize); 496 } 497 498 499 /*! \brief Adds a target to the list of targets to be notified on disk device 500 events. 501 502 \todo List the event mask flags, the events and describe the layout of the 503 notification message. 504 505 If \a target is already listening to events, this method replaces the 506 former event mask with \a eventMask. 507 508 \param target A BMessenger identifying the target to which the events 509 shall be sent. 510 \param eventMask A mask specifying on which events the target shall be 511 notified. 512 \return \c B_OK, if everything went fine, another error code otherwise. 513 */ 514 status_t 515 BDiskDeviceRoster::StartWatching(BMessenger target, uint32 eventMask) 516 { 517 if (eventMask == 0) 518 return B_BAD_VALUE; 519 520 BMessenger::Private messengerPrivate(target); 521 port_id port = messengerPrivate.Port(); 522 int32 token = messengerPrivate.Token(); 523 524 return _kern_start_watching_disks(eventMask, port, token); 525 } 526 527 528 /*! \brief Remove a target from the list of targets to be notified on disk 529 device events. 530 \param target A BMessenger identifying the target to which notfication 531 message shall not longer be sent. 532 \return \c B_OK, if everything went fine, another error code otherwise. 533 */ 534 status_t 535 BDiskDeviceRoster::StopWatching(BMessenger target) 536 { 537 BMessenger::Private messengerPrivate(target); 538 port_id port = messengerPrivate.Port(); 539 int32 token = messengerPrivate.Token(); 540 541 return _kern_stop_watching_disks(port, token); 542 } 543 544 #if 0 545 546 /*! \brief Returns the next partitioning system capable of partitioning. 547 548 The returned \a shortName can be passed to BSession::Partition(). 549 550 \param shortName Pointer to a pre-allocation char buffer, of size 551 \c B_FILE_NAME_LENGTH or larger into which the short name of the 552 partitioning system shall be written. 553 \param longName Pointer to a pre-allocation char buffer, of size 554 \c B_FILE_NAME_LENGTH or larger into which the long name of the 555 partitioning system shall be written. May be \c NULL. 556 \return 557 - \c B_OK: Everything went fine. 558 - \c B_BAD_VALUE: \c NULL \a shortName. 559 - \c B_ENTRY_NOT_FOUND: End of the list has been reached. 560 - other error codes 561 */ 562 status_t 563 BDiskDeviceRoster::GetNextPartitioningSystem(char *shortName, char *longName) 564 { 565 status_t error = (shortName ? B_OK : B_BAD_VALUE); 566 if (error == B_OK) { 567 // search until an add-on has been found or the end of all directories 568 // has been reached 569 bool found = false; 570 do { 571 // get the next add-on in the current dir 572 AddOnImage image; 573 error = _GetNextAddOn(fPartitionAddOnDir, &image); 574 if (error == B_OK) { 575 // add-on loaded: get the function that creates an add-on 576 // object 577 BDiskScannerPartitionAddOn *(*create_add_on)(); 578 if (get_image_symbol(image.ID(), "create_ds_partition_add_on", 579 B_SYMBOL_TYPE_TEXT, 580 (void**)&create_add_on) == B_OK) { 581 // create the add-on object and copy the requested data 582 if (BDiskScannerPartitionAddOn *addOn 583 = (*create_add_on)()) { 584 const char *addOnShortName = addOn->ShortName(); 585 const char *addOnLongName = addOn->LongName(); 586 if (addOnShortName && addOnLongName) { 587 strcpy(shortName, addOnShortName); 588 if (longName) 589 strcpy(longName, addOnLongName); 590 found = true; 591 } 592 delete addOn; 593 } 594 } 595 } else if (error == B_ENTRY_NOT_FOUND) { 596 // end of the current directory has been reached, try next dir 597 error = _GetNextAddOnDir(&fPartitionAddOnDir, 598 &fPartitionAddOnDirIndex, 599 "partition"); 600 } 601 } while (error == B_OK && !found); 602 } 603 return error; 604 } 605 606 607 /*! \brief Returns the next file system capable of initializing. 608 609 The returned \a shortName can be passed to BPartition::Initialize(). 610 611 \param shortName Pointer to a pre-allocation char buffer, of size 612 \c B_FILE_NAME_LENGTH or larger into which the short name of the 613 file system shall be written. 614 \param longName Pointer to a pre-allocation char buffer, of size 615 \c B_FILE_NAME_LENGTH or larger into which the long name of the 616 file system shall be written. May be \c NULL. 617 \return 618 - \c B_OK: Everything went fine. 619 - \c B_BAD_VALUE: \c NULL \a shortName. 620 - \c B_ENTRY_NOT_FOUND: End of the list has been reached. 621 - other error codes 622 */ 623 status_t 624 BDiskDeviceRoster::GetNextFileSystem(char *shortName, char *longName) 625 { 626 status_t error = (shortName ? B_OK : B_BAD_VALUE); 627 if (error == B_OK) { 628 // search until an add-on has been found or the end of all directories 629 // has been reached 630 bool found = false; 631 do { 632 // get the next add-on in the current dir 633 AddOnImage image; 634 error = _GetNextAddOn(fFSAddOnDir, &image); 635 if (error == B_OK) { 636 // add-on loaded: get the function that creates an add-on 637 // object 638 BDiskScannerFSAddOn *(*create_add_on)(); 639 if (get_image_symbol(image.ID(), "create_ds_fs_add_on", 640 B_SYMBOL_TYPE_TEXT, 641 (void**)&create_add_on) == B_OK) { 642 // create the add-on object and copy the requested data 643 if (BDiskScannerFSAddOn *addOn = (*create_add_on)()) { 644 const char *addOnShortName = addOn->ShortName(); 645 const char *addOnLongName = addOn->LongName(); 646 if (addOnShortName && addOnLongName) { 647 strcpy(shortName, addOnShortName); 648 if (longName) 649 strcpy(longName, addOnLongName); 650 found = true; 651 } 652 delete addOn; 653 } 654 } 655 } else if (error == B_ENTRY_NOT_FOUND) { 656 // end of the current directory has been reached, try next dir 657 error = _GetNextAddOnDir(&fFSAddOnDir, &fFSAddOnDirIndex, 658 "fs"); 659 } 660 } while (error == B_OK && !found); 661 } 662 return error; 663 } 664 665 666 /*! \brief Rewinds the partitioning system list iterator. 667 \return \c B_OK, if everything went fine, another error code otherwise. 668 */ 669 status_t 670 BDiskDeviceRoster::RewindPartitiningSystems() 671 { 672 if (fPartitionAddOnDir) { 673 delete fPartitionAddOnDir; 674 fPartitionAddOnDir = NULL; 675 } 676 fPartitionAddOnDirIndex = 0; 677 return B_OK; 678 } 679 680 681 /*! \brief Rewinds the file system list iterator. 682 \return \c B_OK, if everything went fine, another error code otherwise. 683 */ 684 status_t 685 BDiskDeviceRoster::RewindFileSystems() 686 { 687 if (fFSAddOnDir) { 688 delete fFSAddOnDir; 689 fFSAddOnDir = NULL; 690 } 691 fFSAddOnDirIndex = 0; 692 return B_OK; 693 } 694 695 696 /*! \brief Returns a BDiskDevice for a given device, session or partition ID. 697 698 The supplied \a device is initialized to the device the object identified 699 by \a id belongs to. 700 701 \param fieldName "device_id", "sesison_id" or "partition_id" according to 702 the type of object the device shall be retrieved for. 703 \param id The ID of the device, session or partition to be retrieved. 704 \param device Pointer to a pre-allocated BDiskDevice to be initialized 705 to the device to be retrieved. 706 \return 707 - \c B_OK: Everything went fine. 708 - \c B_ENTRY_NOT_FOUND: A device, session or partition respectively with 709 ID \a id could not be found. 710 - other error codes 711 */ 712 status_t 713 BDiskDeviceRoster::_GetObjectWithID(const char *fieldName, int32 id, 714 BDiskDevice *device) const 715 { 716 status_t error = (device ? B_OK : B_BAD_VALUE); 717 // compose request message 718 BMessage request(B_REG_GET_DISK_DEVICE); 719 if (error == B_OK) 720 error = request.AddInt32(fieldName, id); 721 // send request 722 BMessage reply; 723 if (error == B_OK) 724 error = fManager.SendMessage(&request, &reply); 725 // analyze reply 726 if (error == B_OK) { 727 // result 728 status_t result = B_OK; 729 error = reply.FindInt32("result", &result); 730 if (error == B_OK) 731 error = result; 732 // device 733 BMessage archive; 734 if (error == B_OK) 735 error = reply.FindMessage("device", &archive); 736 if (error == B_OK) 737 error = device->_Unarchive(&archive); 738 } 739 return error; 740 } 741 742 743 /*! \brief Finds and loads the next add-on of an add-on subdirectory. 744 \param directory The add-on directory. 745 \param image Pointer to an image_id into which the image ID of the loaded 746 add-on shall be written. 747 \return 748 - \c B_OK: Everything went fine. 749 - \c B_ENTRY_NOT_FOUND: End of directory. 750 - other error codes 751 */ 752 status_t 753 BDiskDeviceRoster::_GetNextAddOn(BDirectory **directory, int32 *index, 754 const char *subdir, AddOnImage *image) 755 { 756 status_t error = (directory && index && subdir && image 757 ? B_OK : B_BAD_VALUE); 758 if (error == B_OK) { 759 // search until an add-on has been found or the end of all directories 760 // has been reached 761 bool found = false; 762 do { 763 // get the next add-on in the current dir 764 error = _GetNextAddOn(*directory, image); 765 if (error == B_OK) { 766 found = true; 767 } else if (error == B_ENTRY_NOT_FOUND) { 768 // end of the current directory has been reached, try next dir 769 error = _GetNextAddOnDir(directory, index, subdir); 770 } 771 } while (error == B_OK && !found); 772 } 773 return error; 774 } 775 776 777 /*! \brief Finds and loads the next add-on of an add-on subdirectory. 778 \param directory The add-on directory. 779 \param image Pointer to an image_id into which the image ID of the loaded 780 add-on shall be written. 781 \return 782 - \c B_OK: Everything went fine. 783 - \c B_ENTRY_NOT_FOUND: End of directory. 784 - other error codes 785 */ 786 status_t 787 BDiskDeviceRoster::_GetNextAddOn(BDirectory *directory, AddOnImage *image) 788 { 789 status_t error = (directory ? B_OK : B_ENTRY_NOT_FOUND); 790 if (error == B_OK) { 791 // iterate through the entry list and try to load the entries 792 bool found = false; 793 while (error == B_OK && !found) { 794 BEntry entry; 795 error = directory->GetNextEntry(&entry); 796 BPath path; 797 if (error == B_OK && entry.GetPath(&path) == B_OK) 798 found = (image->Load(path.Path()) == B_OK); 799 } 800 } 801 return error; 802 } 803 804 805 /*! \brief Gets the next add-on directory path. 806 \param path Pointer to a BPath to be set to the found directory. 807 \param index Pointer to an index into the kAddOnDirs array indicating 808 which add-on dir shall be retrieved next. 809 \param subdir Name of the subdirectory (in the "disk_scanner" subdirectory 810 of the add-on directory) \a directory shall be set to. 811 \return 812 - \c B_OK: Everything went fine. 813 - \c B_ENTRY_NOT_FOUND: End of directory list. 814 - other error codes 815 */ 816 status_t 817 BDiskDeviceRoster::_GetNextAddOnDir(BPath *path, int32 *index, 818 const char *subdir) 819 { 820 status_t error = (*index < kAddOnDirCount ? B_OK : B_ENTRY_NOT_FOUND); 821 // get the add-on dir path 822 if (error == B_OK) { 823 error = find_directory(kAddOnDirs[*index], path); 824 (*index)++; 825 } 826 // construct the subdirectory path 827 if (error == B_OK) { 828 error = path->Append("disk_scanner"); 829 if (error == B_OK) 830 error = path->Append(subdir); 831 } 832 if (error == B_OK) 833 printf(" next add-on dir: `%s'\n", path->Path()); 834 return error; 835 } 836 837 838 /*! \brief Gets the next add-on directory. 839 \param directory Pointer to a BDirectory* to be set to the found directory. 840 \param index Pointer to an index into the kAddOnDirs array indicating 841 which add-on dir shall be retrieved next. 842 \param subdir Name of the subdirectory (in the "disk_scanner" subdirectory 843 of the add-on directory) \a directory shall be set to. 844 \return 845 - \c B_OK: Everything went fine. 846 - \c B_ENTRY_NOT_FOUND: End of directory list. 847 - other error codes 848 */ 849 status_t 850 BDiskDeviceRoster::_GetNextAddOnDir(BDirectory **directory, int32 *index, 851 const char *subdir) 852 { 853 BPath path; 854 status_t error = _GetNextAddOnDir(&path, index, subdir); 855 // create a BDirectory object, if there is none yet. 856 if (error == B_OK && !*directory) { 857 *directory = new BDirectory; 858 if (!*directory) 859 error = B_NO_MEMORY; 860 } 861 // init the directory 862 if (error == B_OK) 863 error = (*directory)->SetTo(path.Path()); 864 // cleanup on error 865 if (error != B_OK && *directory) { 866 delete *directory; 867 *directory = NULL; 868 } 869 return error; 870 } 871 872 873 status_t 874 BDiskDeviceRoster::_LoadPartitionAddOn(const char *partitioningSystem, 875 AddOnImage *image, BDiskScannerPartitionAddOn **_addOn) 876 { 877 status_t error = partitioningSystem && image && _addOn 878 ? B_OK : B_BAD_VALUE; 879 880 // load the image 881 bool found = false; 882 BPath path; 883 BDirectory *directory = NULL; 884 int32 index = 0; 885 while (error == B_OK && !found) { 886 error = _GetNextAddOn(&directory, &index, "partition", image); 887 if (error == B_OK) { 888 // add-on loaded: get the function that creates an add-on 889 // object 890 BDiskScannerPartitionAddOn *(*create_add_on)(); 891 if (get_image_symbol(image->ID(), "create_ds_partition_add_on", 892 B_SYMBOL_TYPE_TEXT, 893 (void**)&create_add_on) == B_OK) { 894 // create the add-on object and copy the requested data 895 if (BDiskScannerPartitionAddOn *addOn = (*create_add_on)()) { 896 if (!strcmp(addOn->ShortName(), partitioningSystem)) { 897 *_addOn = addOn; 898 found = true; 899 } else 900 delete addOn; 901 } 902 } 903 } 904 } 905 // cleanup 906 if (directory) 907 delete directory; 908 if (error != B_OK && image) 909 image->Unload(); 910 return error; 911 } 912 913 #endif // 0 914