summaryrefslogtreecommitdiffstats
path: root/plugins/undelparts
diff options
context:
space:
mode:
authorJoel Andres Granados <jgranado@redhat.com>2008-07-15 21:53:31 +0200
committerJoel Andres Granados <jgranado@redhat.com>2008-07-15 22:08:47 +0200
commite22490465c43b9b223a6c5ca945d3a7d271c5717 (patch)
tree93720d3f46e571c9f9787616a2871c24f9c2d039 /plugins/undelparts
parentafa753016f8d43ce2c886e146093470cd5877f7f (diff)
downloadfirstaidkit-e22490465c43b9b223a6c5ca945d3a7d271c5717.tar.gz
firstaidkit-e22490465c43b9b223a6c5ca945d3a7d271c5717.tar.xz
firstaidkit-e22490465c43b9b223a6c5ca945d3a7d271c5717.zip
Continue with the name standarization and simplification.
- The "_p" is not needed. Avoiding the suffix makes the names simpler. - When the plugin needs to be separated into varios subplugins, use the _ to signify that a subplugin is related to a father plugin. Like rpm_lowlevel, this say that lowlevel is part of the rpm plugin. - When naming a plugin keep the name SHORT and simple. - Be sure to name all the plugin packages as "plugin-name"
Diffstat (limited to 'plugins/undelparts')
-rw-r--r--plugins/undelparts/Makefile38
-rw-r--r--plugins/undelparts/__init__.py20
-rw-r--r--plugins/undelparts/_undelpart.c578
-rw-r--r--plugins/undelparts/undeletePartition.py171
4 files changed, 807 insertions, 0 deletions
diff --git a/plugins/undelparts/Makefile b/plugins/undelparts/Makefile
new file mode 100644
index 0000000..a9ef6cc
--- /dev/null
+++ b/plugins/undelparts/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/undelparts/__init__.py b/plugins/undelparts/__init__.py
new file mode 100644
index 0000000..b3402ea
--- /dev/null
+++ b/plugins/undelparts/__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/undelparts/_undelpart.c b/plugins/undelparts/_undelpart.c
new file mode 100644
index 0000000..aab4f07
--- /dev/null
+++ b/plugins/undelparts/_undelpart.c
@@ -0,0 +1,578 @@
+/*
+* 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};
+
+ // check that its a list.
+ if(!PyList_Check(list)){
+ PyErr_SetString(PyExc_StandardError,
+ "Error malformed argument, list does not contian lisit.");
+ return _list;
+ }
+
+ // check that it has three elements.
+ if(PyList_Size(list) < 3){
+ PyErr_SetString(PyExc_StandardError,
+ "Error Malformed argument, contained list is to small.");
+ return _list;
+ }
+
+ // Populate the _partList array.
+ _list.partnum = PyLong_AsLong( PyNumber_Long(PyList_GetItem(list, 0)) );
+ _list.partstart = PyLong_AsLong( PyList_GetItem(list, 1) );
+ _list.partend = PyLong_AsLong( PyList_GetItem(list, 2) );
+ if( PyErr_Occurred())
+ _list.partnum = '\0';
+ return _list;
+}
+
+static int
+MEGABYTE_SECTORS (PedDevice* dev)
+{
+ return PED_MEGABYTE_SIZE / dev->sector_size;
+}
+
+/*
+ * Tries to recover the partition part in the disk disk. Reutrns null if it
+ * was not possible, or the partition if it was.
+ */
+static PedPartition *
+add_partition(PedDisk * disk, partElem part){
+
+ //PedDisk * clone;
+ PedSector s;
+ PedGeometry * probed;
+ PedGeometry sect_geom;
+ PedGeometry entire_dev;
+ PedPartition * parttemp = NULL;
+ PedConstraint disk_constraint, * part_constraint;
+ PedPartitionType part_type;
+ PedFileSystemType * fs_type;
+
+ /* Initialize the entire_dev geom for the constraint calculation */
+ ped_geometry_init(&entire_dev, disk->dev, 0, disk->dev->length);
+ part_type = _disk_get_part_type_for_sector (disk, (part.partstart + part.partend) / 2);
+
+
+ /* The end is a temporary hack until we get the method of search done */
+ for (s = part.partstart; s < part.partstart+(part.partend-part.partstart)/10; s++) {
+
+ /* Get a part from the specific s sector with the device constraint */
+ ped_geometry_init (&sect_geom, disk->dev, s, 1);
+ ped_constraint_init (&disk_constraint, ped_alignment_any, ped_alignment_any,
+ &sect_geom, &entire_dev, 1, disk->dev->length);
+
+ parttemp = ped_partition_new (disk, part_type, NULL, s, part.partend);
+ if(!parttemp){
+ ped_disk_remove_partition(disk, parttemp);
+ ped_constraint_done(&disk_constraint);
+ parttemp = NULL;
+ continue;
+ }
+
+
+ /* add the partition to the disk */
+ ped_exception_fetch_all(); //dont show errors
+ if(!ped_disk_add_partition(disk, parttemp, &disk_constraint)){
+ ped_disk_remove_partition(disk, parttemp);
+ ped_constraint_done(&disk_constraint);
+ parttemp = NULL;
+ continue;
+ }
+ ped_exception_leave_all();// show errors.
+
+ /* try to detect filesystem in the partition region */
+ fs_type = ped_file_system_probe(&parttemp->geom);
+ if(!fs_type){
+ ped_disk_remove_partition(disk, parttemp);
+ ped_constraint_done(&disk_constraint);
+ parttemp = NULL;
+ continue;
+ }
+
+ /* try to find the exact region the filesystem ocupies */
+ probed = ped_file_system_probe_specific(fs_type, &parttemp->geom);
+ if(!probed){
+ ped_disk_remove_partition(disk, parttemp);
+ ped_constraint_done(&disk_constraint);
+ ped_geometry_destroy(probed);
+ parttemp = NULL;
+ continue;
+ }
+
+ /* see if probed is inside the partition region */
+ if(!ped_geometry_test_inside(&parttemp->geom, probed)) {
+ ped_disk_remove_partition(disk, parttemp);
+ ped_constraint_done(&disk_constraint);
+ ped_geometry_destroy(probed);
+ parttemp = NULL;
+ continue;
+ }
+
+ /* create a constraint for the probed region */
+ part_constraint = ped_constraint_exact (probed);
+
+ /* set the region for the partition */
+ if (!ped_disk_set_partition_geom (parttemp->disk, parttemp, part_constraint,
+ probed->start, probed->end)) {
+ ped_disk_remove_partition(disk, parttemp);
+ ped_constraint_done(part_constraint);
+ ped_constraint_done(&disk_constraint);
+ ped_geometry_destroy(probed);
+ parttemp = NULL;
+ continue;
+ }
+ ped_partition_set_system(parttemp, fs_type);
+ ped_disk_commit(disk);
+ ped_disk_commit_to_dev(disk);
+ ped_disk_commit_to_os(disk);
+ break;
+ }
+ return parttemp;
+}
+
+/* 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;
+ }
+
+ ped_device_free_all();
+ return dict;
+
+ handle_error:
+ assert(PyErr_Occurred());
+
+ Py_XDECREF(diskName);
+ Py_XDECREF(list);
+ Py_XDECREF(dict);
+ ped_device_free_all();
+
+ 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 errors. If the disk
+ * has no rescueable partitions it returns a void list. It is a list of
+ * possible partitions, it will NOT check for rescuability.
+ */
+static PyObject *
+undelpart_getRescuable(PyObject * self, PyObject * args){
+
+ PedDisk * disk;
+ PedPartition * part;
+
+ 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_destroy_disk;
+ }
+
+ /* create the disk */
+ disk = _getDiskFromPath(path);
+ if(disk == NULL){
+ PyErr_SetString(PyExc_StandardError, "Error reading disk information.");
+ goto handle_error_destroy_disk;
+ }
+
+ /*
+ * We start looking for the partitions. The partitions will be detected if
+ * the numpart is less than 1. The basic idea is to traverse all the partitions
+ * and look for holes in between.
+ */
+ for(part = ped_disk_next_partition(disk, NULL); part ;
+ part = ped_disk_next_partition(disk, part) ){
+ // All partitions with partnum less than 1 is a possibility.
+ if(part->num < 1 && part->geom.start < part->geom.end){
+ /* create the python object */
+ tempList = _getPPartList(part->num, part->geom.start, part->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_destroy_disk;
+ }
+ }
+ }
+ ped_disk_destroy(disk);
+ ped_device_free_all();
+
+ return partitions;
+
+ handle_error_destroy_disk:
+ ped_disk_destroy(disk);
+
+ handle_error:
+ assert(PyErr_Occurred());
+
+ Py_XDECREF(partitions);
+ Py_XDECREF(tempList);
+
+ ped_device_free_all();
+ 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;
+ }
+ }
+ ped_device_free_all();
+
+ return partList;
+
+ handle_error:
+ assert(PyErr_Occurred());
+
+ Py_XDECREF(partList);
+ Py_XDECREF(tempList);
+
+ ped_device_free_all();
+
+ 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;
+ PedPartition * part;
+
+ PyObject * partList;
+ PyObject * rescuedParts;
+ PyObject * tempList;
+
+ partElem * _partList = NULL;
+ char * path;
+ int partListSize = 0;
+ int i;
+
+ /* Check the arguments */
+ if(!PyArg_ParseTuple(args, "sO", &path, &partList)){
+ PyErr_SetString(PyExc_TypeError, "Arguments are not valid (String, List)");
+ 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 = malloc(sizeof(partElem)*partListSize+1);
+ if(!_partList){
+ PyErr_SetString(PyExc_StandardError, "Error allocating memory.");
+ goto handle_error;
+ }
+ for(i=0; i < partListSize ; i++){
+ _partList[i] = _getCPartList(PyList_GetItem(partList, i));
+ if( PyErr_Occurred() || _partList[i].partnum == '\0'){
+ 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;
+ }
+
+ /* Try to add each partition. */
+ for(i=0 ; _partList[i].partnum ; i++){
+ part = add_partition(disk, _partList[i]);
+ if(!part){
+ // could not rescue this partition. sorry
+ continue;
+ }else{
+ 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;
+ }
+ }
+ }
+ ped_disk_destroy(disk);
+ free(_partList);
+ ped_device_free_all();
+
+ return rescuedParts;
+
+ handle_error:
+ assert(PyErr_Occurred());
+
+ free(_partList);
+ ped_device_free_all();
+
+ 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."},
+ {"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/undelparts/undeletePartition.py b/plugins/undelparts/undeletePartition.py
new file mode 100644
index 0000000..47566d2
--- /dev/null
+++ b/plugins/undelparts/undeletePartition.py
@@ -0,0 +1,171 @@
+# 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 *
+from pyfirstaidkit.reporting import PLUGIN
+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 restore 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)
+
+ # 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
+
+ #
+ # The diagnose will not be a real diagnose but more of an informative task.
+ # It will report all the possible paritions that could house a rescuable
+ # partition.
+ #
+ def diagnose(self):
+ self._reporting.info("Beginning Diagnose...", origin = self, level = PLUGIN)
+ self.disks = _undelpart.getDiskList()
+ self._reporting.info("Disks present in the system %s"%self.disks.keys(),
+ origin = self, level = PLUGIN)
+ # When we find a rescuable partition we change this to true.
+ rescuablePresent = False
+ for disk, elements in self.disks.iteritems():
+ self.disks[disk] = [ _undelpart.getRescuable(disk), _undelpart.getPartitionList(disk), [] ]
+ if len(self.disks[disk][0]) > 0:
+ self._reporting.info("Possible partitions to recover in disk %s: %s"%(disk, self.disks[disk][0]),
+ origin = self, level = PLUGIN)
+ rescuablePresent = True
+ if not rescuablePresent:
+ self._result = ReturnSuccess
+ self._reporting.info("Did not find any partitions that need rescueing.",
+ origin = self, level = PLUGIN)
+ else:
+ self._result = ReturnFailure
+
+ def backup(self):
+ self._reporting.info("Backing up partition table." , origin = self, level = PLUGIN)
+ # 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("Couldn't backup the partition table for %s."%disk,
+ origin = self, level = PLUGIN)
+ self._reporting.info("To force the recovery of this disk without the backup " \
+ "please run the flow named noBackup from this plugin.",
+ origin = self, level = PLUGIN)
+ backupSane = False
+ self._result = ReturnFailure
+
+ if backupSane:
+ self._result = ReturnSuccess
+
+ #
+ # Every partition that we suspect is rescuable, (given that it has a partition table from
+ # wich we can recover if we mess up) we try to rescue. This will take a long time.
+ #
+ def fix(self):
+ self._reporting.info("Lets see if I can fix this... Starting fix task.",
+ origin = self, level = PLUGIN)
+ self._reporting.info("Might want to go and get a cup of coffee,"
+ "this could take a looooooong time...", origin = self, level = PLUGIN)
+ self._result = ReturnSuccess
+ rescued = []
+ try:
+ for disk, members in self.disks.iteritems():
+ if len(members[0]) > 0:#there are partitions to rescue :)
+ self._reporting.info("Trying to rescue %s from disk %s"%(members[0], disk),
+ origin = self, level = PLUGIN)
+ rescued = _undelpart.rescue(disk,members[0])
+ self._reporting.info("Partitions rescued: %s"%rescued,
+ origin = self, level = PLUGIN)
+ elif len(members[0]) == 0:
+ self._reporting.info("Nothing to rescue on disk %s."%disk,
+ origin = self, level = PLUGIN)
+ else:
+ self_result = ReturnFailure
+ break
+ except KeyboardInterrupt, e:
+ self._reporting.error("Received a user interruption... Moving to Restore task.",
+ origin = self, level = PLUGIN, action = None)
+ # 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.",
+ origin = self, level = PLUGIN)
+ self._result = ReturnFailure
+
+ #
+ # We are not really erasing anything, so recovering is kinda out of the point. That said
+ # anything can happen with partitioning. :) Lets get the current partitionList and try
+ # to add all the partitions that are not in the current part list but are in the backedup
+ # one.
+ #
+ def restore(self):
+ self._reporting.info("Starting Restoring task." , origin = self, level = PLUGIN)
+ tempPartList = []
+ backupPartList = []
+ for disk, members in self.disk.iteritems():
+ tempPartList = _undelpart.getPartitionList(disk)
+ backupPartList = members[1]
+ for part in backupPartList:
+ if part not in tempPartList:# we need to restore
+ self._reporting.info("Trying to restore partition %s on disk %s"%(part, disk),
+ origin = self, level = PLUGIN)
+ restore = _undelpart.rescue(disk, [part])
+ if len(restore) > 0:
+ self._reporting.info("Restored partition %s on disk %s"%(part, disk),
+ origin = self, level = PLUGIN)
+ else:
+ self._reporting.error("Could not restore partititon %s on disk %s"%(part, disk),
+ origin = self, level = PLUGIN, action = None)
+ # Return the signal to its previous state.
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ self._result = ReturnSuccess
+
+ def clean(self):
+ self._reporting.info("Cleanning...",origin = self, level = PLUGIN)
+ self._result = ReturnSuccess
+
+def keyboardInterruptHandler(signum, frame):
+ pass