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 B_ERROR; 576 } 577 578 579 /*! \brief Unmounts the volume. 580 581 The volume can of course only be unmounted, if it currently is mounted. 582 583 \param unmountFlags Currently only \c B_FORCE_UNMOUNT is defined, which 584 forces the partition to be unmounted, even if there are still 585 open FDs. Be careful using this flag -- you risk the user's data. 586 587 \return \c B_OK, if everything went fine, another error code otherwise. 588 */ 589 status_t 590 BPartition::Unmount(uint32 unmountFlags) 591 { 592 if (!IsMounted()) 593 return B_BAD_VALUE; 594 595 // get the partition path 596 BPath path; 597 status_t status = GetMountPoint(&path); 598 if (status != B_OK) 599 return status; 600 601 // unmount 602 status = fs_unmount_volume(path.Path(), unmountFlags); 603 604 // update object, if successful 605 if (status == B_OK) { 606 status = Device()->Update(); 607 608 // Check if we created this mount point on the fly. 609 // If so, clean it up. 610 BPath markerPath = path; 611 markerPath.Append(skAutoCreatePrefix); 612 BEntry pathEntry (markerPath.Path()); 613 if (pathEntry.InitCheck() == B_OK && pathEntry.Exists()) { 614 rmdir(markerPath.Path()); 615 rmdir(path.Path()); 616 } 617 } 618 619 return status; 620 } 621 622 623 /*! \brief Returns the device this partition resides on. 624 \return The device this partition resides on. 625 */ 626 BDiskDevice* 627 BPartition::Device() const 628 { 629 return fDevice; 630 } 631 632 633 BPartition* 634 BPartition::Parent() const 635 { 636 return fParent; 637 } 638 639 640 BPartition* 641 BPartition::ChildAt(int32 index) const 642 { 643 if (fDelegate != NULL) { 644 Delegate* child = fDelegate->ChildAt(index); 645 return child ? child->Partition() : NULL; 646 } 647 648 return _ChildAt(index); 649 } 650 651 652 int32 653 BPartition::CountChildren() const 654 { 655 if (fDelegate != NULL) 656 return fDelegate->CountChildren(); 657 658 return _CountChildren(); 659 } 660 661 662 int32 663 BPartition::CountDescendants() const 664 { 665 int32 count = 1; 666 for (int32 i = 0; BPartition* child = ChildAt(i); i++) 667 count += child->CountDescendants(); 668 return count; 669 } 670 671 672 BPartition* 673 BPartition::FindDescendant(partition_id id) const 674 { 675 IDFinderVisitor visitor(id); 676 return VisitEachDescendant(&visitor); 677 } 678 679 680 status_t 681 BPartition::GetPartitioningInfo(BPartitioningInfo* info) const 682 { 683 if (!info) 684 return B_BAD_VALUE; 685 if (fDelegate == NULL) 686 return B_NO_INIT; 687 688 return fDelegate->GetPartitioningInfo(info); 689 } 690 691 692 BPartition* 693 BPartition::VisitEachChild(BDiskDeviceVisitor* visitor) const 694 { 695 if (visitor != NULL) { 696 int32 level = _Level(); 697 for (int32 i = 0; BPartition* child = ChildAt(i); i++) { 698 if (child->_AcceptVisitor(visitor, level)) 699 return child; 700 } 701 } 702 return NULL; 703 } 704 705 706 BPartition* 707 BPartition::VisitEachDescendant(BDiskDeviceVisitor* visitor) const 708 { 709 if (visitor != NULL) 710 return const_cast<BPartition*>(this)->_VisitEachDescendant(visitor); 711 return NULL; 712 } 713 714 715 bool 716 BPartition::CanDefragment(bool* whileMounted) const 717 { 718 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING, 719 B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED, whileMounted); 720 } 721 722 723 status_t 724 BPartition::Defragment() const 725 { 726 if (fDelegate == NULL) 727 return B_NO_INIT; 728 729 return fDelegate->Defragment(); 730 } 731 732 733 bool 734 BPartition::CanRepair(bool checkOnly, bool* whileMounted) const 735 { 736 uint32 flag; 737 uint32 whileMountedFlag; 738 if (checkOnly) { 739 flag = B_DISK_SYSTEM_SUPPORTS_CHECKING; 740 whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED; 741 } else { 742 flag = B_DISK_SYSTEM_SUPPORTS_REPAIRING; 743 whileMountedFlag = B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED; 744 } 745 746 return _SupportsOperation(flag, whileMountedFlag, whileMounted); 747 } 748 749 750 status_t 751 BPartition::Repair(bool checkOnly) const 752 { 753 if (fDelegate == NULL) 754 return B_NO_INIT; 755 756 return fDelegate->Repair(checkOnly); 757 } 758 759 760 bool 761 BPartition::CanResize(bool* canResizeContents, bool* whileMounted) const 762 { 763 BPartition* parent = Parent(); 764 if (parent == NULL) 765 return false; 766 767 if (!parent->_SupportsChildOperation(this, 768 B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD)) { 769 return false; 770 } 771 772 if (!_HasContent()) 773 return true; 774 775 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_RESIZING, 776 B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED, whileMounted); 777 } 778 779 780 status_t 781 BPartition::ValidateResize(off_t* size) const 782 { 783 BPartition* parent = Parent(); 784 if (parent == NULL || fDelegate == NULL) 785 return B_NO_INIT; 786 787 status_t error = parent->fDelegate->ValidateResizeChild(fDelegate, size); 788 if (error != B_OK) 789 return error; 790 791 if (_HasContent()) { 792 // TODO: We would actually need the parameter for the content size. 793 off_t contentSize = *size; 794 error = fDelegate->ValidateResize(&contentSize); 795 if (error != B_OK) 796 return error; 797 798 if (contentSize > *size) 799 return B_BAD_VALUE; 800 } 801 802 return B_OK; 803 } 804 805 806 status_t 807 BPartition::Resize(off_t size) 808 { 809 BPartition* parent = Parent(); 810 if (!parent || !fDelegate) 811 return B_NO_INIT; 812 813 status_t error; 814 off_t contentSize = size; 815 if (_HasContent()) { 816 error = fDelegate->ValidateResize(&contentSize); 817 if (error != B_OK) 818 return error; 819 820 if (contentSize > size) 821 return B_BAD_VALUE; 822 } 823 824 // If shrinking the partition, resize content first, otherwise last. 825 bool shrink = Size() > size; 826 827 if (shrink && ContentType() != NULL) { 828 error = fDelegate->Resize(contentSize); 829 if (error != B_OK) 830 return error; 831 } 832 833 error = parent->fDelegate->ResizeChild(fDelegate, size); 834 if (error != B_OK) 835 return error; 836 837 if (!shrink && ContentType() != NULL) { 838 error = fDelegate->Resize(contentSize); 839 if (error != B_OK) 840 return error; 841 } 842 843 return B_OK; 844 } 845 846 847 bool 848 BPartition::CanMove(BObjectList<BPartition>* unmovableDescendants, 849 BObjectList<BPartition>* movableOnlyIfUnmounted) const 850 { 851 BPartition* parent = Parent(); 852 if (parent == NULL || fDelegate == NULL) 853 return false; 854 855 if (!parent->_SupportsChildOperation(this, 856 B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD)) { 857 return false; 858 } 859 860 bool whileMounted; 861 bool movable = _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_MOVING, 862 B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED, &whileMounted); 863 if (!movable) 864 return false; 865 866 if (!whileMounted) 867 movableOnlyIfUnmounted->AddItem(const_cast<BPartition*>(this)); 868 869 // collect descendent partitions 870 // TODO: ... 871 // TODO: Currently there's no interface for asking descendents. They'll still 872 // have the same offset (relative to their parent) after moving. The only thing 873 // we really have to ask is whether they need to be unmounted. 874 875 return true; 876 } 877 878 879 status_t 880 BPartition::ValidateMove(off_t* offset) const 881 { 882 BPartition* parent = Parent(); 883 if (parent == NULL || fDelegate == NULL) 884 return B_NO_INIT; 885 886 status_t error = parent->fDelegate->ValidateMoveChild(fDelegate, offset); 887 if (error != B_OK) 888 return error; 889 890 if (_HasContent()) { 891 off_t contentOffset = *offset; 892 error = fDelegate->ValidateMove(&contentOffset); 893 if (error != B_OK) 894 return error; 895 896 if (contentOffset != *offset) 897 return B_BAD_VALUE; 898 } 899 900 return B_OK; 901 } 902 903 904 status_t 905 BPartition::Move(off_t offset) 906 { 907 BPartition* parent = Parent(); 908 if (parent == NULL || fDelegate == NULL) 909 return B_NO_INIT; 910 911 status_t error = parent->fDelegate->MoveChild(fDelegate, offset); 912 if (error != B_OK) 913 return error; 914 915 if (_HasContent()) { 916 error = fDelegate->Move(offset); 917 if (error != B_OK) 918 return error; 919 } 920 921 return B_OK; 922 } 923 924 925 bool 926 BPartition::CanSetName() const 927 { 928 BPartition* parent = Parent(); 929 if (parent == NULL || fDelegate == NULL) 930 return false; 931 932 return parent->_SupportsChildOperation(this, 933 B_DISK_SYSTEM_SUPPORTS_SETTING_NAME); 934 } 935 936 937 status_t 938 BPartition::ValidateSetName(BString* name) const 939 { 940 BPartition* parent = Parent(); 941 if (parent == NULL || fDelegate == NULL) 942 return B_NO_INIT; 943 944 return parent->fDelegate->ValidateSetName(fDelegate, name); 945 } 946 947 948 status_t 949 BPartition::SetName(const char* name) 950 { 951 BPartition* parent = Parent(); 952 if (parent == NULL || fDelegate == NULL) 953 return B_NO_INIT; 954 955 return parent->fDelegate->SetName(fDelegate, name); 956 } 957 958 959 bool 960 BPartition::CanSetContentName(bool* whileMounted) const 961 { 962 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME, 963 B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED, 964 whileMounted); 965 } 966 967 968 status_t 969 BPartition::ValidateSetContentName(BString* name) const 970 { 971 if (fDelegate == NULL) 972 return B_NO_INIT; 973 974 return fDelegate->ValidateSetContentName(name); 975 } 976 977 978 status_t 979 BPartition::SetContentName(const char* name) 980 { 981 if (fDelegate == NULL) 982 return B_NO_INIT; 983 984 return fDelegate->SetContentName(name); 985 } 986 987 988 bool 989 BPartition::CanSetType() const 990 { 991 BPartition* parent = Parent(); 992 if (parent == NULL) 993 return false; 994 995 return parent->_SupportsChildOperation(this, 996 B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE); 997 } 998 999 1000 status_t 1001 BPartition::ValidateSetType(const char* type) const 1002 { 1003 BPartition* parent = Parent(); 1004 if (parent == NULL || fDelegate == NULL) 1005 return B_NO_INIT; 1006 1007 return parent->fDelegate->ValidateSetType(fDelegate, type); 1008 } 1009 1010 1011 status_t 1012 BPartition::SetType(const char* type) 1013 { 1014 BPartition* parent = Parent(); 1015 if (parent == NULL || fDelegate == NULL) 1016 return B_NO_INIT; 1017 1018 return parent->fDelegate->SetType(fDelegate, type); 1019 } 1020 1021 1022 bool 1023 BPartition::CanEditParameters() const 1024 { 1025 BPartition* parent = Parent(); 1026 if (parent == NULL) 1027 return false; 1028 1029 return parent->_SupportsChildOperation(this, 1030 B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS); 1031 } 1032 1033 1034 status_t 1035 BPartition::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type, 1036 BPartitionParameterEditor** editor) 1037 { 1038 if (fDelegate == NULL) 1039 return B_NO_INIT; 1040 1041 return fDelegate->GetParameterEditor(type, editor); 1042 } 1043 1044 1045 status_t 1046 BPartition::SetParameters(const char* parameters) 1047 { 1048 BPartition* parent = Parent(); 1049 if (parent == NULL || fDelegate == NULL) 1050 return B_NO_INIT; 1051 1052 return parent->fDelegate->SetParameters(fDelegate, parameters); 1053 } 1054 1055 1056 bool 1057 BPartition::CanEditContentParameters(bool* whileMounted) const 1058 { 1059 return _SupportsOperation(B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS, 1060 B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED, 1061 whileMounted); 1062 } 1063 1064 1065 status_t 1066 BPartition::SetContentParameters(const char* parameters) 1067 { 1068 if (fDelegate == NULL) 1069 return B_NO_INIT; 1070 1071 return fDelegate->SetContentParameters(parameters); 1072 } 1073 1074 1075 status_t 1076 BPartition::GetNextSupportedType(int32* cookie, BString* type) const 1077 { 1078 TRACE("%p->BPartition::GetNextSupportedType(%ld)\n", this, *cookie); 1079 1080 BPartition* parent = Parent(); 1081 if (parent == NULL || fDelegate == NULL) { 1082 TRACE(" not prepared (parent: %p, fDelegate: %p)!\n", parent, 1083 fDelegate); 1084 return B_NO_INIT; 1085 } 1086 1087 return parent->fDelegate->GetNextSupportedChildType(fDelegate, cookie, 1088 type); 1089 } 1090 1091 1092 status_t 1093 BPartition::GetNextSupportedChildType(int32* cookie, BString* type) const 1094 { 1095 TRACE("%p->BPartition::GetNextSupportedChildType(%ld)\n", this, *cookie); 1096 1097 if (fDelegate == NULL) { 1098 TRACE(" not prepared!\n"); 1099 return B_NO_INIT; 1100 } 1101 1102 return fDelegate->GetNextSupportedChildType(NULL, cookie, type); 1103 } 1104 1105 1106 bool 1107 BPartition::BPartition::IsSubSystem(const char* diskSystem) const 1108 { 1109 BPartition* parent = Parent(); 1110 if (parent == NULL || fDelegate == NULL) 1111 return false; 1112 1113 return parent->fDelegate->IsSubSystem(fDelegate, diskSystem); 1114 } 1115 1116 1117 bool 1118 BPartition::CanInitialize(const char* diskSystem) const 1119 { 1120 if (Size() == 0 || BlockSize() == 0 || fDelegate == NULL) 1121 return false; 1122 1123 return fDelegate->CanInitialize(diskSystem); 1124 } 1125 1126 1127 status_t 1128 BPartition::ValidateInitialize(const char* diskSystem, BString* name, 1129 const char* parameters) 1130 { 1131 if (fDelegate == NULL) 1132 return B_NO_INIT; 1133 1134 return fDelegate->ValidateInitialize(diskSystem, name, parameters); 1135 } 1136 1137 1138 status_t 1139 BPartition::Initialize(const char* diskSystem, const char* name, 1140 const char* parameters) 1141 { 1142 if (fDelegate == NULL) 1143 return B_NO_INIT; 1144 1145 return fDelegate->Initialize(diskSystem, name, parameters); 1146 } 1147 1148 1149 status_t 1150 BPartition::Uninitialize() 1151 { 1152 // TODO: Implement! 1153 return B_NOT_SUPPORTED; 1154 } 1155 1156 1157 bool 1158 BPartition::CanCreateChild() const 1159 { 1160 return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD); 1161 } 1162 1163 1164 status_t 1165 BPartition::ValidateCreateChild(off_t* offset, off_t* size, const char* type, 1166 BString* name, const char* parameters) const 1167 { 1168 if (fDelegate == NULL) 1169 return B_NO_INIT; 1170 1171 return fDelegate->ValidateCreateChild(offset, size, type, name, parameters); 1172 } 1173 1174 1175 status_t 1176 BPartition::CreateChild(off_t offset, off_t size, const char* type, 1177 const char* name, const char* parameters, BPartition** child) 1178 { 1179 if (fDelegate == NULL) 1180 return B_NO_INIT; 1181 1182 return fDelegate->CreateChild(offset, size, type, name, parameters, child); 1183 } 1184 1185 1186 bool 1187 BPartition::CanDeleteChild(int32 index) const 1188 { 1189 BPartition* child = ChildAt(index); 1190 if (fDelegate == NULL || child == NULL) 1191 return false; 1192 1193 return _SupportsChildOperation(child, 1194 B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD); 1195 } 1196 1197 1198 status_t 1199 BPartition::DeleteChild(int32 index) 1200 { 1201 if (fDelegate == NULL) 1202 return B_NO_INIT; 1203 1204 BPartition* child = ChildAt(index); 1205 if (child == NULL || child->Parent() != this) 1206 return B_BAD_VALUE; 1207 1208 return fDelegate->DeleteChild(child->fDelegate); 1209 } 1210 1211 1212 /*! \brief Privatized copy constructor to avoid usage. 1213 */ 1214 BPartition::BPartition(const BPartition &) 1215 { 1216 } 1217 1218 1219 /*! \brief Privatized assignment operator to avoid usage. 1220 */ 1221 BPartition & 1222 BPartition::operator=(const BPartition &) 1223 { 1224 return *this; 1225 } 1226 1227 1228 status_t 1229 BPartition::_SetTo(BDiskDevice* device, BPartition* parent, 1230 user_partition_data* data) 1231 { 1232 _Unset(); 1233 if (device == NULL || data == NULL) 1234 return B_BAD_VALUE; 1235 1236 fPartitionData = data; 1237 fDevice = device; 1238 fParent = parent; 1239 fPartitionData->user_data = this; 1240 1241 // create and init children 1242 status_t error = B_OK; 1243 for (int32 i = 0; error == B_OK && i < fPartitionData->child_count; i++) { 1244 BPartition* child = new(nothrow) BPartition; 1245 if (child) { 1246 error = child->_SetTo(fDevice, this, fPartitionData->children[i]); 1247 if (error != B_OK) 1248 delete child; 1249 } else 1250 error = B_NO_MEMORY; 1251 } 1252 1253 // cleanup on error 1254 if (error != B_OK) 1255 _Unset(); 1256 return error; 1257 } 1258 1259 1260 void 1261 BPartition::_Unset() 1262 { 1263 // delete children 1264 if (fPartitionData != NULL) { 1265 for (int32 i = 0; i < fPartitionData->child_count; i++) { 1266 if (BPartition* child = ChildAt(i)) 1267 delete child; 1268 } 1269 fPartitionData->user_data = NULL; 1270 } 1271 1272 fDevice = NULL; 1273 fParent = NULL; 1274 fPartitionData = NULL; 1275 fDelegate = NULL; 1276 } 1277 1278 1279 status_t 1280 BPartition::_RemoveObsoleteDescendants(user_partition_data* data, bool* updated) 1281 { 1282 // remove all children not longer persistent 1283 // Not exactly efficient: O(n^2), considering BList::RemoveItem() 1284 // O(1). We could do better (O(n*log(n))), when sorting the arrays before, 1285 // but then the list access is more random and we had to find the 1286 // BPartition to remove, which makes the list operation definitely O(n). 1287 int32 count = CountChildren(); 1288 for (int32 i = count - 1; i >= 0; i--) { 1289 BPartition* child = ChildAt(i); 1290 bool found = false; 1291 for (int32 k = data->child_count - 1; k >= 0; k--) { 1292 if (data->children[k]->id == child->ID()) { 1293 // found partition: ask it to remove its obsolete descendants 1294 found = true; 1295 status_t error = child->_RemoveObsoleteDescendants( 1296 data->children[k], updated); 1297 if (error != B_OK) 1298 return error; 1299 1300 // set the user data to the BPartition object to find it 1301 // quicker later 1302 data->children[k]->user_data = child; 1303 break; 1304 } 1305 } 1306 1307 // if partition is obsolete, remove it 1308 if (!found) { 1309 *updated = true; 1310 _RemoveChild(i); 1311 } 1312 } 1313 return B_OK; 1314 } 1315 1316 1317 status_t 1318 BPartition::_Update(user_partition_data* data, bool* updated) 1319 { 1320 user_partition_data* oldData = fPartitionData; 1321 fPartitionData = data; 1322 // check for changes 1323 if (data->offset != oldData->offset 1324 || data->size != oldData->size 1325 || data->block_size != oldData->block_size 1326 || data->status != oldData->status 1327 || data->flags != oldData->flags 1328 || data->volume != oldData->volume 1329 || data->disk_system != oldData->disk_system // not needed 1330 || compare_string(data->name, oldData->name) 1331 || compare_string(data->content_name, oldData->content_name) 1332 || compare_string(data->type, oldData->type) 1333 || compare_string(data->content_type, oldData->content_type) 1334 || compare_string(data->parameters, oldData->parameters) 1335 || compare_string(data->content_parameters, 1336 oldData->content_parameters)) { 1337 *updated = true; 1338 } 1339 1340 // add new children and update existing ones 1341 status_t error = B_OK; 1342 for (int32 i = 0; i < data->child_count; i++) { 1343 user_partition_data* childData = data->children[i]; 1344 BPartition* child = (BPartition*)childData->user_data; 1345 if (child) { 1346 // old partition 1347 error = child->_Update(childData, updated); 1348 if (error != B_OK) 1349 return error; 1350 } else { 1351 // new partition 1352 *updated = true; 1353 child = new(nothrow) BPartition; 1354 if (!child) 1355 return B_NO_MEMORY; 1356 1357 error = child->_SetTo(fDevice, this, childData); 1358 if (error != B_OK) { 1359 delete child; 1360 return error; 1361 } 1362 1363 childData->user_data = child; 1364 } 1365 } 1366 return error; 1367 } 1368 1369 1370 void 1371 BPartition::_RemoveChild(int32 index) 1372 { 1373 int32 count = CountChildren(); 1374 if (!fPartitionData || index < 0 || index >= count) 1375 return; 1376 1377 // delete the BPartition and its children 1378 delete ChildAt(index); 1379 1380 // compact the children array 1381 for (int32 i = index + 1; i < count; i++) 1382 fPartitionData->children[i - 1] = fPartitionData->children[i]; 1383 fPartitionData->child_count--; 1384 } 1385 1386 1387 BPartition* 1388 BPartition::_ChildAt(int32 index) const 1389 { 1390 if (index < 0 || index >= fPartitionData->child_count) 1391 return NULL; 1392 return (BPartition*)fPartitionData->children[index]->user_data; 1393 } 1394 1395 1396 int32 1397 BPartition::_CountChildren() const 1398 { 1399 return fPartitionData->child_count; 1400 } 1401 1402 1403 int32 1404 BPartition::_CountDescendants() const 1405 { 1406 int32 count = 1; 1407 for (int32 i = 0; BPartition* child = _ChildAt(i); i++) 1408 count += child->_CountDescendants(); 1409 return count; 1410 } 1411 1412 1413 int32 1414 BPartition::_Level() const 1415 { 1416 int32 level = 0; 1417 const BPartition* ancestor = this; 1418 while ((ancestor = ancestor->Parent())) 1419 level++; 1420 return level; 1421 } 1422 1423 1424 bool 1425 BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level) 1426 { 1427 return visitor->Visit(this, level); 1428 } 1429 1430 1431 BPartition* 1432 BPartition::_VisitEachDescendant(BDiskDeviceVisitor* visitor, int32 level) 1433 { 1434 if (level < 0) 1435 level = _Level(); 1436 if (_AcceptVisitor(visitor, level)) 1437 return this; 1438 for (int32 i = 0; BPartition* child = ChildAt(i); i++) { 1439 if (BPartition* result = child->_VisitEachDescendant(visitor, 1440 level + 1)) { 1441 return result; 1442 } 1443 } 1444 return NULL; 1445 } 1446 1447 1448 const user_partition_data* 1449 BPartition::_PartitionData() const 1450 { 1451 return fDelegate ? fDelegate->PartitionData() : fPartitionData; 1452 } 1453 1454 1455 bool 1456 BPartition::_HasContent() const 1457 { 1458 return ContentType() != NULL; 1459 } 1460 1461 1462 bool 1463 BPartition::_SupportsOperation(uint32 flag, uint32 whileMountedFlag, 1464 bool* whileMounted) const 1465 { 1466 if (fDelegate == NULL) 1467 return false; 1468 1469 uint32 supported = fDelegate->SupportedOperations(flag | whileMountedFlag); 1470 1471 if (whileMounted) 1472 *whileMounted = supported & whileMountedFlag; 1473 1474 return (supported & flag) != 0; 1475 } 1476 1477 1478 bool 1479 BPartition::_SupportsChildOperation(const BPartition* child, uint32 flag) const 1480 { 1481 if (fDelegate == NULL || (child != NULL && child->fDelegate == NULL)) 1482 return false; 1483 1484 uint32 supported = fDelegate->SupportedChildOperations( 1485 child != NULL ? child->fDelegate : NULL, flag); 1486 1487 return (supported & flag) != 0; 1488 } 1489 1490 1491 status_t 1492 BPartition::_CreateDelegates() 1493 { 1494 if (fDelegate != NULL || fPartitionData == NULL) 1495 return B_NO_INIT; 1496 1497 // create and init delegate 1498 fDelegate = new(nothrow) Delegate(this); 1499 if (fDelegate == NULL) 1500 return B_NO_MEMORY; 1501 1502 status_t error = fDelegate->InitHierarchy(fPartitionData, 1503 fParent != NULL ? fParent->fDelegate : NULL); 1504 if (error != B_OK) 1505 return error; 1506 1507 // create child delegates 1508 int32 count = _CountChildren(); 1509 for (int32 i = 0; i < count; i++) { 1510 BPartition* child = _ChildAt(i); 1511 error = child->_CreateDelegates(); 1512 if (error != B_OK) 1513 return error; 1514 } 1515 1516 return B_OK; 1517 } 1518 1519 1520 status_t 1521 BPartition::_InitDelegates() 1522 { 1523 // init delegate 1524 status_t error = fDelegate->InitAfterHierarchy(); 1525 if (error != B_OK) 1526 return error; 1527 1528 // recursively init child delegates 1529 int32 count = CountChildren(); 1530 for (int32 i = 0; i < count; i++) { 1531 error = ChildAt(i)->_InitDelegates(); 1532 if (error != B_OK) 1533 return error; 1534 } 1535 1536 return B_OK; 1537 } 1538 1539 1540 void 1541 BPartition::_DeleteDelegates() 1542 { 1543 // recursively delete child delegates 1544 int32 count = CountChildren(); 1545 for (int32 i = count - 1; i >= 0; i--) 1546 ChildAt(i)->_DeleteDelegates(); 1547 1548 // delete delegate 1549 delete fDelegate; 1550 fDelegate = NULL; 1551 1552 // Commit suicide, if the delegate was our only link to reality (i.e. 1553 // there's no physically existing partition we represent). 1554 if (fPartitionData == NULL) 1555 delete this; 1556 } 1557 1558 1559 bool 1560 BPartition::_IsModified() const 1561 { 1562 if (fDelegate == NULL) 1563 return false; 1564 1565 return fDelegate->IsModified(); 1566 } 1567