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