diff options
author | robi <robi> | 2010-05-20 00:15:01 +0000 |
---|---|---|
committer | robi <robi> | 2010-05-20 00:15:01 +0000 |
commit | 4efdf36026aa2c76ea8a0b8667598e49bde7b678 (patch) | |
tree | de14b911cf2fb2e77a8913b9fdce3c3348320ceb /src/lookup_local.c |
Initial revision
Diffstat (limited to 'src/lookup_local.c')
-rw-r--r-- | src/lookup_local.c | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/src/lookup_local.c b/src/lookup_local.c new file mode 100644 index 0000000..f77f00d --- /dev/null +++ b/src/lookup_local.c @@ -0,0 +1,732 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +//header util.h + +#include <stdio.h> +#include <string.h> +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <ext2fs/ext2fs.h> +#include <ext2fs/ext2_io.h> +#include "ext2fsP.h" +#include <ctype.h> +#include <time.h> +#include "dir_list.h" +#include "util.h" +#include "inode.h" + +extern ext2_filsys current_fs ; + +static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + + +// local sub function for printing the dir entry +static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry, + struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *private) +{ + struct ext2_inode inode; + ext2_ino_t ino; + struct tm *tm_p; + time_t modtime; + char name[EXT2_NAME_LEN + 1]; + char tmp[EXT2_NAME_LEN + 16]; + char datestr[80]; + char lbr, rbr; + int thislen,retval = 0; + int col = 0; + struct priv_dir_iterate_struct *ls = (struct priv_dir_iterate_struct *) private; + + thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ? + (dirent->name_len & 0xFF) : EXT2_NAME_LEN; + strncpy(name, dirent->name, thislen); + name[thislen] = '\0'; + ino = dirent->inode; + +//FIXME: + // if ((entry == DIRENT_DELETED_FILE) || (ls->options & DELETED_DIR)) { + if (entry == DIRENT_DELETED_FILE) { + lbr = '<'; + rbr = '>'; + } else { + lbr = rbr = ' '; + } + if (ls->options & PARSE_OPT) { + if (ino && intern_read_inode(ino, &inode)) return 0; + fprintf(stdout,"/%u/%06o/%d/%d/%s/",ino,inode.i_mode,inode.i_uid, inode.i_gid,name); + if (LINUX_S_ISDIR(inode.i_mode)) + fprintf(stdout, "/"); + else + fprintf(stdout, "%lld/", inode.i_size | ((__u64)inode.i_size_high << 32)); + fprintf(stdout, "\n"); + } + else if (ls->options & LONG_OPT) { + if ((ino && (ino <= current_fs->super->s_inodes_count))) { + if (ls->options & ONLY_JOURNAL) + retval = read_journal_inode( ino,&inode, ls->transaction->start); + if (retval || !(ls->options & ONLY_JOURNAL)) + if (intern_read_inode(ino, &inode)) + return 0; + modtime = inode.i_mtime; + tm_p = localtime(&modtime); + sprintf(datestr, "%2d-%s-%4d %02d:%02d", + tm_p->tm_mday, monstr[tm_p->tm_mon], + 1900 + tm_p->tm_year, tm_p->tm_hour, + tm_p->tm_min); + } else { + strcpy(datestr, " "); + memset(&inode, 0, sizeof(struct ext2_inode)); + } + fprintf(stdout, "%c%8u%c %c %4o (%d) %5d %5d ", lbr, ino, rbr, get_inode_mode_type(inode.i_mode),inode.i_mode & 07777 , dirent->name_len >> 8, inode_uid(inode), inode_gid(inode)); + if (LINUX_S_ISDIR(inode.i_mode)) + fprintf(stdout, "%5d", inode.i_size); + else + fprintf(stdout, "%5llu", inode.i_size | + ((unsigned long long) inode.i_size_high << 32)); + fprintf (stdout, " %s %s\n", datestr, name); + } else { + sprintf(tmp, "%c%u%c (%d) %s ", lbr, dirent->inode, rbr, + dirent->rec_len, name); + thislen = strlen(tmp); + + if (col + thislen > 80) { + fprintf(stdout, "\n"); + col = 0; + } + fprintf(stdout, "%s", tmp); + col += thislen; + } + return 0; +} + + +// list dir by using real fs inode and real fs dir blocks an real dir-entry-inodes +void list_dir(ext2_ino_t inode) +{ + int retval; + int flags; + struct priv_dir_iterate_struct ls; + + ls.options = 0; + if (!inode) return; + + ls.options |= LONG_OPT; + ls.options |= DELETED_OPT; + + flags = DIRENT_FLAG_INCLUDE_EMPTY; + if (ls.options & DELETED_OPT) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + + retval = ext2fs_dir_iterate2(current_fs, inode, flags,0, list_dir_proc, &ls); + fprintf(stdout, "\n"); + if (retval) + fprintf(stderr,"Error %d \n", retval); + + return; +} + + + + + +//--------------------------------------------------------------- +/* + * This function checks to see whether or not a potential deleted + * directory entry looks valid. What we do is check the deleted entry + * and each successive entry to make sure that they all look valid and + * that the last deleted entry ends at the beginning of the next + * undeleted entry. Returns 1 if the deleted entry looks valid, zero + * if not valid. + * + * copy of a not public function from e2fsprogs ext2fs_validate_entry() + * use only from local_process_dir_block() + */ +static int local_validate_entry(ext2_filsys fs, char *buf, + unsigned int offset, + unsigned int final_offset) +{ + struct ext2_dir_entry *dirent; + unsigned int rec_len = 0; + +#define DIRENT_MIN_LENGTH 12 + + while ((offset < final_offset) && + (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { + dirent = (struct ext2_dir_entry *)(buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return 0; + + offset += rec_len; + if ((rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) + return 0; + } + + return (offset == final_offset); +} + + + +// convert dir_block for bigendian inclusive deleted entry +static int convert_dir_block(char *buf, int flags){ + errcode_t retval; + char *p, *end; + struct ext2_dir_entry *dirent; + unsigned int name_len, rec_len; + + p = (char *) buf; + end = (char *) buf + current_fs->blocksize; + while (p < end-8) { + dirent = (struct ext2_dir_entry *) p; +#ifdef WORDS_BIGENDIAN + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + name_len = dirent->name_len; +#ifdef WORDS_BIGENDIAN + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + if ((retval = ext2fs_get_rec_len(current_fs, dirent, &rec_len)) != 0) + return retval; + if ((rec_len < 8) || (rec_len % 4)) { + rec_len = 8; + retval = EXT2_ET_DIR_CORRUPTED; + } else if (((name_len & 0xFF) + 8) > rec_len) + retval = EXT2_ET_DIR_CORRUPTED; + // p += rec_len; + p += ((name_len & 0xFF) + 11) & ~3; + } + return retval; +} + + +/* + * Helper function which is private to this module. Used by + * local_dir_iterate3() (modi of ext2fs_process_dir_block + */ + static int local_process_dir_block(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct dir_context *ctx = (struct dir_context *) priv_data; + unsigned int offset = 0; + unsigned int next_real_entry = 0; + int ret = 0; + int changed = 0; + int do_abort = 0; + unsigned int rec_len, size; + int entry; + struct ext2_dir_entry *dirent; + trans_range_t *transaction = NULL; + + if (blockcnt < 0) + return 0; + entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; + + transaction = (trans_range_t*) ((struct priv_dir_iterate_struct*)ctx->priv_data)->transaction; + if (get_last_block(ctx->buf, blocknr, transaction->start, transaction->end)){ + if (ctx->flags & ONLY_JOURNAL ) return BLOCK_ABORT; +//FIXME: if not found dir-block in journal, read from real filesystem and hope is ok ?????? + ctx->errcode = io_channel_read_blk(fs->io, *blocknr, 1, ctx->buf); + } +#ifdef WORDS_BIGENDIAN + if(!ctx->errcode) + ctx->errcode = convert_dir_block(ctx->buf,0); +#endif + if (ctx->errcode) + return BLOCK_ABORT; + + while (offset < fs->blocksize) { + dirent = (struct ext2_dir_entry *) (ctx->buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + if (((offset + rec_len) > fs->blocksize) || + (rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { + + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + + ret = (ctx->func)(ctx->dir, + (next_real_entry > offset) ? + DIRENT_DELETED_FILE : entry, + dirent, offset, + fs->blocksize, ctx->buf, + ctx->priv_data); + if (entry < DIRENT_OTHER_FILE) + entry++; + + if (ret & DIRENT_CHANGED) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + changed++; + } + if (ret & DIRENT_ABORT) { + do_abort++; + break; + } +next: + if (next_real_entry == offset) + next_real_entry += rec_len; + + if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { + size = ((dirent->name_len & 0xFF) + 11) & ~3; + + if (rec_len != size) { + unsigned int final_offset = 0; + + final_offset = offset + rec_len; + offset += size; + while (offset < final_offset && + !local_validate_entry(fs, ctx->buf, + offset, + final_offset)) + offset += 4; + continue; + } + } + offset += rec_len; + } + + if (changed) { + ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); + if (ctx->errcode) + return BLOCK_ABORT; + } + if (do_abort) + return BLOCK_ABORT; + return 0; +} + + + + +//------------------------------------------------------ +// Sub function for search the path, use from get_dir3() +static int find_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry, + struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *private) +{ + ext2_ino_t ino; + char name[EXT2_NAME_LEN + 1]; + int thislen; + struct priv_dir_iterate_struct *fl = (struct priv_dir_iterate_struct *) private; + + thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ? + (dirent->name_len & 0xFF) : EXT2_NAME_LEN; + if (thislen){ + strncpy(name, dirent->name, thislen); + name[thislen] = '\0'; + ino = dirent->inode; + + if ((ino) && (ino <= current_fs->super->s_inodes_count)) + add_list_item(fl->dir_list,ino,name,(char) entry); + } + return 0; +} + + + + +//------------------------------------------------------- +// local directory iterate for use inode +static errcode_t local_dir_iterate3(ext2_filsys fs, + ext2_ino_t dir, + struct ext2_inode *inode, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct dir_context ctx; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + ctx.dir = dir; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + retval = local_block_iterate3(fs, *inode, BLOCK_FLAG_READ_ONLY, 0, + local_process_dir_block, &ctx); + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + + + + + +// list dir over journal inode use real fs dir blocks an real dir-entry-inodes +void list_dir2(ext2_ino_t ino, struct ext2_inode *inode) +{ + int retval; + int flags; + struct priv_dir_iterate_struct ls; + + ls.options = 0; + if (!ino || (! LINUX_S_ISDIR(inode->i_mode))) return; + + ls.options |= LONG_OPT; + ls.options |= DELETED_OPT; + if (! ext2fs_test_inode_bitmap ( current_fs->inode_map, ino )) ls.options |= DELETED_DIR ; + + flags = DIRENT_FLAG_INCLUDE_EMPTY; + if (ls.options & DELETED_OPT) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + + retval = local_dir_iterate3(current_fs,ino, inode, flags,0, list_dir_proc, &ls); + fprintf(stdout, "\n"); + if (retval) + fprintf(stderr,"Error %d \n", retval); + + return; +} + + + + +//-------------------------------------------------- +// +// local sub function +// load dir-index in a dir_list + struct dir_list_head_t* get_dir3(struct ext2_inode* d_inode,ext2_ino_t path_ino, ext2_ino_t ino, + char* path, char* name, + __u32 t_after , __u32 t_before, + int options ) +{ + int retval; + int flags; + struct ext2_inode* inode; + struct ring_buf *i_list = NULL; + r_item *item = NULL; + struct priv_dir_iterate_struct fl; + struct dir_list_head_t * l_dir = NULL; + + if(d_inode) d_inode->i_links_count = 0; //set flag for test + i_list = (struct ring_buf*) get_j_inode_list(current_fs->super, ino); + if (! i_list) return NULL; + + item = get_undel_inode(i_list , t_after , t_before); + if ( item && item->inode ) { + inode = (struct ext2_inode*)item->inode; + if(d_inode) + memcpy(d_inode,inode,current_fs->super->s_inode_size); + if (!ino || (! LINUX_S_ISDIR(inode->i_mode))) + goto errout; + +//get dir + l_dir = new_dir_list ( path_ino,ino, path, name); + if (l_dir) + { + fl.options = options; + fl.dir_list = l_dir; + fl.transaction = &item->transaction; + +// flags = DIRENT_FLAG_INCLUDE_EMPTY; + flags = 0; + if (options & DELETED_OPT ) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + retval = local_dir_iterate3(current_fs,ino, inode, flags,0, find_dir_proc, &fl); + if (retval ) + fprintf(stderr,"Warning: ERROR %d can not found the file: %s\n", retval, path); + } + } + +errout: + if (i_list) + ring_del(i_list); + return (l_dir) ? clean_up_dir_list(l_dir) : NULL ; +} + + + +// main worker function for recover and list +// lookup_local() : search recursiv through the filesystem +// use inode and dirblocks from journal +// flag = 0 only undelete, flag = 2 process all +void lookup_local(char* des_dir, struct dir_list_head_t * dir, __u32 t_after , __u32 t_before, int flag){ + struct dir_list_head_t *d_list = NULL; + struct dir_list_t *lp; + struct ext2_inode* inode = NULL; + struct ring_buf *i_list = NULL; + r_item *item = NULL; + char c = ' '; + int allocated; + + if (! dir) { + d_list = get_dir3(NULL, EXT2_ROOT_INO , EXT2_ROOT_INO , "","", t_after,t_before, flag); + if (!d_list) return; +//recursion + lookup_local(des_dir,d_list, t_after, t_before, flag); + } + else { + if (flag & DOUPLE_QUOTES_LIST) + c = '"'; + lp = GET_FIRST(dir); + inode = malloc(current_fs->super->s_inode_size); + while (lp){ + if (! strcmp(lp->filename,"..")) { + if ((dir->path_inode != lp->inode_nr) && (dir->path_inode != 0)) break; + lp = GET_NEXT(dir,lp); + continue; + } + if (! strcmp(lp->filename,".")) + { +// prefunction for directory + switch (flag & REC_FILTER){ + case LIST_ALL : + if (dir->dir_inode != lp->inode_nr) break; + printf("DIR %lu %c%s%c\n",lp->inode_nr,c,dir->pathname,c); + break; + case LIST_STATUS : + break; + case RECOV_DEL : + break; + case RECOV_ALL : + //create_all_dir(des_dir,dir->pathname, lp->inode_nr); + break; + case HIST_DIR : + add_coll_list(lp->inode_nr); + break; + } + + } + else{ + d_list = get_dir3(inode, dir->dir_inode,lp->inode_nr, + dir->pathname,lp->filename,t_after,t_before, flag); + + if (d_list){ +//recursion for directory + lookup_local(des_dir, d_list, t_after, t_before, flag); + } + else{ +//function for all files apart from dir + switch (flag & REC_FILTER){ + case LIST_ALL : + printf("--- %lu %c%s%s%s%c\n",lp->inode_nr,c,dir->pathname, + ((strlen(dir->pathname) > 0) && strcmp(dir->pathname,"/")) ? "/" : "", + lp->filename,c); + break; + case LIST_STATUS : + allocated = check_file_recover(inode); + if (allocated) + printf("%5u%% %c%s%s%s%c\n",allocated,c,dir->pathname, + ((strlen(dir->pathname) > 0) && strcmp(dir->pathname,"/")) ? "/" : "", + lp->filename,c); + break; + case RECOV_DEL : + if (inode->i_links_count) + recover_file(des_dir,dir->pathname, lp->filename, inode, lp->inode_nr,1); + break; + case RECOV_ALL : + if (inode->i_links_count) + recover_file(des_dir,dir->pathname, lp->filename, inode, lp->inode_nr,0); + break; + case HIST_DIR : + add_coll_list(lp->inode_nr); + break; + } + + } + if (d_list) clear_dir_list(d_list); + d_list = NULL; + } + lp = GET_NEXT(dir,lp); + } + + //if (inode) + free(inode); + + +// postfunction for directory + switch (flag & REC_FILTER){ + case LIST_ALL : + break; + case LIST_STATUS : + break; + case RECOV_DEL : + break; + case RECOV_ALL : + i_list = get_j_inode_list(current_fs->super, dir->dir_inode); + if (! i_list) break; + item = get_undel_inode(i_list , t_after , t_before); + if (item) + set_dir_attributes(des_dir, dir->pathname,(struct ext2_inode*)item->inode); + if (i_list) ring_del(i_list); + break; + case HIST_DIR : + break; + } + + } +if(d_list) + clear_dir_list(d_list); +} + + + +// list dir over journal inode and journal dir blocks an journal dir-entry-inodes +void list_dir3(ext2_ino_t ino, struct ext2_inode *inode, trans_range_t* transaction) +{ + int retval; + int flags; + struct priv_dir_iterate_struct ls; + + ls.options = 0; + if (!ino || (! LINUX_S_ISDIR(inode->i_mode))) return; + + ls.options |= LONG_OPT; + ls.options |= DELETED_OPT; + ls.options |= ONLY_JOURNAL; + + if (! ext2fs_test_inode_bitmap ( current_fs->inode_map, ino )) ls.options |= DELETED_DIR ; + + ls.transaction = transaction; + + flags = DIRENT_FLAG_INCLUDE_EMPTY + ONLY_JOURNAL; + + if (ls.options & DELETED_OPT) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + + retval = local_dir_iterate3(current_fs,ino, inode, flags,0, list_dir_proc, &ls); + fprintf(stdout, "\n"); + if (retval) + fprintf(stderr,"Error %d \n", retval); + + return; +} + + + +// search the Inode for an pathname (recursiv function) +ext2_ino_t local_namei(struct dir_list_head_t * dir, char *path, __u32 t_after , __u32 t_before, int flag){ + struct dir_list_head_t *d_list = NULL; + struct dir_list_t *lp; + ext2_ino_t ret_inode = 0; + char *p_path = NULL; + char *p1; + char c; + char *p2; + + if (! dir) { + d_list = get_dir3(NULL,EXT2_ROOT_INO , EXT2_ROOT_INO , "","", t_after,t_before, flag); + if (!d_list) { + fprintf(stderr,"no rootdir at filesystem found\n"); + return 0; + } + ret_inode = local_namei(d_list, path, t_after, t_before, flag); + } + else { +// Check end recursion + if (!(strlen(path))) { + ret_inode = dir->dir_inode; + goto out; + } + + p_path = (char*) malloc(strlen(path)+1); + p2 = p_path; + p1 = path + strspn(path,"/"); + c = *p1; + while ( c != '/' ){ + *p2++ = c; + if (c == 0) break; + p1++; + c = *p1; + } + if ( c ) *p2 = 0; + + lp = GET_FIRST(dir); + while (lp){ + if (! strcmp(lp->filename,"..")) { + if (dir->path_inode != lp->inode_nr) { + break; + } + } + if (! strcmp(lp->filename,".")) + { + if (dir->dir_inode != lp->inode_nr) { + break; + } + } + else{ + if (strcmp( p_path , lp->filename)) { + lp = GET_NEXT(dir,lp); + continue; + } + d_list = get_dir3(NULL,dir->dir_inode,lp->inode_nr,dir->pathname,lp->filename,t_after,t_before, flag); + if (d_list){ + ret_inode = local_namei(d_list, p1, t_after, t_before, flag); +// break if found + if (ret_inode) goto out; + } + else{ +// recursions end (file find) + ret_inode = lp->inode_nr; + goto out; + } + } + lp = GET_NEXT(dir,lp); + } + } +out: +if(p_path) + free(p_path); +if(d_list) + clear_dir_list(d_list); + +return ret_inode; +} |