summaryrefslogtreecommitdiff
path: root/myraidmaths.py
blob: c0d3b8bff076c645e83ed4439ead442fdb4b7897 (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
#!/usr/bin/env python

# RaidGuessFS, a FUSE pseudo-filesystem to guess RAID parameters of a damaged device
# Copyright (C) 2015 Ludovic Pouzenc <ludovic@pouzenc.fr>
#
# This file is part of RaidGuessFS.
#
# RaidGuessFS 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.
#
# RaidGuessFS 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 RaidGuessFS. If not, see <http://www.gnu.org/licenses/>

import numpy

class MyRaidMaths():
    """Auxiliary class, managing RAID layer, mathematics algorithms"""

    @staticmethod
    def xor_blocks(fd_list, offset, size):
        """Compute bitwise XOR against a bunch of disks slice"""
        logging.info("Enter xor_blocks(fd_list(%i),0x%011x,%d)"%(len(fd_list), offset, size))
        
        if size % 8 != 0:
            raise ValueError('xor_blocks : size must be multiple of 8')
        dt = numpy.dtype('<Q8')

        fd_list[0].seek(offset)
        str_b1=fd_list[0].read(size)
        numpy_b1 = numpy.fromstring(str_b1, dtype=dt)
        all_zero = (numpy.count_nonzero(numpy_b1) == 0 )
        any_zero = all_zero

        for fd in fd_list[1:]:
            fd.seek(offset)
            str_b2=fd.read(size)
            numpy_b2 = numpy.fromstring(str_b2, dtype=dt)
            b2_zero = (numpy.count_nonzero(numpy_b2) == 0 )
            if all_zero == True:
                all_zero = b2_zero
            if any_zero == False:
                any_zero = b2_zero

            numpy.bitwise_xor(numpy_b1,numpy_b2,numpy_b1)

        if all_zero == True:
            result = 'z'
        elif numpy.count_nonzero(numpy_b1) == 0:
            if any_zero:
                result = 'g'
            else:
                result = 'G'
        else:
            result = 'b'

        #import sys, binascii
        #print sys.stderr.write(binascii.hexlify(numpy_b1))
        return (result,numpy_b1.tostring())

    @staticmethod
    def apply_raid_layout(wanted_raid_offset, wanted_read_size, raid_type, raid_layout, raid_chunk_size, raid_disk_count, raid_start, nested_subraid):
        """Returns disk numbers, offset and so on to known where read data for a given RAID offset/size and layout"""

        if raid_type == '0':
            segment_no = wanted_raid_offset / raid_chunk_size
            segment_off = wanted_raid_offset % raid_chunk_size
            stripe_no = segment_no / raid_disk_count
            subraid_no = -1
            par_disk = -1
            data_disk = segment_no % raid_disk_count
            off_disk = raid_start + stripe_no * raid_chunk_size + segment_off
            aligned_read_size = min(wanted_read_size, (segment_no+1) * raid_chunk_size - wanted_raid_offset)

        elif raid_type == '1':
            segment_no = -1
            segment_off = -1
            stripe_no = -1
            subraid_no = -1
            par_disk = 1
            data_disk = 0
            off_disk = raid_start + wanted_raid_offset
            aligned_read_size = wanted_read_size

        elif raid_type == '5':
            segment_no = wanted_raid_offset / raid_chunk_size
            segment_off = wanted_raid_offset % raid_chunk_size
            stripe_no = segment_no / (raid_disk_count-1)
            subraid_no = -1

            if raid_layout in ['ls','la']:
                par_disk = (raid_disk_count-1) - (stripe_no % raid_disk_count)
            else: # raid_layout in ['rs','ra']:
                par_disk = stripe_no % raid_disk_count

            if raid_layout in ['ls','rs']:
                data_disk = (par_disk+1 + (segment_no % (raid_disk_count-1)) ) % raid_disk_count
            else: # raid_layout in ['la','ra']:
                data_disk = segment_no % (raid_disk_count-1)
                if data_disk >= par_disk:
                    data_disk = data_disk + 1

            off_disk = raid_start + stripe_no * raid_chunk_size + segment_off
            # Note : could make error-free shorter reads than asked but convince the reader to be chunck aligned, which is great for perf
            aligned_read_size = min(wanted_read_size, (segment_no+1) * raid_chunk_size - wanted_raid_offset)

        elif raid_type == '5+0':
            subraid_disk_count = raid_disk_count / nested_subraid
            segment_no = wanted_raid_offset / raid_chunk_size
            segment_off = wanted_raid_offset % raid_chunk_size
            stripe_no = segment_no / (raid_disk_count - nested_subraid)
            subraid_no = (segment_no / (subraid_disk_count-1) ) % nested_subraid

            if raid_layout in ['ls','la']:
                subraid_par_disk = (subraid_disk_count-1) - (stripe_no % subraid_disk_count)
            else: # raid_layout in ['rs','ra']:
                subraid_par_disk = stripe_no % subraid_disk_count

            if raid_layout in ['ls','rs']:
                subraid_data_disk = (subraid_par_disk+1 + (segment_no % (subraid_disk_count-1)) ) % subraid_disk_count
            else: # raid_layout in ['la','ra']:
                subraid_data_disk = segment_no % (subraid_disk_count-1)
                if subraid_data_disk >= subraid_par_disk:
                    subraid_data_disk = subraid_data_disk + 1

            par_disk  = subraid_no * subraid_disk_count + subraid_par_disk
            data_disk = subraid_no * subraid_disk_count + subraid_data_disk

            off_disk = raid_start + stripe_no * raid_chunk_size + segment_off
            # Note : could make error-free shorter reads than asked but convince the reader to be chunck aligned, which is great for perf
            aligned_read_size = min(wanted_read_size, (segment_no+1) * raid_chunk_size - wanted_raid_offset)

        return (segment_no, segment_off, stripe_no, subraid_no, par_disk, data_disk, off_disk, aligned_read_size)