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