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