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