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