/*************************************************************************** * 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 #include #include #include #include #include #include #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif #include #include "util.h" #include "hard_link_stack.h" #include "inode.h" //defines for error in recover_file #define CREATE_ERROR 0x80 #define SEEK_ERROR 0x40 #define MOVE_ERROR 0x20 #define CHOWN_ERROR 0x10 #define CHMOD_ERROR 0x08 #define UTIME_ERROR 0x04 #define ATTRI_ERROR 0x02 //in this version unused #define ACL_ERROR 0x01 #ifdef FILE_ATTR #include "e2p/e2p.h" //#FLAGS_MODIFIABLE = EXT2_NOATIME_FL | EXT2_SYNC_FL | EXT2_DIRSYNC_FL | EXT2_APPEND_FL | EXT2_COMPR_FL | EXT2_NODUMP_FL | // EXT2_IMMUTABLE_FL | EXT3_JOURNAL_DATA_FL | EXT2_SECRM_FL | EXT2_UNRM_FL | EXT2_NOTAIL_FL | EXT2_TOPDIR_FL #define FLAGS_MODIFIABLE 0x0001E0FF #endif extern ext2_filsys current_fs; struct privat { int fd; char* buf; char flag; int error;}; struct alloc_stat{ __u32 allocated; __u32 not_allocated;}; // recover files from a "double quotes" listfile void recover_list(char *des_dir, char *input_file,__u32 t_after, __u32 t_before, int flag){ FILE *f; char *lineptr = NULL ; char *filename = NULL ; char *p1 , *p2; char c; size_t maxlen; int l; size_t got; ext2_ino_t inode_nr; struct ring_buf *i_list; struct ext2_inode* r_inode; r_item *item = NULL; f = fopen(input_file,"r"); if (f) { rewind(f); maxlen = 512; lineptr = malloc(maxlen); filename = malloc(maxlen); if ((!lineptr) || (!filename)) goto errout; while (! (feof(f))){ got = getline (&lineptr, &maxlen, f); if (got != -1){ p1 = strchr(lineptr,'"'); p2 = strrchr(lineptr,'"'); if ((p1) && (p2) && (p1 != p2)){ p1++; *p2 = 0; strcpy(filename,p1); if (*filename == 0) continue; inode_nr = local_namei(NULL,filename,t_after,t_before,DELETED_OPT); if(! inode_nr){ #ifdef DEBUG printf("Filename %s not found\n",filename); #endif continue; } i_list = get_j_inode_list(current_fs->super, inode_nr); item = get_undel_inode(i_list,t_after,t_before); if (item) { r_inode = (struct ext2_inode*)item->inode; if (! LINUX_S_ISDIR(r_inode->i_mode) ) recover_file(des_dir,"", filename, r_inode, inode_nr ,flag); } #ifdef DEBUG else printf("no Inode found for %s\n",filename); #endif if (i_list) ring_del(i_list); } #ifdef DEBUG else printf("Filename not found in : \"%s\"",lineptr); #endif } } } errout: if (f) fclose(f); if (lineptr) free(lineptr); if (filename) free(filename); return ; } // Subfunction for "local_block_iterate3()" for check if the blocks allocated static int check_block(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t /*ref_blk*/x, int /*ref_offset*/y, void *priv ) { //FIXME: if (*blocknr >= fs->super->s_blocks_count) return BLOCK_ERROR; struct alloc_stat *stat = priv; if ( ext2fs_test_block_bitmap ( fs->block_map, *blocknr )) (stat->allocated)++ ; else (stat->not_allocated)++ ; if (bmap) ext2fs_mark_generic_bitmap(bmap, *blocknr); return 0; } //Subfunction for "local_block_iterate3()" read a blocks of a long symlink static int read_syslink_block ( ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t /*ref_blk*/x, int /*ref_offset*/y, void *priv ) { char *charbuf =((struct privat*)priv)->buf; __u32 nbytes; errcode_t retval; int blocksize = fs->blocksize; if (*blocknr >= fs->super->s_blocks_count) return BLOCK_ERROR; if (((struct privat*)priv)->flag){ int allocated = ext2fs_test_block_bitmap ( fs->block_map, *blocknr ); if ( allocated ){ ((struct privat*)priv)->error = 1; // fprintf(stderr,"Block %10lu is allocated.\n",*blocknr); return (BLOCK_ABORT | BLOCK_ERROR); } } retval = io_channel_read_blk ( fs->io, *blocknr, 1, charbuf ); if (retval){ ((struct privat*)priv)->error = retval; return (BLOCK_ERROR); } if (bmap) ext2fs_mark_generic_bitmap(bmap, *blocknr); return 0; } //Subfunction for "local_block_iterate3()" for recover the blocks of a real file static int write_block ( ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t /*ref_blk*/x, int /*ref_offset*/y, void *priv ) { int fd = ((struct privat*)priv)->fd; char *charbuf =((struct privat*)priv)->buf; __u32 nbytes; errcode_t retval; int blocksize = fs->blocksize; if (*blocknr >= fs->super->s_blocks_count) return BLOCK_ERROR; #ifdef DEBUG printf("%c",(ext2fs_test_block_bitmap ( fs->block_map, *blocknr ))? 'X' : 'O'); #endif if (((struct privat*)priv)->flag){ int allocated = ext2fs_test_block_bitmap ( fs->block_map, *blocknr ); if ( allocated ){ // fprintf(stderr,"Block %10lu is allocated.\n",*blocknr); ((struct privat*)priv)->error = BLOCK_ABORT | BLOCK_ERROR ; return (BLOCK_ABORT | BLOCK_ERROR); } } retval = io_channel_read_blk ( fs->io, *blocknr, 1, charbuf ); if (retval){ ((struct privat*)priv)->error = BLOCK_ERROR ; return (BLOCK_ERROR); } if (bmap) ext2fs_mark_generic_bitmap(bmap, *blocknr); lseek(fd,(unsigned long long )blocksize * blockcnt, SEEK_SET); nbytes = write(fd, charbuf, blocksize); if ((unsigned) nbytes != blocksize){ fprintf(stderr, "Error while writing file\n"); ((struct privat*)priv)->error = BLOCK_ERROR ; return 8; } return retval; } //check if the target directory existent, (recursive function) int check_dir(char* pathname){ char *buffer; struct stat filestat; char *p1; int retval = 1; if(! strlen(pathname)) return 1; buffer = malloc(strlen(pathname) +3); strcpy(buffer,pathname); p1 = strrchr(buffer,'/'); if (p1) *p1 = 0; if (stat(buffer, &filestat) || (! S_ISDIR(filestat.st_mode)) ){ //&& (filestat.st_mode & S_IRWXU) == S_IRWXU)) if (! check_dir(buffer)) { retval = mkdir(buffer,S_IRWXU); } } else retval = 0; if(buffer) free(buffer); return retval; } //subfunction for recover_file static char* get_error_string(char* buf, int error){ int mask = 0x80; int i; for (i = 0; i< 8 ; i++){ *(buf+i) = (error & mask) ? '-' : 'X' ; mask /= 2 ; } *(buf+i) = 0; return buf; } // recover Datafile int recover_file( char* des_dir,char* pathname, char* filename, struct ext2_inode *inode, ext2_ino_t inode_nr, int flag){ int rec_error = 255; char err_string[9]; int retval =-1; int hardlink = 0; char i = 0; char *buf=NULL; struct privat priv; struct stat filestat; char *helpname = NULL; char *recovername = NULL; char *linkname = NULL; char *p1; unsigned long long i_size; struct utimbuf touchtime; mode_t i_mode; int major, minor, type; #ifdef DEBUG printf("RECOVER : INODE=%ld FILENAME=%s/%s\n",inode_nr, pathname,filename); dump_inode(stdout, "",inode_nr, (struct ext2_inode *)inode, 0); #endif if (!(inode->i_mode & LINUX_S_IFMT)) //no type flag - no recover return 1; p1 = pathname; while (*p1 == '/') p1++; helpname = malloc(strlen(des_dir) + 15); recovername = malloc(strlen(des_dir) + strlen(pathname) + strlen(filename) +10); if (helpname && recovername){ strcpy(helpname,des_dir); strcat(helpname,"/"); strcpy(recovername,helpname); strcat(recovername,p1); if (strlen(p1)) strcat(recovername,"/"); strcat(recovername,filename); while (!(stat(recovername, &filestat)) && (i<5)){ strcat(recovername,"#"); i++; } p1 = strchr(helpname,0); sprintf(p1,"#%u#",inode_nr); unlink (helpname); priv.flag = flag; //FIXME: hardlink if (inode->i_links_count > 1){ p1 = check_link_stack(inode_nr, inode->i_generation); if (p1) { //hardlink found if (! stat(p1, &filestat)){ retval = check_dir(recovername); if (retval) fprintf(stderr,"Unknown error at target directory by file: %s\ntrying to continue normal\n", recovername); else { if (! link(p1,recovername)){ if (match_link_stack(inode_nr, inode->i_generation)) rec_error -= CREATE_ERROR ; goto out; } else{ rec_error -= CREATE_ERROR ; } } } } else{ //flag as hardlink hardlink = 1; } } switch (inode->i_mode & LINUX_S_IFMT){ //regular File; case LINUX_S_IFREG : priv.fd = open(helpname, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRWXU); if (! priv.fd ){ fprintf(stderr,"Error: open File %s for writing\n",helpname); retval = errno; goto errout; } buf=malloc(current_fs->blocksize); if (buf) { priv.buf = buf; priv.error = 0; // iterate Data Blocks and if not allocated, write to file retval = local_block_iterate3 ( current_fs, *inode, BLOCK_FLAG_DATA_ONLY, NULL, write_block, &priv ); #ifdef DEBUG printf("\n"); #endif if (retval || priv.error){ // error or blocks allocated , we delete the tempfile and goto out close(priv.fd); unlink(helpname); retval = -1; goto errout; } else{ i_size = (unsigned long long)(inode->i_size | ((unsigned long long)inode->i_size_high << 32)); retval = ftruncate(priv.fd,i_size); if (retval){ rec_error -= SEEK_ERROR ; } } } else { fprintf(stderr,"ERROR: can no allocate memory\n"); retval = -1; close(priv.fd); goto errout; } close(priv.fd); break; //symbolic link case LINUX_S_IFLNK : if (ext2fs_inode_data_blocks(current_fs,inode)){ buf = malloc(current_fs->blocksize + 1); if (buf) { memset(buf,0,current_fs->blocksize + 1); priv.buf = buf; priv.error = 0; retval = local_block_iterate3 ( current_fs, *inode, BLOCK_FLAG_DATA_ONLY, NULL, read_syslink_block, &priv ); if (retval || priv.error) goto errout; } else { fprintf(stderr,"ERROR: can no allocate memory\n"); retval = -1; goto errout; } } else { int i; if((! inode->i_size) || (inode->i_size >= 60)) goto errout; buf = malloc(inode->i_size + 1); linkname = (char*) &(inode->i_block[0]); for (i = 0; i < inode->i_size ; i++){ *(buf+i) = (char) *linkname; linkname++; } *(buf+i) = 0; } linkname = buf; retval = symlink(linkname, helpname); if (retval){ rec_error -= (CREATE_ERROR + SEEK_ERROR); } break; //block or char device case LINUX_S_IFBLK : case LINUX_S_IFCHR : type = (LINUX_S_ISBLK(inode->i_mode)) ? S_IFBLK : S_IFCHR ; if (inode->i_block[0]) { major = (inode->i_block[0] >> 8) & 255; minor = inode->i_block[0] & 255; } else { major = (inode->i_block[1] & 0xfff00) >> 8; minor = ((inode->i_block[1] & 0xff) | ((inode->i_block[1] >> 12) & 0xfff00)); } retval = mknod(helpname, type ,makedev(major, minor)); if (retval){ rec_error -= (CREATE_ERROR + SEEK_ERROR); } break; //fifo case LINUX_S_IFIFO: retval = mkfifo(helpname, S_IRWXU); if (retval){ rec_error -= (CREATE_ERROR + SEEK_ERROR); } break; //socket and not define default: retval = -1; goto errout; break; } //end switch retval = check_dir(recovername); if (retval) fprintf(stderr,"Unknown error at target directory by file: %s \ntrying to continue normal\n", recovername); else { retval = rename(helpname,recovername); if (retval && (errno != EEXIST)){ rec_error -= MOVE_ERROR ; } if (! LINUX_S_ISLNK(inode->i_mode)){ retval = chown(recovername,inode_uid(*inode), inode_gid(*inode)); if (retval){ rec_error -= CHOWN_ERROR ; } i_mode = inode->i_mode & 07777; retval = chmod(recovername,i_mode); if (retval){ rec_error -= CHMOD_ERROR ; } touchtime.actime = inode->i_atime; touchtime.modtime = inode->i_mtime; retval = utime(recovername,&touchtime); if (retval){ rec_error -= UTIME_ERROR ; } #ifdef FILE_ATTR if( LINUX_S_ISREG(inode->i_mode)){ unsigned long flags; if (fgetflags(recovername, &flags) == -1) { rec_error -= ATTRI_ERROR; } else { if (fsetflags(recovername, flags | (inode->i_flags & FLAGS_MODIFIABLE )) == -1 ){ rec_error -= ATTRI_ERROR; } } } #endif } else { retval = lchown (recovername, inode_uid(*inode), inode_gid(*inode)); if (retval){ rec_error -= CHOWN_ERROR ; } } } if ((hardlink) && (rec_error & SEEK_ERROR )) add_link_stack(inode_nr, inode->i_links_count, recovername,inode->i_generation ); out: printf("%s %s\n",get_error_string(err_string,rec_error),recovername); errout: if(buf) free(buf); if (helpname) free(helpname); if (recovername) free(recovername); } //helpname return retval; } // check Datafile return the percentage of not allocated blocks int check_file_recover(struct ext2_inode *inode){ int retval =-1; struct alloc_stat stat; if (!(inode->i_mode & LINUX_S_IFMT)) // no type flag return 0; stat.allocated = 0; stat.not_allocated = 0; if ((! inode->i_blocks) || (LINUX_S_ISLNK(inode->i_mode) && (inode->i_size < EXT2_N_BLOCKS*4)) || ! (ext2fs_inode_data_blocks(current_fs,inode))) retval = 100; else{ retval = local_block_iterate3 ( current_fs, *inode, BLOCK_FLAG_DATA_ONLY, NULL, check_block, &stat ); if ( retval ) return 0; if (stat.not_allocated) retval = ((stat.not_allocated * 100) / (stat.allocated + stat.not_allocated)); } return retval; } //set all attributes for directory void set_dir_attributes(char* des_dir,char* pathname,struct ext2_inode *inode){ char *fullname; char *p1; char err_string[9]; int retval, rec_error = 255; mode_t i_mode; struct stat filestat; struct utimbuf touchtime; unsigned long flags; fullname = malloc(strlen(des_dir) + strlen(pathname) + 3); if (fullname){ p1 = pathname; while (*p1 == '/') p1++; strcpy(fullname,des_dir); strcat(fullname,"/"); strcat(fullname,p1); retval = check_dir(fullname); if ( stat(fullname, &filestat)){ retval = mkdir(fullname,S_IRWXU); if (retval) rec_error -= (CREATE_ERROR + SEEK_ERROR); } retval = chown(fullname,inode_uid(*inode), inode_gid(*inode)); if (retval){ rec_error -= CHOWN_ERROR ; } i_mode = inode->i_mode & 07777; retval = chmod(fullname,i_mode); if (retval){ rec_error -= CHMOD_ERROR ; } touchtime.actime = inode->i_atime; touchtime.modtime = inode->i_mtime; retval = utime(fullname,&touchtime); if (retval){ rec_error -= UTIME_ERROR ; } #ifdef FILE_ATTR if (fgetflags(fullname, &flags) == -1) { rec_error -= ATTRI_ERROR; } else { if (fsetflags(fullname, flags | (inode->i_flags & FLAGS_MODIFIABLE )) == -1 ){ rec_error -= ATTRI_ERROR; } } #endif printf("%s %s\n",get_error_string(err_string,rec_error),fullname); free(fullname); } }