summaryrefslogtreecommitdiff
path: root/myraid.py
diff options
context:
space:
mode:
authorLudovic Pouzenc <lpouzenc@lud-GB1>2015-06-12 08:34:26 +0200
committerLudovic Pouzenc <lpouzenc@lud-GB1>2015-06-12 08:34:26 +0200
commit5943acb92ce0159e9f482748e4fa4aadddae6851 (patch)
treee4fc66dabda439f8a98535f10287018e871c2e41 /myraid.py
parent49c830d2d70547c31cab1b1f0bc9f26d77d62a7e (diff)
downloadraidguessfs-5943acb92ce0159e9f482748e4fa4aadddae6851.tar.gz
raidguessfs-5943acb92ce0159e9f482748e4fa4aadddae6851.tar.bz2
raidguessfs-5943acb92ce0159e9f482748e4fa4aadddae6851.zip
Initial import.
RAID 5 xor parity checking is working, data reading on left-assymetric RAID 5 is working. Nothing done on other RAID types.
Diffstat (limited to 'myraid.py')
-rw-r--r--myraid.py154
1 files changed, 154 insertions, 0 deletions
diff --git a/myraid.py b/myraid.py
new file mode 100644
index 0000000..60e8b28
--- /dev/null
+++ b/myraid.py
@@ -0,0 +1,154 @@
+#!/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 logging, numpy
+
+class MyRaid():
+ """Auxiliary class, managing RAID layer"""
+
+ def __init__(self, *args, **kwargs):
+ self.raid_start = 0
+ self.raid_end = 0
+ self.raid_size = 0
+ self.raid_sector_size = 512
+ self.raid_chunk_size = 65536
+ self.raid_disk_order = range(15)
+ self.raid_types = [ '0', '1', '5', '5+0' ]
+
+ def _update_raid_size(self):
+ if self.raid_end > self.raid_start:
+ self.raid_size = self.raid_end - self.raid_start
+ else:
+ self.raid_size = 0
+
+ def get_raid_start(self):
+ return self.raid_start
+
+ def get_raid_chunk_size(self):
+ return self.raid_chunk_size
+
+ def get_raid_disk_order(self):
+ return self.raid_disk_order
+
+ def get_raid_disk_order_str(self):
+ return ' '.join(map(str,self.raid_disk_order))
+
+ def set_raid_start(self, new_raid_start):
+ """Update the start offset of raid data on underlying disks"""
+ self.raid_start = new_raid_start
+ self._update_raid_size()
+
+ def set_raid_end(self, new_raid_end):
+ """Update the end offset of raid data on underlying disks"""
+ self.raid_end = new_raid_end
+ self._update_raid_size()
+
+ def set_raid_chunk_size(self, new_raid_chunk_size):
+ """Update the size of chucks of data (or slice size)"""
+ self.raid_chunk_size = new_raid_chunk_size
+
+ def set_raid_disk_order(self, new_raid_disk_order):
+ """Update the raid logical disk order"""
+ self.raid_disk_order = new_raid_disk_order
+
+
+ def read_data(self,raid_type,disks,offset,size):
+ """TODO"""
+ disk_count = len(self.raid_disk_order)
+
+ # This code is RAID 5 only
+
+ slice_no = offset / self.raid_chunk_size
+ slice_off = offset % self.raid_chunk_size
+ segment=slice_no/(disk_count-1)
+ par_disk=(disk_count-1) - (segment % disk_count) # TODO : equivalent a : segment-1 % disk_count ?
+ data_disk=( par_disk + 1 + (slice_no % (disk_count-1)) ) % disk_count
+ off_disk = self.raid_start + segment * self.raid_chunk_size + slice_off
+
+ size2 = min(size, (slice_no+1) * self.raid_chunk_size - offset)
+
+ logging.info("raid.read_data(%s): offset=%d,slice_no=%d,slice_off=%d,segment=%d,par_disk=%d,data_disk=%d,off_disk=%d,size2=%d,slice_off+size2=%d"
+ % (raid_type,offset,slice_no,slice_off,segment,par_disk,data_disk,off_disk,size2,slice_off+size2) )
+
+ data_fd = disks[self.raid_disk_order[data_disk]]
+ data_fd.seek(off_disk)
+ data = data_fd.read(size2)
+
+ # This kills performance but don't make short reads before EOF
+ #if size2 < size:
+ # data += self.read_data(self,raid_type,disks,offset+size2,size-size2)
+
+ return data
+
+ def xor_blocks(self,fd_list, offset, size):
+ """TODO"""
+ logging.info("Enter xor_blocks(fd_list,%d,%d)"%(offset, size))
+
+ assert(size % 8 == 0), "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'
+
+ logging.info("Exit. xor_blocks(fd_list,%d,%d)"%(offset, size))
+ return result
+
+ def check_data(self,raid_type,disks,offset,size):
+ """TODO"""
+ logging.warn("Enter check_data(%s,disks,%d,%d)"%(raid_type,offset,size))
+ #import binascii
+ #logging.warn(binascii.hexlify(numpy_b1))
+
+ #result = ''.join([ self.xor_blocks(disks, (offset+i)*self.raid_sector_size, self.raid_sector_size) for i in range(size)])
+ result = ''.join([ '0x%011x %c\n'%( (offset/16+i)*self.raid_sector_size, self.xor_blocks(disks, (offset/16+i)*self.raid_sector_size, self.raid_sector_size)) for i in range(size/16) ])
+ # TODO donner des offests RAID et pas disques
+
+ logging.warn("Exit. check_data(%s,disks,%d,%d)"%(raid_type,offset,size))
+
+ return result
+
+
+
+