summaryrefslogtreecommitdiffstats
path: root/plugins/plugin_undelete_partitions/undeletePartition.py
blob: 47566d266edd6abe4e0cedcbf93873f974cda321 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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