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