diff options
Diffstat (limited to 'plugins/undelparts/undeletePartition.py')
-rw-r--r-- | plugins/undelparts/undeletePartition.py | 171 |
1 files changed, 171 insertions, 0 deletions
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 |