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(B_PARAMETER_EDITOR_TYPE type, 1087 BPartitionParameterEditor** editor) 1088 { 1089 if (!fDelegate) 1090 return B_NO_INIT; 1091 1092 return fDelegate->GetParameterEditor(type, 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 // SetContentParameters 1119 status_t 1120 BPartition::SetContentParameters(const char* parameters) 1121 { 1122 if (!fDelegate) 1123 return B_NO_INIT; 1124 1125 return fDelegate->SetContentParameters(parameters); 1126 } 1127 1128 1129 // GetNextSupportedType 1130 status_t 1131 BPartition::GetNextSupportedType(int32* cookie, BString* type) const 1132 { 1133 TRACE("%p->BPartition::GetNextSupportedType(%ld)\n", this, *cookie); 1134 1135 BPartition* parent = Parent(); 1136 if (!parent || !fDelegate) { 1137 TRACE(" not prepared (parent: %p, fDelegate: %p)!\n", parent, 1138 fDelegate); 1139 return B_NO_INIT; 1140 } 1141 1142 return parent->fDelegate->GetNextSupportedChildType(fDelegate, cookie, 1143 type); 1144 } 1145 1146 1147 // GetNextSupportedChildType 1148 status_t 1149 BPartition::GetNextSupportedChildType(int32* cookie, BString* type) const 1150 { 1151 TRACE("%p->BPartition::GetNextSupportedChildType(%ld)\n", this, *cookie); 1152 1153 if (!fDelegate) { 1154 TRACE(" not prepared!\n"); 1155 return B_NO_INIT; 1156 } 1157 1158 return fDelegate->GetNextSupportedChildType(NULL, cookie, type); 1159 } 1160 1161 1162 // IsSubSystem 1163 bool 1164 BPartition::BPartition::IsSubSystem(const char* diskSystem) const 1165 { 1166 BPartition* parent = Parent(); 1167 if (!parent || !fDelegate) 1168 return false; 1169 1170 return parent->fDelegate->IsSubSystem(fDelegate, diskSystem); 1171 } 1172 1173 1174 // CanInitialize 1175 bool 1176 BPartition::CanInitialize(const char* diskSystem) const 1177 { 1178 return fDelegate && fDelegate->CanInitialize(diskSystem); 1179 } 1180 1181 1182 // ValidateInitialize 1183 status_t 1184 BPartition::ValidateInitialize(const char* diskSystem, BString* name, 1185 const char* parameters) 1186 { 1187 if (!fDelegate) 1188 return B_NO_INIT; 1189 1190 return fDelegate->ValidateInitialize(diskSystem, name, parameters); 1191 } 1192 1193 1194 // Initialize 1195 status_t 1196 BPartition::Initialize(const char* diskSystem, const char* name, 1197 const char* parameters) 1198 { 1199 if (!fDelegate) 1200 return B_NO_INIT; 1201 1202 return fDelegate->Initialize(diskSystem, name, parameters); 1203 } 1204 1205 1206 // Uninitialize 1207 status_t 1208 BPartition::Uninitialize() 1209 { 1210 // TODO: Implement! 1211 return B_NOT_SUPPORTED; 1212 } 1213 1214 1215 // CanCreateChild 1216 bool 1217 BPartition::CanCreateChild() const 1218 { 1219 return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD); 1220 } 1221 1222 1223 // ValidateCreateChild 1224 status_t 1225 BPartition::ValidateCreateChild(off_t* offset, off_t* size, const char* type, 1226 BString* name, const char* parameters) const 1227 { 1228 if (!fDelegate) 1229 return B_NO_INIT; 1230 1231 return fDelegate->ValidateCreateChild(offset, size, type, name, parameters); 1232 } 1233 1234 1235 // CreateChild 1236 status_t 1237 BPartition::CreateChild(off_t offset, off_t size, const char* type, 1238 const char* name, const char* parameters, BPartition** child) 1239 { 1240 if (!fDelegate) 1241 return B_NO_INIT; 1242 1243 return fDelegate->CreateChild(offset, size, type, name, parameters, child); 1244 } 1245 1246 1247 // CanDeleteChild 1248 bool 1249 BPartition::CanDeleteChild(int32 index) const 1250 { 1251 BPartition* child = ChildAt(index); 1252 if (!fDelegate || !child) 1253 return false; 1254 1255 return _SupportsChildOperation(child, 1256 B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD); 1257 } 1258 1259 1260 // DeleteChild 1261 status_t 1262 BPartition::DeleteChild(int32 index) 1263 { 1264 if (!fDelegate) 1265 return B_NO_INIT; 1266 BPartition* child = ChildAt(index); 1267 if (!child || child->Parent() != this) 1268 return B_BAD_VALUE; 1269 1270 return fDelegate->DeleteChild(child->fDelegate); 1271 } 1272 1273 1274 // constructor 1275 /*! \brief Privatized copy constructor to avoid usage. 1276 */ 1277 BPartition::BPartition(const BPartition &) 1278 { 1279 } 1280 1281 1282 // = 1283 /*! \brief Privatized assignment operator to avoid usage. 1284 */ 1285 BPartition & 1286 BPartition::operator=(const BPartition &) 1287 { 1288 return *this; 1289 } 1290 1291 1292 // _SetTo 1293 status_t 1294 BPartition::_SetTo(BDiskDevice* device, BPartition* parent, 1295 user_partition_data* data) 1296 { 1297 _Unset(); 1298 if (!device || !data) 1299 return B_BAD_VALUE; 1300 fPartitionData = data; 1301 fDevice = device; 1302 fParent = parent; 1303 fPartitionData->user_data = this; 1304 1305 // create and init children 1306 status_t error = B_OK; 1307 for (int32 i = 0; error == B_OK && i < fPartitionData->child_count; i++) { 1308 BPartition* child = new(nothrow) BPartition; 1309 if (child) { 1310 error = child->_SetTo(fDevice, this, fPartitionData->children[i]); 1311 if (error != B_OK) 1312 delete child; 1313 } else 1314 error = B_NO_MEMORY; 1315 } 1316 1317 // cleanup on error 1318 if (error != B_OK) 1319 _Unset(); 1320 return error; 1321 } 1322 1323 1324 // _Unset 1325 void 1326 BPartition::_Unset() 1327 { 1328 // delete children 1329 if (fPartitionData) { 1330 for (int32 i = 0; i < fPartitionData->child_count; i++) { 1331 if (BPartition* child = ChildAt(i)) 1332 delete child; 1333 } 1334 fPartitionData->user_data = NULL; 1335 } 1336 1337 fDevice = NULL; 1338 fParent = NULL; 1339 fPartitionData = NULL; 1340 fDelegate = NULL; 1341 } 1342 1343 1344 // _RemoveObsoleteDescendants 1345 status_t 1346 BPartition::_RemoveObsoleteDescendants(user_partition_data* data, bool* updated) 1347 { 1348 // remove all children not longer persistent 1349 // Not exactly efficient: O(n^2), considering BList::RemoveItem() 1350 // O(1). We could do better (O(n*log(n))), when sorting the arrays before, 1351 // but then the list access is more random and we had to find the 1352 // BPartition to remove, which makes the list operation definitely O(n). 1353 int32 count = CountChildren(); 1354 for (int32 i = count - 1; i >= 0; i--) { 1355 BPartition* child = ChildAt(i); 1356 bool found = false; 1357 for (int32 k = data->child_count - 1; k >= 0; k--) { 1358 if (data->children[k]->id == child->ID()) { 1359 // found partition: ask it to remove its obsolete descendants 1360 found = true; 1361 status_t error = child->_RemoveObsoleteDescendants( 1362 data->children[k], updated); 1363 if (error != B_OK) 1364 return error; 1365 1366 // set the user data to the BPartition object to find it 1367 // quicker later 1368 data->children[k]->user_data = child; 1369 break; 1370 } 1371 } 1372 1373 // if partition is obsolete, remove it 1374 if (!found) { 1375 *updated = true; 1376 _RemoveChild(i); 1377 } 1378 } 1379 return B_OK; 1380 } 1381 1382 1383 // _Update 1384 status_t 1385 BPartition::_Update(user_partition_data* data, bool* updated) 1386 { 1387 user_partition_data* oldData = fPartitionData; 1388 fPartitionData = data; 1389 // check for changes 1390 if (data->offset != oldData->offset 1391 || data->size != oldData->size 1392 || data->block_size != oldData->block_size 1393 || data->status != oldData->status 1394 || data->flags != oldData->flags 1395 || data->volume != oldData->volume 1396 || data->disk_system != oldData->disk_system // not needed 1397 || compare_string(data->name, oldData->name) 1398 || compare_string(data->content_name, oldData->content_name) 1399 || compare_string(data->type, oldData->type) 1400 || compare_string(data->content_type, oldData->content_type) 1401 || compare_string(data->parameters, oldData->parameters) 1402 || compare_string(data->content_parameters, 1403 oldData->content_parameters)) { 1404 *updated = true; 1405 } 1406 1407 // add new children and update existing ones 1408 status_t error = B_OK; 1409 for (int32 i = 0; i < data->child_count; i++) { 1410 user_partition_data* childData = data->children[i]; 1411 BPartition* child = (BPartition*)childData->user_data; 1412 if (child) { 1413 // old partition 1414 error = child->_Update(childData, updated); 1415 if (error != B_OK) 1416 return error; 1417 } else { 1418 // new partition 1419 *updated = true; 1420 child = new(nothrow) BPartition; 1421 if (!child) 1422 return B_NO_MEMORY; 1423 1424 error = child->_SetTo(fDevice, this, childData); 1425 if (error != B_OK) { 1426 delete child; 1427 return error; 1428 } 1429 1430 childData->user_data = child; 1431 } 1432 } 1433 return error; 1434 } 1435 1436 1437 // _RemoveChild 1438 void 1439 BPartition::_RemoveChild(int32 index) 1440 { 1441 int32 count = CountChildren(); 1442 if (!fPartitionData || index < 0 || index >= count) 1443 return; 1444 1445 // delete the BPartition and its children 1446 delete ChildAt(index); 1447 1448 // compact the children array 1449 for (int32 i = index + 1; i < count; i++) 1450 fPartitionData->children[i - 1] = fPartitionData->children[i]; 1451 fPartitionData->child_count--; 1452 } 1453 1454 1455 // _ChildAt 1456 BPartition* 1457 BPartition::_ChildAt(int32 index) const 1458 { 1459 if (index < 0 || index >= fPartitionData->child_count) 1460 return NULL; 1461 return (BPartition*)fPartitionData->children[index]->user_data; 1462 } 1463 1464 1465 // _CountChildren 1466 int32 1467 BPartition::_CountChildren() const 1468 { 1469 return fPartitionData->child_count; 1470 } 1471 1472 1473 // _CountDescendants 1474 int32 1475 BPartition::_CountDescendants() const 1476 { 1477 int32 count = 1; 1478 for (int32 i = 0; BPartition* child = _ChildAt(i); i++) 1479 count += child->_CountDescendants(); 1480 return count; 1481 } 1482 1483 1484 // _Level 1485 int32 1486 BPartition::_Level() const 1487 { 1488 int32 level = 0; 1489 const BPartition* ancestor = this; 1490 while ((ancestor = ancestor->Parent())) 1491 level++; 1492 return level; 1493 } 1494 1495 1496 // _AcceptVisitor 1497 bool 1498 BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level) 1499 { 1500 return visitor->Visit(this, level); 1501 } 1502 1503 1504 // _VisitEachDescendant 1505 BPartition* 1506 BPartition::_VisitEachDescendant(BDiskDeviceVisitor* visitor, int32 level) 1507 { 1508 if (level < 0) 1509 level = _Level(); 1510 if (_AcceptVisitor(visitor, level)) 1511 return this; 1512 for (int32 i = 0; BPartition* child = ChildAt(i); i++) { 1513 if (BPartition* result = child->_VisitEachDescendant(visitor, 1514 level + 1)) { 1515 return result; 1516 } 1517 } 1518 return NULL; 1519 } 1520 1521 1522 // _PartitionData 1523 const user_partition_data* 1524 BPartition::_PartitionData() const 1525 { 1526 return fDelegate ? fDelegate->PartitionData() : fPartitionData; 1527 } 1528 1529 1530 // _HasContent 1531 bool 1532 BPartition::_HasContent() const 1533 { 1534 return (ContentType() != NULL); 1535 } 1536 1537 1538 // _SupportsOperation 1539 bool 1540 BPartition::_SupportsOperation(uint32 flag, uint32 whileMountedFlag, 1541 bool* whileMounted) const 1542 { 1543 if (!fDelegate) 1544 return false; 1545 1546 uint32 supported = fDelegate->SupportedOperations(flag | whileMountedFlag); 1547 1548 if (whileMounted) 1549 *whileMounted = supported & whileMountedFlag; 1550 1551 return supported & flag; 1552 } 1553 1554 1555 // _SupportsChildOperation 1556 bool 1557 BPartition::_SupportsChildOperation(const BPartition* child, uint32 flag) const 1558 { 1559 if (!fDelegate || (child && !child->fDelegate)) 1560 return false; 1561 1562 uint32 supported = fDelegate->SupportedChildOperations( 1563 child ? child->fDelegate : NULL, flag); 1564 1565 return supported & flag; 1566 } 1567 1568 1569 // _CreateDelegates 1570 status_t 1571 BPartition::_CreateDelegates() 1572 { 1573 if (fDelegate || !fPartitionData) 1574 return B_NO_INIT; 1575 1576 // create and init delegate 1577 fDelegate = new(nothrow) Delegate(this); 1578 if (!fDelegate) 1579 return B_NO_MEMORY; 1580 1581 status_t error = fDelegate->InitHierarchy(fPartitionData, 1582 fParent ? fParent->fDelegate : NULL); 1583 if (error != B_OK) 1584 return error; 1585 1586 // create child delegates 1587 int32 count = _CountChildren(); 1588 for (int32 i = 0; i < count; i++) { 1589 BPartition* child = _ChildAt(i); 1590 error = child->_CreateDelegates(); 1591 if (error != B_OK) 1592 return error; 1593 } 1594 1595 return B_OK; 1596 } 1597 1598 1599 // _InitDelegates 1600 status_t 1601 BPartition::_InitDelegates() 1602 { 1603 // init delegate 1604 status_t error = fDelegate->InitAfterHierarchy(); 1605 if (error != B_OK) 1606 return error; 1607 1608 // recursively init child delegates 1609 int32 count = CountChildren(); 1610 for (int32 i = 0; i < count; i++) { 1611 error = ChildAt(i)->_InitDelegates(); 1612 if (error != B_OK) 1613 return error; 1614 } 1615 1616 return B_OK; 1617 } 1618 1619 1620 // _DeleteDelegates 1621 void 1622 BPartition::_DeleteDelegates() 1623 { 1624 // recursively delete child delegates 1625 int32 count = CountChildren(); 1626 for (int32 i = count - 1; i >= 0; i--) 1627 ChildAt(i)->_DeleteDelegates(); 1628 1629 // delete delegate 1630 delete fDelegate; 1631 fDelegate = NULL; 1632 1633 // Commit suicide, if the delegate was our only link to reality (i.e. 1634 // there's no physically existing partition we represent). 1635 if (fPartitionData == NULL) 1636 delete this; 1637 } 1638 1639 1640 // _IsModified 1641 bool 1642 BPartition::_IsModified() const 1643 { 1644 if (!fDelegate) 1645 return false; 1646 1647 return fDelegate->IsModified(); 1648 } 1649