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