diff options
-rw-r--r-- | plugins/plugin_undelete_partitions/Makefile | 38 | ||||
-rw-r--r-- | plugins/plugin_undelete_partitions/__init__.py | 20 | ||||
-rw-r--r-- | plugins/plugin_undelete_partitions/_undelpart.c | 760 | ||||
-rw-r--r-- | plugins/plugin_undelete_partitions/undeletePartition.py | 143 |
4 files changed, 961 insertions, 0 deletions
diff --git a/plugins/plugin_undelete_partitions/Makefile b/plugins/plugin_undelete_partitions/Makefile new file mode 100644 index 0000000..1b615be --- /dev/null +++ b/plugins/plugin_undelete_partitions/Makefile @@ -0,0 +1,38 @@ +# Makefile +# +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com> +# +# 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# + +PYVER := $(shell python -c 'import sys; print sys.version[0:3]') +PYTHON = python$(PYVER) +PYTHONINCLUDE = /usr/include/$(PYTHON) + + +CFLAGS = -I$(PYTHONINCLUDE) + +PYMODULE = _undelpart.so + +#Build with libparted (parted-1.8.3 and higher have a pkg-config file) +LDFLAGS = $(shell pkg-config --libs libparted) + +build: + $(CC) $(CFLAGS) -shared -fPIC -o _undelpart.so _undelpart.c $(LDFLAGS) + +clean: + rm -f _undelpart.so *.pyc diff --git a/plugins/plugin_undelete_partitions/__init__.py b/plugins/plugin_undelete_partitions/__init__.py new file mode 100644 index 0000000..cae0a96 --- /dev/null +++ b/plugins/plugin_undelete_partitions/__init__.py @@ -0,0 +1,20 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com> +# +# 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import undeletePartition +def get_plugin(): + return undeletePartition.UndeletePartition diff --git a/plugins/plugin_undelete_partitions/_undelpart.c b/plugins/plugin_undelete_partitions/_undelpart.c new file mode 100644 index 0000000..ef6448b --- /dev/null +++ b/plugins/plugin_undelete_partitions/_undelpart.c @@ -0,0 +1,760 @@ +/* +* First Aid Kit - diagnostic and repair tool for Linux +* Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com> +* +* 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*/ + + +#include <Python.h> +#include <assert.h> +#include <string.h> +#include "parted/parted.h" + +/* + * Simple representation of the partition. + */ +typedef struct{ + int partnum; + PedSector partstart; + PedSector partend; +} partElem; + + +/* + * Helper function + */ + +/* + * Returns a disk with the path of the device. + */ +static PedDisk * +_getDiskFromPath(char * path){ + PedDevice * dev; + PedDisk * disk; + /* Try to create the device with the path */ + dev = ped_device_get(path); + if(dev == NULL) + return NULL; + + /* Read the partition table off of the device */ + disk = ped_disk_new(dev); + if(disk == NULL) + return NULL; + + return disk; +} + +/* + * Return the partition type. + */ +static PedPartitionType +_disk_get_part_type_for_sector (PedDisk* disk, PedSector sector) +{ + PedPartition* extended; + + extended = ped_disk_extended_partition (disk); + if (!extended + || !ped_geometry_test_sector_inside (&extended->geom, sector)) + return 0; + + return PED_PARTITION_LOGICAL; +} + +/* + * Create a python list from one partElement struct + */ +static PyObject * +_getPPartList( int partnum, int partstart, int partend ){ + PyObject * num, * start, * end; + PyObject * list; + /* + * Create a new temporary list. and add all the related values + */ + num = PyString_FromFormat("%d",partnum); + start = PyLong_FromLong(partstart); + end = PyLong_FromLong(partend); + list = PyList_New(3); + if(num == NULL || start == NULL || end == NULL || + list == NULL || + PyList_SetItem(list, 0, num) == -1 || + PyList_SetItem(list, 1, start) == -1 || + PyList_SetItem(list, 2, end) == -1){ + goto handle_error; + } + return list; + + handle_error: + + Py_XDECREF(num); + Py_XDECREF(start); + Py_XDECREF(end); + Py_XDECREF(list); + return NULL; +} + +/* + * Create a array of partElem with the python list + */ +static partElem +_getCPartList( PyObject * list ){ + partElem _list = {0}; + _list.partnum = -1; + + // check that its a list. + if(!PyList_Check(list)) + return _list; + + // check that it has three elements. + if(PyList_Size(list) < 3) + return _list; + + // Populate the _partList array. + _list.partnum = PyInt_AsLong( PyList_GetItem(list, 0) ); + _list.partstart = PyLong_AsLong( PyList_GetItem(list, 1) ); + _list.partend = PyLong_AsLong( PyList_GetItem(list, 2) ); + if( PyErr_Occurred()) + return _list; + return _list; +} + +static int MEGABYTE_SECTORS (PedDevice* dev) +{ + return PED_MEGABYTE_SIZE / dev->sector_size; +} + + +/* + * Returns the partition if it is rescuable. Null if nothing was done. + */ +static PedPartition * +rescuable(PedDisk * disk, PedSector start, PedSector end){ + + //PedDisk * clone; + PedSector s; + PedGeometry * probed; + PedGeometry sect_geom; + PedGeometry entire_dev; + PedPartition * part = NULL; + PedConstraint disk_constraint, * part_constraint; + PedPartitionType part_type; + PedFileSystemType * fs_type; + + /* Initialize the entire_dev geom for the contraint calculation */ + ped_geometry_init(&entire_dev, disk->dev, 0, disk->dev->length); + part_type = _disk_get_part_type_for_sector (disk, (start + end) / 2); + + ped_exception_fetch_all(); //dont show errors + + for (s = start; s < end; s++) { + + /* Get a part from the specific s sector with the device constraint */ + ped_geometry_init (§_geom, disk->dev, s, 1); + ped_constraint_init (&disk_constraint, ped_alignment_any, ped_alignment_any, + §_geom, &entire_dev, 1, disk->dev->length); + + part = ped_partition_new (disk, part_type, NULL, s, end); +//printf("1\n"); + if(!part){ + ped_disk_remove_partition(disk, part); + ped_constraint_done(&disk_constraint); + part = NULL; + continue; + } + +//printf("2\n"); + /* add the partition to the disk */ + if(!ped_disk_add_partition(disk, part, &disk_constraint)){ + ped_disk_remove_partition(disk, part); + ped_constraint_done(&disk_constraint); + part = NULL; + continue; + } + +//printf("3\n"); + /* try to detect filesystem in the partition region */ + fs_type = ped_file_system_probe(&part->geom); + if(!fs_type){ + ped_disk_remove_partition(disk, part); + ped_constraint_done(&disk_constraint); + part = NULL; + continue; + } + +//printf("4\n"); + /* try to find the exact region the filesystem ocupies */ + probed = ped_file_system_probe_specific(fs_type, &part->geom); + if(!probed){ + ped_disk_remove_partition(disk, part); + ped_constraint_done(&disk_constraint); + ped_geometry_destroy(probed); + part = NULL; + continue; + } + +//printf("5\n"); + /* see if probed is inside the partition region */ + if(!ped_geometry_test_inside(&part->geom, probed)) { + ped_disk_remove_partition(disk, part); + ped_constraint_done(&disk_constraint); + ped_geometry_destroy(probed); + part = NULL; + continue; + } + +//printf("6\n"); + /* create a constraint for the probed region */ + part_constraint = ped_constraint_exact (probed); + +//printf("7\n"); + /* set the region for the partition */ + if (!ped_disk_set_partition_geom (part->disk, part, part_constraint, + probed->start, probed->end)) { + ped_disk_remove_partition(disk, part); + ped_constraint_done(part_constraint); + ped_constraint_done(&disk_constraint); + ped_geometry_destroy(probed); + part = NULL; + continue; + } +//printf("8\n"); + break; + } + ped_exception_leave_all();// show errors. +// if(part != NULL){ +// ped_constraint_done(part_constraint); +// ped_constraint_done(&disk_constraint); +// ped_geometry_destroy(probed); +// ped_geometry_destroy(entire_dev); +// } + printf("returnrin\n"); + return part; +} + +/* + * Copy from parted. + */ +static int add_partition(PedPartition * part){ + const PedFileSystemType* fs_type; + PedGeometry* probed; + PedExceptionOption ex_opt; + PedConstraint* constraint; + char* found_start; + char* found_end; + + fs_type = ped_file_system_probe (&part->geom); + if (!fs_type) + return 0; + probed = ped_file_system_probe_specific (fs_type, &part->geom); + if (!probed) + return 0; + + if (!ped_geometry_test_inside (&part->geom, probed)) { + ped_geometry_destroy (probed); + return 0; + } + + constraint = ped_constraint_exact (probed); + if (!ped_disk_set_partition_geom (part->disk, part, constraint, + probed->start, probed->end)) { + ped_constraint_destroy (constraint); + return 0; + } + ped_constraint_destroy (constraint); + + found_start = ped_unit_format (probed->dev, probed->start); + found_end = ped_unit_format (probed->dev, probed->end); + + ped_partition_set_system (part, fs_type); + ped_disk_commit (part->disk); + return 1; +} + +/* Pythong facing functions. + * + * Returns a dictionary of the form { DISK : [None, None, None, None] ...} + */ +static PyObject * +undelpart_getDiskList(PyObject * self, PyObject * args){ + + PedDevice * dev; + + PyObject * dict; + PyObject * list; + PyObject * diskName; + + int i; + + dict = PyDict_New(); + if(dict == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new dictionary."); + goto handle_error; + } + + /* Search for the disks on the system */ + ped_device_probe_all(); + + for(dev=ped_device_get_next(dev); dev ; dev=ped_device_get_next(dev)){ + /* + * Build the list for this particular disk and fill it with Python + * None Objects. + */ + list = PyList_New(4); + if(list == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error; + } + for(i=0 ; i < 4 ; i++){ //We set all items to None. + if(PyList_SetItem(list, i, Py_None) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error setting up the None valued list."); + goto handle_error; + } + } + + /* + * Take the name out of the PedDevice structure and place it as a + * dictionary key. Use the device path. + */ + diskName = Py_BuildValue("s", dev->path); + if(diskName == NULL){ + PyErr_SetString(PyExc_StandardError, + "Error creating key for dictionary."); + goto handle_error; + } + + if(PyDict_SetItem(dict, diskName, list) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error while creating the dictionary entry"); + goto handle_error; + } + } + + /* If the dictionary's length is 0, something is wrong. */ + if(PyDict_Size(dict) == 0){ + PyErr_SetString(PyExc_StandardError, + "libparted was unable to get a disk list. Are you root?"); + goto handle_error; + } + + return dict; + + handle_error: + assert(PyErr_Occurred()); + + Py_XDECREF(diskName); + Py_XDECREF(list); + Py_XDECREF(dict); + + return NULL; +} + +/* + * Returns a list of partitions that are present in the disk but not in its + * partition table. If the disk does not exist it returns None. If the disk + * has no rescueable partitions it returns a void list. Most of this is + * a copy of the parted code. + */ +static PyObject * +undelpart_getRescuable(PyObject * self, PyObject * args){ + + PedDisk * disk, * clone; + PedDevice * dev; + PedPartition * part; + PedPartition * recoverablePart = NULL; + PedSector current, start, end; + + PyObject * tempList; + PyObject * partitions; + + char * path; + + if(!PyArg_ParseTuple(args, "s", &path)){ + PyErr_SetString(PyExc_TypeError, "Argument is not a String"); + goto handle_error; + } + + /* Build the empty list*/ + partitions = PyList_New(0); + if(partitions == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error; + } + + /* create the disk an dev */ + disk = _getDiskFromPath(path); + if(disk == NULL){ + PyErr_SetString(PyExc_StandardError, "Error reading disk information."); + goto handle_error; + } + dev = disk->dev; + + /* + * We start looking for the partitions. The partitions will be detected if + * it contains a filesystem. The basic idea is to traverse all the partitions + * and look for holes in between. When a hole is found, we look for a + * partition inside the hole. + */ + start = (PedSector)0; + current = start; + end = dev->length; + part = ped_disk_next_partition(disk, NULL); + while(part){ + + /* We clone the disk to avoid strangeness in the loop with the disk object */ + clone = ped_disk_duplicate(disk); + if(clone == NULL){ + part = ped_disk_next_partition(disk, part); + continue; + } + + printf(" current %d, part->geomS %d, part->geom %d, num %d\n", current, part->geom.start, part->geom.end, part->num); + if(part->num == -1 && part->geom.start < part->geom.end){ + /* There might be a partition between current and part->geom.start */ + recoverablePart = rescuable(clone, part->geom.start, part->geom.end); + //printf("after the rescuable\n"); + if(recoverablePart != NULL){ + /* create the python object */ + tempList = _getPPartList(recoverablePart->num, + recoverablePart->geom.start, + recoverablePart->geom.end); + /* Append the list to the return value */ + if(tempList == NULL || PyList_Append(partitions, tempList) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error creating the partition information."); + goto handle_error; + } + /* free used objects */ + ped_disk_remove_partition(clone, recoverablePart); + ped_disk_destroy(clone); + recoverablePart = NULL; + clone = NULL; + } + } + //printf("next partitino\n"); + part = ped_disk_next_partition(disk, part); + } + + if(disk != NULL) + ped_disk_destroy(disk); + + return partitions; + + handle_error: + assert(PyErr_Occurred()); + + if(disk != NULL ) + ped_disk_destroy(disk); + + Py_XDECREF(partitions); + Py_XDECREF(tempList); + + return NULL; +} + +/* + * Returns a list of valid partitions at time of scan. + */ +static PyObject * +undelpart_getPartitionList(PyObject * self, PyObject * args){ + + PedDisk * disk; + PedDevice * dev; + PedPartition * part; //libparted object + + PyObject * partList; //python list of partitions + PyObject * tempList; //python temporary object to hold the temprorary list. + + char * path; + + if(!PyArg_ParseTuple(args, "s", &path)){ + PyErr_SetString(PyExc_TypeError, "Argument is not a String"); + goto handle_error; + } + + /* create the disk an dev */ + disk = _getDiskFromPath(path); + if(disk == NULL){ + PyErr_SetString(PyExc_StandardError, "Error reading disk information."); + goto handle_error; + } + dev = disk->dev; + + /* Create the python list that we are to fill */ + partList = PyList_New(0); + if(partList == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error; + } + + /* Get all the active partitions from disk */ + for(part = ped_disk_next_partition(disk, NULL) ; + part ; part = ped_disk_next_partition(disk, part)){ + if(part->num < 0) + continue; + + tempList = _getPPartList(part->num, + part->geom.start, + part->geom.end); + /* Append the list to the return value */ + if(tempList == NULL || PyList_Append(partList, tempList) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error appending the partition to the list."); + goto handle_error; + } + } + + return partList; + + handle_error: + assert(PyErr_Occurred()); + + Py_XDECREF(partList); + Py_XDECREF(tempList); + + return NULL; +} + +/* + * Set the partition table for a specific disk. It recieves a part number and + * start and end sectors for a specific partitions. If the partition is in + * the list but not in the system, the function will try to add it. If the + * partition is in the system but not in the table, it will be removed. + * The object recieved must be (string), [[(string), ing, int],...] + */ +static PyObject * +undelpart_setPartitionList(PyObject * self, PyObject * args){ + + PedPartitionType part_type; + PedDisk * disk; + PedDevice * dev; + PedPartition * part; //individual partitions. + + PyObject * partList; //list of partitions. + PyObject * tempList; + PyObject * partNum, * partStart, * partEnd; + + partElem * _partList; //Resulting list from the python object. + partElem * _partListSystem; //partitions that are in the system. + int partListSize = 0; //The size of both python and local lists. + int i,j; + int inTheList, inTheSystem; + char * path; + + /* Check the arguments */ + if(!PyArg_ParseTuple(args, "sO", &path, &partList)){ + PyErr_SetString(PyExc_TypeError, "Arguments passed are of the wrong type."); + goto handle_error; + } + if(! PyList_Check(partList)){ + PyErr_SetString(PyExc_TypeError, + "The object that was passed is not a list."); + goto handle_error; + } + + /* Put the values of the list into a array of partElem */ + partListSize = PyList_Size(partList); + _partList[partListSize+1]; + for(i=0; i < partListSize ; i++){ + _partList[i] = _getCPartList(PyList_GetItem(partList, i)); + if( PyErr_Occurred() || _partList[i].partnum == -1 ) + goto handle_error; + } + _partList[partListSize].partnum = '\0'; + + /* create the disk an dev */ + disk = _getDiskFromPath(path); + if(disk == NULL){ + PyErr_SetString(PyExc_StandardError, "Error reading disk information."); + goto handle_error; + } + dev = disk->dev; + + /* Travers all the disks and erase the ones that are not on the list */ + inTheList = 0; + _partListSystem[partListSize+1]; + _partListSystem[0].partnum = '\0'; + j=0; //it will be the offset of _partListSystem + for(part = ped_disk_next_partition(disk, NULL) ; part ; + part = ped_disk_next_partition(disk, part)){ + if(part->num < 0) + continue; + + /*look at the list*/ + for(i=0 ; _partList[i].partnum != '\0' ; i++){ + if(part->num == _partList[i].partnum){ + inTheList = 1; + _partListSystem[j] = _partList[i]; + _partListSystem[++j].partnum = '\0'; + break; + } + } + + if(! inTheList){ + if(ped_disk_remove_partition(disk, part)){ + PyErr_SetString(PyExc_StandardError, + "Could not remove partition from disk."); + goto handle_error; + } + } + inTheList = 0; + } + + /* Travers all the disks and erase the ones that are not on the system */ + inTheSystem = 0; + for(i=0; _partList[i].partnum != '\0' ; i++){ + for(j=0; _partListSystem[j].partnum != '\0' ; j++){ + if(_partList[i].partnum == _partListSystem[j].partnum){ + inTheSystem = 1; + break; + } + } + + if(! inTheSystem){ + /* try to add the partition */ + + part_type = _disk_get_part_type_for_sector(disk, + (_partList[i].partstart + _partList[i].partend) /2 ); + part = ped_partition_new (disk, part_type, NULL, + _partList[i].partstart, _partList[i].partend); + if(part) + add_partition(part); + inTheSystem=0; + } + } + + return Py_True; + + handle_error: + + assert(PyErr_Occurred()); + + return NULL; +} + +/* + * On a specific disk try to rescue a list of partitions. Return the list of partitions + * that was recovered. The partitions should be in the [[partNum, start, end]...] + * format. + */ +static PyObject * +undelpart_rescue(PyObject * self, PyObject * args){ + + PedDisk * disk; + PedDevice * dev; + PedPartition * part; + PedPartitionType part_type; + + PyObject * partList; + PyObject * rescuedParts; + PyObject * tempList; + PyObject * partNum, * partStart, *partEnd; + + partElem * _partList; + char * path; + int partListSize; + int i; + + /* Check the arguments */ + if(!PyArg_ParseTuple(args, "sO", &path, &partList)){ + PyErr_SetString(PyExc_TypeError, "Arguments are not valid (String, [])"); + goto handle_error; + } + if(! PyList_Check(partList)){ + PyErr_SetString(PyExc_TypeError, + "The object that was passed is not a list."); + goto handle_error; + } + + /* Build the empty list, this is the return value. */ + rescuedParts = PyList_New(0); + if(rescuedParts == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error; + } + + /* Put the values of the list into a array of partElem */ + partListSize = PyList_Size(partList); + _partList[partListSize+1]; + for(i=0; i < partListSize ; i++){ + _partList[i] = _getCPartList(PyList_GetItem(partList, i)); + if( PyErr_Occurred() || _partList[i].partnum == -1) + goto handle_error; + } + _partList[partListSize].partnum = '\0'; + + /* create the disk an dev */ + disk = _getDiskFromPath(path); + if(disk == NULL){ + PyErr_SetString(PyExc_StandardError, "Error reading disk information."); + goto handle_error; + } + dev = disk->dev; + + /* Try to add each partition. */ + for(i=0 ; i < partListSize ; i++){ + part_type = _disk_get_part_type_for_sector(disk, + (_partList[i].partstart + _partList[i].partend) /2 ); + part = ped_partition_new (disk, part_type, NULL, + _partList[i].partstart, _partList[i].partend); + if(part && add_partition(part)){ + tempList = _getPPartList(part->num, part->geom.start, part->geom.end); + /* Append the list to the return value */ + if(tempList == NULL || PyList_Append(rescuedParts, tempList) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error creating the partition information."); + goto handle_error; + } + } + } + + return rescuedParts; + + handle_error: + assert(PyErr_Occurred()); + + return NULL; +} + +static struct PyMethodDef undelpart_methods [] = { + { "getDiskList", + (PyCFunction)undelpart_getDiskList, + METH_VARARGS, "Generaly returns the system disk list. Receives nothing." }, + { "getRescuable", + (PyCFunction)undelpart_getRescuable, + METH_VARARGS, "Get a list of partitions from a specific disk that might " + "be rescuable. It returns the partitions that are not in the partition " + "table but where present after a disk scan. It expects the disk name."}, + { "getPartitionList", + (PyCFunction)undelpart_getPartitionList, + METH_VARARGS, "Get the partition list off of a certain disk. This is intended " + "to be used as a backup. It returns the number of the partition, start " + "sector and the end sector."}, + { "setPartitionList", + (PyCFunction)undelpart_setPartitionList, + METH_VARARGS, "This does NOT add new partitions to the disk. It scans the " + "disk and deletes partitions that are in the disk partition table but " + "not in the list."}, + { "rescue", + (PyCFunction)undelpart_rescue, + METH_VARARGS, "Try to put the list of rescuable partitions into the partition " + "table. If the partitions are already there, nothing will be done. A list " + "of rescued partitions is returned. This does NOT delete any partitions."} +}; + +void init_undelpart(void){ + (void) Py_InitModule("_undelpart", undelpart_methods); +} diff --git a/plugins/plugin_undelete_partitions/undeletePartition.py b/plugins/plugin_undelete_partitions/undeletePartition.py new file mode 100644 index 0000000..dc6d9a6 --- /dev/null +++ b/plugins/plugin_undelete_partitions/undeletePartition.py @@ -0,0 +1,143 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com> +# +# 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +from pyfirstaidkit.plugins import Plugin,Flow +from pyfirstaidkit.returns import * +import signal +import _undelpart + +class UndeletePartition(Plugin): + """Plugin to detect and correct deleted partitions from system disks. + + Uses parted libriary to search for partitions that are not included in + the partition table of a disk. If it is possible, this plugin will put + the partition back into the parition table so it is visible to the + system again. + """ + + flows = Flow.init(Plugin) + # We have not resotre in the noBackup flow because we have no information to restore with. + flows["noBackup"] = Flow({ + Plugin.initial: {Return: "prepare"}, + "prepare" : {ReturnSuccess: "diagnose"}, + "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "fix"}, + "fix" : {ReturnSuccess: "clean", ReturnFailure: "clean"}, + "clean" : {ReturnSuccess: Plugin.final} + }, description="This flow skips the backup test. Use with care.") + + name = "Undelete Partitions" + version = "0.1.0" + author = "Joel Andres Granados" + def __init__(self, *args, **kwargs): + Plugin.__init__(self, *args, **kwargs) + # The reporting object + self.reporting = kwargs.get('reporting') + + # Dictionary that will hold the partitions that are not included in the + # partition table of a certain disk and can be recovered. It will also + # house the initial partition table and the partition table that is a + # result of running the fix. The structure is: + # slef.disks={diskname: [ [recoverables], initialPT, finalPT ], .... } + self.disks = {} + + def prepare(self): + # For now there is no real action in the prepare task. + self._result=ReturnSuccess + self.reporting.info("Prepare task", UndeletePartition.name) + + def diagnose(self): + self.reporting.info("Beginning Diagnose...", UndeletePartition.name) + self.disks = _undelpart.getDiskList() + self.reporting.info("%s: Disks present in the system %s" % + (UndeletePartition.name, self.disks.keys()), self ) + # When we find a rescuable partition we change this to true. + rescuablePresent = False + for key, value in self.disks.iteritems(): + self.disks[key] = [ _undelpart.getRescuable(key), _undelpart.getPartitionTable(key), [] ] + if len(self.disks[key][0]) > 0: + self.reporting.info("Found %s recoverable partitions in %s disk." % + (self.disks[key], key), UndeletePartition.name ) + rescuablePresent = True + if not rescuablePresent: + self._result = ReturnSuccess + self.reporting.info("Did not find any partitions that need rescueing.", + UndeletePartition.name) + else: + self._result = ReturnFailure + + def backup(self): + self.reporting.info("Backing up partition table." , UndeletePartition.name) + # We actually already have the backup of the partition table in the self.disks dict. + # Lets check anyway. + backupSane = True + for disk, members in self.disks.iteritems(): + if members[1] == None or len(members[1]) <= 0: + # We don't really have the partition table backup. + self.reporting.info("%s: Couldn't backup the partition table for %s."% + (UndeletePartition.name, disk), self) + self.reporting.info("To force the recovery of this disk without the backup " \ + "please run the flow named noBackup from this plugin.", UndeletePartition.name) + backupSane = False + self._result = ReturnFailure + + if backupSane: + self._result = ReturnSuccess + + def fix(self): + self.reporting.info("Lets see if I can fix this... Starting fix task.", UndeletePartition.name ) + self._result = ReturnSuccess + try: + for disk, members in self.disks.iteritems(): + if len(members[0]) > 0: + self.reporting.info("Recovering %s from %s."% (members[0], disk),UndeletePartition.name) + recoveredDisks = _undelpart.rescue(disk, members[0]) + self.reporting.info("Recovered %s of %s from %s partitions."%(recoveredDisks, + members[0], disk),UndeletePartition.name ) + self.disks[disk][2] = _undelpart.getPartitionTable(disk) + elif len(members[0]) == 0: + self.reporting.info("Nothing to recover on disk %s."%disk,UndeletePartition.name ) + else: + self_result = ReturnFailure + break + except KeyboardInterrupt, e: + self.reporting.info("Received a user interruption... Moving to Restore task.",UndeletePartition.name) + # The user might want to keep on pushing ctrl-c, lets lock the SIGINT signal. + signal.signal(signal.SIGINT, keyboaordInterruptHandler) + self.reporting.info("Please wait until the original partition table is recovered.",UndeletePartition.name) + self._result = ReturnFailure + + def restore(self): + self.reporting.info("Starting Restoring task." , UndeletePartition.name) + # All the disks that have a new partition are ok, Lets make sure that we + # restore all of the other disks. + for disk, members in self.disks.iteritems(): + if len(members[2]) == 0: + self.reporting.info("Restoring %s partition table."%disk,UndeletePartition.name) + _undelpart.setPartitionTable(disk, members[1]) + else: + self.reporting.info("Disk %s does not need restore."%disk, UndeletePartition.name) + + # Return the signal to its previous state. + signal.signal(signal.SIGINT, signal.SIG_DFL) + self._result = ReturnSuccess + + def clean(self): + self.reporting.info("Cleanning...",UndeletePartition.name) + self._result = ReturnSuccess + +def keyboardInterruptHandler(signum, frame): + pass |