1 /* 2 * Copyright 2003-2007, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <errno.h> 7 #include <new> 8 #include <unistd.h> 9 #include <sys/stat.h> 10 11 #include <Directory.h> 12 #include <DiskDevice.h> 13 #include <DiskDevicePrivate.h> 14 #include <DiskDeviceVisitor.h> 15 #include <DiskSystem.h> 16 #include <fs_volume.h> 17 #include <Message.h> 18 #include <Partition.h> 19 #include <PartitioningInfo.h> 20 #include <Path.h> 21 #include <String.h> 22 #include <Volume.h> 23 24 #include <AutoDeleter.h> 25 26 #include <ddm_userland_interface_defs.h> 27 #include <syscalls.h> 28 29 #include "PartitionDelegate.h" 30 31 32 using std::nothrow; 33 34 35 /*! \class BPartition 36 \brief A BPartition object represent a partition and provides a lot of 37 methods to retrieve information about it and some to manipulate it. 38 39 Not all BPartitions represent actual on-disk partitions. Some exist only 40 to make all devices fit smoothly into the framework (e.g. for floppies, 41 \see IsVirtual()), others represents merely partition slots 42 (\see IsEmpty()). 43 */ 44 45 46 // compare_string 47 /*! \brief \c NULL aware strcmp(). 48 49 \c NULL is considered the least of all strings. \c NULL equals \c NULL. 50 51 \param str1 First string. 52 \param str2 Second string. 53 \return A value less than 0, if \a str1 is less than \a str2, 54 0, if they are equal, or a value greater than 0, if 55 \a str1 is greater \a str2. 56 */ 57 static inline int 58 compare_string(const char* str1, const char* str2) 59 { 60 if (str1 == NULL) { 61 if (str2 == NULL) 62 return 0; 63 return 1; 64 } else if (str2 == NULL) 65 return -1; 66 return strcmp(str1, str2); 67 } 68 69 70 // constructor 71 BPartition::BPartition() 72 : fDevice(NULL), 73 fParent(NULL), 74 fPartitionData(NULL), 75 fDelegate(NULL) 76 { 77 } 78 79 80 // destructor 81 /*! \brief Frees all resources associated with this object. 82 */ 83 BPartition::~BPartition() 84 { 85 _Unset(); 86 } 87 88 89 // Offset 90 /*! \brief Returns the partition's offset relative to the beginning of the 91 device it resides on. 92 \return The partition's offset in bytes relative to the beginning of the 93 device it resides on. 94 */ 95 off_t 96 BPartition::Offset() const 97 { 98 return _PartitionData()->offset; 99 } 100 101 102 // Size 103 /*! \brief Returns the size of the partition. 104 \return The size of the partition in bytes. 105 */ 106 off_t 107 BPartition::Size() const 108 { 109 return _PartitionData()->size; 110 } 111 112 113 // ContentSize 114 off_t 115 BPartition::ContentSize() const 116 { 117 return _PartitionData()->content_size; 118 } 119 120 // BlockSize 121 /*! \brief Returns the block size of the device. 122 \return The block size of the device in bytes. 123 */ 124 uint32 125 BPartition::BlockSize() const 126 { 127 return _PartitionData()->block_size; 128 } 129 130 131 // Index 132 /*! \brief Returns the index of the partition in its session's list of 133 partitions. 134 \return The index of the partition in its session's list of partitions. 135 */ 136 int32 137 BPartition::Index() const 138 { 139 return _PartitionData()->index; 140 } 141 142 143 // Status 144 uint32 145 BPartition::Status() const 146 { 147 return _PartitionData()->status; 148 } 149 150 151 // ContainsFileSystem 152 bool 153 BPartition::ContainsFileSystem() const 154 { 155 return _PartitionData()->flags & B_PARTITION_FILE_SYSTEM; 156 } 157 158 159 // ContainsPartitioningSystem 160 bool 161 BPartition::ContainsPartitioningSystem() const 162 { 163 return _PartitionData()->flags & B_PARTITION_PARTITIONING_SYSTEM; 164 } 165 166 167 // IsDevice 168 bool 169 BPartition::IsDevice() const 170 { 171 return _PartitionData()->flags & B_PARTITION_IS_DEVICE; 172 } 173 174 175 // IsReadOnly 176 bool 177 BPartition::IsReadOnly() const 178 { 179 return _PartitionData()->flags & B_PARTITION_READ_ONLY; 180 } 181 182 183 // IsMounted 184 /*! \brief Returns whether the volume is mounted. 185 \return \c true, if the volume is mounted, \c false otherwise. 186 */ 187 bool 188 BPartition::IsMounted() const 189 { 190 return _PartitionData()->flags & B_PARTITION_MOUNTED; 191 // alternatively: 192 // return _PartitionData()->volume >= 0; 193 } 194 195 196 // IsBusy 197 bool 198 BPartition::IsBusy() const 199 { 200 return _PartitionData()->flags & B_PARTITION_BUSY; 201 } 202 203 204 // Flags 205 /*! \brief Returns the flags for this partitions. 206 207 The partition flags are a bitwise combination of: 208 - \c B_HIDDEN_PARTITION: The partition can not contain a file system. 209 - \c B_VIRTUAL_PARTITION: There exists no on-disk partition this object 210 represents. E.g. for floppies there will be a BPartition object spanning 211 the whole floppy disk. 212 - \c B_EMPTY_PARTITION: The partition represents no physical partition, 213 but merely an empty slot. This mainly used to keep the indexing of 214 partitions more persistent. This flag implies also \c B_HIDDEN_PARTITION. 215 216 \return The flags for this partition. 217 */ 218 uint32 219 BPartition::Flags() const 220 { 221 return _PartitionData()->flags; 222 } 223 224 225 // Name 226 /*! \brief Returns the name of the partition. 227 228 Note, that not all partitioning system support names. The method returns 229 \c NULL, if the partition doesn't have a name. 230 231 \return The name of the partition, or \c NULL, if the partitioning system 232 does not support names. 233 */ 234 const char* 235 BPartition::Name() const 236 { 237 return _PartitionData()->name; 238 } 239 240 241 // ContentName 242 const char* 243 BPartition::ContentName() const 244 { 245 return _PartitionData()->content_name; 246 } 247 248 249 // Type 250 /*! \brief Returns a human readable string for the type of the partition. 251 \return A human readable string for the type of the partition. 252 */ 253 const char* 254 BPartition::Type() const 255 { 256 return _PartitionData()->type; 257 } 258 259 260 // ContentType 261 const char* 262 BPartition::ContentType() const 263 { 264 return _PartitionData()->content_type; 265 } 266 267 268 // ID 269 /*! \brief Returns a unique identifier for this partition. 270 271 The ID is not persistent, i.e. in general won't be the same after 272 rebooting. 273 274 \see BDiskDeviceRoster::GetPartitionWithID(). 275 276 \return A unique identifier for this partition. 277 */ 278 int32 279 BPartition::ID() const 280 { 281 return _PartitionData()->id; 282 } 283 284 285 // Parameters 286 const char* 287 BPartition::Parameters() const 288 { 289 return _PartitionData()->parameters; 290 } 291 292 293 // ContentParameters 294 const char* 295 BPartition::ContentParameters() const 296 { 297 return _PartitionData()->content_parameters; 298 } 299 300 301 // GetDiskSystem 302 status_t 303 BPartition::GetDiskSystem(BDiskSystem* diskSystem) const 304 { 305 const user_partition_data* data = _PartitionData(); 306 if (!data || !diskSystem) 307 return B_BAD_VALUE; 308 309 if (data->disk_system < 0) 310 return B_ENTRY_NOT_FOUND; 311 312 return diskSystem->_SetTo(data->disk_system); 313 } 314 315 316 // GetPath 317 status_t 318 BPartition::GetPath(BPath* path) const 319 { 320 // The path is constructed on the fly using our parent 321 if (path == NULL || Parent() == NULL || Index() < 0) 322 return B_BAD_VALUE; 323 324 // get the parent's path 325 status_t error = Parent()->GetPath(path); 326 if (error != B_OK) 327 return error; 328 329 char indexBuffer[24]; 330 331 if (Parent()->IsDevice()) { 332 // Our parent is a device, so we replace `raw' by our index. 333 const char* leaf = path->Leaf(); 334 if (!leaf || strcmp(leaf, "raw") != B_OK) 335 return B_ERROR; 336 337 snprintf(indexBuffer, sizeof(indexBuffer), "%ld", Index()); 338 } else { 339 // Our parent is a normal partition, no device: Append our index. 340 snprintf(indexBuffer, sizeof(indexBuffer), "%s_%ld", path->Leaf(), Index()); 341 } 342 343 error = path->GetParent(path); 344 if (error == B_OK) 345 error = path->Append(indexBuffer); 346 347 return error; 348 } 349 350 351 // GetVolume 352 /*! \brief Returns a BVolume for the partition. 353 354 This can only succeed, if the partition is mounted. 355 356 \param volume Pointer to a pre-allocated BVolume, to be initialized to 357 represent the volume. 358 \return \c B_OK, if the volume is mounted and the parameter could be set 359 accordingly, another error code otherwise. 360 */ 361 status_t 362 BPartition::GetVolume(BVolume* volume) const 363 { 364 if (!volume) 365 return B_BAD_VALUE; 366 367 return volume->SetTo(_PartitionData()->volume); 368 } 369 370 371 // GetIcon 372 /*! \brief Returns an icon for this partition. 373 374 Note, that currently there are only per-device icons, i.e. the method 375 returns the same icon for each partition of a device. But this may change 376 in the future. 377 378 \param icon Pointer to a pre-allocated BBitmap to be set to the icon of 379 the partition. 380 \param which Size of the icon to be retrieved. Can be \c B_MINI_ICON or 381 \c B_LARGE_ICON. 382 \return \c B_OK, if everything went fine, another error code otherwise. 383 */ 384 status_t 385 BPartition::GetIcon(BBitmap* icon, icon_size which) const 386 { 387 if (icon == NULL) 388 return B_BAD_VALUE; 389 390 status_t error; 391 392 if (IsMounted()) { 393 // mounted: get the icon from the volume 394 BVolume volume; 395 error = GetVolume(&volume); 396 if (error == B_OK) 397 error = volume.GetIcon(icon, which); 398 } else { 399 // not mounted: retrieve the icon ourselves 400 if (BDiskDevice* device = Device()) { 401 BPath path; 402 error = device->GetPath(&path); 403 // get the icon 404 if (error == B_OK) 405 error = get_device_icon(path.Path(), icon, which); 406 } else 407 error = B_ERROR; 408 } 409 return error; 410 } 411 412 413 status_t 414 BPartition::GetIcon(uint8** _data, size_t* _size, type_code* _type) const 415 { 416 if (_data == NULL || _size == NULL || _type == NULL) 417 return B_BAD_VALUE; 418 419 status_t error; 420 421 if (IsMounted()) { 422 // mounted: get the icon from the volume 423 BVolume volume; 424 error = GetVolume(&volume); 425 if (error == B_OK) 426 error = volume.GetIcon(_data, _size, _type); 427 } else { 428 // not mounted: retrieve the icon ourselves 429 if (BDiskDevice* device = Device()) { 430 BPath path; 431 error = device->GetPath(&path); 432 // get the icon 433 if (error == B_OK) 434 error = get_device_icon(path.Path(), _data, _size, _type); 435 } else 436 error = B_ERROR; 437 } 438 return error; 439 } 440 441 442 // GetMountPoint 443 /*! \brief Returns the mount point for the partition. 444 445 If the partition is mounted this is the actual mount point. If it is not 446 mounted, but contains a file system, derived from the partition name 447 the name for a not yet existing directory in the root directory is 448 constructed and the path to it returned. 449 450 For partitions not containing a file system the method returns an error. 451 452 \param mountPoint Pointer to the path to be set to refer the mount point 453 (respectively potential mount point) of the partition. 454 \return \c B_OK, if everything went fine, an error code otherwise. 455 */ 456 status_t 457 BPartition::GetMountPoint(BPath* mountPoint) const 458 { 459 if (!mountPoint || !ContainsFileSystem()) 460 return B_BAD_VALUE; 461 462 // if the partition is mounted, return the actual mount point 463 BVolume volume; 464 if (GetVolume(&volume) == B_OK) { 465 BDirectory dir; 466 status_t error = volume.GetRootDirectory(&dir); 467 if (error == B_OK) 468 error = mountPoint->SetTo(&dir, NULL); 469 return error; 470 } 471 472 // partition not mounted 473 // get the volume name 474 const char* volumeName = ContentName(); 475 if (!volumeName || strlen(volumeName) == 0) 476 volumeName = Name(); 477 if (!volumeName || strlen(volumeName) == 0) 478 volumeName = "unnamed volume"; 479 480 // construct a path name from the volume name 481 // replace '/'s and prepend a '/' 482 BString mountPointPath(volumeName); 483 mountPointPath.ReplaceAll('/', '-'); 484 mountPointPath.Insert("/", 0); 485 486 // make the name unique 487 BString basePath(mountPointPath); 488 int counter = 1; 489 while (true) { 490 BEntry entry; 491 status_t error = entry.SetTo(mountPointPath.String()); 492 if (error != B_OK) 493 return error; 494 495 if (!entry.Exists()) 496 break; 497 mountPointPath = basePath; 498 mountPointPath << counter; 499 counter++; 500 } 501 502 return mountPoint->SetTo(mountPointPath.String()); 503 } 504 505 506 // Mount 507 /*! \brief Mounts the volume. 508 509 The volume can only be mounted, if the partition contains a recognized 510 file system (\see ContainsFileSystem()) and it is not already mounted. 511 512 If no mount point is given, one will be created automatically under the 513 root directory (derived from the volume name). If one is given, the 514 directory must already exist. 515 516 \param mountPoint The directory where to mount the file system. May be 517 \c NULL, in which case a mount point in the root directory will be 518 created automatically. 519 \param mountFlags Currently only \c B_MOUNT_READ_ONLY is defined, which 520 forces the volume to be mounted read-only. 521 \param parameters File system specific mount parameters. 522 \return \c B_OK, if everything went fine, another error code otherwise. 523 */ 524 dev_t 525 BPartition::Mount(const char* mountPoint, uint32 mountFlags, 526 const char* parameters) 527 { 528 if (IsMounted() || !ContainsFileSystem()) 529 return B_BAD_VALUE; 530 531 // get the partition path 532 BPath partitionPath; 533 status_t error = GetPath(&partitionPath); 534 if (error != B_OK) 535 return error; 536 537 // create a mount point, if none is given 538 bool deleteMountPoint = false; 539 BPath mountPointPath; 540 if (!mountPoint) { 541 // get a unique mount point 542 error = GetMountPoint(&mountPointPath); 543 if (error != B_OK) 544 return error; 545 546 mountPoint = mountPointPath.Path(); 547 548 // create the directory 549 if (mkdir(mountPoint, S_IRWXU | S_IRWXG | S_IRWXO) < 0) 550 return errno; 551 552 deleteMountPoint = true; 553 } 554 555 // mount the partition 556 dev_t device = fs_mount_volume(mountPoint, partitionPath.Path(), NULL, 557 mountFlags, parameters); 558 559 // delete the mount point on error, if we created it 560 if (device < B_OK && deleteMountPoint) 561 rmdir(mountPoint); 562 563 // update object, if successful 564 if (device >= 0) 565 error = Device()->Update(); 566 567 return device; 568 } 569 570 571 // Unmount 572 /*! \brief Unmounts the volume. 573 574 The volume can of course only be unmounted, if it currently is mounted. 575 576 \param unmountFlags Currently only \c B_FORCE_UNMOUNT is defined, which 577 forces the partition to be unmounted, even if there are still 578 open FDs. Be careful using this flag -- you risk the user's data. 579 580 \return \c B_OK, if everything went fine, another error code otherwise. 581 */ 582 status_t 583 BPartition::Unmount(uint32 unmountFlags) 584 { 585 if (!IsMounted()) 586 return B_BAD_VALUE; 587 588 // get the partition path 589 BPath path; 590 status_t status = GetMountPoint(&path); 591 if (status != B_OK) 592 return status; 593 594 // unmount 595 status = fs_unmount_volume(path.Path(), unmountFlags); 596 597 // update object, if successful 598 if (status == B_OK) 599 status = Device()->Update(); 600 601 return status; 602 } 603 604 605 // Device 606 /*! \brief Returns the device this partition resides on. 607 \return The device this partition resides on. 608 */ 609 BDiskDevice* 610 BPartition::Device() const 611 { 612 return fDevice; 613 } 614 615 616 // Parent 617 BPartition* 618 BPartition::Parent() const 619 { 620 return fParent; 621 } 622 623 624 // ChildAt 625 BPartition* 626 BPartition::ChildAt(int32 index) const 627 { 628 if (fDelegate) { 629 Delegate* child = fDelegate->ChildAt(index); 630 return child ? child->Partition() : NULL; 631 } 632 633 return _ChildAt(index); 634 } 635 636 637 // CountChildren 638 int32 639 BPartition::CountChildren() const 640 { 641 if (fDelegate) 642 return fDelegate->CountChildren(); 643 644 return _CountChildren(); 645 } 646 647 648 // CountDescendants 649 int32 650 BPartition::CountDescendants() const 651 { 652 int32 count = 1; 653 for (int32 i = 0; BPartition* child = ChildAt(i); i++) 654 count += child->CountDescendants(); 655 return count; 656 } 657 658 659 // FindDescendant 660 BPartition* 661 BPartition::FindDescendant(partition_id id) const 662 { 663 IDFinderVisitor visitor(id); 664 return VisitEachDescendant(&visitor); 665 } 666 667 668 // GetPartitioningInfo 669 status_t 670 BPartition::GetPartitioningInfo(BPartitioningInfo* info) const 671 { 672 if (!info) 673 return B_BAD_VALUE; 674 if (!fDelegate) 675 return B_NO_INIT; 676 677 return fDelegate->GetPartitioningInfo(info); 678 } 679 680 681 // VisitEachChild 682 BPartition* 683 BPartition::VisitEachChild(BDiskDeviceVisitor* visitor) const 684 { 685 if (visitor) { 686 int32 level = _Level(); 687 for (int32 i = 0; BPartition* child = ChildAt(i); i++) { 688 if (child->_AcceptVisitor(visitor, level)) 689 return child; 690 } 691 } 692 return NULL; 693 } 694 695 696 // VisitEachDescendant 697 BPartition* 698 BPartition::VisitEachDescendant(BDiskDeviceVisitor* visitor) const 699 { 700 if (visitor) 701 return const_cast<BPartition*>(this)->_VisitEachDescendant(visitor); 702 return NULL; 703 } 704 705 706 // CanDefragment 707 bool 708 BPartition::CanDefragment(bool* whileMounted) const 709 { 710 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING, 711 B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED, whileMounted); 712 } 713 714 715 // Defragment 716 status_t 717 BPartition::Defragment() const 718 { 719 if (!fDelegate) 720 return B_NO_INIT; 721 722 return fDelegate->Defragment(); 723 } 724 725 726 // CanRepair 727 bool 728 BPartition::CanRepair(bool checkOnly, bool* whileMounted) const 729 { 730 uint32 flag; 731 uint32 whileMountedFlag; 732 if (checkOnly) { 733 flag = B_DISK_SYSTEM_SUPPORTS_CHECKING; 734 whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED; 735 } else { 736 flag = B_DISK_SYSTEM_SUPPORTS_REPAIRING; 737 whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED; 738 } 739 740 return _SupportsOperation(flag, whileMountedFlag, whileMounted); 741 } 742 743 744 // Repair 745 status_t 746 BPartition::Repair(bool checkOnly) const 747 { 748 if (!fDelegate) 749 return B_NO_INIT; 750 751 return fDelegate->Repair(checkOnly); 752 } 753 754 755 // CanResize 756 bool 757 BPartition::CanResize(bool* canResizeContents, bool* whileMounted) const 758 { 759 BPartition* parent = Parent(); 760 if (!parent) 761 return false; 762 763 if (!parent->_SupportsChildOperation(this, 764 B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD)) { 765 return false; 766 } 767 768 if (!_HasContent()) 769 return true; 770 771 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_RESIZING, 772 B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED, whileMounted); 773 } 774 775 776 // ValidateResize 777 status_t 778 BPartition::ValidateResize(off_t* size) const 779 { 780 BPartition* parent = Parent(); 781 if (!parent || !fDelegate) 782 return B_NO_INIT; 783 784 status_t error = parent->fDelegate->ValidateResizeChild(fDelegate, size); 785 if (error != B_OK) 786 return error; 787 788 if (_HasContent()) { 789 // TODO: We would actually need the parameter for the content size. 790 off_t contentSize = *size; 791 error = fDelegate->ValidateResize(&contentSize); 792 if (error != B_OK) 793 return error; 794 795 if (contentSize > *size) 796 return B_BAD_VALUE; 797 } 798 799 return B_OK; 800 } 801 802 803 // Resize 804 status_t 805 BPartition::Resize(off_t size) 806 { 807 BPartition* parent = Parent(); 808 if (!parent || !fDelegate) 809 return B_NO_INIT; 810 811 status_t error; 812 off_t contentSize = size; 813 if (_HasContent()) { 814 error = fDelegate->ValidateResize(&contentSize); 815 if (error != B_OK) 816 return error; 817 818 if (contentSize > size) 819 return B_BAD_VALUE; 820 } 821 822 // If shrinking the partition, resize content first, otherwise last. 823 bool shrink = (Size() > size); 824 825 if (shrink && ContentType() != NULL) { 826 error = fDelegate->Resize(contentSize); 827 if (error != B_OK) 828 return error; 829 } 830 831 error = parent->fDelegate->ResizeChild(fDelegate, size); 832 if (error != B_OK) 833 return error; 834 835 if (!shrink && ContentType() != NULL) { 836 error = fDelegate->Resize(contentSize); 837 if (error != B_OK) 838 return error; 839 } 840 841 return B_OK; 842 } 843 844 845 // CanMove 846 bool 847 BPartition::CanMove(BObjectList<BPartition>* unmovableDescendants, 848 BObjectList<BPartition>* movableOnlyIfUnmounted) const 849 { 850 BPartition* parent = Parent(); 851 if (!parent || !fDelegate) 852 return false; 853 854 if (!parent->_SupportsChildOperation(this, 855 B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD)) { 856 return false; 857 } 858 859 bool whileMounted; 860 bool movable = _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_MOVING, 861 B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED, &whileMounted); 862 if (!movable) 863 return false; 864 865 if (!whileMounted) 866 movableOnlyIfUnmounted->AddItem(const_cast<BPartition*>(this)); 867 868 // collect descendent partitions 869 // TODO: ... 870 // TODO: Currently there's no interface for asking descendents. They'll still 871 // have the same offset (relative to their parent) after moving. The only thing 872 // we really have to ask is whether they need to be unmounted. 873 874 return true; 875 } 876 877 878 // ValidateMove 879 status_t 880 BPartition::ValidateMove(off_t* offset) const 881 { 882 BPartition* parent = Parent(); 883 if (!parent || !fDelegate) 884 return B_NO_INIT; 885 886 status_t error = parent->fDelegate->ValidateMoveChild(fDelegate, offset); 887 if (error != B_OK) 888 return error; 889 890 if (_HasContent()) { 891 off_t contentOffset = *offset; 892 error = fDelegate->ValidateMove(&contentOffset); 893 if (error != B_OK) 894 return error; 895 896 if (contentOffset != *offset) 897 return B_BAD_VALUE; 898 } 899 900 return B_OK; 901 } 902 903 904 // Move 905 status_t 906 BPartition::Move(off_t offset) 907 { 908 BPartition* parent = Parent(); 909 if (!parent || !fDelegate) 910 return B_NO_INIT; 911 912 status_t error = parent->fDelegate->MoveChild(fDelegate, offset); 913 if (error != B_OK) 914 return error; 915 916 if (_HasContent()) { 917 error = fDelegate->Move(offset); 918 if (error != B_OK) 919 return error; 920 } 921 922 return B_OK; 923 } 924 925 926 // CanSetName 927 bool 928 BPartition::CanSetName() const 929 { 930 BPartition* parent = Parent(); 931 if (!parent || !fDelegate) 932 return false; 933 934 return parent->_SupportsChildOperation(this, 935 B_DISK_SYSTEM_SUPPORTS_SETTING_NAME); 936 } 937 938 939 // ValidateSetName 940 status_t 941 BPartition::ValidateSetName(BString* name) const 942 { 943 BPartition* parent = Parent(); 944 if (!parent || !fDelegate) 945 return B_NO_INIT; 946 947 return parent->fDelegate->ValidateSetName(fDelegate, name); 948 } 949 950 951 // SetName 952 status_t 953 BPartition::SetName(const char* name) 954 { 955 BPartition* parent = Parent(); 956 if (!parent || !fDelegate) 957 return B_NO_INIT; 958 959 return parent->fDelegate->SetName(fDelegate, name); 960 } 961 962 963 // CanSetContentName 964 bool 965 BPartition::CanSetContentName(bool* whileMounted) const 966 { 967 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME, 968 B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED, 969 whileMounted); 970 } 971 972 973 // ValidateSetContentName 974 status_t 975 BPartition::ValidateSetContentName(BString* name) const 976 { 977 if (!fDelegate) 978 return B_NO_INIT; 979 980 return fDelegate->ValidateSetContentName(name); 981 } 982 983 984 // SetContentName 985 status_t 986 BPartition::SetContentName(const char* name) 987 { 988 if (!fDelegate) 989 return B_NO_INIT; 990 991 return fDelegate->SetContentName(name); 992 } 993 994 995 // CanSetType 996 bool 997 BPartition::CanSetType() const 998 { 999 BPartition* parent = Parent(); 1000 if (!parent) 1001 return false; 1002 1003 return parent->_SupportsChildOperation(this, 1004 B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE); 1005 } 1006 1007 1008 // ValidateSetType 1009 status_t 1010 BPartition::ValidateSetType(const char* type) const 1011 { 1012 BPartition* parent = Parent(); 1013 if (!parent || !fDelegate) 1014 return B_NO_INIT; 1015 1016 return parent->fDelegate->ValidateSetType(fDelegate, type); 1017 } 1018 1019 1020 // SetType 1021 status_t 1022 BPartition::SetType(const char* type) 1023 { 1024 BPartition* parent = Parent(); 1025 if (!parent || !fDelegate) 1026 return B_NO_INIT; 1027 1028 return parent->fDelegate->SetType(fDelegate, type); 1029 } 1030 1031 1032 // CanEditParameters 1033 bool 1034 BPartition::CanEditParameters() const 1035 { 1036 BPartition* parent = Parent(); 1037 if (!parent) 1038 return false; 1039 1040 return parent->_SupportsChildOperation(this, 1041 B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS); 1042 } 1043 1044 1045 // GetParameterEditor 1046 status_t 1047 BPartition::GetParameterEditor(BDiskDeviceParameterEditor** editor) 1048 { 1049 BPartition* parent = Parent(); 1050 if (!parent || !fDelegate) 1051 return B_NO_INIT; 1052 1053 return parent->fDelegate->GetParameterEditor(fDelegate, editor); 1054 } 1055 1056 1057 // SetParameters 1058 status_t 1059 BPartition::SetParameters(const char* parameters) 1060 { 1061 BPartition* parent = Parent(); 1062 if (!parent || !fDelegate) 1063 return B_NO_INIT; 1064 1065 return parent->fDelegate->SetParameters(fDelegate, parameters); 1066 } 1067 1068 1069 // CanEditContentParameters 1070 bool 1071 BPartition::CanEditContentParameters(bool* whileMounted) const 1072 { 1073 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS, 1074 B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED, 1075 whileMounted); 1076 } 1077 1078 1079 // GetContentParameterEditor 1080 status_t 1081 BPartition::GetContentParameterEditor(BDiskDeviceParameterEditor** editor) 1082 { 1083 if (!fDelegate) 1084 return B_NO_INIT; 1085 1086 return fDelegate->GetContentParameterEditor(editor); 1087 } 1088 1089 1090 // SetContentParameters 1091 status_t 1092 BPartition::SetContentParameters(const char* parameters) 1093 { 1094 if (!fDelegate) 1095 return B_NO_INIT; 1096 1097 return fDelegate->SetContentParameters(parameters); 1098 } 1099 1100 1101 // GetNextSupportedType 1102 status_t 1103 BPartition::GetNextSupportedType(int32 *cookie, BString* type) const 1104 { 1105 BPartition* parent = Parent(); 1106 if (!parent || !fDelegate) 1107 return B_NO_INIT; 1108 1109 return parent->fDelegate->GetNextSupportedChildType(fDelegate, cookie, 1110 type); 1111 } 1112 1113 1114 // GetNextSupportedChildType 1115 status_t 1116 BPartition::GetNextSupportedChildType(int32 *cookie, BString* type) const 1117 { 1118 if (!fDelegate) 1119 return B_NO_INIT; 1120 1121 return fDelegate->GetNextSupportedChildType(NULL, cookie, type); 1122 } 1123 1124 1125 // IsSubSystem 1126 bool 1127 BPartition::BPartition::IsSubSystem(const char* diskSystem) const 1128 { 1129 BPartition* parent = Parent(); 1130 if (!parent || !fDelegate) 1131 return false; 1132 1133 return parent->fDelegate->IsSubSystem(fDelegate, diskSystem); 1134 } 1135 1136 1137 // CanInitialize 1138 bool 1139 BPartition::CanInitialize(const char* diskSystem) const 1140 { 1141 return fDelegate && fDelegate->CanInitialize(diskSystem); 1142 } 1143 1144 1145 // GetInitializationParameterEditor 1146 status_t 1147 BPartition::GetInitializationParameterEditor(const char* diskSystem, 1148 BDiskDeviceParameterEditor** editor) const 1149 { 1150 if (!fDelegate) 1151 return B_NO_INIT; 1152 1153 return fDelegate->GetInitializationParameterEditor(diskSystem, editor); 1154 } 1155 1156 1157 // ValidateInitialize 1158 status_t 1159 BPartition::ValidateInitialize(const char* diskSystem, BString* name, 1160 const char* parameters) 1161 { 1162 if (!fDelegate) 1163 return B_NO_INIT; 1164 1165 return fDelegate->ValidateInitialize(diskSystem, name, parameters); 1166 } 1167 1168 1169 // Initialize 1170 status_t 1171 BPartition::Initialize(const char* diskSystem, const char* name, 1172 const char* parameters) 1173 { 1174 if (!fDelegate) 1175 return B_NO_INIT; 1176 1177 return fDelegate->Initialize(diskSystem, name, parameters); 1178 } 1179 1180 1181 // Uninitialize 1182 status_t 1183 BPartition::Uninitialize() 1184 { 1185 // TODO: Implement! 1186 return B_NOT_SUPPORTED; 1187 } 1188 1189 1190 // CanCreateChild 1191 bool 1192 BPartition::CanCreateChild() const 1193 { 1194 return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD); 1195 } 1196 1197 1198 // GetChildCreationParameterEditor 1199 status_t 1200 BPartition::GetChildCreationParameterEditor(const char* type, 1201 BDiskDeviceParameterEditor** editor) const 1202 { 1203 if (!fDelegate) 1204 return B_NO_INIT; 1205 1206 return fDelegate->GetChildCreationParameterEditor(type, editor); 1207 } 1208 1209 1210 // ValidateCreateChild 1211 status_t 1212 BPartition::ValidateCreateChild(off_t* offset, off_t* size, const char* type, 1213 BString* name, const char* parameters) const 1214 { 1215 if (!fDelegate) 1216 return B_NO_INIT; 1217 1218 return fDelegate->ValidateCreateChild(offset, size, type, name, parameters); 1219 } 1220 1221 1222 // CreateChild 1223 status_t 1224 BPartition::CreateChild(off_t offset, off_t size, const char* type, 1225 const char* name, const char* parameters, BPartition** child) 1226 { 1227 if (!fDelegate) 1228 return B_NO_INIT; 1229 1230 return fDelegate->CreateChild(offset, size, type, name, parameters, child); 1231 } 1232 1233 1234 // CanDeleteChild 1235 bool 1236 BPartition::CanDeleteChild(int32 index) const 1237 { 1238 BPartition* child = ChildAt(index); 1239 if (!fDelegate || !child) 1240 return false; 1241 1242 return _SupportsChildOperation(child, 1243 B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD); 1244 } 1245 1246 1247 // DeleteChild 1248 status_t 1249 BPartition::DeleteChild(int32 index) 1250 { 1251 if (!fDelegate) 1252 return B_NO_INIT; 1253 BPartition* child = ChildAt(index); 1254 if (!child || child->Parent() != this) 1255 return B_BAD_VALUE; 1256 1257 return fDelegate->DeleteChild(child->fDelegate); 1258 } 1259 1260 1261 // constructor 1262 /*! \brief Privatized copy constructor to avoid usage. 1263 */ 1264 BPartition::BPartition(const BPartition &) 1265 { 1266 } 1267 1268 1269 // = 1270 /*! \brief Privatized assignment operator to avoid usage. 1271 */ 1272 BPartition & 1273 BPartition::operator=(const BPartition &) 1274 { 1275 return *this; 1276 } 1277 1278 1279 // _SetTo 1280 status_t 1281 BPartition::_SetTo(BDiskDevice* device, BPartition* parent, 1282 user_partition_data* data) 1283 { 1284 _Unset(); 1285 if (!device || !data) 1286 return B_BAD_VALUE; 1287 fPartitionData = data; 1288 fDevice = device; 1289 fParent = parent; 1290 fPartitionData->user_data = this; 1291 1292 // create and init children 1293 status_t error = B_OK; 1294 for (int32 i = 0; error == B_OK && i < fPartitionData->child_count; i++) { 1295 BPartition* child = new(nothrow) BPartition; 1296 if (child) { 1297 error = child->_SetTo(fDevice, this, fPartitionData->children[i]); 1298 if (error != B_OK) 1299 delete child; 1300 } else 1301 error = B_NO_MEMORY; 1302 } 1303 1304 // cleanup on error 1305 if (error != B_OK) 1306 _Unset(); 1307 return error; 1308 } 1309 1310 1311 // _Unset 1312 void 1313 BPartition::_Unset() 1314 { 1315 // delete children 1316 if (fPartitionData) { 1317 for (int32 i = 0; i < fPartitionData->child_count; i++) { 1318 if (BPartition* child = ChildAt(i)) 1319 delete child; 1320 } 1321 fPartitionData->user_data = NULL; 1322 } 1323 1324 fDevice = NULL; 1325 fParent = NULL; 1326 fPartitionData = NULL; 1327 fDelegate = NULL; 1328 } 1329 1330 1331 // _RemoveObsoleteDescendants 1332 status_t 1333 BPartition::_RemoveObsoleteDescendants(user_partition_data* data, bool* updated) 1334 { 1335 // remove all children not longer persistent 1336 // Not exactly efficient: O(n^2), considering BList::RemoveItem() 1337 // O(1). We could do better (O(n*log(n))), when sorting the arrays before, 1338 // but then the list access is more random and we had to find the 1339 // BPartition to remove, which makes the list operation definitely O(n). 1340 int32 count = CountChildren(); 1341 for (int32 i = count - 1; i >= 0; i--) { 1342 BPartition* child = ChildAt(i); 1343 bool found = false; 1344 for (int32 k = data->child_count - 1; k >= 0; k--) { 1345 if (data->children[k]->id == child->ID()) { 1346 // found partition: ask it to remove its obsolete descendants 1347 found = true; 1348 status_t error = child->_RemoveObsoleteDescendants( 1349 data->children[k], updated); 1350 if (error != B_OK) 1351 return error; 1352 1353 // set the user data to the BPartition object to find it 1354 // quicker later 1355 data->children[k]->user_data = child; 1356 break; 1357 } 1358 } 1359 1360 // if partition is obsolete, remove it 1361 if (!found) { 1362 *updated = true; 1363 _RemoveChild(i); 1364 } 1365 } 1366 return B_OK; 1367 } 1368 1369 1370 // _Update 1371 status_t 1372 BPartition::_Update(user_partition_data* data, bool* updated) 1373 { 1374 user_partition_data* oldData = fPartitionData; 1375 fPartitionData = data; 1376 // check for changes 1377 if (data->offset != oldData->offset 1378 || data->size != oldData->size 1379 || data->block_size != oldData->block_size 1380 || data->status != oldData->status 1381 || data->flags != oldData->flags 1382 || data->volume != oldData->volume 1383 || data->disk_system != oldData->disk_system // not needed 1384 || compare_string(data->name, oldData->name) 1385 || compare_string(data->content_name, oldData->content_name) 1386 || compare_string(data->type, oldData->type) 1387 || compare_string(data->content_type, oldData->content_type) 1388 || compare_string(data->parameters, oldData->parameters) 1389 || compare_string(data->content_parameters, 1390 oldData->content_parameters)) { 1391 *updated = true; 1392 } 1393 1394 // add new children and update existing ones 1395 status_t error = B_OK; 1396 for (int32 i = 0; i < data->child_count; i++) { 1397 user_partition_data* childData = data->children[i]; 1398 BPartition* child = (BPartition*)childData->user_data; 1399 if (child) { 1400 // old partition 1401 error = child->_Update(childData, updated); 1402 if (error != B_OK) 1403 return error; 1404 } else { 1405 // new partition 1406 *updated = true; 1407 child = new(nothrow) BPartition; 1408 if (!child) 1409 return B_NO_MEMORY; 1410 1411 error = child->_SetTo(fDevice, this, childData); 1412 if (error != B_OK) { 1413 delete child; 1414 return error; 1415 } 1416 1417 childData->user_data = child; 1418 } 1419 } 1420 return error; 1421 } 1422 1423 1424 // _RemoveChild 1425 void 1426 BPartition::_RemoveChild(int32 index) 1427 { 1428 int32 count = CountChildren(); 1429 if (!fPartitionData || index < 0 || index >= count) 1430 return; 1431 1432 // delete the BPartition and its children 1433 delete ChildAt(index); 1434 1435 // compact the children array 1436 for (int32 i = index + 1; i < count; i++) 1437 fPartitionData->children[i - 1] = fPartitionData->children[i]; 1438 fPartitionData->child_count--; 1439 } 1440 1441 1442 // _ChildAt 1443 BPartition* 1444 BPartition::_ChildAt(int32 index) const 1445 { 1446 if (index < 0 || index >= fPartitionData->child_count) 1447 return NULL; 1448 return (BPartition*)fPartitionData->children[index]->user_data; 1449 } 1450 1451 1452 // _CountChildren 1453 int32 1454 BPartition::_CountChildren() const 1455 { 1456 return fPartitionData->child_count; 1457 } 1458 1459 1460 // _CountDescendants 1461 int32 1462 BPartition::_CountDescendants() const 1463 { 1464 int32 count = 1; 1465 for (int32 i = 0; BPartition* child = _ChildAt(i); i++) 1466 count += child->_CountDescendants(); 1467 return count; 1468 } 1469 1470 1471 // _Level 1472 int32 1473 BPartition::_Level() const 1474 { 1475 int32 level = 0; 1476 const BPartition* ancestor = this; 1477 while ((ancestor = ancestor->Parent())) 1478 level++; 1479 return level; 1480 } 1481 1482 1483 // _AcceptVisitor 1484 bool 1485 BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level) 1486 { 1487 return visitor->Visit(this, level); 1488 } 1489 1490 1491 // _VisitEachDescendant 1492 BPartition* 1493 BPartition::_VisitEachDescendant(BDiskDeviceVisitor* visitor, int32 level) 1494 { 1495 if (level < 0) 1496 level = _Level(); 1497 if (_AcceptVisitor(visitor, level)) 1498 return this; 1499 for (int32 i = 0; BPartition* child = ChildAt(i); i++) { 1500 if (BPartition* result = child->_VisitEachDescendant(visitor, 1501 level + 1)) { 1502 return result; 1503 } 1504 } 1505 return NULL; 1506 } 1507 1508 1509 // _PartitionData 1510 const user_partition_data* 1511 BPartition::_PartitionData() const 1512 { 1513 return fDelegate ? fDelegate->PartitionData() : fPartitionData; 1514 } 1515 1516 1517 // _HasContent 1518 bool 1519 BPartition::_HasContent() const 1520 { 1521 return (ContentType() != NULL); 1522 } 1523 1524 1525 // _SupportsOperation 1526 bool 1527 BPartition::_SupportsOperation(uint32 flag, uint32 whileMountedFlag, 1528 bool* whileMounted) const 1529 { 1530 if (!fDelegate) 1531 return false; 1532 1533 uint32 supported = fDelegate->SupportedOperations(flag | whileMountedFlag); 1534 1535 if (whileMounted) 1536 *whileMounted = supported & whileMountedFlag; 1537 1538 return supported & flag; 1539 } 1540 1541 1542 // _SupportsChildOperation 1543 bool 1544 BPartition::_SupportsChildOperation(const BPartition* child, uint32 flag) const 1545 { 1546 if (!fDelegate || child && !child->fDelegate) 1547 return false; 1548 1549 uint32 supported = fDelegate->SupportedChildOperations( 1550 child ? child->fDelegate : NULL, flag); 1551 1552 return supported & flag; 1553 } 1554 1555 1556 // _CreateDelegates 1557 status_t 1558 BPartition::_CreateDelegates() 1559 { 1560 if (fDelegate || !fPartitionData) 1561 return B_NO_INIT; 1562 1563 // create and init delegate 1564 fDelegate = new(nothrow) Delegate(this); 1565 if (!fDelegate) 1566 return B_NO_MEMORY; 1567 1568 status_t error = fDelegate->InitHierarchy(fPartitionData, 1569 fParent ? fParent->fDelegate : NULL); 1570 if (error != B_OK) 1571 return error; 1572 1573 // create child delegates 1574 int32 count = _CountChildren(); 1575 for (int32 i = 0; i < count; i++) { 1576 BPartition* child = _ChildAt(i); 1577 error = child->_CreateDelegates(); 1578 if (error != B_OK) 1579 return error; 1580 } 1581 1582 return B_OK; 1583 } 1584 1585 1586 // _InitDelegates 1587 status_t 1588 BPartition::_InitDelegates() 1589 { 1590 // init delegate 1591 status_t error = fDelegate->InitAfterHierarchy(); 1592 if (error != B_OK) 1593 return error; 1594 1595 // recursively init child delegates 1596 int32 count = CountChildren(); 1597 for (int32 i = 0; i < count; i++) { 1598 error = ChildAt(i)->_InitDelegates(); 1599 if (error != B_OK) 1600 return error; 1601 } 1602 1603 return B_OK; 1604 } 1605 1606 1607 // _DeleteDelegates 1608 void 1609 BPartition::_DeleteDelegates() 1610 { 1611 // recursively delete child delegates 1612 int32 count = CountChildren(); 1613 for (int32 i = count - 1; i >= 0; i--) 1614 ChildAt(i)->_DeleteDelegates(); 1615 1616 // delete delegate 1617 delete fDelegate; 1618 fDelegate = NULL; 1619 1620 // Commit suicide, if the delegate was our only link to reality (i.e. 1621 // there's no physically existing partition we represent). 1622 if (fPartitionData == NULL) 1623 delete this; 1624 } 1625 1626 1627 // _IsModified 1628 bool 1629 BPartition::_IsModified() const 1630 { 1631 if (!fDelegate) 1632 return false; 1633 1634 return fDelegate->IsModified(); 1635 } 1636