#!/usr/bin/env python # RaidGuessFS, a FUSE pseudo-filesystem to guess RAID parameters of a damaged device # Copyright (C) 2015 Ludovic Pouzenc # # 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 import os, multiprocessing, binascii, logging import mydisks def do_find_files(d,state): logging.info("Enter do_find_files()") try: state['state'] = 'initializing' ref_paths = state['filepaths'] ref_count = len(ref_paths) ref_fds = [None]*ref_count ref_sizes = [None]*ref_count ref_offset = [None]*ref_count ref_cur_sect = [None]*ref_count for ref_no in range(ref_count): path = state['filepaths'][ref_no] logging.debug("Try to open reffile '%s'"%path) ref_offset[ref_no] = 0 ref_sizes[ref_no] = os.lstat(path).st_size ref_fds[ref_no] = open(path, "r") ref_fds[ref_no].seek(0) ref_cur_sect[ref_no] = ref_fds[ref_no].read(512) logging.debug("Opened reffile '%s'"%path) start = 0 end = min(d.disks_size) one_percent = (end - start) / 100 one_percent = one_percent + ( (-one_percent)%512 ) logging.debug("start/end/1pc : %i / %i / %i"%(start,end,one_percent)) state['found'] = [] state['progress'] = 0 state['state'] = 'searching' for offset in range(start, end, 512): for disk_no in range(d.disk_count): d.disks[disk_no].seek(offset) data = d.disks[disk_no].read(512) for ref_no in range(ref_count): #TODO : gerer les buffers plus court que 512 if data == ref_cur_sect[ref_no]: #TODO : chercher la suite du fichier f = state['found'] if len(f) < 200: # TODO agreger les matches f.append((ref_paths[ref_no],ref_offset[ref_no],disk_no,offset)) state['found'] = f else: state['state'] = 'aborted' raise Exception('Aborting after too many matches') if offset % one_percent == 0: state['progress'] = state['progress'] + 1 state['state'] = 'finished' state['progress'] = 100 except Exception as e: logging.exception(e) logging.info("Exit. do_find_files()") def do_find_bootsect(d,state): logging.info("Enter do_find_bootsect()") try: state['state'] = 'initializing' ref_sig = binascii.unhexlify('55AA') start = 0 end = min(d.disks_size) one_percent = (end - start) / 100 one_percent = one_percent + ( (-one_percent)%512 ) logging.debug("start/end/1pc : %i / %i / %i"%(start,end,one_percent)) state['found'] = [] state['progress'] = 0 state['state'] = 'searching' for offset in range(start, end, 512): for disk_no in range(d.disk_count): d.disks[disk_no].seek(offset) data = d.disks[disk_no].read(512) sig = data[510:] if sig == ref_sig: f = state['found'] if len(f) < 200: f.append((disk_no,offset)) state['found'] = f else: state['state'] = 'aborted' raise Exception('Aborting after too many matches') if offset % one_percent == 0: state['progress'] = state['progress'] + 1 state['progress'] = 100 state['state'] = 'finished' except Exception as e: logging.exception(e) logging.info("Exit. do_find_bootsect()") class MyTasks(): """Auxiliary class, managing long or background tasks""" TASK_NAMES = [ 'find_bootsect', 'find_files' ] def __init__(self, mydisks): self.tasks = [] self.d = mydisks self.find_files_pathlist = [] m = multiprocessing.Manager() self.find_bootsect_state = m.dict() self.find_bootsect_process = None self.find_files_state = m.dict() self.find_files_process = None def get_find_files_pathlist(self): return self.find_files_pathlist def get_find_files_pathlist_str(self): return '\n'.join(self.find_files_pathlist) def task_start(self, task_name): if task_name == 'find_files': self.find_files_state['filepaths'] = list(self.find_files_pathlist) self.find_files_process = multiprocessing.Process( target = do_find_files, args = (self.d, self.find_files_state) ) self.find_files_process.start() elif task_name == 'find_bootsect': self.find_bootsect_process = multiprocessing.Process( target = do_find_bootsect, args = (self.d, self.find_bootsect_state) ) self.find_bootsect_process.start() else: raise ValueError('Valid task names are : %s'%','.join(MyTasks.TASK_NAMES)) def task_kill(self, task_name): if task_name == 'find_bootsect': if self.find_bootsect_process != None and self.find_bootsect_process.is_alive(): self.find_bootsect_process.terminate() elif task_name == 'find_files': if self.find_files_process != None and self.find_files_process.is_alive(): self.find_files_process.terminate() else: raise ValueError('Valid task names are : %s'%','.join(MyTasks.TASK_NAMES)) def append_find_files_pathlist(self, pathlist): # TODO : should receive a list, make changes in raidguessfs.py self.find_files_pathlist.extend(pathlist.split('\n')) def set_find_files_pathlist(self, new_find_files_pathlist): self.find_files_pathlist = new_find_files_pathlist def read_find_bootsect(self): if self.find_bootsect_process == None: return 'This task has never been started\n' else: return '%s\n'%self.find_bootsect_state def read_find_files(self): if self.find_files_process == None: return 'This task has never been started\n' else: return '%s\n'%self.find_files_state