#!/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)