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
compare_string(const char * str1,const char * str2)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
BPartition()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 */
~BPartition()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
Offset() const111 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
Size() const121 BPartition::Size() const
122 {
123 return _PartitionData()->size;
124 }
125
126
127 off_t
ContentSize() const128 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
BlockSize() const138 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
PhysicalBlockSize() const148 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
Index() const159 BPartition::Index() const
160 {
161 return _PartitionData()->index;
162 }
163
164
165 uint32
Status() const166 BPartition::Status() const
167 {
168 return _PartitionData()->status;
169 }
170
171
172 bool
ContainsFileSystem() const173 BPartition::ContainsFileSystem() const
174 {
175 return _PartitionData()->flags & B_PARTITION_FILE_SYSTEM;
176 }
177
178
179 bool
ContainsPartitioningSystem() const180 BPartition::ContainsPartitioningSystem() const
181 {
182 return _PartitionData()->flags & B_PARTITION_PARTITIONING_SYSTEM;
183 }
184
185
186 bool
IsDevice() const187 BPartition::IsDevice() const
188 {
189 return _PartitionData()->flags & B_PARTITION_IS_DEVICE;
190 }
191
192
193 bool
IsReadOnly() const194 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
IsMounted() const204 BPartition::IsMounted() const
205 {
206 return _PartitionData()->flags & B_PARTITION_MOUNTED;
207 // alternatively:
208 // return _PartitionData()->volume >= 0;
209 }
210
211
212 bool
IsBusy() const213 BPartition::IsBusy() const
214 {
215 return _PartitionData()->flags & B_PARTITION_BUSY;
216 }
217
218
219 bool
SupportsChildName() const220 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
Flags() const240 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*
Name() const255 BPartition::Name() const
256 {
257 return _PartitionData()->name;
258 }
259
260
261 BString
ContentName() const262 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*
RawContentName() const289 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*
Type() const299 BPartition::Type() const
300 {
301 return _PartitionData()->type;
302 }
303
304
305 const char*
ContentType() const306 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
ID() const322 BPartition::ID() const
323 {
324 return _PartitionData()->id;
325 }
326
327
328 const char*
Parameters() const329 BPartition::Parameters() const
330 {
331 return _PartitionData()->parameters;
332 }
333
334
335 const char*
ContentParameters() const336 BPartition::ContentParameters() const
337 {
338 return _PartitionData()->content_parameters;
339 }
340
341
342 status_t
GetDiskSystem(BDiskSystem * diskSystem) const343 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
GetPath(BPath * path) const357 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
GetVolume(BVolume * volume) const401 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
GetIcon(BBitmap * icon,icon_size which) const423 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
GetIcon(uint8 ** _data,size_t * _size,type_code * _type) const452 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
GetMountPoint(BPath * mountPoint) const494 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
Mount(const char * mountPoint,uint32 mountFlags,const char * parameters)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
Unmount(uint32 unmountFlags)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*
Device() const664 BPartition::Device() const
665 {
666 return fDevice;
667 }
668
669
670 BPartition*
Parent() const671 BPartition::Parent() const
672 {
673 return fParent;
674 }
675
676
677 BPartition*
ChildAt(int32 index) const678 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
CountChildren() const690 BPartition::CountChildren() const
691 {
692 if (fDelegate != NULL)
693 return fDelegate->CountChildren();
694
695 return _CountChildren();
696 }
697
698
699 int32
CountDescendants() const700 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*
FindDescendant(partition_id id) const710 BPartition::FindDescendant(partition_id id) const
711 {
712 IDFinderVisitor visitor(id);
713 return VisitEachDescendant(&visitor);
714 }
715
716
717 status_t
GetPartitioningInfo(BPartitioningInfo * info) const718 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*
VisitEachChild(BDiskDeviceVisitor * visitor) const730 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*
VisitEachDescendant(BDiskDeviceVisitor * visitor) const744 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
CanDefragment(bool * whileMounted) const753 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
Defragment() const761 BPartition::Defragment() const
762 {
763 if (fDelegate == NULL)
764 return B_NO_INIT;
765
766 return fDelegate->Defragment();
767 }
768
769
770 bool
CanRepair(bool checkOnly,bool * whileMounted) const771 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
Repair(bool checkOnly) const788 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
CanResize(bool * canResizeContents,bool * whileMounted) const798 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
ValidateResize(off_t * size) const818 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
Resize(off_t size)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
CanMove(BObjectList<BPartition> * unmovableDescendants,BObjectList<BPartition> * movableOnlyIfUnmounted) const885 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
ValidateMove(off_t * offset) const917 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
Move(off_t offset)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
CanSetName() const963 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
ValidateSetName(BString * name) const975 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
SetName(const char * name)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
CanSetContentName(bool * whileMounted) const997 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
ValidateSetContentName(BString * name) const1006 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
SetContentName(const char * name)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
CanSetType() const1026 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
ValidateSetType(const char * type) const1038 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
SetType(const char * type)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
CanEditParameters() const1060 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
GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,BPartitionParameterEditor ** editor)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
SetParameters(const char * parameters)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
CanEditContentParameters(bool * whileMounted) const1106 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
SetContentParameters(const char * parameters)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
GetNextSupportedType(int32 * cookie,BString * type) const1125 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
GetNextSupportedChildType(int32 * cookie,BString * type) const1142 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
IsSubSystem(const char * diskSystem) const1156 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
CanInitialize(const char * diskSystem) const1167 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
ValidateInitialize(const char * diskSystem,BString * name,const char * parameters)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
Initialize(const char * diskSystem,const char * name,const char * parameters)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
Uninitialize()1199 BPartition::Uninitialize()
1200 {
1201 return fDelegate->Uninitialize();
1202 }
1203
1204
1205 bool
CanCreateChild() const1206 BPartition::CanCreateChild() const
1207 {
1208 return _SupportsChildOperation(NULL, B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD);
1209 }
1210
1211
1212 status_t
ValidateCreateChild(off_t * offset,off_t * size,const char * type,BString * name,const char * parameters) const1213 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
CreateChild(off_t offset,off_t size,const char * type,const char * name,const char * parameters,BPartition ** child)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
CanDeleteChild(int32 index) const1235 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
DeleteChild(int32 index)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 */
BPartition(const BPartition &)1262 BPartition::BPartition(const BPartition &)
1263 {
1264 }
1265
1266
1267 /*! \brief Privatized assignment operator to avoid usage.
1268 */
1269 BPartition &
operator =(const BPartition &)1270 BPartition::operator=(const BPartition &)
1271 {
1272 return *this;
1273 }
1274
1275
1276 status_t
_SetTo(BDiskDevice * device,BPartition * parent,user_partition_data * data)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
_Unset()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
_RemoveObsoleteDescendants(user_partition_data * data,bool * updated)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
_Update(user_partition_data * data,bool * updated)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
_RemoveChild(int32 index)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*
_ChildAt(int32 index) const1437 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
_CountChildren() const1446 BPartition::_CountChildren() const
1447 {
1448 return fPartitionData->child_count;
1449 }
1450
1451
1452 int32
_CountDescendants() const1453 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
_Level() const1463 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
_AcceptVisitor(BDiskDeviceVisitor * visitor,int32 level)1474 BPartition::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level)
1475 {
1476 return visitor->Visit(this, level);
1477 }
1478
1479
1480 BPartition*
_VisitEachDescendant(BDiskDeviceVisitor * visitor,int32 level)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*
_PartitionData() const1498 BPartition::_PartitionData() const
1499 {
1500 return fDelegate ? fDelegate->PartitionData() : fPartitionData;
1501 }
1502
1503
1504 bool
_HasContent() const1505 BPartition::_HasContent() const
1506 {
1507 return ContentType() != NULL;
1508 }
1509
1510
1511 bool
_SupportsOperation(uint32 flag,uint32 whileMountedFlag,bool * whileMounted) const1512 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
_SupportsChildOperation(const BPartition * child,uint32 flag) const1528 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
_CreateDelegates()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
_InitDelegates()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
_DeleteDelegates()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
_IsModified() const1609 BPartition::_IsModified() const
1610 {
1611 if (fDelegate == NULL)
1612 return false;
1613
1614 return fDelegate->IsModified();
1615 }
1616