From 0f2c685db3d3790ce9bdc9598df8dae7d6b67eae Mon Sep 17 00:00:00 2001 From: Ludovic Pouzenc Date: Sun, 9 Oct 2011 12:33:48 +0000 Subject: On range tout le code actuel du trunk dans une branche nommée "0.x" et on crée une branche 1.x qui contiendra une nouvelle mouture (re-conception, méta info de packaging...). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///var/svn/2011-ddhardrescue/branches/0.x@29 d3078510-dda0-49f1-841c-895ef4b7ec81 --- Makefile | 31 ++++++ inc/cursesview.h | 9 ++ inc/recover.h | 11 ++ inc/slices.h | 50 +++++++++ inc/slices_evt.h | 19 ++++ inc/utils.h | 17 +++ src/cursesview.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ddhardrescue.c | 133 +++++++++++++++++++++++ src/essais/compil.sh | 4 + src/essais/test.c | 86 +++++++++++++++ src/essais/test2.c | 121 +++++++++++++++++++++ src/essais/test3.c | 169 ++++++++++++++++++++++++++++++ src/recover.c | 128 ++++++++++++++++++++++ src/slices.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++ src/slices_evt.c | 29 +++++ src/utils.c | 27 +++++ todo.txt | 12 +++ 17 files changed, 1419 insertions(+) create mode 100644 Makefile create mode 100644 inc/cursesview.h create mode 100644 inc/recover.h create mode 100755 inc/slices.h create mode 100644 inc/slices_evt.h create mode 100755 inc/utils.h create mode 100644 src/cursesview.c create mode 100644 src/ddhardrescue.c create mode 100755 src/essais/compil.sh create mode 100644 src/essais/test.c create mode 100644 src/essais/test2.c create mode 100644 src/essais/test3.c create mode 100644 src/recover.c create mode 100644 src/slices.c create mode 100644 src/slices_evt.c create mode 100644 src/utils.c create mode 100644 todo.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fccc821 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +BIN=ddhardrescue +CFLAGS=-Iinc -Wall -Werror -Wfatal-errors -g -pg +LIBS=-lpanel -lncurses -lpthread + +#FIXME : liste des dépendances = tous les .c trouvés. C'est nul. + +all: $(BIN) +$(BIN): bin/$(BIN) + +bin/$(BIN): obj/$(BIN).o $(patsubst src/%.c,obj/%.o,$(wildcard src/*.c)) + gcc -o $@ $^ $(CFLAGS) $(LIBS) + +obj/%.o: + gcc -c -o $@ $< $(CFLAGS) + +deps/%.d: src/%.c + gcc -MM -o $@ $< $(CFLAGS) -MG -MT $(patsubst src/%.c,obj/%.o,$<) + +Makefile src/%.c inc/%.h: ; + +.PHONY: clean + +clean: + -rm -f obj/*.o deps/*.d bin/$(BIN) + +cont: + while true; do clear; date; LANG=C make; echo "make returns '$$?'";\ + inotifywait -e modify -r . --exclude '(~$$|\.swp$$)' 2>/dev/null; done + +include $(patsubst src/%.c,deps/%.d,$(wildcard src/*.c)) + diff --git a/inc/cursesview.h b/inc/cursesview.h new file mode 100644 index 0000000..f4d343a --- /dev/null +++ b/inc/cursesview.h @@ -0,0 +1,9 @@ +#ifndef CURSESVIEW_H +#define CURSESVIEW_H + +#include "slices_evt.h" + +void cursesMainLoop(slices_evt_t *slicesEvt); + +#endif /*CURSESVIEW_H*/ + diff --git a/inc/recover.h b/inc/recover.h new file mode 100644 index 0000000..eee8141 --- /dev/null +++ b/inc/recover.h @@ -0,0 +1,11 @@ +#ifndef RECOVER_H +#define RECOVER_H + +#include "slices_evt.h" + +extern int end; + +void recover(slices_evt_t *slicesEvt, char *src, char *dst, char*ddOpts); +int tryRecoverUntilError(slice_t *sliceToRead, address_t *firstError, char *src, char *dst, char *ddOpts); + +#endif /*RECOVER_H*/ diff --git a/inc/slices.h b/inc/slices.h new file mode 100755 index 0000000..b994636 --- /dev/null +++ b/inc/slices.h @@ -0,0 +1,50 @@ +#ifndef SLICES_H +#define SLICES_H + +#include +#include +#include + +/* IMPORTANT NOTES +Slice are inclusive intervals. Let say sliceNew(1,2,S_UNKNOWN,NULL) return a [1;2] interval, + so interval lenght is end-begin+1. Here, it is 2 sectors lenght slice. +*/ + +typedef enum { S_UNKNOWN, S_RECOVERED, S_UNREADABLE } sliceStatus_t; +typedef unsigned long long int address_t; + +typedef struct _slice { + address_t begin, end; + sliceStatus_t status; + struct _slice *next; +} slice_t; + +typedef struct { + int count; + slice_t *first, *last; + address_t min, max; + pthread_mutex_t writeOrConsistentReadMutex; +} slices_t; + +slice_t *sliceNew(address_t begin, address_t end, sliceStatus_t status, slice_t *next); +void sliceDelete(slice_t *s); + +// Return the numbers of slices after split (3 in the general case, 2 or 1 in particular cases. -1 is memory error) +int sliceSplit(slices_t *slices, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter); + +void sliceDumpUpdate(char *dump, slice_t *s, address_t blockSize, unsigned int charCount, address_t begin, address_t end); + +slices_t *slicesNewEmpty(); + +slices_t *slicesNewSingleton(address_t begin, address_t end, sliceStatus_t status); +void slicesDelete(slices_t *slices); + +void slicesAppend(slices_t *slices, slice_t *slice); + +slice_t *slicesFindLargest(slices_t *slices, sliceStatus_t status); + +slice_t *slicesFindLargestFast(slices_t *slices, address_t *foundMax, sliceStatus_t status, address_t knownMax, slice_t *firstToTry); + +char *slicesDump(slices_t *slices, address_t *blockSize, unsigned int charCount, address_t begin, address_t end); + +#endif /*SLICES_H*/ diff --git a/inc/slices_evt.h b/inc/slices_evt.h new file mode 100644 index 0000000..d5772b3 --- /dev/null +++ b/inc/slices_evt.h @@ -0,0 +1,19 @@ +#ifndef SLICES_EVT_H +#define SLICES_EVT_H + +#include "slices.h" + +//typedef enum { EV_BOUNDARY, EV_TYPE } sliceEvtKind_t; + +typedef struct _slices_evt_t { + slices_t *data; + void (*eventListener)(/*sliceEvtKind_t evtKind,*/ struct _slices_evt_t *slicesEvt, slice_t *slice); + pthread_mutex_t eventListenerMutex; +} slices_evt_t; + +int sliceEvtSplit(slices_evt_t *slicesEvt, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter); + +//void sliceEvtPutEvent(slices_evt_t *slicesEvt, slice_t *modifiedSlice); + +#endif /*SLICES_EVT_H */ + diff --git a/inc/utils.h b/inc/utils.h new file mode 100755 index 0000000..8492179 --- /dev/null +++ b/inc/utils.h @@ -0,0 +1,17 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include "slices.h" + +struct progArgs { + char *src, *dst, *ddOpts, *dump; + address_t beginSector, endSector, blockSize; +}; + +int parseArgs(int argc, char **argv, struct progArgs *args); +void usage(char *progname); +int cursesInit(WINDOW *wins[], PANEL *panels[], int count); +void cursesUnInit(WINDOW *wins[], PANEL *panels[], int count); + +#endif /*UTILS_H*/ diff --git a/src/cursesview.c b/src/cursesview.c new file mode 100644 index 0000000..fcfa2d2 --- /dev/null +++ b/src/cursesview.c @@ -0,0 +1,291 @@ +#include "cursesview.h" + +#include +#include +#include +#include + +#define CURSESWIN_COUNT 3 + +// Global variable shared by all threads to say "finish current operation and go away" +extern int end; + +// window updated by cursesUpdateSliceDump callback from "worker" thread, with a mutex for prevent fuzzy concurrent updates +WINDOW *winUpdateSliceDump=NULL; +static pthread_mutex_t ncursesWriteMutex = PTHREAD_MUTEX_INITIALIZER; +address_t sliceDumpBegin, sliceDumpEnd, sliceDumpMin, sliceDumpMax; + +// Helpers declaration (below the interesting code of this file) +int cursesInit(WINDOW *wins[], PANEL *panels[], int count); +void cursesUnInit(WINDOW *wins[], PANEL *panels[], int count); +void cursesUpdateSliceDump(slices_evt_t *slicesEvt, slice_t *modifiedSlice); +//void printInMiddle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); +void makeWin(WINDOW **win, PANEL **panel, int h, int w, int y, int x, char title[]); + +void cursesMainLoop(slices_evt_t *slicesEvt) { + char msg[255]; + int ch, i; + + WINDOW *wins[CURSESWIN_COUNT]; + PANEL *panels[CURSESWIN_COUNT]; + +#ifdef NCURSES_MOUSE_VERSION + int dispatched, res; + int (*mouseEventListener)(MEVENT mevent, WINDOW *winDebug); + MEVENT mevent; + PANEL *p; +#endif + + cursesInit(wins, panels, CURSESWIN_COUNT); + + sliceDumpBegin=sliceDumpMin=slicesEvt->data->min; + sliceDumpEnd=sliceDumpMax=slicesEvt->data->max; + + wattron(wins[CURSESWIN_COUNT-1], COLOR_PAIR(4)); + mvwprintw(wins[CURSESWIN_COUNT-1], 1, 0, "F2:Exit F5:Zoom left F6:Unzoom left F7:Unzoom right F8:Zoom right"); +// wattroff(wins[CURSESWIN_COUNT-1], COLOR_PAIR(4)); + +// update_panels(); + doupdate(); + + /* Enable worker listener */ + //FIXME : check pointers ? + winUpdateSliceDump=wins[1]; + slicesEvt->eventListener=cursesUpdateSliceDump; + + while((ch = getch()) != KEY_F(2)) { + + pthread_mutex_lock(&ncursesWriteMutex); + + switch(ch) { + // Zoom handling + //FIXME : Do something less stupid. Try to reuse mouse event zoom code for keyboard + case KEY_F(5): + sliceDumpEnd=(sliceDumpBegin+sliceDumpEnd)/2; + break; + case KEY_F(6): + sliceDumpBegin=sliceDumpMin; + break; + case KEY_F(7): + sliceDumpEnd=sliceDumpMax; + break; + case KEY_F(8): + sliceDumpBegin=(sliceDumpBegin+sliceDumpEnd)/2; + break; + + // Panel focus + case '1': + case '2': + case '3': + i=ch-'0'; + if (i>0 && i<=CURSESWIN_COUNT) { + top_panel(panels[i-1]); + update_panels(); + } + break; +#ifdef NCURSES_MOUSE_VERSION + case KEY_MOUSE: + // Seems to have a mouse event + res = getmouse(&mevent); + if ( res == OK ) { + // Try to find in which panel (search first in top-level panel and go down) + p=NULL; + while ( ( p=panel_below(p) ) != NULL) { + if ( wenclose(panel_window(p), mevent.y, mevent.x) ) { + break; + } + } + + // If we found a panel, dispatch the event if a listener is set + if (p != NULL) { + mouseEventListener=(int (*)(MEVENT mevent, WINDOW *winDebug)) panel_userptr(p); + dispatched=(mouseEventListener==NULL)?0:mouseEventListener(mevent, wins[0]); + if ( ! dispatched ) { + // If no listener or event not consumed, use the default behavior : set the panel on top if BUTTON1_CLICKED + if ( (mevent.bstate & BUTTON1_CLICKED) == BUTTON1_CLICKED) { + top_panel(p); + } + } + } + } + break; +#endif + default: + sprintf(msg, "Unhandled key ch==0x%x", ch); + wattron(wins[0], COLOR_PAIR(4)); + mvwprintw(wins[0], 2, 0, msg); + + } + + doupdate(); + + sprintf(msg, "Viewing [%lli-%lli] of [%lli-%lli] working region ", sliceDumpBegin, sliceDumpEnd, slicesEvt->data->min, slicesEvt->data->max); + wattron(wins[0], COLOR_PAIR(4)); + mvwprintw(wins[0], 1, 0, msg); + + pthread_mutex_unlock(&ncursesWriteMutex); + } + + pthread_mutex_lock(&(slicesEvt->eventListenerMutex)); + slicesEvt->eventListener=NULL; + pthread_mutex_unlock(&(slicesEvt->eventListenerMutex)); + + end=1; + cursesUnInit(wins, panels, CURSESWIN_COUNT); +} + +// TODO : faire une structure avec tous les éléments graphiques utiles au listener et tout passer par référence de la mainloop à ce listener +#define DEFAULT_ZOOM_FACTOR 2.f + +int winSlicesMouseEventListener(MEVENT mevent, WINDOW *winDebug) { + bool resb; + int pX, pY, maxX, maxY; + address_t delta; + float pos; + // For debugging char buf[255]; + + pY=mevent.y; + pX=mevent.x; + resb=wmouse_trafo(winUpdateSliceDump, &pY, &pX, false); + if ( resb == TRUE ) { + getmaxyx(winUpdateSliceDump, maxY, maxX); + // First ligne (pY) is line 1 but first char (pX) is char 0 so we have to translate Y coords by -1 + pos=(0.0f+(pY-1)*maxX+pX)/((maxY-1)*maxX-1); + delta=sliceDumpEnd-sliceDumpBegin; + sliceDumpBegin+=delta*pos/DEFAULT_ZOOM_FACTOR*(DEFAULT_ZOOM_FACTOR-1); + sliceDumpEnd-=delta*(1-pos)/DEFAULT_ZOOM_FACTOR*(DEFAULT_ZOOM_FACTOR-1); + } + +/* + // Debug code + sprintf(buf, "Debug : pX==%i, pY==%i, maxX==%i, maxY==%i, pos==%f, sliceDumpEnd==%lli, sliceDumpBegin=%lli, delta==%lli", pX, pY, maxX, maxY, pos, sliceDumpEnd, sliceDumpBegin, delta); + wattron(winDebug, COLOR_PAIR(4)); + mvwprintw(winDebug, 2, 0, buf); +*/ + return 1; +} + +int cursesInit(WINDOW *wins[], PANEL *panels[], int count) { + int screenH, screenW; +#ifdef NCURSES_MOUSE_VERSION + char buf[255]; + mmask_t mmask; +#endif + + /* Initialize curses */ + initscr(); + start_color(); + raw(); + keypad(stdscr, TRUE); + noecho(); +#ifdef NCURSES_MOUSE_VERSION + mmask = mousemask(REPORT_MOUSE_POSITION|BUTTON1_CLICKED, NULL); +#endif + + /* Initialize all the colors */ + init_pair(1, COLOR_WHITE, COLOR_BLACK); + init_pair(2, COLOR_WHITE, COLOR_BLUE); + init_pair(3, COLOR_BLUE, COLOR_BLACK); + init_pair(4, COLOR_CYAN, COLOR_BLACK); + + /* Initialize windows and panels */ + getmaxyx(stdscr, screenH, screenW); + if ( screenH < 8 || screenW < 40 ) return 1; + + makeWin(wins+0, panels+0, 3 , screenW, 0 , 0, "Menu"); + makeWin(wins+1, panels+1, screenH-6 , screenW, 3 , 0, "Main Win"); + makeWin(wins+2, panels+2, 2 , screenW, screenH-3 , 0, "Commands"); + + /* Set up the user pointers to the next panel + set_panel_userptr(panels[0], panels[1]); + set_panel_userptr(panels[1], panels[2]); + set_panel_userptr(panels[2], panels[0]); + */ + + set_panel_userptr(panels[1], winSlicesMouseEventListener); + +#ifdef NCURSES_MOUSE_VERSION + sprintf(buf, "Debug infos : mmask=%lx", mmask); + mvprintw(LINES - 3, 0, buf); +#endif + + /* Update the stacking order. 2nd panel will be on top */ + update_panels(); + + return 0; +} + +void cursesUnInit(WINDOW *wins[], PANEL *panels[], int count) { + int i; + + for (i=0;idata, &blockSize, charCount, sliceDumpBegin, sliceDumpEnd); + if (toPrint != NULL) { + attron(COLOR_PAIR(4)); + mvwprintw(winUpdateSliceDump, 1, 0, toPrint); +// attroff(COLOR_PAIR(4)); + + update_panels(); + doupdate(); + + free(toPrint); + } + +/* sprintf(toPrint, "%c - %p %p", strProgress[progress], slicesEvt, modifiedSlice); + progress=(progress+1)%strlen(strProgress); +*/ + pthread_mutex_unlock(&ncursesWriteMutex); +} + +/* +void printInMiddle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) +{ int length, x, y; + float temp; + + if(win == NULL) + win = stdscr; + getyx(win, y, x); + if(startx != 0) + x = startx; + if(starty != 0) + y = starty; + if(width == 0) + width = 80; + + length = strlen(string); + temp = (width - length)/ 2; + x = startx + (int)temp; + wattron(win, color); + mvwprintw(win, y, x, "%s", string); + wattroff(win, color); + refresh(); +} +*/ +void makeWin(WINDOW **win, PANEL **panel, int h, int w, int y, int x, char title[]) { + int i; + *win = newwin(h, w, y, x); + mvwprintw(*win, 0, 0, "%s", title); + mvwchgat(*win, 0, 0, -1, A_BOLD, 2, NULL); + for(i=1;i +#include +#include /* For memset() */ + +#include "slices_evt.h" +#include "utils.h" +#include "recover.h" +#include "cursesview.h" + +// Global variable shared by all threads to say "finish current operation and go away" +int end=0; + +void sigHookAbrt() { + end=1; +} + +// Main thread procedures declaration and a struct for passing arguments in a clean way +void *procWorker(void *a); +void *procViewer(void *a); + +struct threadArgs { + struct progArgs *progArgs; + slices_evt_t *slices; +}; + +int main(int argc, char **argv) { + // System structures + struct sigaction sa; + pthread_t tWorker; + + // Main data structure + slices_evt_t slices; + + // Progam and threads arguments + struct progArgs args; + struct threadArgs tArgs; + + // Algorithmic needs + int res; + + // Parse command-line arguments + res=parseArgs(argc, argv, &args); + if (res!=0) { + usage(argv[0]); + return 1; + } + + // Set signals behavior + memset(&sa,0,sizeof(sa)); + sa.sa_handler=sigHookAbrt; + res=sigaction(SIGABRT, &sa, NULL); + if (res!=0) { + return 2; + } + + //XXX Remove srand : only for simulation + srand(4); + + // Initialize main data structure + //XXX provide a method to do that ? + memset(&slices, 0, sizeof(slices)); + slices.data=slicesNewSingleton(args.beginSector, args.endSector, S_UNKNOWN); + if ( slices.data == NULL ) { + return 3; + } + slices.data->min=args.beginSector; + slices.data->max=args.endSector; + res=pthread_mutex_init(&(slices.eventListenerMutex), NULL); + if (res!=0) { + return 4; + } + + // Threads preparation, creation and start + memset(&tArgs, 0, sizeof(tArgs)); + tArgs.progArgs=&args; + tArgs.slices=&slices; + + + res=pthread_create(&tWorker, NULL, procWorker, &tArgs); + if (res!=0) { + return 5; + } + + // Ncurses interface run in the main thread + (void) procViewer((void*)&tArgs); + + // Thread join point (wait worker thread when viewer is done) + (void) pthread_join(tWorker, NULL); + + //Final dump of datas + address_t blockSize=0; + char *dump; + dump=slicesDump(slices.data, &blockSize, 10000, args.beginSector, args.endSector); + if (dump != NULL) { + puts(dump); + free(dump); + } + printf("blockSize==%lld\n", blockSize); + printf("slices->count==%d\n", slices.data->count); + + + //Resources desallocation + (void) pthread_mutex_destroy(&(slices.eventListenerMutex)); + slicesDelete(slices.data); + + return 0; +} + +void *procWorker(void *a) { + struct threadArgs *tArgs = (struct threadArgs *)a; + + //XXX : We will need something more controlable than just a blocking call to the main algorithm + recover( + tArgs->slices, + tArgs->progArgs->src, + tArgs->progArgs->dst, + tArgs->progArgs->ddOpts + ); + + return a; +} + + +void *procViewer(void *a) { + struct threadArgs *tArgs = (struct threadArgs *)a; + + cursesMainLoop( + tArgs->slices + ); + + return a; +} + diff --git a/src/essais/compil.sh b/src/essais/compil.sh new file mode 100755 index 0000000..a867c5a --- /dev/null +++ b/src/essais/compil.sh @@ -0,0 +1,4 @@ +#!/bin/bash +gcc -Wall -o ../../bin/test -g -lncurses test.c +gcc -Wall -o ../../bin/test2 -g -lncurses -lpanel test2.c +gcc -Wall -o ../../bin/test3 -g -lncurses -lpanel test3.c diff --git a/src/essais/test.c b/src/essais/test.c new file mode 100644 index 0000000..3a3e73f --- /dev/null +++ b/src/essais/test.c @@ -0,0 +1,86 @@ +#include + +WINDOW *create_newwin(int height, int width, int starty, int startx) +{ WINDOW *local_win; + + local_win = newwin(height, width, starty, startx); + wborder(local_win, '|', '|', '-', '-', '+', '+', '+', '+'); +// box(local_win,0,0); + wrefresh(local_win); + return local_win; +} + +int main() { + +/* + A_NORMAL Normal display (no highlight) + A_STANDOUT Best highlighting mode of the terminal. + A_UNDERLINE Underlining + A_REVERSE Reverse video + A_BLINK Blinking + A_DIM Half bright + A_BOLD Extra bright or bold + A_PROTECT Protected mode + A_INVIS Invisible or blank mode + A_ALTCHARSET Alternate character set + A_CHARTEXT Bit-mask to extract a character + COLOR_PAIR(n) Color-pair number n +*/ + int end, ch; + int row,col; + char msg[256]; + WINDOW *w1; + MEVENT event; + + initscr(); + getmaxyx(stdscr,row,col); + raw(); + keypad(stdscr, TRUE); + noecho(); + + start_color(); + init_pair(1, COLOR_WHITE, COLOR_BLUE); + + mousemask(ALL_MOUSE_EVENTS, NULL); +//http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/windows.html + //w1 = create_newwin(row/2, col/2, row/4, col/4); + w1 = create_newwin(10, 10, 10, 10); + attron(COLOR_PAIR(1) | A_BOLD); + mvprintw(row-2,0,"This screen has %d rows and %d columns\n",row,col); + attroff(COLOR_PAIR(1) | A_BOLD); + move(0,0); + + + end=0; + while (!end) { + refresh(); + ch=getch(); + switch(ch) { + case KEY_MOUSE: + if(getmouse(&event) == OK) { /* When the user clicks left mouse button */ + if(event.bstate & BUTTON1_PRESSED) { + attrset(A_NORMAL); + printw("mouse button1\n"); + } + } + break; + case KEY_F(2): + attrset(A_NORMAL); + printw("F2 key\n"); + break; + case 'q': + end=1; + break; + case 'b': + mvwchgat(w1,1,0,-1, A_REVERSE, 0, NULL); + break; + default: + sprintf(msg, "%c key\n", ch); + wattrset(w1,A_BOLD | A_UNDERLINE); + wprintw(w1, msg); + } + } + endwin(); + + return 0; +} diff --git a/src/essais/test2.c b/src/essais/test2.c new file mode 100644 index 0000000..3512329 --- /dev/null +++ b/src/essais/test2.c @@ -0,0 +1,121 @@ + +#include +#include + +#define NLINES 10 +#define NCOLS 40 + +void init_wins(WINDOW **wins, int n); +void win_show(WINDOW *win, char *label, int label_color); +void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); + +int main() +{ WINDOW *my_wins[3]; + PANEL *my_panels[3]; + PANEL *top; + int ch; + + /* Initialize curses */ + initscr(); + start_color(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + + /* Initialize all the colors */ + init_pair(1, COLOR_RED, COLOR_BLACK); + init_pair(2, COLOR_GREEN, COLOR_BLACK); + init_pair(3, COLOR_BLUE, COLOR_BLACK); + init_pair(4, COLOR_CYAN, COLOR_BLACK); + + init_wins(my_wins, 3); + + /* Attach a panel to each window */ /* Order is bottom up */ + my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */ + my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */ + my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */ + + /* Set up the user pointers to the next panel */ + set_panel_userptr(my_panels[0], my_panels[1]); + set_panel_userptr(my_panels[1], my_panels[2]); + set_panel_userptr(my_panels[2], my_panels[0]); + + /* Update the stacking order. 2nd panel will be on top */ + update_panels(); + + /* Show it on the screen */ + attron(COLOR_PAIR(4)); + mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F2 to Exit)"); + attroff(COLOR_PAIR(4)); + doupdate(); + + top = my_panels[2]; + while((ch = getch()) != KEY_F(2)) + { switch(ch) + { case 9: + top = (PANEL *)panel_userptr(top); + top_panel(top); + break; + } + update_panels(); + doupdate(); + } + endwin(); + return 0; +} + +/* Put all the windows */ +void init_wins(WINDOW **wins, int n) +{ + int x, y, i; + char label[80]; + + y = 2; + x = 10; + for(i = 0; i < n; ++i) + { wins[i] = newwin(NLINES, NCOLS, y, x); + sprintf(label, "Window Number %d", i + 1); + win_show(wins[i], label, i + 1); + y += 3; + x += 7; + } +} + +/* Show the window with a border and a label */ +void win_show(WINDOW *win, char *label, int label_color) +{ int startx, starty, height, width; + + getbegyx(win, starty, startx); + getmaxyx(win, height, width); + + box(win, 0, 0); + mvwaddch(win, 2, 0, ACS_LTEE); + mvwhline(win, 2, 1, ACS_HLINE, width - 2); + mvwaddch(win, 2, width - 1, ACS_RTEE); + + print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color)); +} + +void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) +{ int length, x, y; + float temp; + + if(win == NULL) + win = stdscr; + getyx(win, y, x); + if(startx != 0) + x = startx; + if(starty != 0) + y = starty; + if(width == 0) + width = 80; + + length = strlen(string); + temp = (width - length)/ 2; + x = startx + (int)temp; + wattron(win, color); + mvwprintw(win, y, x, "%s", string); + wattroff(win, color); + refresh(); +} + diff --git a/src/essais/test3.c b/src/essais/test3.c new file mode 100644 index 0000000..c763cf3 --- /dev/null +++ b/src/essais/test3.c @@ -0,0 +1,169 @@ + +#include +#include + +#define NLINES 10 +#define NCOLS 40 + +void init_wins(WINDOW **wins, int n); +void win_show(WINDOW *win, char *label, int label_color); +void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); + +int main() +{ WINDOW *my_wins[3]; + PANEL *my_panels[3]; + PANEL *top, *p, *pe; + int ch, res; + mmask_t mmask; + MEVENT mevent; + char buf[255]; + + /* Initialize curses */ + initscr(); + start_color(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + mmask = mousemask(REPORT_MOUSE_POSITION|BUTTON1_CLICKED, NULL); + + /* Initialize all the colors */ + init_pair(1, COLOR_RED, COLOR_BLACK); + init_pair(2, COLOR_GREEN, COLOR_BLACK); + init_pair(3, COLOR_BLUE, COLOR_BLACK); + init_pair(4, COLOR_CYAN, COLOR_BLACK); + + init_wins(my_wins, 3); + + /* Attach a panel to each window */ /* Order is bottom up */ + my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */ + my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */ + my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */ + + /* Set up the user pointers to the next panel */ + set_panel_userptr(my_panels[0], my_panels[1]); + set_panel_userptr(my_panels[1], my_panels[2]); + set_panel_userptr(my_panels[2], my_panels[0]); + + /* Update the stacking order. 2nd panel will be on top */ + update_panels(); + + /* Show it on the screen */ + attron(COLOR_PAIR(4)); + mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F2 to Exit)"); + sprintf(buf, "Debug infos : mmask=%lx", mmask); + mvprintw(LINES - 3, 0, buf); + attroff(COLOR_PAIR(4)); + doupdate(); + + top = my_panels[2]; + while((ch = getch()) != KEY_F(2)) + { switch(ch) + { case 9: // This is tab key, but ive not found the right symbol... other that '\t' + top = (PANEL *)panel_userptr(top); + top_panel(top); + break; + case KEY_MOUSE: + // Seems to have a mouse event + res = getmouse(&mevent); + if ( res == OK ) { + // Try to find in which panel (search first in top-level panel and go down) + pe=NULL; + p=top; + do { + if ((p == NULL) || wenclose(panel_window(p), mevent.y, mevent.x) ) { + pe = p; + break; + } + p = (PANEL *)panel_userptr(top); + } while (p != top); + + // If we found a panel + if (pe != NULL) { + // "switch" depending on event kind + if ( (mevent.bstate & BUTTON1_CLICKED) == BUTTON1_CLICKED) { + // A panel was clicked, set that panel on top + top = pe; + top_panel(top); + // FIXME : the userptr chain of panel must reflect panel order ! + // Or may be we can crawl panels by order with ncurses method + } + } +/* + if ( (mevent.bstate & BUTTON1_CLICKED) == BUTTON1_CLICKED) { + attron(COLOR_PAIR(4)); + sprintf(buf, "BUTTON1_CLICKED at %i/%i/%i ", mevent.x, mevent.y, mevent.z); + mvprintw(LINES - 4, 0, buf); + attroff(COLOR_PAIR(4)); + + } + */ + } + break; + default: + attron(COLOR_PAIR(4)); + sprintf(buf, "Unknown key : 0x%x ", ch); + mvprintw(LINES - 3, 0, buf); + attroff(COLOR_PAIR(4)); + } + update_panels(); + doupdate(); + } + endwin(); + return 0; +} + +/* Put all the windows */ +void init_wins(WINDOW **wins, int n) +{ + int x, y, i; + char label[80]; + + y = 2; + x = 10; + for(i = 0; i < n; ++i) + { wins[i] = newwin(NLINES, NCOLS, y, x); + sprintf(label, "Window Number %d", i + 1); + win_show(wins[i], label, i + 1); + y += 3; + x += 7; + } +} + +/* Show the window with a border and a label */ +void win_show(WINDOW *win, char *label, int label_color) +{ int startx, starty, height, width; + + getbegyx(win, starty, startx); + getmaxyx(win, height, width); + + box(win, 0, 0); + mvwaddch(win, 2, 0, ACS_LTEE); + mvwhline(win, 2, 1, ACS_HLINE, width - 2); + mvwaddch(win, 2, width - 1, ACS_RTEE); + + print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color)); +} + +void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) +{ int length, x, y; + float temp; + + if(win == NULL) + win = stdscr; + getyx(win, y, x); + if(startx != 0) + x = startx; + if(starty != 0) + y = starty; + if(width == 0) + width = 80; + + length = strlen(string); + temp = (width - length)/ 2; + x = startx + (int)temp; + wattron(win, color); + mvwprintw(win, y, x, "%s", string); + wattroff(win, color); + refresh(); +} + diff --git a/src/recover.c b/src/recover.c new file mode 100644 index 0000000..2131542 --- /dev/null +++ b/src/recover.c @@ -0,0 +1,128 @@ +#include "recover.h" + +#include +#include +#include /* for usleep - to be removed */ + +// Main algorithm for recover datas +void recover(slices_evt_t *slicesEvt, char *src, char *dst, char *ddOpts) { + slice_t *sliceToRead; + address_t firstError=0, median, foundMax=0; + int res; + + //sliceToRead=slicesEvt->data->first; + sliceToRead=slicesFindLargest(slicesEvt->data, S_UNKNOWN); + while (!end) { + // try to recover sliceToRead and split it if read error + switch ( tryRecoverUntilError(sliceToRead, &firstError, src, dst, ddOpts) ) { + case 0: + // slice recovery has been executed without read error + sliceToRead->status=S_RECOVERED; + break; + case EIO: + // slice recovery has encountered a readerror + res=sliceEvtSplit(slicesEvt, sliceToRead, firstError, S_RECOVERED, S_UNREADABLE, S_UNKNOWN); + if (res<1) { + //TODO + printf("sliceEvtSplit return %d\n", res); + exit(5); + } + break; + default: + exit(2); //TODO + } + + /* Now, search the largest S_UNKNOWN zone + split it in two parts */ + //sliceToRead=slicesFindLargest(slices, S_UNKNOWN); + sliceToRead=slicesFindLargestFast(slicesEvt->data, &foundMax, S_UNKNOWN, foundMax, sliceToRead->next); + if ( sliceToRead == NULL ) { + // There is nothing more to recover, bailout + end=1; + continue; + } + + + median=(sliceToRead->begin+sliceToRead->end)/2; + res=sliceEvtSplit(slicesEvt, sliceToRead, median, S_UNKNOWN, S_UNKNOWN, S_UNKNOWN); + switch (res) { + case 1: + // No split, try analyse this zone + // Should be a slice of length 1 + break; + case 2: + /* After splitting an S_UNKNOWN zone in two parts + take the second for further analysis. + We already now that this first one is just preceded by + a read error, and errors are frequently grouped in zones, + so trying to read a sector just after a faulty sector is + most likely a waste of time. + */ + sliceToRead=sliceToRead->next; + break; + case 3: + // Internal error of sliceSlpit because this set of parameters prevent split by 3 + exit(6); // TODO + break; + case -1: + // Memory error + exit(5); //TODO + break; + default: + // API error, all necessary cases are already listed + exit(7); // TODO + } + } +} + +// Method tha read source (and clone to dest) until the first read error +int tryRecoverUntilError(slice_t *sliceToRead, address_t *firstError, char *src, char *dst, char*ddOpts) { + //TODO : implement realy that + //TODO : bail out hardly if WRITE error (on dest) + +// char ddinvocation[256]; + int res; + address_t seek, count; + + seek=sliceToRead->begin; + count=sliceToRead->end - seek + 1; +// res=snprintf(ddinvocation, 255, "dd %s %s %s seek=%lld skip=%lld count=%lld", src, dst, ddOpts, seek, seek, count); +//TODO : listener to put that info on the interface puts(ddinvocation); + +/* + // Simulate that we have systematically a read error at first sector + *firstError=sliceToRead->begin; + res=EIO; +*/ +/* + // Simulate that we have systematically a read error at last sector + *firstError=sliceToRead->end; + res=EIO; +*/ +/* + // Simulate that we have systematically a read error at first sector + // Simulate for each read, tha we have an error just in the middle if read for mor than one sector + if ( sliceToRead->begin == sliceToRead->end ) { + res=0; + } else { + *firstError=(sliceToRead->begin + sliceToRead->end)/2; + res=EIO; + } +*/ + + // Simulate for each read a pseudo random error position and generate some cases of full read without error + //address_t error=sliceToRead->begin + rand()%(count); + address_t error=sliceToRead->begin + rand()%(count/3); + if ( error % 42 == 0 ) { + res=0; + } else { + res=EIO; + *firstError=error; + } + + // Keep things humanly understandable (to be removed when real reads will be done) + //usleep(10000); + + return res; +} + diff --git a/src/slices.c b/src/slices.c new file mode 100644 index 0000000..e8f1d49 --- /dev/null +++ b/src/slices.c @@ -0,0 +1,282 @@ +#include "slices.h" + +#include +#include /* For perror() */ + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +slice_t *sliceNew(address_t begin, address_t end, sliceStatus_t status, slice_t *next) { + slice_t *s; + + s = malloc(1*sizeof(slice_t)); + if (s!=NULL) { + s->begin=begin; + s->end=end; + s->status=status; + s->next=next; + } + + return s; +} + +void sliceDelete(slice_t *s) { + free(s); +} + +// Return the numbers of slices after split (3 in the general case, 2 or 1 in particular cases. -1 is memory error) +int sliceSplit(slices_t *slices, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter) { + slice_t *secondSlice, *thirdSlice, *rightSlice; + int splitAfterSingularity, splitBeforeSingularity; + + /* Basically, we want to split the slice in 3 : + [a;b] shoud be transformed in : [a;splitAt-1], [splitAt;splitAt], [splitAt+1;b] + There is exceptions and singularities : + * If splitAt is not within [a;b], bail out, no coherent solution + * If splitAt==a, the first slice should not exists + * If splitAt==b, the last slice shoud not exists + * If a==b (and so, ==splitAt), there is nothing to split, just change status + But, if statusBefore==statusAt, we don't want an interval [splitAt;splitAt], we want just split in 2. + This unwanted interval should be kept merged with the first interval. + + For pratical reasons with pointer mess-up, the first action is to split between the second and the last slice + and then between he first and second if needed. + */ + pthread_mutex_lock(&(slices->writeOrConsistentReadMutex)); + + if ( splitAt < initialSlice->begin || splitAt > initialSlice->end ) { + pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex)); + return -2; + } + + // Test before act because we'll change values of the initialSlice because + // it would become the firstSlice or even the second one if the first is zero-lenght + splitAfterSingularity=(splitAt != initialSlice->end); + splitBeforeSingularity=(splitAt != initialSlice->begin) && (statusBefore != statusAt); + + if ( splitAfterSingularity ) { + thirdSlice = sliceNew(splitAt+1, initialSlice->end, statusAfter, initialSlice->next); + if ( thirdSlice == NULL ) { + pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex)); + return -1; + } + + initialSlice->end = splitAt; + // No status change because we'll split again in 2 parts or not + initialSlice->next = thirdSlice; + if ( initialSlice == slices->last ) slices->last = thirdSlice; + (slices->count)++; + + rightSlice=thirdSlice; + } else { + rightSlice=initialSlice->next; + } + + if ( splitBeforeSingularity ) { + secondSlice = sliceNew(splitAt, splitAt, statusAt, rightSlice); + if ( secondSlice == NULL ) { + pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex)); + return -1; + } + + initialSlice->end = splitAt-1; + initialSlice->status=statusBefore; + initialSlice->next = secondSlice; + if ( initialSlice == slices->last ) slices->last = secondSlice; + (slices->count)++; + } else { + initialSlice->status=statusAt; // Two cases : a==splitAt or statusAt==statusBefore + } + + pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex)); + return 1 + (splitBeforeSingularity?1:0) + (splitAfterSingularity?1:0); +} + +void sliceDumpUpdate(char *dump, slice_t *s, address_t blockSize, unsigned int charCount, address_t begin, address_t end) { + address_t sb,se,i; + char ci; + + // If "s" slice is (partially) contained/visible in the [begin,end] display interval + if ( !(s->end < begin) && !(s->begin > end) ) { + // Draw the slice on the right number of characters + // Mathematically correct, but crashes because address_t is UNSIGNED + // sb=MAX(0, (s->begin - begin) / *blockSize); + sb=(s->begin < begin)?0:(s->begin - begin) / blockSize; + se=MIN((s->end - begin) / blockSize, charCount-1); + + /* Debug "assertion" + if (sb >= charCount || se >= charCount) { + printf("BUG : sb==%lli, se=%lli, charCount==%i\n", sb, se, charCount); + printf("BUG : MAX(0, (%lli - %lli) / %lli)", s->begin, begin, blockSize); + exit(42); + }*/ + + // Choose from the sent slice status the right char to draw + switch (s->status) { + case S_UNKNOWN: ci='_'; break; + case S_UNREADABLE: ci='!'; break; + case S_RECOVERED: ci='.'; break; + default: ci='~'; break; + } + + // Draw on the right number of characters, paying attention with information collision + for (i=sb;i<=se;i++) { + if (dump[i] == ' ' ) { + // This is a new information + dump[i]=ci; + } else if ( dump[i] == ci || dump[i] == '!' ) { + // Already the right information or error, don't modify + } else { + // Multiple information on the same character + dump[i]='#'; + } + } + } +} + +slices_t *slicesNewEmpty() { + int res; + slices_t *ss = malloc(1*sizeof(slices_t)); + + if (ss==NULL) { + return NULL; + } + + memset(ss, 0, sizeof(slices_t)); + res=pthread_mutex_init(&(ss->writeOrConsistentReadMutex), NULL); + if (res!=0) { + free(ss); + return NULL; + } + + return ss; +} + +slices_t *slicesNewSingleton(address_t begin, address_t end, sliceStatus_t status) { + slice_t *s=NULL; + slices_t *ss = slicesNewEmpty(); + if (ss==NULL) { + return NULL; + } + s=sliceNew(begin,end,status,NULL); + if (s==NULL) { + free(ss); + return NULL; + } + slicesAppend(ss,s); + + return ss; +} + +void slicesDelete(slices_t *ss) { + slice_t *curr, *toFree; + + curr=ss->first; + while (curr!=NULL) { + toFree=curr; + curr=curr->next; + sliceDelete(toFree); + } + free(ss); +} + +void slicesAppend(slices_t *slices, slice_t *slice) { + pthread_mutex_lock(&(slices->writeOrConsistentReadMutex)); + + slice->next=NULL; //XXX Could be generalized + if (slices->first==NULL || slices->last==NULL) { + slices->first = slice; + } else { + slices->last->next=slice; + } + slices->last=slice; + (slices->count)++; + + pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex)); +} + +slice_t *slicesFindLargest(slices_t *slices, sliceStatus_t status) { + slice_t *curr, *sMax = NULL; + address_t i, iMax = 0; + + pthread_mutex_lock(&(slices->writeOrConsistentReadMutex)); + + curr = slices->first; + while (curr != NULL) { + i=curr->end - curr->begin + 1; + if ( curr->status == status && i > iMax ) { + iMax = i; + sMax = curr; + } + curr=curr->next; + } + + pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex)); + return sMax; +} + +slice_t *slicesFindLargestFast(slices_t *slices, address_t *foundMax, sliceStatus_t status, address_t knownMax, slice_t *firstToTry) { + slice_t *curr, *sMax = NULL; + address_t i, iMax = 0; + +//FIXME : pthread_lock à faire avant l'appel là :-s (car argument firstToTry peut pointer vers n'importe quoi si autre thread modifie + curr = firstToTry; + while (curr != NULL) { + i=curr->end - curr->begin + 1; + if ( curr->status == status ) { + if ( knownMax == i ) { *foundMax=i; return curr; } + if ( i > iMax ) { + iMax = i; + sMax = curr; + } + } + curr=curr->next; + } + curr = slices->first; + while (curr != firstToTry) { + i=curr->end - curr->begin + 1; + if ( curr->status == status && i > iMax ) { + iMax = i; + sMax = curr; + } + curr=curr->next; + } + *foundMax=iMax; + return sMax; +} + +char *slicesDump(slices_t *slices, address_t *blockSize, unsigned int charCount, address_t begin, address_t end) { + int res; + slice_t *curr; + char *dump; + + res=pthread_mutex_lock(&(slices->writeOrConsistentReadMutex)); + if (res!=0) { + //FIXME Trashy code + perror("slicesDump, pb lock mutex"); + exit(42); + } + + // If blockSize is 0, try to autodetect to display entire slice chain + if (*blockSize == 0) { + *blockSize=(end-begin+1)/(charCount-1); + // If we have a too big zoom factor, draw it at 1:1 scale + if (*blockSize==0) *blockSize=1; + } + + dump = malloc(charCount+1); + if (dump==NULL) { return NULL; } + memset(dump, ' ', charCount); + dump[charCount]=0; + + //For each slice write in dump ASCII representation + curr = slices->first; + while (curr != NULL) { + sliceDumpUpdate(dump, curr, *blockSize, charCount, begin, end); + curr=curr->next; + } + + pthread_mutex_unlock(&(slices->writeOrConsistentReadMutex)); + return dump; +} + diff --git a/src/slices_evt.c b/src/slices_evt.c new file mode 100644 index 0000000..4ed07c6 --- /dev/null +++ b/src/slices_evt.c @@ -0,0 +1,29 @@ +#include "slices_evt.h" + +#include +#include + +// Event-aware version of sliceSplit (reusing it, of course) +int sliceEvtSplit(slices_evt_t *slicesEvt, slice_t *initialSlice, address_t splitAt, sliceStatus_t statusBefore, sliceStatus_t statusAt, sliceStatus_t statusAfter) { + int res; + res=sliceSplit(slicesEvt->data, initialSlice, splitAt, statusBefore, statusAt, statusAfter); + + pthread_mutex_lock(&(slicesEvt->eventListenerMutex)); + if ( slicesEvt->eventListener != NULL ) { + switch(res) { + case 3: + slicesEvt->eventListener(slicesEvt, initialSlice->next->next); + case 2: + slicesEvt->eventListener(slicesEvt, initialSlice->next); + case 1: + slicesEvt->eventListener(slicesEvt, initialSlice); + break; + default: + // No events on split errors + break; + } + } + pthread_mutex_unlock(&(slicesEvt->eventListenerMutex)); + return res; +} + diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..7305b22 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,27 @@ +#include +#include + +#include "utils.h" + +int parseArgs(int argc, char **argv, struct progArgs *args) { + //TODO : implement that + args->src="/dev/sdb"; + args->dst="./test.img"; + args->ddOpts=""; + args->beginSector=0; + args->endSector=21474836480ULL; //10 Tio + + return 0; +} + +void usage(char *progname) { + printf( +"Usage %s [ [ddOpts]]\n\ + \t\t\n\ + \t\t\n\ + \t\n\ + \t\n\ + \t\n\ +", progname); +} + diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..67f9930 --- /dev/null +++ b/todo.txt @@ -0,0 +1,12 @@ + +Limitations de l'implémentation actuelle : + +Il n'y a pas de mise à jour incrémentale du ascii dump car le buffer de la WINDOW curse n'est pas readable et que la méthodesliceDump embarque le calcul des paramètres de combien de secteurs représente 1 char, etc... + +Le seul event qui remonte c'est au moment d'un sliceSplit. Ces évènements, sur un disque dur pas trop trop malade seront très infréquents. + +Il n'y a pas de controle des paramètres de l'algo dans l'IHM, il n'y a pas de controle de l'exécution de l'algo non plus. + +Il n'y a pas de sauvegarde périodique de l'état d'avancement (slices mais aussi avancement dans la lecture du splice courant). + +Il n'y a pas de packaging de fait. -- cgit v1.2.3