2d690920ac
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12360 a95241bf-73f2-0310-859d-f6bbb57e9c96
488 lines
14 KiB
C++
488 lines
14 KiB
C++
// KDiskDeviceJobGenerator.cpp
|
|
|
|
#include "KDiskDevice.h"
|
|
#include "KDiskDeviceJob.h"
|
|
#include "KDiskDeviceJobFactory.h"
|
|
#include "KDiskDeviceJobQueue.h"
|
|
#include "KDiskDeviceJobGenerator.h"
|
|
#include "KDiskDeviceUtils.h"
|
|
#include "KDiskSystem.h"
|
|
#include "KShadowPartition.h"
|
|
|
|
// move_info
|
|
struct KDiskDeviceJobGenerator::move_info {
|
|
KPartition *partition;
|
|
off_t position;
|
|
off_t target_position;
|
|
off_t size;
|
|
};
|
|
|
|
// constructor
|
|
KDiskDeviceJobGenerator::KDiskDeviceJobGenerator(
|
|
KDiskDeviceJobFactory *jobFactory, KDiskDevice *device,
|
|
KDiskDeviceJobQueue *jobQueue)
|
|
: fJobFactory(jobFactory),
|
|
fDevice(device),
|
|
fJobQueue(jobQueue),
|
|
fMoveInfos(NULL),
|
|
fMoveInfoCount(0),
|
|
fPartitionIDs(NULL),
|
|
fPartitionIDCount(0)
|
|
{
|
|
if (fDevice && !fJobQueue) {
|
|
fJobQueue = new(nothrow) KDiskDeviceJobQueue(fDevice);
|
|
if (fDevice->ShadowPartition()) {
|
|
fMoveInfoCount = max(fDevice->CountDescendants(),
|
|
fDevice->ShadowPartition()->CountDescendants());
|
|
}
|
|
fMoveInfos = new(nothrow) move_info[fMoveInfoCount];
|
|
fPartitionIDs = new(nothrow) partition_id[fMoveInfoCount];
|
|
}
|
|
}
|
|
|
|
// destructor
|
|
KDiskDeviceJobGenerator::~KDiskDeviceJobGenerator()
|
|
{
|
|
delete[] fMoveInfos;
|
|
delete[] fPartitionIDs;
|
|
delete fJobQueue;
|
|
}
|
|
|
|
// JobFactory
|
|
KDiskDeviceJobFactory *
|
|
KDiskDeviceJobGenerator::JobFactory() const
|
|
{
|
|
return fJobFactory;
|
|
}
|
|
|
|
// Device
|
|
KDiskDevice *
|
|
KDiskDeviceJobGenerator::Device() const
|
|
{
|
|
return fDevice;
|
|
}
|
|
|
|
// JobQueue
|
|
KDiskDeviceJobQueue *
|
|
KDiskDeviceJobGenerator::JobQueue() const
|
|
{
|
|
return fJobQueue;
|
|
}
|
|
|
|
// DetachJobQueue
|
|
KDiskDeviceJobQueue *
|
|
KDiskDeviceJobGenerator::DetachJobQueue()
|
|
{
|
|
KDiskDeviceJobQueue *jobQueue = fJobQueue;
|
|
fJobQueue = NULL;
|
|
return jobQueue;
|
|
}
|
|
|
|
// GenerateJobs
|
|
status_t
|
|
KDiskDeviceJobGenerator::GenerateJobs()
|
|
{
|
|
// check parameters
|
|
if (!fJobFactory || !fDevice || !fJobQueue || !fMoveInfos || !fPartitionIDs
|
|
|| fJobQueue->Device() != fDevice || !fDevice->ShadowPartition()) {
|
|
return B_BAD_VALUE;
|
|
}
|
|
// 1) Generate jobs for all physical partitions that don't have an
|
|
// associated shadow partition, i.e. those that shall be deleted.
|
|
// 2) Generate uninitialize jobs for all partition whose initialization
|
|
// changes, also those that shall be initialized with a disk system.
|
|
// This simplifies moving and resizing.
|
|
status_t error = _GenerateCleanupJobs(fDevice);
|
|
if (error != B_OK)
|
|
return error;
|
|
// Generate jobs that move and resize the remaining physical partitions
|
|
// to their final position/size.
|
|
error = _GeneratePlacementJobs(fDevice);
|
|
if (error != B_OK)
|
|
return error;
|
|
// Generate the remaining jobs in one run: initialization, creation of
|
|
// partitions, and changing of name, content name, type, parameters, and
|
|
// content parameters.
|
|
error = _GenerateRemainingJobs(NULL, fDevice->ShadowPartition());
|
|
if (error != B_OK)
|
|
return error;
|
|
return B_OK;
|
|
}
|
|
|
|
// _AddJob
|
|
status_t
|
|
KDiskDeviceJobGenerator::_AddJob(KDiskDeviceJob *job)
|
|
{
|
|
if (!job)
|
|
return B_NO_MEMORY;
|
|
if (!fJobQueue->AddJob(job)) {
|
|
delete job;
|
|
return B_NO_MEMORY;
|
|
}
|
|
return B_OK;
|
|
}
|
|
|
|
// _GenerateCleanupJobs
|
|
status_t
|
|
KDiskDeviceJobGenerator::_GenerateCleanupJobs(KPartition *partition)
|
|
{
|
|
// TODO: Depending on how this shall be handled, we might want to unmount
|
|
// all descendants of a partition to be uninitialized or removed.
|
|
if (KPartition *shadow = partition->ShadowPartition()) {
|
|
if (shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION) {
|
|
// partition changes initialization
|
|
status_t error = _AddJob(fJobFactory->CreateUninitializeJob(
|
|
partition->ID()));
|
|
if (error != B_OK)
|
|
return error;
|
|
} else {
|
|
// recurse
|
|
for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) {
|
|
status_t error = _GenerateCleanupJobs(child);
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
}
|
|
} else if (KPartition *parent = partition->Parent()) {
|
|
// create job and add it to the queue
|
|
status_t error = _AddJob(fJobFactory->CreateDeleteChildJob(
|
|
parent->ID(), partition->ID()));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
return B_OK;
|
|
}
|
|
|
|
// _GeneratePlacementJobs
|
|
status_t
|
|
KDiskDeviceJobGenerator::_GeneratePlacementJobs(KPartition *partition)
|
|
{
|
|
if (KPartition *shadow = partition->ShadowPartition()) {
|
|
// Don't resize/move partitions that have an unrecognized contents.
|
|
// They must have been uninitialized before.
|
|
if (shadow->Status() == B_PARTITION_UNRECOGNIZED
|
|
&& (shadow->Size() != partition->Size()
|
|
|| shadow->Offset() != partition->Offset())) {
|
|
return B_ERROR;
|
|
}
|
|
if (shadow->Size() > partition->Size()) {
|
|
// size grows: resize first
|
|
status_t error = _GenerateResizeJob(partition);
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
// place the children
|
|
status_t error = _GenerateChildPlacementJobs(partition);
|
|
if (error != B_OK)
|
|
return error;
|
|
if (shadow->Size() < partition->Size()) {
|
|
// size shrinks: resize now
|
|
status_t error = _GenerateResizeJob(partition);
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
}
|
|
return B_OK;
|
|
}
|
|
|
|
// _GenerateResizeJob
|
|
status_t
|
|
KDiskDeviceJobGenerator::_GenerateResizeJob(KPartition *partition)
|
|
{
|
|
KPartition *shadow = partition->ShadowPartition();
|
|
if (!shadow)
|
|
return B_BAD_VALUE;
|
|
if (!partition->Parent())
|
|
return B_ERROR;
|
|
return _AddJob(fJobFactory->CreateResizeJob(partition->Parent()->ID(),
|
|
partition->ID(),
|
|
shadow->Size()));
|
|
}
|
|
|
|
// _GenerateChildPlacementJobs
|
|
status_t
|
|
KDiskDeviceJobGenerator::_GenerateChildPlacementJobs(KPartition *partition)
|
|
{
|
|
KPartition *shadow = partition->ShadowPartition();
|
|
// nothing to do, if the partition contains no partitioning system or
|
|
// shall be re-initialized
|
|
if (!shadow->DiskSystem()
|
|
|| (shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) {
|
|
return B_OK;
|
|
}
|
|
// first resize all children that shall shrink and place their descendants
|
|
int32 childCount = 0;
|
|
int32 moveForth = 0;
|
|
int32 moveBack = 0;
|
|
for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) {
|
|
if (KPartition *childShadow = child->ShadowPartition()) {
|
|
// add a move_info for the child
|
|
move_info &info = fMoveInfos[childCount];
|
|
childCount++;
|
|
info.partition = child;
|
|
info.position = child->Offset();
|
|
info.target_position = childShadow->Offset();
|
|
info.size = child->Size();
|
|
if (info.position < info.target_position)
|
|
moveForth++;
|
|
else if (info.position > info.target_position)
|
|
moveBack++;
|
|
// resize the child, if it shall shrink
|
|
if (childShadow->Size() < child->Size()) {
|
|
status_t error = _GeneratePlacementJobs(child);
|
|
if (error != B_OK)
|
|
return error;
|
|
info.size = childShadow->Size();
|
|
}
|
|
}
|
|
}
|
|
// sort the move infos
|
|
if (childCount > 0 && moveForth + moveBack > 0) {
|
|
qsort(fMoveInfos, childCount, sizeof(move_info),
|
|
_CompareMoveInfoPosition);
|
|
}
|
|
// move the children to their final positions
|
|
while (moveForth + moveBack > 0) {
|
|
int32 moved = 0;
|
|
if (moveForth < moveBack) {
|
|
// move children back
|
|
for (int32 i = 0; i < childCount; i++) {
|
|
move_info &info = fMoveInfos[i];
|
|
if (info.position > info.target_position) {
|
|
if (i == 0
|
|
|| info.target_position >= fMoveInfos[i - 1].position
|
|
+ fMoveInfos[i - 1].size) {
|
|
// check OK -- the partition wouldn't be moved before
|
|
// the end of the preceding one
|
|
status_t error = _GenerateMoveJob(info.partition);
|
|
if (error != B_OK)
|
|
return error;
|
|
info.position = info.target_position;
|
|
moved++;
|
|
moveBack--;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// move children forth
|
|
for (int32 i = childCount - 1; i >= 0; i--) {
|
|
move_info &info = fMoveInfos[i];
|
|
if (info.position > info.target_position) {
|
|
if (i == childCount - 1
|
|
|| info.target_position + info.size
|
|
<= fMoveInfos[i - 1].position) {
|
|
// check OK -- the partition wouldn't be moved before
|
|
// the end of the preceding one
|
|
status_t error = _GenerateMoveJob(info.partition);
|
|
if (error != B_OK)
|
|
return error;
|
|
info.position = info.target_position;
|
|
moved++;
|
|
moveForth--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// terminate, if no partition could be moved
|
|
if (moved == 0)
|
|
return B_ERROR;
|
|
}
|
|
// now resize all children that shall grow/keep their size and place
|
|
// their descendants
|
|
for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) {
|
|
if (KPartition *childShadow = child->ShadowPartition()) {
|
|
if (childShadow->Size() >= child->Size()) {
|
|
status_t error = _GeneratePlacementJobs(child);
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
return B_OK;
|
|
}
|
|
|
|
// _GenerateMoveJob
|
|
status_t
|
|
KDiskDeviceJobGenerator::_GenerateMoveJob(KPartition *partition)
|
|
{
|
|
KPartition *shadow = partition->ShadowPartition();
|
|
if (!shadow)
|
|
return B_BAD_VALUE;
|
|
if (!partition->Parent())
|
|
return B_ERROR;
|
|
// collect all descendants whose contents need to be moved
|
|
fPartitionIDCount = 0;;
|
|
status_t error = _CollectContentsToMove(partition);
|
|
if (error != B_OK)
|
|
return B_OK;
|
|
// add the job
|
|
return _AddJob(fJobFactory->CreateMoveJob(partition->Parent()->ID(),
|
|
partition->ID(),
|
|
shadow->Offset(), fPartitionIDs,
|
|
fPartitionIDCount));
|
|
}
|
|
|
|
// _CollectContentsToMove
|
|
status_t
|
|
KDiskDeviceJobGenerator::_CollectContentsToMove(KPartition *partition)
|
|
{
|
|
KPartition *shadow = partition->ShadowPartition();
|
|
if (!shadow)
|
|
return B_OK;
|
|
if (shadow->Status() == B_PARTITION_UNRECOGNIZED)
|
|
return B_ERROR;
|
|
// if the partition has contents, push its ID
|
|
if (shadow->DiskSystem()
|
|
&& !(shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) {
|
|
_PushPartitionID(partition->ID());
|
|
}
|
|
// recurse
|
|
for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) {
|
|
status_t error = _CollectContentsToMove(child);
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
return B_OK;
|
|
}
|
|
|
|
// _GenerateRemainingJobs
|
|
status_t
|
|
KDiskDeviceJobGenerator::_GenerateRemainingJobs(KShadowPartition *parent,
|
|
KShadowPartition *partition)
|
|
{
|
|
KPhysicalPartition *physicalPartition = partition->PhysicalPartition();
|
|
// get correct ID of partition and parent partition
|
|
partition_id partitionID = (physicalPartition ? physicalPartition->ID()
|
|
: partition->ID());
|
|
partition_id parentID = -1;
|
|
if (parent) {
|
|
parentID = (parent->PhysicalPartition()
|
|
? parent->PhysicalPartition()->ID() : parent->ID());
|
|
}
|
|
// create the partition, if not existing yet
|
|
if (!physicalPartition) {
|
|
if (!parent)
|
|
return B_BAD_VALUE;
|
|
status_t error = _AddJob(fJobFactory->CreateCreateChildJob(
|
|
parentID, partitionID, partition->Offset(), partition->Size(),
|
|
partition->Type(), partition->Parameters()));
|
|
if (error != B_OK)
|
|
return error;
|
|
} else {
|
|
// partition already exists: set non-content properties
|
|
// name
|
|
if ((partition->ChangeFlags() & B_PARTITION_CHANGED_NAME)
|
|
|| compare_string(partition->Name(), physicalPartition->Name())) {
|
|
if (!parent)
|
|
return B_BAD_VALUE;
|
|
status_t error = _AddJob(fJobFactory->CreateSetNameJob(
|
|
parentID, partitionID, partition->Name()));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
// type
|
|
if ((partition->ChangeFlags() & B_PARTITION_CHANGED_TYPE)
|
|
|| compare_string(partition->Type(), physicalPartition->Type())) {
|
|
if (!parent)
|
|
return B_BAD_VALUE;
|
|
status_t error = _AddJob(fJobFactory->CreateSetTypeJob(
|
|
parentID, partitionID, partition->Type()));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
// parameters
|
|
if ((partition->ChangeFlags() & B_PARTITION_CHANGED_PARAMETERS)
|
|
|| compare_string(partition->Parameters(),
|
|
physicalPartition->Parameters())) {
|
|
if (!parent)
|
|
return B_BAD_VALUE;
|
|
status_t error = _AddJob(fJobFactory->CreateSetParametersJob(
|
|
parentID, partitionID, partition->Parameters()));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
}
|
|
if (partition->DiskSystem()) {
|
|
// initialize the partition, if required
|
|
if (partition->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION) {
|
|
status_t error = _AddJob(fJobFactory->CreateInitializeJob(
|
|
partitionID, partition->DiskSystem()->ID(),
|
|
partition->ContentName(), partition->ContentParameters()));
|
|
if (error != B_OK)
|
|
return error;
|
|
} else {
|
|
// partition not (re-)initialized, set content properties
|
|
// content name
|
|
if ((partition->ChangeFlags() & B_PARTITION_CHANGED_NAME)
|
|
|| compare_string(partition->ContentName(),
|
|
physicalPartition->ContentName())) {
|
|
status_t error = _AddJob(fJobFactory->CreateSetContentNameJob(
|
|
partitionID, partition->ContentName()));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
// content parameters
|
|
if ((partition->ChangeFlags() & B_PARTITION_CHANGED_PARAMETERS)
|
|
|| compare_string(partition->ContentParameters(),
|
|
physicalPartition->ContentParameters())) {
|
|
status_t error = _AddJob(
|
|
fJobFactory->CreateSetContentParametersJob(
|
|
partitionID, partition->ContentParameters()));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
// defragment
|
|
if (partition->ChangeFlags()
|
|
& B_PARTITION_CHANGED_DEFRAGMENTATION) {
|
|
status_t error = _AddJob(fJobFactory->CreateDefragmentJob(
|
|
partitionID));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
// check / repair
|
|
bool repair = (partition->ChangeFlags()
|
|
& B_PARTITION_CHANGED_REPAIR);
|
|
if ((partition->ChangeFlags() & B_PARTITION_CHANGED_CHECK)
|
|
|| repair) {
|
|
status_t error = _AddJob(fJobFactory->CreateRepairJob(
|
|
partitionID, repair));
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
// recurse
|
|
for (int32 i = 0; KPartition *_child = partition->ChildAt(i); i++) {
|
|
// ToDo: was:
|
|
// KShadowPartition *child = dynamic_cast<KShadowPartition *>(_child);
|
|
KShadowPartition *child = static_cast<KShadowPartition *>(_child);
|
|
if (!child)
|
|
return B_BAD_VALUE;
|
|
status_t error = _GenerateRemainingJobs(partition, child);
|
|
if (error != B_OK)
|
|
return error;
|
|
}
|
|
return B_OK;
|
|
}
|
|
|
|
// _PushPartitionID
|
|
void
|
|
KDiskDeviceJobGenerator::_PushPartitionID(partition_id id)
|
|
{
|
|
fPartitionIDs[fPartitionIDCount++] = id;
|
|
}
|
|
|
|
// _CompareMoveInfoOffset
|
|
int
|
|
KDiskDeviceJobGenerator::_CompareMoveInfoPosition(const void *_a,
|
|
const void *_b)
|
|
{
|
|
const move_info *a = static_cast<const move_info *>(_a);
|
|
const move_info *b = static_cast<const move_info *>(_b);
|
|
if (a->position < b->position)
|
|
return -1;
|
|
if (a->position > b->position)
|
|
return 1;
|
|
return 0;
|
|
}
|