/*************************************************************************** * Copyright (C) 2010 by Roberto Maar * * robi6@users.sf.net * * * * This program 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 2 of the License, or * * (at your option) any later version. * * * * This program 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 this program; if not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #else extern int optind; extern char *optarg; #endif #ifdef HAVE_ERRNO_H #include #endif #include #include #include /* ext3/4 libraries */ #include #include //local header files #include "util.h" #include "ext4magic.h" #include "journal.h" #include "inode.h" #include "hard_link_stack.h" #include "block.h" extern char* magicfile; ext2_filsys current_fs = NULL; ext2_ino_t root, cwd; ext2fs_inode_bitmap imap = NULL ; ext2fs_block_bitmap bmap = NULL ; time_t now_time ; //print Versions an CPU-endian-type void print_version ( void ) { printf("ext4magic version : %s\n",(char*) &VERSION); const char *libver; ext2fs_get_library_version ( &libver, NULL ); printf("libext2fs version : %s\n",libver); int n = 1 ; if ( * ( char * ) &n == 1 ) // True if the cpu is little endian. printf("CPU is little endian.\n"); else printf("CPU is big endian.\n"); #ifdef EXPERT_MODE printf("Expert Mode is activ\n"); #endif } //subfunction for show_super_stats() #ifdef EXT2_FLAG_64BITS static void print_bg_opts(ext2_filsys fs, dgrp_t group, int mask, const char *str, int *first, FILE *f) { if (ext2fs_bg_flags_test(fs, group, mask)) { if (*first) { fputs(" [", f); *first = 0; } else fputs(", ", f); fputs(str, f); } } #else static void print_bg_opts(struct ext2_group_desc *gdp, int mask, const char *str, int *first, FILE *f) { if (gdp->bg_flags & mask) { if (*first) { fputs(" [", f); *first = 0; } else fputs(", ", f); fputs(str, f); } } #endif //print superblock void show_super_stats(int header_only) { #ifdef EXT2_FLAG_64BITS const char *units ="block"; #endif dgrp_t i; FILE *out; struct ext2_group_desc *gdp; int numdirs = 0, first, gdt_csum; out=stdout; list_super2(current_fs->super, out); if (! header_only) { return; } for (i=0; i < current_fs->group_desc_count; i++) #ifdef EXT2_FLAG_64BITS numdirs += ext2fs_bg_used_dirs_count(current_fs, i); #else numdirs += current_fs->group_desc[i].bg_used_dirs_count; #endif fprintf(out, "Directories: %d\n", numdirs); gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(current_fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM); #ifdef EXT2_FLAG_64BITS for (i = 0; i < current_fs->group_desc_count; i++) { fprintf(out, " Group %2d: block bitmap at %llu, " "inode bitmap at %llu, " "inode table at %llu\n" " %u free %s%s, " "%u free %s, " "%u used %s%s", i, ext2fs_block_bitmap_loc(current_fs, i), ext2fs_inode_bitmap_loc(current_fs, i), ext2fs_inode_table_loc(current_fs, i), ext2fs_bg_free_blocks_count(current_fs, i), units, ext2fs_bg_free_blocks_count(current_fs, i) != 1 ? "s" : "", ext2fs_bg_free_inodes_count(current_fs, i), ext2fs_bg_free_inodes_count(current_fs, i) != 1 ? "inodes" : "inode", ext2fs_bg_used_dirs_count(current_fs, i), ext2fs_bg_used_dirs_count(current_fs, i) != 1 ? "directories" : "directory", gdt_csum ? ", " : "\n"); if (gdt_csum) fprintf(out, "%u unused %s\n", ext2fs_bg_itable_unused(current_fs, i), ext2fs_bg_itable_unused(current_fs, i) != 1 ? "inodes" : "inode"); first = 1; print_bg_opts(current_fs, i, EXT2_BG_INODE_UNINIT, "Inode not init", &first, out); print_bg_opts(current_fs, i, EXT2_BG_BLOCK_UNINIT, "Block not init", &first, out); if (gdt_csum) { fprintf(out, "%sChecksum 0x%04x", first ? " [":", ", ext2fs_bg_checksum(current_fs, i)); first = 0; } if (!first) fputs("]\n", out); } #else gdp = ¤t_fs->group_desc[0]; for (i = 0; i < current_fs->group_desc_count; i++, gdp++) { fprintf(out, " Group %2d: block bitmap at %u, " "inode bitmap at %u, " "inode table at %u\n" " %d free %s, " "%d free %s, " "%d used %s%s", i, gdp->bg_block_bitmap, gdp->bg_inode_bitmap, gdp->bg_inode_table, gdp->bg_free_blocks_count, gdp->bg_free_blocks_count != 1 ? "blocks" : "block", gdp->bg_free_inodes_count, gdp->bg_free_inodes_count != 1 ? "inodes" : "inode", gdp->bg_used_dirs_count, gdp->bg_used_dirs_count != 1 ? "directories" : "directory", gdt_csum ? ", " : "\n"); if (gdt_csum) fprintf(out, "%d unused %s\n", gdp->bg_itable_unused, gdp->bg_itable_unused != 1 ? "inodes":"inode"); first = 1; print_bg_opts(gdp, EXT2_BG_INODE_UNINIT, "Inode not init", &first, out); print_bg_opts(gdp, EXT2_BG_BLOCK_UNINIT, "Block not init", &first, out); if (gdt_csum) { fprintf(out, "%sChecksum 0x%04x", first ? " [":", ", gdp->bg_checksum); first = 0; } if (!first) fputs("]\n", out); } #endif return; } //open and init the Filesystem, use in main() static void open_filesystem(char *device, int open_flags, blk_t superblock, blk_t blocksize, int magicscan, char *data_filename) { int retval; io_channel data_io = 0; if (superblock != 0 && blocksize == 0) { fprintf(stderr,"if you specify the superblock, you must also specify the block size\n"); current_fs = NULL; return; } if (data_filename) { if ((open_flags & EXT2_FLAG_IMAGE_FILE) == 0) { fprintf(stderr,"The -d option is only valid when reading an e2image file\n"); current_fs = NULL; return; } retval = unix_io_manager->open(data_filename, 0, &data_io); if (retval) { fprintf(stderr,"%s while opening data source\n" ,data_filename); current_fs = NULL; return; } } if (open_flags & EXT2_FLAG_RW) { open_flags &= ~EXT2_FLAG_RW; } // open_flags |= EXT2_FLAG_64BITS; retval = ext2fs_open(device, open_flags, superblock, blocksize, unix_io_manager, ¤t_fs); if (retval) { fprintf(stderr,"%s Error %d while opening filesystem \n", device, retval); current_fs = NULL; return; } retval = ext2fs_read_inode_bitmap(current_fs); if (retval) { fprintf(stderr,"%s Error %d while reading inode bitmap\n", device, retval); goto errout; } retval = ext2fs_read_block_bitmap(current_fs); if (retval) { fprintf(stderr,"%s Error %d while reading block bitmap\n",device, retval); goto errout; } if (magicscan){ // switch on magic scan function #ifdef EXT2_FLAG_64BITS if( ext2fs_copy_generic_bmap(current_fs->inode_map, &imap) || ext2fs_copy_generic_bmap(current_fs->block_map, &bmap)){ #else if( ext2fs_copy_bitmap(current_fs->inode_map, &imap) || ext2fs_copy_bitmap(current_fs->block_map, &bmap)){ #endif fprintf(stderr,"%s Error while copy bitmap\n",device ); imap = NULL; bmap = NULL; }else{ int i; #ifdef EXT2_FLAG_64BITS ext2fs_clear_generic_bmap(imap); for (i = 1; i < current_fs->super->s_first_ino; i++)//mark inode 1-8 ext2fs_mark_generic_bmap(imap,(blk64_t) i); ext2fs_clear_generic_bmap(bmap); #else ext2fs_clear_inode_bitmap(imap); for (i = 1; i < current_fs->super->s_first_ino; i++)//mark inode 1-8 ext2fs_mark_generic_bitmap(imap,i); ext2fs_clear_block_bitmap(bmap); #endif } } if (data_io) { retval = ext2fs_set_data_io(current_fs, data_io); if (retval) { fprintf(stderr,"%s Error %d while setting data source\n", device, retval); goto errout; } } root = cwd = EXT2_ROOT_INO; return; errout: #ifdef DEBUG printf("Errout : open ext2fs\n"); #endif retval = ext2fs_close(current_fs); if (retval) fprintf(stderr, "%s Error %d while trying to close filesystem\n", device, retval); current_fs = NULL; } //subfunction for main void print_modus_error(){ char message0[] = "Invalide parameter : only input of one modus allowed [ -M | -m | -R | -r | -L | -l | -H | D ]\n"; fprintf(stderr,"%s",message0); } //------------------------------------------------------------------------------------------------------------- // the main() function int main(int argc, char *argv[]){ char* progname = argv[0]; int retval, exitval; char defaultdir[] = "RECOVERDIR" ; //FIXME : usage is not correct const char *usage = "\next4magic -M [-j ] [-d ] \n\ ext4magic -m [-j ] [-d ] \n\ ext4magic [-S|-J|-H|-V|-T] [-x] [-j ] [-B n|-I n|-f |-i ] [-t n|[[-a n][-b n]]] [-d ] [-R|-r|-L|-l] [-Q] "; int l,c ; int open_flags = EXT2_FLAG_SOFTSUPP_FEATURES; int recovermodus = 0 ; int disaster = 0; int recoverquality = DELETED_OPT; // default use also delete dir entry char *j_file_name = NULL; char *pathname = NULL; char *des_dir = NULL; char *input_filename = NULL; blk_t superblock = 0; blk_t blocksize = 0; __u32 transaction_nr = 0; int magicscan = 0; char *data_filename = 0; int mode = 0; ext2_ino_t inode_nr = EXT2_ROOT_INO ; blk_t block_nr = 0; int format = 0; int journal_flag = JOURNAL_CLOSE ; int journal_backup = 0; __u32 t_before = 0; __u32 t_after = 0; struct stat filestat; magicfile = NULL; // Sanity checks on the user. if ((argc == 1) || ((argc == 2) && (!(strstr(argv[1],"-V"))))) // allow '-V' as single argument { printf("%s : Error: Missing device name and options.\n", progname); printf("%s \n", usage); return EXIT_FAILURE; } // set default Time from "now -1 day" to "now" time( &now_time ); t_before = (__u32) now_time; t_after = t_before - 86400 ; // decode arguments #ifdef EXPERT_MODE while ((c = getopt (argc, argv, "TJRMLlmrQSxi:t:j:f:Vd:B:b:a:I:Hs:n:cD")) != EOF) { #else while ((c = getopt (argc, argv, "TJRMLlmrSxi:t:j:f:Vd:B:b:a:I:H")) != EOF) { #endif switch (c) { case 'M': //not active, still in development if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= READ_JOURNAL; recovermodus = RECOV_ALL ; magicscan = 1; break; case 'm': //not active, still in development if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= READ_JOURNAL; recovermodus = RECOV_DEL ; magicscan = 1; break; case 'S': mode |= PRINT_SUPERBLOCK ; break; case 'H': if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= PRINT_HISTOGRAM ; break; case 'R': if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= READ_JOURNAL; recovermodus = RECOV_ALL ; break; case 'r': if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= READ_JOURNAL; recovermodus = RECOV_DEL ; break; case 'L': if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= READ_JOURNAL; recovermodus = LIST_ALL ; break; case 'l': if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= READ_JOURNAL; recovermodus = LIST_STATUS ; break; case 'd': des_dir = optarg; //we check later break; case 'i': mode |= RECOVER_LIST; mode |= READ_JOURNAL; input_filename = optarg; retval = stat (input_filename, &filestat); if (retval){ fprintf(stderr,"Error: Invalid parameter: -i %s\n", optarg); fprintf(stderr,"filestat %s returns error %d\n", input_filename, retval); exitval = EXIT_FAILURE ; goto errout; } else{ if (S_ISREG(filestat.st_mode) && (! access(input_filename,R_OK))){ printf("\"%s\" accept for inputfile \n",input_filename); } else { fprintf(stderr,"ERROR: can not use \"%s\" for inputfile\n",input_filename); exitval = EXIT_FAILURE ; goto errout; } } break; case 'I': if (mode & COMMAND_INODE){ fprintf(stderr,"Warning: only input of one inodeNR or filename allowed\n"); break; } mode |= COMMAND_INODE ; errno = 0; inode_nr = strtoul ( optarg, NULL, 10 ); if ( errno ) { fprintf(stderr,"Error: Invalid parameter: -I %s \n", optarg ); exitval = EXIT_FAILURE ; goto errout; } if ( inode_nr < 1 ) { fprintf(stderr,"Error: %s -I: inodeNR \n", progname); fprintf(stderr,"%lu is out of range\n", (long unsigned int)inode_nr); exitval = EXIT_FAILURE ; goto errout; } break; case 'B': mode |= COMMAND_BLOCK ; errno = 0; block_nr = strtoul ( optarg, NULL, 10 ); if ( errno ) { fprintf(stderr,"Error: Invalid parameter: -B %s \n", optarg ); exitval = EXIT_FAILURE ; goto errout; } if ( block_nr < 1 ) { fprintf(stderr,"Error: %s -B: blockNR \n", progname); fprintf(stderr,"%lu is out of range\n", (long unsigned int)block_nr); exitval = EXIT_FAILURE ; goto errout; } break; case 't': mode |= PRINT_TRANSACTION ; mode |= READ_JOURNAL; errno = 0; transaction_nr = strtoul ( optarg, NULL, 10 ); if ( errno ) { fprintf(stderr,"Error: Invalid parameter: -t %s \n", optarg ); exitval = EXIT_FAILURE ; goto errout; } if ( transaction_nr < 1 ) { fprintf(stderr,"Error: %s -t: transactionNR \n", progname); fprintf(stderr,"%lu is out of range\n", (long unsigned int)transaction_nr); exitval = EXIT_FAILURE ; goto errout; } break; case 'x': format = 1; break; case 'j': j_file_name = optarg; retval = stat (j_file_name, &filestat); if (retval){ fprintf(stderr,"Error: Invalid parameter: -j %s\n", optarg); fprintf(stderr,"filestat %s returns error %d\n", j_file_name, retval); exitval = EXIT_FAILURE ; goto errout; } break; case 'f': if (mode & COMMAND_INODE){ fprintf(stderr,"Warning: only input of one inodeNR or filename allowed\n"); break; } mode |= COMMAND_INODE ; mode |= COMMAND_PATHNAME ; l = strlen(optarg); pathname = malloc( l+1 ); strcpy(pathname,optarg); break; #ifdef EXPERT_MODE case 'Q': mode |= HIGH_QUALITY; break; case 'c': // use a backup journal inode journal_backup=1; break; case 'D': // Disaster recovery if(mode & RECOVER_INODE){ print_modus_error(); exitval = EXIT_FAILURE ; goto errout; } mode |= RECOVER_INODE; mode |= READ_JOURNAL; recovermodus = RECOV_ALL ; disaster = 2; break; case 's': blocksize = parse_ulong(optarg, argv[0], "block size", 0); if ((!blocksize) || (blocksize % 1024)){ fprintf(stderr,"Error: Invalid parameter: -s %d\n", blocksize); fprintf(stderr,"ERROR: blocksize allowed only 1024 ; 2048 or 4096\n"); exitval = EXIT_FAILURE ; goto errout; } break; case 'n': superblock = parse_ulong(optarg, argv[0], "superblock number", 0); if ((!superblock) || (!blocksize) || ((blocksize * 8) > superblock)){ fprintf(stderr,"Error: Invalid parameter: -s %d\n", superblock); switch (blocksize){ case 1024: fprintf(stderr, "blocksize 1024 possible 8193, 24577, 40961, 57345 ....\n"); break; case 2048: fprintf(stderr, "blocksize 2048 possible 16384, 49152, 81920, 114688 ....\n"); break; case 4096: fprintf(stderr, "blocksize 4096 possible 32768, 98304, 163840, 229376 ....\n"); break; default: fprintf(stderr, "blocksize unknown\n"); } exitval = EXIT_FAILURE ; goto errout; } break; #endif case 'T': mode |= PRINT_BLOCKLIST; mode |= READ_JOURNAL; break; case 'J': mode |= PRINT_J_SUPERBLOCK; mode |= READ_JOURNAL; break; case 'b': errno = 0; t_before = strtoul ( optarg, NULL, 10 ); if ( errno ) { fprintf(stderr,"Error: Invalid parameter: -b %s \n", optarg ); exitval = EXIT_FAILURE ; goto errout; } if ( t_before < 1 ) { fprintf(stderr,"Error: %s -b: time \n", progname); fprintf(stderr,"%lu is out of range\n", (long unsigned int)inode_nr); exitval = EXIT_FAILURE ; goto errout; } mode |= INPUT_TIME; break; case 'a': errno = 0; t_after = strtoul ( optarg, NULL, 10 ); if ( errno ) { fprintf(stderr,"Error: Invalid parameter: -a %s \n", optarg ); exitval = EXIT_FAILURE ; goto errout; } if ( t_after < 1 ) { fprintf(stderr,"Error: %s -a: time \n", progname); fprintf(stderr,"%d is out of range\n", inode_nr); exitval = EXIT_FAILURE ; goto errout; } mode |= INPUT_TIME; break; case 'z': //experimental not activ at default data_filename = optarg; break; case 'y': //experimental not activ at default open_flags |= EXT2_FLAG_IMAGE_FILE; break; case 'V': print_version(); exit(0); default: fprintf(stderr,"Usage: %s %s\n",argv[0], usage); exitval = EXIT_FAILURE ; goto errout; } } if (getuid()) mode = 0; if (optind < argc) open_filesystem(argv[optind], open_flags,superblock, blocksize, magicscan, data_filename); if (! current_fs) mode = 0; #ifdef DEBUG printf("Operation-mode = %d\n", mode); #endif //-------------------------------------------------------------------------------------------- // check any parameter and options // check time option if ((mode && magicscan) || disaster){ printf("Warning: Activate magic-scan or disaster-recovery function, may be some command line options ignored\n"); inode_nr = EXT2_ROOT_INO; t_before = (__u32) now_time; if (disaster){ t_after = t_before - 60; printf("Start ext4magic Disaster Recovery\n"); } else{ mode &= MASK_MAGIC_SCAN; if ((!(mode & INPUT_TIME)) || (t_after == t_before - 86400)){ t_after = get_last_delete_time(current_fs); if (! (t_after + 1) ){ exitval = EXIT_FAILURE ; goto errout; } mode |= INPUT_TIME; } magicfile = malloc(64); strncpy (magicfile,"/usr/share/misc/ext4magic",25); retval = stat (magicfile, &filestat); if (retval){ strncpy (magicfile,"/usr/local/share/misc/ext4magic",31); retval = stat (magicfile, &filestat); } if ((! retval) && (S_ISREG(filestat.st_mode) && (! access(magicfile,R_OK)))) { printf("use magic-db on \"%s\"\n",magicfile); } else { free(magicfile); magicfile=NULL; } } } if (mode & INPUT_TIME){ if (! ((t_after > 315601200) && (t_after < t_before))) // 315601200 = "1980-01-01 20:00:00" { fprintf(stderr,"Invalide parameter: range \"AFTER <--> BEFORE\"\n"); fprintf(stderr,"the automatic default parameter AFTER=\"now -1 day\" ; BEFORE=\"now\"\n"); fprintf(stderr,"\"-b before-timestamp\" must greater then \"-a after-timestamp\"\n"); fprintf(stderr,"Example : %s -H -b $(date +%%s) -a $(date -d \"-1 day\" +%%s) %s\n",progname,current_fs->device_name); exitval = EXIT_FAILURE ; goto errout; } if (mode & PRINT_TRANSACTION){ fprintf(stderr,"Invalide parameter: use either Transaction-Nr or Timestamps for search in Journal\n"); exitval = EXIT_FAILURE ; goto errout; } mode |= READ_JOURNAL; } //check for the recoverdir if (((mode & RECOVER_INODE) && (recovermodus & REC_DIR_NEEDED)) || mode & RECOVER_LIST || magicscan) { struct stat st_buf; dev_t file_rdev=0; if(!des_dir) des_dir = defaultdir; retval = stat (des_dir, &filestat); if (retval){ retval = mkdir(des_dir , S_IRWXU); if (retval && (errno != EEXIST)){ fprintf(stderr,"ERROR: can not create the recover directory: %s\n", des_dir); exitval = EXIT_FAILURE ; goto errout; } else retval = stat (des_dir, &filestat); } if (!retval){ if (S_ISDIR(filestat.st_mode) && (filestat.st_mode & S_IRWXU) == S_IRWXU){ if (stat(argv[optind], &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ file_rdev = st_buf.st_rdev; #endif /* __GNU__ */ } else { file_rdev = st_buf.st_dev; } } if ((filestat.st_dev == file_rdev) && (S_ISBLK(st_buf.st_mode))){ fprintf(stderr,"ERROR: can not use \"%s\" for recover directory. It's the same filesystem : \"%s\"\n", des_dir, argv[optind]); exitval = EXIT_FAILURE ; #ifdef DEBUG printf("recover_dir_dev : %d filesystem_dev : %d\n",(int)filestat.st_dev, (int)file_rdev); #endif goto errout; } printf("\"%s\" accept for recoverdir\n",des_dir); } else { fprintf(stderr,"ERROR: can not use \"%s\" for recover directory\n", des_dir); exitval = EXIT_FAILURE ; goto errout; } } else{ fprintf(stderr,"Error: Invalid parameter: -d %s \n", des_dir ); exitval = EXIT_FAILURE ; goto errout; } // set quality if (mode & HIGH_QUALITY) recoverquality = 0 ; // not use of delete dir entry } //mark for douple quotes filename if ((recovermodus & (LIST_ALL | LIST_STATUS)) && format) recovermodus |= DOUPLE_QUOTES_LIST; //................................................................................................ // begin Operation if (current_fs) { // printf( "Open mode: read-%s\n", current_fs->flags & EXT2_FLAG_RW ? "write" : "only"); printf("Filesystem in use: %s\n\n",current_fs ? current_fs->device_name : "--none--"); init_link_stack(); // print filesystem superblock if ((mode & PRINT_SUPERBLOCK) && (! ( mode & READ_JOURNAL))) show_super_stats(format); // print histogram over inodeblock if (mode & PRINT_HISTOGRAM){ if (mode & COMMAND_INODE){ mode |= READ_JOURNAL; recovermodus = HIST_DIR ; } else { mode = 0 ; read_all_inode_time(current_fs,t_after,t_before,format); } } // no file or directory is given, use the default root-inode at now and set COMMAND_INODE flag if((mode & RECOVER_INODE) && (! (mode & COMMAND_INODE))) mode |= COMMAND_INODE ; // print filesystem inode if ((mode & COMMAND_INODE) && (! (mode & READ_JOURNAL))) { int allocated; retval = 0; struct ext2_inode *inode_buf; // search for inode if the pathname is given if (mode & COMMAND_PATHNAME){ if(! strcmp(pathname,"/") || ! strcmp(pathname,"")){ inode_nr = EXT2_ROOT_INO ; *pathname = 0; } else{ int l=strlen(pathname) -1; if (*(pathname + l) == '/') *(pathname + l) = 0 ; retval = ext2fs_namei(current_fs,EXT2_ROOT_INO ,EXT2_ROOT_INO , pathname, &inode_nr); } if (retval) { fprintf(stderr,"Error: Filename \"%s\" not found in Filesystem\n", pathname); fprintf(stderr,"if \"%s\" deleted, use InodeNr or try Journaling options\n",pathname); exitval = EXIT_FAILURE ; goto errout; } } inode_buf = malloc(EXT2_INODE_SIZE(current_fs->super)); if (intern_read_inode_full(inode_nr, inode_buf,EXT2_INODE_SIZE(current_fs->super))){ fprintf(stderr,"Fatal Error: can not read InodeNr. %lu \n", (long unsigned int)inode_nr); return EXIT_FAILURE; } allocated = ext2fs_test_inode_bitmap ( current_fs->inode_map, inode_nr ); fprintf(stdout,"\nDump internal Inode %d\nStatus : %s\n\n",inode_nr,(allocated) ? "Inode is Allocated" : "Inode is Unallocated"); dump_inode(stdout, "",inode_nr, inode_buf, format); if (LINUX_S_ISDIR(inode_buf->i_mode)) list_dir(inode_nr); free(inode_buf); } // print filesystem block if ((mode & COMMAND_BLOCK) && (! (mode & READ_JOURNAL))) { void *block_buf; int allocated; block_buf = malloc(EXT2_BLOCK_SIZE(current_fs->super )); if(!read_block ( current_fs , &block_nr , block_buf )){ allocated = ext2fs_test_block_bitmap ( current_fs->block_map, block_nr ); fprintf(stdout,"Dump Filesystemblock %10lu Status : %s\n",(long unsigned int)block_nr, (allocated) ? "Block is Allocated" : "Block is Unallocated"); blockhex ( stdout , block_buf , format , EXT2_BLOCK_SIZE(current_fs->super )); } free(block_buf); } //----------------------------------------------------- // loading Journal if (mode & READ_JOURNAL){ if (journal_flag) journal_flag = journal_open( j_file_name , journal_backup ); if (journal_flag != JOURNAL_OPEN){ exitval = EXIT_FAILURE ; goto errout; } //print the after and before Time if (mode & INPUT_TIME){ printf("Activ Time after : %s", time_to_string(t_after)); printf("Activ Time before : %s", time_to_string(t_before)); } //recover from inputlist if(mode & RECOVER_LIST){ recover_list(des_dir, input_filename,t_after,t_before,(recovermodus & RECOV_ALL )? 0 : 1); mode = 0; } // search inodeNr from pathName use journal if (mode & COMMAND_PATHNAME){ if(! strcmp(pathname,"/") || ! strcmp(pathname,"")){ inode_nr = EXT2_ROOT_INO ; *pathname = 0; } else{ int l=strlen(pathname) -1 ; if (*(pathname + l) == '/') *(pathname + l) = 0 ; inode_nr = local_namei(NULL,pathname,t_after,t_before,DELETED_OPT); } if (inode_nr) { printf("Inode found \"%s\" %lu \n", pathname, (long unsigned int)inode_nr); } else{ fprintf(stderr,"Error: Inode not found for \"%s\"\n",pathname); fprintf(stderr,"Check the valid PATHNAME \"%s\" and the BEFORE option \"%s\"\n", pathname,time_to_string(t_before)); exitval = EXIT_FAILURE ; goto journalout; } } else{ if (mode & COMMAND_INODE){ if (!pathname) pathname = malloc(20); if (!pathname) { fprintf(stderr,"ERROR: can not allocate memory\n"); goto journalout; } if (inode_nr == EXT2_ROOT_INO) *pathname = 0; else sprintf(pathname,"<%lu>",(long unsigned int)inode_nr); } } // recover all mode if possible // activate magic level 1 and 2 for recover from the root inode if ((inode_nr == EXT2_ROOT_INO) && (mode & COMMAND_INODE) && (mode & RECOVER_INODE) && (recoverquality) && (recovermodus & REC_DIR_NEEDED) && (! magicscan)) { if( ext2fs_copy_bitmap(current_fs->inode_map, &imap)){ fprintf(stderr,"%s Error while copy bitmap\n",argv[optind]); imap = NULL; }else{ int i; #ifdef EXT2_FLAG_64BITS ext2fs_clear_generic_bmap(imap); for (i = 1; i < current_fs->super->s_first_ino; i++) ext2fs_mark_generic_bmap(imap,(blk64_t) i); //mark inode 1-8 #else ext2fs_clear_inode_bitmap(imap); for (i = 1; i < current_fs->super->s_first_ino; i++)//mark inode 1-8 ext2fs_mark_generic_bitmap(imap,i); #endif } } // print journal transaction bocklist if (mode & PRINT_BLOCKLIST){ if (mode & COMMAND_BLOCK) print_block_transaction((blk64_t)block_nr,format); if (mode & COMMAND_INODE){ struct inode_pos_struct pos; block_nr = get_inode_pos(current_fs->super, &pos , inode_nr, 1); print_block_transaction((blk64_t)block_nr,format); } if (!(mode & (COMMAND_INODE + COMMAND_BLOCK))) print_block_list( format ); } // print journal superblock if (mode & PRINT_J_SUPERBLOCK) dump_journal_superblock(); //print journal block if ((mode & COMMAND_BLOCK) && (mode & PRINT_TRANSACTION )){ __u32 journal_block; journal_block = get_journal_blocknr(block_nr, transaction_nr); if ( journal_block ){ printf("dump Journalblock %lu : a copy of Filesystemblock %lu : Transaction %lu\n", (long unsigned int)journal_block, (long unsigned int)block_nr, (long unsigned int)transaction_nr); dump_journal_block( journal_block, format ); } else fprintf(stderr,"Error: Filesystemblock %lu not found in Journaltransaction %lu\n", (long unsigned int)block_nr, (long unsigned int)transaction_nr); } //print or recover a single inode from transactionnumber if ((mode & COMMAND_INODE) && (mode & PRINT_TRANSACTION )){ struct ext2_inode_large *inode; inode = malloc( current_fs->blocksize ); if (inode){ if (! get_transaction_inode(inode_nr, transaction_nr, inode)) print_j_inode(inode , inode_nr , transaction_nr , format); if ((mode & RECOVER_INODE) && (recovermodus & (RECOV_ALL | RECOV_DEL)) && (!(LINUX_S_ISDIR(inode->i_mode)))){ recover_file(des_dir,"", pathname,(struct ext2_inode*)inode, inode_nr ,(recovermodus & RECOV_ALL )? 0 : 1 ); mode = 0; } free(inode); } } // start recursiv over the filesystem, used for recover, list and tree-history if ((mode & COMMAND_INODE) && (mode & RECOVER_INODE)) { struct ring_buf *i_list; struct ext2_inode* r_inode; r_item *item = NULL; if (ext2fs_test_inode_bitmap ( current_fs->inode_map, inode_nr )) { fprintf(stdout,"Inode %lu is allocated\n",(long unsigned int)inode_nr); } i_list = get_j_inode_list(current_fs->super, inode_nr); if (mode & INPUT_TIME) item = get_undel_inode(i_list,t_after,t_before); else item = get_last_undel_inode(i_list); if (item) { r_inode = (struct ext2_inode*)item->inode; if (LINUX_S_ISDIR(r_inode->i_mode) ) { struct dir_list_head_t * dir = NULL; dir = get_dir3(NULL,0, inode_nr , "",pathname, t_after,t_before, recoverquality ); if (dir) { lookup_local(des_dir, dir,t_after,t_before, recoverquality | recovermodus ); if (recovermodus & HIST_DIR ) print_coll_list(t_after, t_before, format); //Magic step 1 + 2 +3 if (imap){ imap_search(des_dir, t_after, t_before, disaster ); // we use imap as a flag for the disaster mode ext2fs_free_inode_bitmap(imap); imap = NULL; if (bmap){ if (!(current_fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)){ printf("MAGIC function for ext3 not available, use ext4magic 0.2.4 instead\n"); // magic_block_scan3(des_dir, t_after); } else{ //if (bmap) printf("The MAGIC Function is currently only for ext3 filesystems available\n"); magic_block_scan4(des_dir,t_after); } } } clear_dir_list(dir); } else printf("Inode %lu is a directory but not found after %lu and before %lu\n", (long unsigned int)inode_nr, (long unsigned int)t_after, (long unsigned int)t_before); } else { if (recovermodus & (RECOV_ALL | RECOV_DEL)) recover_file(des_dir,"", pathname, r_inode, inode_nr ,(recovermodus & RECOV_ALL )? 0 : 1 ); else printf("Single file can only recovered with option -R or -r\n"); } } else fprintf(stdout,"No undeled inode %u in journal found\n",inode_nr); #ifdef EXPERT_MODE if (disaster && imap){ // Nothing worked, trying to recover all undeleted files under catastrophic conditions printf("Force a disaster recovery\n"); imap_search(des_dir, t_after, t_before, disaster ); ext2fs_free_inode_bitmap(imap); imap = NULL; } #endif if (i_list) ring_del(i_list); } //print all journal inode if ((mode & COMMAND_INODE) && !(mode & RECOVER_INODE) && !(mode & PRINT_TRANSACTION)) { struct ring_buf* i_list; r_item *item = NULL; __u32 time_stamp; i_list = get_j_inode_list(current_fs->super, inode_nr); if (i_list){ if (mode & INPUT_TIME){ item = get_undel_inode(i_list,t_after,t_before); if(item){ dump_inode(stdout, "",inode_nr, (struct ext2_inode *)item->inode, format); if ( LINUX_S_ISDIR(item->inode->i_mode)){ time_stamp = (item->transaction.start) ? get_trans_time(item->transaction.start) : 0 ; #ifdef DEBUG printf ("Transaction-time for search %ld : %s\n", time_stamp, time_to_string(time_stamp)); #endif list_dir3(inode_nr, (struct ext2_inode*) item->inode, &(item->transaction) , time_stamp); } } else printf("No entry with this time found\n"); } else{ dump_inode_list(i_list, format); } if (i_list) ring_del(i_list); } } journalout: if(!journal_flag) journal_flag = journal_close(); }// end open Journal } // end Operation exitval = EXIT_SUCCESS; #ifdef DEBUG printf("EXIT_SUCCESS : %d, close and clear\n",exitval); #endif errout: if (current_fs) { /*//FIXME in development if (bmap){ struct ext2fs_struct_loc_generic_bitmap *d_bmap = bmap ; blockhex(stdout,(void*)d_bmap->bitmap,0,current_fs->super->s_blocks_count >> 3); }*/ if (imap) ext2fs_free_inode_bitmap(imap); if (bmap) ext2fs_free_inode_bitmap(bmap); imap = NULL; bmap = NULL; if (magicfile) free(magicfile); magicfile = NULL; retval = ext2fs_close(current_fs); if (retval) { fprintf(stderr, "ext2fs_close\n"); current_fs = NULL; } } if (pathname) free(pathname); clear_link_stack(); if (! exitval) printf("ext4magic : EXIT_SUCCESS\n"); return exitval; } //-------------------------------------------------------------------------------------------------------------------------------