/*************************************************************************** * 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 . * * * * * * C Implementation: imap_search * ***************************************************************************/ //header util.h #include "util.h" #include "inode.h" #include #include #include #include "magic.h" #include "journal.h" #include "block.h" #include "hard_link_stack.h" extern ext2_filsys current_fs; extern time_t now_time ; struct privat { int count; int error; unsigned char* buf; char flag; }; //Subfunction for "local_block_iterate3()" for load the first blocks to identify filetype int first_blocks ( ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t /*ref_blk*/x, int /*ref_offset*/y, void *priv ) { char *charbuf = NULL; errcode_t retval; int blocksize = fs->blocksize; if ((blockcnt >= 12) || ((struct privat*)priv)->count >=12) return BLOCK_ABORT; charbuf = (char*)((struct privat*)priv)->buf + (blocksize * blockcnt); if (((struct privat*)priv)->flag){ int allocated = ext2fs_test_block_bitmap ( fs->block_map, *blocknr ); if ( allocated ){ ((struct privat*)priv)->error = BLOCK_ABORT | BLOCK_ERROR ; return (BLOCK_ABORT | BLOCK_ERROR); } } retval = io_channel_read_blk ( fs->io, *blocknr, 1, charbuf ); ((struct privat*)priv)->count = blockcnt; if (retval){ ((struct privat*)priv)->error = BLOCK_ERROR ; return (BLOCK_ERROR); } return retval; } static char* get_pathname(blk_t inode_nr, char* i_pathname, char *magic_buf, unsigned char* buf){ struct found_data_t *help_data = NULL; int str_len; __u32 name_len; char *c; int def_len = 22; char name_str[22]; __u32 scan = 0; help_data = malloc(sizeof(struct found_data_t)); if (!help_data) return NULL; memset (help_data,0, sizeof(struct found_data_t)); if ( ident_file(help_data,&scan,magic_buf,buf)){ help_data->type = scan; str_len = strlen(magic_buf) + 1; c = strpbrk(magic_buf,";:, "); if (c){ *c = 0; name_len = c - magic_buf + def_len; } else name_len = str_len + def_len; help_data->scan_result = malloc(str_len); help_data->name = malloc(name_len); if((!help_data->name) || (!help_data->scan_result)){ free_file_data( help_data ); fprintf(stderr,"ERROR: allocate memory\n"); } else{ strcpy(help_data->scan_result,magic_buf); strncpy(help_data->name, magic_buf , name_len - def_len+1); sprintf(name_str,"/I_%010lu",(long unsigned int)inode_nr); strcat(help_data->name,name_str); get_file_property(help_data); } i_pathname = malloc(name_len); if (i_pathname) strncpy (i_pathname,help_data->name,name_len); } free_file_data(help_data); return i_pathname; } static magic_t cookie = 0; char* identify_filename(char* i_pathname, unsigned char *tmp_buf, struct ext2_inode* inode, blk_t inode_nr){ struct privat priv ; char magic_buf[100]; int retval= 0; priv.count = priv.error = priv.flag = 0; priv.buf = tmp_buf; memset(magic_buf,0,100); if ((inode->i_mode & LINUX_S_IFMT) == LINUX_S_IFREG){ memset(tmp_buf,0,12 * current_fs->blocksize); // iterate first 12 Data Blocks retval = local_block_iterate3 ( current_fs, *inode, BLOCK_FLAG_DATA_ONLY, NULL, first_blocks, &priv ); if (priv.count <12){ strncpy(magic_buf, magic_buffer(cookie , tmp_buf, ((inode->i_size < 12 * current_fs->blocksize) ? inode->i_size : (12 * current_fs->blocksize))), 60); i_pathname = get_pathname(inode_nr, i_pathname, magic_buf, tmp_buf); } } return i_pathname; } // search inode by use imap (step1: flag 1 = only directory ; step2: flag 0 = only file) static void search_imap_inode(char* des_dir, __u32 t_after, __u32 t_before, int flag) { struct ext2_group_desc *gdp; struct ext2_inode_large *inode; //struct dir_list_head_t *dir = NULL; struct ring_buf* i_list = NULL; r_item* item = NULL; int zero_flag, retval, load, x ,i ; char *pathname = NULL; char *i_pathname = NULL; char *buf= NULL; unsigned char *tmp_buf = NULL; __u32 blocksize, inodesize, inode_max, inode_per_group, block_count; __u32 inode_per_block , inode_block_group, group; blk_t block_nr; __u32 c_time, d_time, mode; ext2_ino_t first_block_inode_nr , inode_nr; pathname = malloc(26); blocksize = current_fs->blocksize; inodesize = current_fs->super->s_inode_size; inode_max = current_fs->super->s_inodes_count; inode_per_group = current_fs->super->s_inodes_per_group; buf = malloc(blocksize); if (! (flag & 0x01) ){ tmp_buf = malloc (12 * blocksize); if (!tmp_buf) goto errout; cookie = magic_open(MAGIC_MIME | MAGIC_NO_CHECK_COMPRESS | MAGIC_NO_CHECK_ELF | MAGIC_CONTINUE); if ((! cookie) || magic_load(cookie, NULL)){ fprintf(stderr,"ERROR: can't find libmagic\n"); goto errout; } } inode_per_block = blocksize / inodesize; inode_block_group = inode_per_group / inode_per_block; for (group = 0 ; group < current_fs->group_desc_count ; group++){ #ifdef EXT2_FLAG_64BITS gdp = ext2fs_group_desc(current_fs, current_fs->group_desc, group); #else gdp = ¤t_fs->group_desc[group]; #endif zero_flag = 0; if (!(flag & 0x02)){ //skip this in disaster mode // NEXT GROUP IF INODE NOT INIT if (gdp->bg_flags & (EXT2_BG_INODE_UNINIT)) continue; // SET ZERO-FLAG IF FREE INODES == INODE/GROUP for fast ext3 if (gdp->bg_free_inodes_count == inode_per_group) zero_flag = 1; } //FIXME for struct ext4_group_desc 48/64BIT for (block_nr = gdp->bg_inode_table , block_count = 0 ; block_nr < (gdp->bg_inode_table + inode_block_group); block_nr++, block_count++) { if (!(flag & 0x02)){ //skip this in disaster mode // break if the first block only zero inode if ((block_count ==1) && (zero_flag == (inode_per_block + 1))) break; } //FIXME inode_max ???? first_block_inode_nr = (group * inode_per_group) + (block_count * inode_per_block) + 1; load = 0; for (i = 0; ii_ctime); mode = ext2fs_le32_to_cpu(inode->i_mode); if ( ! ( flag & 0x02)) { //no check this inode in disaster mode if ((! c_time ) && (!(inode->i_mode & LINUX_S_IFMT)) ) { if(zero_flag) zero_flag++ ; continue; } d_time = ext2fs_le32_to_cpu(inode->i_dtime); if ( (! d_time) || d_time <= t_after){ ext2fs_mark_generic_bitmap(imap,inode_nr); continue; } } // 1. magical step if (LINUX_S_ISDIR(mode) && ( flag & 0x01) && (pathname)){ sprintf(pathname,"<%lu>",(long unsigned int)inode_nr); struct dir_list_head_t * dir = NULL; if (flag & 0x02){ //disaster mode //only search for undeleted entry dir = get_dir3(NULL,0, inode_nr , "MAGIC-1",pathname, t_after,t_before, 0); if (dir) { lookup_local(des_dir, dir,t_after,t_before, RECOV_ALL | LOST_DIR_SEARCH ); clear_dir_list(dir); } } else{ //search for all dir = get_dir3(NULL,0, inode_nr , "MAGIC-1",pathname, t_after,t_before, DELETED_OPT); if (dir) { lookup_local(des_dir,dir,t_after,t_before,DELETED_OPT|RECOV_ALL|LOST_DIR_SEARCH); clear_dir_list(dir); } } } // 2. magical step if (! (flag & 0x01) ){ i_list = get_j_inode_list(current_fs->super, inode_nr); item = get_undel_inode(i_list,t_after,t_before); ext2fs_mark_generic_bitmap(imap,inode_nr); if (item) { if (! LINUX_S_ISDIR(item->inode->i_mode) ) { i_pathname = identify_filename(i_pathname, tmp_buf, (struct ext2_inode*)item->inode, inode_nr); sprintf(pathname,"<%lu>",(long unsigned int)inode_nr); recover_file(des_dir,"MAGIC-2", ((i_pathname)?i_pathname : pathname), (struct ext2_inode*)item->inode, inode_nr, 0); if(i_pathname){ free(i_pathname); i_pathname = NULL; } } } if (i_list) ring_del(i_list); } } } } } } errout: if (pathname) free(pathname); if(buf) { free(buf); buf = NULL; } if (tmp_buf){ free(tmp_buf); tmp_buf = NULL; } if (cookie){ magic_close(cookie); cookie = 0; } return; } //check if the directory always recovert. if, then move to right place //return 0 if directory is moved //this function is called from lookup_local() during 1. magical step int check_find_dir(char *des_dir,ext2_ino_t inode_nr,char *pathname,char *filename){ char *recovername = NULL; char *dirname = NULL; struct stat filestat; int retval = 0; recovername = malloc(strlen(des_dir) + strlen(pathname) + 30); dirname = malloc(strlen(des_dir) + strlen(pathname) + strlen(filename) +10); if (recovername && dirname){ sprintf(recovername,"%s/MAGIC-1/<%lu>",des_dir,(long unsigned int)inode_nr); sprintf(dirname,"%s/%s/%s",des_dir,pathname,filename); retval = stat (recovername, &filestat); if ((!retval) && (S_ISDIR(filestat.st_mode))){ retval = stat (dirname, &filestat); if (! retval){ printf("Warning :can't move %s to %s ;file exist; will recover it again\n",recovername,dirname); retval = 1; } else{ if(check_dir(dirname)){ fprintf(stderr,"Unknown error at target directory by file: %s\ntrying to continue normal\n", dirname); }else{ retval = rename(recovername, dirname); rename_hardlink_path(recovername, dirname); } #ifdef DEBUG printf("move return: %d : ernno %d : %s -> %s\n",retval, errno, recovername, dirname); #endif } } free(recovername); free(dirname); return retval; } return 0; } void search_journal_lost_inode(char* des_dir, __u32 t_after, __u32 t_before, int flag){ struct ext2_inode *p_inode; struct ext2_inode inode; int i; char *pathname = NULL; char *i_pathname = NULL; char *buf= NULL; unsigned char *tmp_buf = NULL; __u32 blocksize, inodesize; __u32 inode_per_block; ext2_ino_t inode_max, inode_nr; pathname = malloc(26); blocksize = current_fs->blocksize; inodesize = current_fs->super->s_inode_size; inode_max = current_fs->super->s_inodes_count; inode_per_block = blocksize / inodesize; inode_nr = inode_max ; buf = malloc(blocksize); if (! (flag & 0x01) ){ tmp_buf = malloc (12 * blocksize); if (!tmp_buf) goto errout; cookie = magic_open(MAGIC_MIME | MAGIC_NO_CHECK_COMPRESS | MAGIC_NO_CHECK_ELF | MAGIC_CONTINUE); if ((! cookie) || magic_load(cookie, NULL)){ fprintf(stderr,"ERROR: can't find libmagic\n"); goto errout; } } while ( get_pool_block(buf) ){ for (i=0 ;i < inode_per_block; i++){ inode_nr++; p_inode = (struct ext2_inode*) (buf + (i * inodesize)); memset(&inode, 0, sizeof(struct ext2_inode)); #ifdef WORDS_BIGENDIAN ext2fs_swap_inode(current_fs, &inode, p_inode, 0); #else memcpy(&inode, p_inode,128); #endif if((inode.i_dtime) || (!inode.i_size) || (!inode.i_blocks) || (!LINUX_S_ISREG(inode.i_mode))) continue; if (check_file_stat(&inode)){ i_pathname = identify_filename(i_pathname, tmp_buf, &inode, inode_nr); sprintf(pathname,"<%lu>",(long unsigned int)inode_nr); recover_file(des_dir,"MAGIC-2", ((i_pathname)?i_pathname : pathname), &inode, inode_nr, 1); } if(i_pathname){ free(i_pathname); i_pathname = NULL; } } } errout: if (pathname) free(pathname); if(buf) { free(buf); buf = NULL; } if (tmp_buf){ free(tmp_buf); tmp_buf = NULL; } if (cookie){ magic_close(cookie); cookie = 0; } return; } //2 step search for journalinode, will find some lost directory and files void imap_search(char* des_dir, __u32 t_after, __u32 t_before , int disaster ){ printf("MAGIC-1 : start lost directory search\n"); search_imap_inode(des_dir, t_after, t_before, 1 | disaster ); //search for lost fragments of directorys printf("MAGIC-2 : start lost file search\n"); search_imap_inode(des_dir, t_after, t_before, 0 | disaster ); //search for lost files if ((!disaster) && (t_before == (__u32)now_time)){ printf("MAGIC-2 : start lost in journal search\n"); search_journal_lost_inode(des_dir, t_after, t_before, 0); } return; }