fdisk / src / queue.c

/*
    GNU fdisk - a clone of Linux fdisk.

    Copyright (C) 2006
    Free Software Foundation, Inc.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/

#include <parted/parted.h>
#include "queue.h"


typedef struct {
        PedGeometry geom;
        const PedFileSystemType* type;
} MkfsParameters;

typedef struct {
        PedGeometry geom;
        PedGeometry new_geom;
} ResizeParameters;

typedef struct {
	PedGeometry src_geom;
        PedGeometry dst_geom;
} CopyParameters;


typedef struct {
	PedPartition *part;
} ForceParameters;

Queue* queue_new (PedDisk *disk, PedTimer *timer)
{
	Queue *queue = malloc(sizeof(Queue));
	queue->first = NULL;
	queue->last = NULL;
	queue->last_disk = disk;
	queue->timer = timer;
}

static void* parameters_mkfs_get(va_list args)
{
	MkfsParameters* param;

	PedGeometry		*geom = va_arg(args, PedGeometry*);
	const PedFileSystemType	*type = va_arg(args, PedFileSystemType*);
	
	if (!geom || !type)
		return NULL;
	
	param = malloc(sizeof(MkfsParameters));
	
	param->geom = *geom;
	param->type = type;
		
	return param;
}

static void* parameters_resize_get(va_list args)
{
	ResizeParameters* param;

	PedGeometry *geom	= va_arg(args, PedGeometry*);
	PedGeometry *new_geom	= va_arg(args, PedGeometry*);
	
	if (!geom || !new_geom)
		return NULL;
	
	param = malloc(sizeof(ResizeParameters));

	param->geom = *geom;
	param->new_geom = *new_geom;

	return param;
}

static void* parameters_copy_get (va_list args)
{
	CopyParameters* param;

	PedGeometry *src_geom = va_arg(args, PedGeometry *);
	PedGeometry *dst_geom = va_arg(args, PedGeometry *);

	if (!src_geom || !dst_geom)
		return NULL;


	param = malloc(sizeof(CopyParameters));

	param->src_geom = *src_geom;
	param->dst_geom = *dst_geom;

	return param;
}

static void* parameters_force_get (va_list args)
{
	ForceParameters* param;
	
	PedPartition *part = va_arg(args, PedPartition*);
	
	param = malloc(sizeof(ForceParameters));
	
	/* part = NULL when it is just used for forced intermediate disk comitting */
	param->part = part;
	
	return param;
}

/* Appends a new element to the queue */
int queue_push (Queue* queue, PedDisk *prev_disk, OperationType type, ...)
{
	va_list args;
	void *parameters;
	QueueElem *new_element;

	/* We get the parameters */
	va_start (args, type);
	switch (type) {
		case OPER_MKFS:
			parameters = parameters_mkfs_get(args);
			break;
		case OPER_RESIZE:
			parameters = parameters_resize_get(args);
			break;
		case OPER_COPY:
			parameters = parameters_copy_get(args);
			break;
		case OPER_FORCE_FSTYPE:
			parameters = parameters_force_get(args);
			break;
	}
	va_end(args);
	if (!parameters)
		return 0;
	
	/* We initialise the new element */
	new_element = malloc(sizeof(QueueElem));

	new_element->prev_disk = prev_disk;
	new_element->type = type;
	new_element->parameters = parameters;
	new_element->next = NULL;

	/* We append the element to the queue */
	if (queue->last)
		queue->last->next = new_element;
	else
		queue->first = new_element;
	queue->last = new_element;

}

/* Removes the first element from the queue and returns it */
static QueueElem* queue_shift(Queue* queue)
{
	QueueElem* element = queue->first;
	if (!element)
		return NULL;
	queue->first = element->next;
	if (!element->next)
		queue->last = NULL;
	return element;
}

/* TODO: This should be inline, but I'm not familiar with portability issues */
static void queue_element_destroy(QueueElem *elem)
{
	ped_disk_destroy(elem->prev_disk);
	free(elem->parameters);
	free(elem);
}

/* This runs through all the changes on the disk, and fixes the fs type,
 * if it was changed during a copy or move */
static int fix_filesystem(QueueElem* elem, PedGeometry* geom,
			  PedFileSystemType *fs_type)
{
	PedPartition *part;
	PedDisk *disk;
	int status = 1;
	while (elem) {
		disk = elem->prev_disk;
		part = ped_disk_get_partition_by_sector(disk, geom->start);
		if (!part)
			break;
		/* FIXME: If the partition has changed in size, 
		 * we don't know what to do */
		if (!ped_geometry_test_equal(&part->geom, geom))
			break;
		/* The user converted this to an extended partition */
		if (part->type & PED_PARTITION_EXTENDED)
			break;
		/* The user has changed the filesystem on this one, go away */
		if (elem->type == OPER_FORCE_FSTYPE) {
			ForceParameters *param = elem->parameters;
			if (param->part && ped_geometry_test_equal(&param->part->geom, geom))
				break;
		}
		
		status = status && ped_partition_set_system (part, fs_type);
		
		/* If this is about to get moved, or copied to, we don't care */
		if (elem->type == OPER_RESIZE) {
			ResizeParameters *param = elem->parameters;
			if (ped_geometry_test_equal(&param->geom, geom))
				break;
		}
		if (elem->type == OPER_COPY) {
			CopyParameters *param = elem->parameters;
			if (ped_geometry_test_equal(&param->dst_geom, geom))
				break;
		}
		elem = elem->next;
	}
	return status;
		      
}

static int commit_mkfs(QueueElem* elem, PedTimer *timer)
{
	MkfsParameters *param = elem->parameters;
	
	PedFileSystem *fs;
	fs = ped_file_system_create(&param->geom, param->type, timer);
	if (!fs)
		return 0;
	ped_file_system_close(fs);
	return 1;
}

static int commit_resize(QueueElem* elem, PedTimer *timer)
{
	ResizeParameters *param = elem->parameters;
	
	PedFileSystem *fs;
	int status;

	fs = ped_file_system_open(&param->geom);
	if (!fs)
		return 0;
	status = ped_file_system_resize(fs, &param->new_geom, timer);
	if (status)
		status = fix_filesystem(elem, &param->new_geom, fs->type);
	ped_file_system_close(fs);
	return status;

}


static int commit_copy(QueueElem* elem, PedTimer *timer)
{
	CopyParameters *param = elem->parameters;
	
	PedFileSystem *src_fs, *dst_fs;
	int status;
	
	src_fs = ped_file_system_open(&param->src_geom);
	if (!src_fs)
		return 0;
	dst_fs = ped_file_system_copy(src_fs, &param->dst_geom, timer);
	ped_file_system_close(src_fs);
	if (!dst_fs)
		return 0;
	status = fix_filesystem(elem, &param->dst_geom, dst_fs->type);
	ped_filesystem_close(dst_fs);
	return 1;
}

int queue_commit(Queue *queue)
{
	int status = 1;
	/* TODO: We should commit only to disk, and should commit to system once */
	while (queue->first) {
		/* First we need to commit to the disk */
		if (!ped_disk_commit(queue->first->prev_disk))
			return 0;
		/* Then we perform the operation */
		switch(queue->first->type) {
			case OPER_MKFS:
				status = commit_mkfs(queue->first,
				                     queue->timer);
				break;
			case OPER_RESIZE:
				status = commit_resize(queue->first,
				                       queue->timer);
				break;
			case OPER_COPY:
				status = commit_copy(queue->first,
				                     queue->timer);
				break;
			case OPER_FORCE_FSTYPE:
				break;
		}
		if (!status)
			return 0;
		queue_element_destroy(queue_shift(queue));
	
	}
	if(!ped_disk_commit(queue->last_disk))
		return 0;
}

void queue_destroy(Queue *queue)
{
	QueueElem *element;
	while(element = queue_shift(queue))
		queue_element_destroy(element);
	ped_disk_destroy(queue->last_disk);
	ped_timer_destroy(queue->timer);
	free(queue);
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.