/***************************************************************************
* 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 . *
***************************************************************************/
#include
#include
#include
#include
#include
#include
#ifndef HAVE_ERRNO_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "jfs_user.h"
#include
#include "ext4magic.h"
#include "util.h"
#include "journal.h"
#include "inode.h"
enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL, JOURNAL_IS_DUMMY};
//flags for journaldescriptor control
#define ANY_BLOCK ((blk_t) -1)
#define WRAP_UNDEF 0
#define WRAP_ON 1
#define WRAP_OFF 2
#define WRAP_READY 3
struct journal_source
{
enum journal_location where;
int fd;
ext2_file_t file;
char* j_dummy;
};
//static variable for work with journal
struct journal_source journal_source;
extern ext2_filsys current_fs ;
extern time_t now_time ;
struct ext2_super_block *jsb_pointer; //pointer for find & open journal
char jsb_buffer[1024]; //buffer for journal-superblock (be_to_CPU)
void* pt_buff; /*pointer of the privat descriptor Table */
journal_descriptor_tag_t *pt; /*pointer for descriptor Table*/
__u32 *ptl; /*reverse pointer for lost inodeblocks*/
__u32 pt_count; /*counter for privat descriptor Table */
__u32 ptl_count; /*counter for lost inodeblocks*/
struct j_bitmap_list_t jbbm ; /* for Block Bitmaps of Journal */
//print journal superblock
void dump_journal_superblock( void)
{
int i;
__u32 nr_users;
char buffer[40];
uuid_t *uu;
journal_superblock_t *jsb = (journal_superblock_t*)jsb_buffer;
fprintf(stdout,"\nJournal Super Block:\n");
fprintf(stdout," Signature: 0x%08x \n",jsb->s_header.h_magic);
fprintf(stdout," Blocktype : %s \n",type_to_name(jsb->s_header.h_blocktype));
fprintf(stdout," Journal block size: %lu \n",(long unsigned int)jsb->s_blocksize);
fprintf(stdout," Number of journal blocks: %lu \n",(long unsigned int)jsb->s_maxlen);
fprintf(stdout," Journal block where the journal actually starts: %lu\n",(long unsigned int)jsb->s_first);
fprintf(stdout," Sequence number of first transaction: %lu\n", (long unsigned int)jsb->s_sequence);
fprintf(stdout," Journal block of first transaction: %lu\n", (long unsigned int)jsb->s_start);
fprintf(stdout," Error number: %ld\n",(long int)jsb->s_errno);
if ((jsb->s_header.h_blocktype) != JFS_SUPERBLOCK_V2)
return ;
// Remaining fields are only valid in a version-2 superblock
//FIXME: Strings of Features
fprintf(stdout," Compatible Features: %lu\n",(long unsigned int)jsb->s_feature_compat);
fprintf(stdout," Incompatible features: %lu\n",(long unsigned int)jsb->s_feature_incompat);
fprintf(stdout," Read only compatible features: %lu\n",(long unsigned int)jsb->s_feature_ro_compat);
uuid_unparse(jsb->s_uuid, buffer);
fprintf(stdout," Journal UUID: %s \n",buffer);
nr_users = jsb->s_nr_users;
fprintf(stdout," Number of file systems using journal: %lu\n", (long unsigned int)jsb->s_nr_users);
fprintf(stdout," Location of superblock copy: %lu\n",(long unsigned int)jsb->s_dynsuper);
fprintf(stdout," Max journal blocks per transaction: %lu\n",(long unsigned int)jsb->s_max_transaction);
fprintf(stdout," Max file system blocks per transaction: %lu\n",(long unsigned int)jsb->s_max_trans_data);
if (nr_users && (nr_users < 48)) {
fprintf(stdout," IDs of all file systems using the journal:\n");
for (i = 0; i < nr_users; i++){
uu=(void*) &jsb->s_users[i*16];
uuid_unparse(*uu, buffer);
fprintf(stdout,"%02d. : %s\n",i+1,buffer);
}
}
fprintf(stdout," \n\n");
return;
}
// local subfunction
static void journal_superblock_to_cpu ( __u32 *jsb )
{
int i;
for (i=0;i<12;i++)
*(jsb+i) = ext2fs_be32_to_cpu(*(jsb+i));
// UUIDs are endian-independent, so don't swap those bytes
for (i=16;i<20;i++)
*(jsb+i) = ext2fs_be32_to_cpu(*(jsb+i));
// User IDs are endian-independent
}
// open an extract the blocklist from journal
extern int journal_open( char *journal_file_name, int journal_backup_flag )
{
char *journal_dev_name = NULL;
int journal_fd = 0;
ext2_ino_t journal_inum;
struct ext2_inode journal_inode;
int retval;
ext2_file_t journal_file;
#ifdef EXPERT_MODE
char *dummy_journal = NULL;
#endif
pt_buff = NULL;
ptl = NULL;
if (current_fs) jsb_pointer = current_fs->super;
else {
fprintf(stderr,"No filesystem open, this must be a bug\n");
return(JOURNAL_ERROR);
}
if (journal_file_name) {
/* Set up to read journal from a regular file somewhere */
journal_fd = open(journal_file_name, O_RDONLY, 0);
if (journal_fd < 0) {
fprintf(stderr,"Error %d while opening %s for read external journal\n",errno,journal_file_name);
goto errout;
}
journal_source.where = JOURNAL_IS_EXTERNAL;
journal_source.fd = journal_fd;
journal_source.file = NULL;
journal_source.j_dummy = NULL;
printf("Using external Journal at File %s \n",journal_file_name);
} else if ((journal_inum = jsb_pointer->s_journal_inum)) {
if (journal_backup_flag) {
if (jsb_pointer->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS) {
fprintf(stderr,"no journal backup in super block\n");
goto errout;
}
memset(&journal_inode, 0, sizeof(struct ext2_inode));
memcpy(&journal_inode.i_block[0], jsb_pointer->s_jnl_blocks, EXT2_N_BLOCKS*4);
journal_inode.i_size = jsb_pointer->s_jnl_blocks[16];
journal_inode.i_links_count = 1;
journal_inode.i_mode = LINUX_S_IFREG | 0600;
if (jsb_pointer->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)
journal_inode.i_flags |= EXT4_EXTENTS_FL;
} else {
if (intern_read_inode(journal_inum, &journal_inode))
goto errout;
}
retval = ext2fs_file_open2(current_fs, journal_inum,
&journal_inode, 0, &journal_file);
#ifdef EXPERT_MODE
if (journal_backup_flag){
//check the Journal Magic Number
char* jsb_buffer = NULL;
unsigned int got;
journal_superblock_t* jsb;
jsb_buffer = malloc(1024);
journal_source.where = JOURNAL_IS_INTERNAL;
journal_source.file = journal_file;
if ((jsb_buffer) && (! read_journal_block(0,jsb_buffer, 1024, &got))){
jsb = (journal_superblock_t *) jsb_buffer;
if ((ext2fs_be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) ||
(current_fs->blocksize != ext2fs_be32_to_cpu(jsb->s_blocksize))) {
retval = 1;
}
free(jsb_buffer);
}
else return JOURNAL_ERROR ;
}
#endif
if (retval) {
fprintf(stderr," Error %d while opening journal\n",retval);
#ifdef EXPERT_MODE
if (journal_backup_flag) {
journal_superblock_t *jsb;
//the journal is defect, create a insubstantial blank journal
dummy_journal = (char*) malloc(current_fs->blocksize * 2);
if (dummy_journal){
memset (dummy_journal,0,current_fs->blocksize *2);
jsb = (journal_superblock_t*) dummy_journal;
jsb->s_header.h_magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER);
jsb->s_header.h_blocktype = ext2fs_cpu_to_be32(JFS_SUPERBLOCK_V2);
jsb->s_blocksize = ext2fs_cpu_to_be32(current_fs->blocksize);
jsb->s_maxlen = ext2fs_cpu_to_be32(2);
jsb->s_nr_users = ext2fs_cpu_to_be32(1);
jsb->s_first = ext2fs_cpu_to_be32(1);
jsb->s_sequence = ext2fs_cpu_to_be32(1);
memcpy(jsb->s_uuid, current_fs->super->s_uuid, sizeof(current_fs->super->s_uuid));
journal_source.where = JOURNAL_IS_DUMMY;
journal_source.file = NULL;
journal_source.fd = -1 ;
journal_source.j_dummy = dummy_journal;
printf("Journal defect, using a dummy Journal\n");
}else
goto errout;
}
else
#endif
goto errout;
}
else {
journal_source.where = JOURNAL_IS_INTERNAL;
journal_source.file = journal_file;
journal_source.fd = -1 ;
journal_source.j_dummy = NULL;
printf("Using %s internal Journal at Inode %d\n",(journal_backup_flag)?"a recovered":"",journal_inum);
}
} else {
char uuid[37];
uuid_unparse(jsb_pointer->s_journal_uuid, uuid);
journal_dev_name = blkid_get_devname(NULL, "UUID", uuid);
if (!journal_dev_name)
journal_dev_name = blkid_devno_to_devname(jsb_pointer->s_journal_dev);
if (!journal_dev_name) {
fprintf(stderr,"filesystem has no journal\n");
goto errout;
}
journal_fd = open(journal_dev_name, O_RDONLY, 0);
if (journal_fd < 0) {
fprintf(stderr,"%d while opening %s for external journal\n",errno,journal_dev_name);
free(journal_dev_name);
goto errout;
}
printf("Using external journal found at %s\n", journal_dev_name);
free(journal_dev_name);
journal_source.where = JOURNAL_IS_EXTERNAL;
journal_source.fd = journal_fd;
journal_source.file = NULL;
}
return init_journal();
errout:
fprintf(stderr,"Error: while open journal\n" );
jsb_pointer = NULL;
return (JOURNAL_CLOSE);
}
// close the journal (last function in main() if the journal open)
extern int journal_close(void)
{
jsb_pointer = NULL;
pt_count = 0;
if ( pt_buff ) {
free(pt_buff);
pt_buff = NULL;
}
switch (journal_source.where){
case JOURNAL_IS_DUMMY :
if (journal_source.j_dummy){
free(journal_source.j_dummy);
journal_source.j_dummy = NULL;
}
break;
case JOURNAL_IS_INTERNAL :
if (journal_source.file){
ext2fs_file_close(journal_source.file);
journal_source.file = NULL;
}
break;
case JOURNAL_IS_EXTERNAL :
if (journal_source.fd > 0) {
close(journal_source.fd);
journal_source.fd = -1;
}
}
return (JOURNAL_CLOSE);
}
//print hexdump of a journalblock
int dump_journal_block( __u32 block_nr , int flag){
char *buf = NULL;
unsigned int got;
int retval = 0;
int blocksize = current_fs->blocksize;
buf = (char*) malloc(blocksize);
if(! buf) return 1 ;
retval = read_journal_block(block_nr * blocksize ,buf,blocksize,&got);
if (retval || got != blocksize)
return retval;
blockhex (stdout, buf, flag, blocksize);
if (buf)
free(buf);
return 0;
}
//read a journal block
int read_journal_block(off_t offset, char *buf, int size, unsigned int *got)
{
int retval = 1;
switch (journal_source.where) {
case JOURNAL_IS_EXTERNAL :
if (lseek(journal_source.fd, offset, SEEK_SET) < 0) {
retval = errno;
fprintf(stderr,"Error %d while seeking in reading journal",retval);
return retval;
}
retval = read(journal_source.fd, buf, size);
if (retval >= 0) {
*got = retval;
retval = 0;
} else retval = errno;
break;
case JOURNAL_IS_INTERNAL :
retval = ext2fs_file_lseek(journal_source.file, offset, EXT2_SEEK_SET, NULL);
if (retval) {
fprintf(stderr,"Error %d while seeking in reading journal",retval);
return retval;
}
retval = ext2fs_file_read(journal_source.file, buf, size, got);
break;
case JOURNAL_IS_DUMMY :
if((offset + size) > (2 *current_fs->blocksize)) retval = -1 ;
memcpy(buf,journal_source.j_dummy + offset,size);
*got = (unsigned int) size;
retval = 0;
break;
}
if (retval)
fprintf(stderr,"Error %d while reading journal",retval);
else if (*got != (unsigned int) size) {
fprintf(stderr,"short read (read %d, expected %d) while reading journal", *got, size);
retval = -1;
}
return retval;
}
const char *type_to_name(int btype)
{
switch (btype) {
case JFS_DESCRIPTOR_BLOCK:
return "descriptor block";
case JFS_COMMIT_BLOCK:
return "commit block";
case JFS_SUPERBLOCK_V1:
return "V1 superblock";
case JFS_SUPERBLOCK_V2:
return "V2 superblock";
case JFS_REVOKE_BLOCK:
return "revoke table";
}
return "unrecognised type";
}
//check if journal block is a lost inodeblock local function
static int jb_is_inodetable(char *buf){
struct ext2_inode *inode;
__u16 mode;
__u32 atime;
__u32 ctime;
__u32 mtime;
__u32 dtime;
int i;
__u32 min = 315601200; // 315601200 = "1980-01-01 20:00:00"
__u32 max = (__u32) now_time;
int i_size = EXT2_INODE_SIZE(current_fs->super);
int inodes_per_block = (current_fs->blocksize / i_size );
int flag = 0;
for (i=0; ii_mode);
atime = ext2fs_le32_to_cpu(inode->i_atime);
ctime = ext2fs_le32_to_cpu(inode->i_ctime);
mtime = ext2fs_le32_to_cpu(inode->i_mtime);
dtime = ext2fs_le32_to_cpu(inode->i_dtime);
if ((!mode) && (!atime) && (!ctime) && (!mtime) && (!dtime) && (!i))
return 0;
if ((!dtime) && (ctime > min) && (ctime < max) && (atime > min) && (atime < max)
&& (mtime > min) && (mtime < max) && inode->i_size && inode->i_blocks
&& LINUX_S_ISREG(mode) && inode->i_block[0] )
flag++;
if (ctime > max)
return 0;
}
return flag;
}
//check if block is a inodeblock local function
static int block_is_inodetable(blk64_t block){
int group;
struct ext2_group_desc *gdp;
blk64_t last;
last = current_fs->super->s_inodes_count / (current_fs->blocksize / current_fs->super->s_inode_size ) ;
last -= ((current_fs->group_desc_count - 1) * current_fs->inode_blocks_per_group );
//FIXME for struct ext4_group_desc 48/64BIT
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
if (block >= (gdp->bg_inode_table + current_fs->inode_blocks_per_group))
continue;
if (block >= gdp->bg_inode_table){
if ( group == (current_fs->group_desc_count - 1 )){
if (block >= gdp->bg_inode_table + last)
break;
}
return 1;
}
else
break;
}
return 0;
}
// get last timestamp of all inode in this journalblock
static __u32 get_transaction_time( __u32 j_block){
char *buf;
struct ext2_inode *inode;
__u32 t_time = 0;
int ino, retval = 0;
unsigned int got;
int inode_size = EXT2_INODE_SIZE(current_fs->super);
int blocksize = current_fs->blocksize;
buf = (char*) malloc(blocksize);
if(! buf) return 0 ;
retval = read_journal_block(j_block * blocksize ,buf,blocksize,&got);
if (retval || got != blocksize){
fprintf(stderr,"Error while read journal block %u\n",(unsigned int)j_block);
t_time = 0;
goto errout;
}
for (ino = 0; ino < EXT2_INODES_PER_BLOCK(current_fs->super);ino++){
inode = (struct ext2_inode*) (buf + (inode_size * ino));
if (t_time < ext2fs_le32_to_cpu(inode->i_ctime))
t_time = ext2fs_le32_to_cpu(inode->i_ctime);
if (t_time < ext2fs_le32_to_cpu(inode->i_atime))
t_time = ext2fs_le32_to_cpu(inode->i_atime);
}
errout:
if (buf) free(buf);
return t_time;
}
//get the transactiontime of a transactionnumber
__u32 get_trans_time( __u32 transaction){
journal_descriptor_tag_t *pointer;
__u32 counter;
pointer = pt_buff;
for (counter=0 ; countertransaction != transaction) continue;
if (block_is_inodetable(pointer->f_blocknr)) {
return get_transaction_time(pointer->j_blocknr);
}
else continue;
}
return 0;
}
//print all usable copy of filesystem blocks are found in journal
void print_block_list(int flag){
journal_descriptor_tag_t *pointer;
__u32 counter;
__u32 transaction_time = 0;
pointer = pt_buff;
printf("\nFound %d copy of Filesystemblock in Journal\n",pt_count);
printf("FS-Block Journal Transact %s\n",(flag) ? "Time in sec Time of Transaction" :"" );
for (counter=0 ; counterf_blocknr)){
transaction_time = get_transaction_time(pointer->j_blocknr);
}
else transaction_time = 0;
}
if (transaction_time)
fprintf(stdout,"%12llu %8lu %8lu %8lu %s",(__u64) pointer->f_blocknr , (long unsigned int)pointer->j_blocknr ,
(long unsigned int)pointer->transaction, (long unsigned int)transaction_time, time_to_string(transaction_time) );
else
fprintf(stdout,"%12llu %8lu %8lu\n",(__u64) pointer->f_blocknr , (long unsigned int)pointer->j_blocknr ,
(long unsigned int)pointer->transaction);
pointer++ ;
}
}
//print all transactions for a fs-block
void print_block_transaction(blk64_t block_nr, int flag){
journal_descriptor_tag_t *pointer;
__u32 counter;
int is_inode_block = 0;
__u32 transaction_time = 0;
if (flag) is_inode_block = block_is_inodetable(block_nr);
pointer = pt_buff;
printf("\nTransactions of Filesystemblock %llu in Journal\n",block_nr);
printf("FS-Block Journal Transact %s\n",(is_inode_block) ? "Time in sec Time of Transaction" :"");
for (counter=0 ; counterf_blocknr == block_nr){
if (is_inode_block){
transaction_time = get_transaction_time(pointer->j_blocknr);
fprintf(stdout,"%12llu %8lu %8lu %8lu %s",(__u64) pointer->f_blocknr ,
(long unsigned int)pointer->j_blocknr, (long unsigned int)pointer->transaction,
(long unsigned int)transaction_time, time_to_string(transaction_time));
}else
fprintf(stdout,"%12llu %8lu %8lu\n",(__u64) pointer->f_blocknr ,
(long unsigned int)pointer->j_blocknr , (long unsigned int)pointer->transaction);
}
pointer++ ;
}
}
//get the journalblocknumber for a transactionnumber
__u32 get_journal_blocknr(blk64_t block_nr, __u32 trans_nr){
__u32 journal_nr = 0;
journal_descriptor_tag_t *pointer;
int i;
pointer = pt_buff;
for (i=0; itransaction != trans_nr) continue;
if ( pointer->f_blocknr == block_nr ) journal_nr = pointer->j_blocknr ;
}
return journal_nr;
}
// get the last dir-block for transaction from journal or if not, found the real block
//FIXME: blk64_t ????
int get_last_block(char *buf, blk64_t *block, __u32 t_start, __u32 t_end){
int retval = 0;
int i , count ;
unsigned int got;
char *journal_tag_buf = NULL;
journal_descriptor_tag_t *block_list;
blk_t j_block = 0;
int blksize = current_fs->blocksize;
if ((!t_start) && (!t_end)){
//there is no transaction, it is not from a journal Inode
#ifdef DEBUG
printf("DIR_BLOCK (F) %lld\n", (blk64_t)*block);
#endif
return io_channel_read_blk(current_fs->io, *block, 1, buf);
}
count = get_block_list_count((blk64_t)*block) ;
journal_tag_buf = (void *)malloc((sizeof(journal_descriptor_tag_t) * count));
if (!journal_tag_buf) {
// fprintf(stderr,"Error: while allocate Memory for blocklist\n");
retval = BLOCK_ABORT;
goto errout;
}
block_list = (journal_descriptor_tag_t *) journal_tag_buf;
count = get_block_list(block_list, *block, count);
for (i = 0 ; i < count ; i++ , block_list++ ) {
if (block_list->transaction < t_start )
continue;
if (block_list->transaction <= t_end){
j_block = block_list->j_blocknr;
if (i != (count -1))
continue;
}
retval = read_journal_block((j_block) ? (j_block * blksize) : (block_list->j_blocknr * blksize),
buf , current_fs->blocksize , &got);
#ifdef DEBUG
printf("DIR_BLOCK (J) %lld (%ld <-> %ld) JB=%ld : %d\n", (blk64_t)*block,t_start,t_end, j_block, retval );
#endif
if (retval) {
retval = BLOCK_ERROR ;
}
goto errout;
}
// if not in journal found, use real block
#ifdef DEBUG
printf("DIR_BLOCK (N) %lld\n", (blk64_t)*block);
#endif
retval = io_channel_read_blk(current_fs->io, *block, 1, buf);
errout:
if (journal_tag_buf) free(journal_tag_buf);
return retval;
}
//get count of journal blocklist
int get_block_list_count(blk64_t block){
int counter = 0;
int i;
journal_descriptor_tag_t *list_pointer = pt_buff ;
for (i=0;if_blocknr == block) counter++ ;
return counter;
}
//local sortfunction for journalblocks
static int sort_block_list(journal_descriptor_tag_t *pointer, int counter ){
journal_descriptor_tag_t p1;
int c1,c2,flag=1;
c1 = 0;
//FIXME : for faster sort
while (flag) {
flag = 0;
for (c2 = 0;c2 < (counter -c1 -1);c2++){
if ((pointer+c2)->transaction > (pointer+c2+1)->transaction ){
p1=*(pointer+c2);
*(pointer+c2)=*(pointer+c2+1);
*(pointer+c2+1)=p1;
flag = 1 ;
}
}
c1++ ;
}
return counter;
}
//get a sorted list of all copys of a filesystemblock
int get_block_list(journal_descriptor_tag_t *pointer, blk64_t block , int counter ){
int i,j = 0;
journal_descriptor_tag_t *list_pointer = pt_buff ;
journal_descriptor_tag_t *fill_pointer = pointer ;
if (counter) {
for (i=0,j=0;(if_blocknr == block) {
fill_pointer->f_blocknr = block;
fill_pointer->j_blocknr = list_pointer->j_blocknr;
fill_pointer->transaction = list_pointer->transaction;
fill_pointer++ ;
j++;
}
sort_block_list(pointer,counter);
}
return j;
}
//extract the journal in the local intern blocklist
static void extract_descriptor_block(char *buf, journal_superblock_t *jsb,
unsigned int *blockp, int blocksize,
tid_t transaction, unsigned int *wrapflag )
{
int offset, tag_size = JBD_TAG_SIZE32;
char *tagp;
journal_block_tag_t *tag;
unsigned int blocknr;
__u32 tag_block;
__u32 tag_flags;
if (jsb->s_feature_incompat & JFS_FEATURE_INCOMPAT_64BIT)
tag_size = JBD_TAG_SIZE64;
offset = sizeof(journal_header_t);
blocknr = *blockp;
#ifdef DEBUG
fprintf(stdout, "D-Block %u & %u: ", transaction, blocknr);
#endif
++blocknr;
if (blocknr >= jsb->s_maxlen) {
if (*wrapflag == WRAP_ON) {
blocknr -= (jsb->s_maxlen - jsb->s_first);
*wrapflag = WRAP_READY;
}
else {
*blockp = blocknr;
return;
}
}
do {
/* Work out the location of the current tag, and skip to
* the next one... */
tagp = &buf[offset];
tag = (journal_block_tag_t *) tagp;
offset += tag_size;
/* ... and if we have gone too far, then we've reached the
end of this block. */
if (offset > blocksize) break;
tag_block = ext2fs_be32_to_cpu(tag->t_blocknr) ;
tag_flags = ext2fs_be32_to_cpu(tag->t_flags);
if (!(tag_flags & JFS_FLAG_SAME_UUID))
offset += 16;
#ifdef DEBUG
// fprintf(stderr, " FS block %12lu logged at ", tag_block);
// fprintf(stderr, "sequence %u, ", transaction);
// fprintf(stderr, "journal block %u (flags 0x%x)\n", blocknr,tag_flags);
fprintf(stdout,"*");
#endif
pt->f_blocknr = tag_block ;
if (tag_size > JBD_TAG_SIZE32) pt->f_blocknr |= (__u64)ext2fs_be32_to_cpu(tag->t_blocknr_high) << 32;
pt->j_blocknr = blocknr;
pt->transaction = transaction;
pt++;
pt_count++;
++blocknr;
if (blocknr >= jsb->s_maxlen) {
if (*wrapflag == WRAP_ON) {
blocknr -= (jsb->s_maxlen - jsb->s_first);
*wrapflag = WRAP_READY;
}
else {
*blockp = blocknr;
return;
}
}
} while (!(tag_flags & JFS_FLAG_LAST_TAG));
*blockp = (*wrapflag == WRAP_READY) ? jsb->s_maxlen : blocknr ;
}
// init and extract the journal in the local private data
int init_journal(void)
{
struct ext2_super_block *sb;
char buf[8192];
journal_superblock_t *jsb;
unsigned int blocksize = 1024;
unsigned int got;
int retval;
__u32 magic, sequence, blocktype;
journal_header_t *header;
tid_t transaction;
unsigned int blocknr = 0;
unsigned int maxlen = 0;
unsigned int wrapflag;
/* First, check to see if there's an ext2 superblock header */
retval = read_journal_block( 0, buf, 2048, &got);
if (retval) goto errout;
jsb = (journal_superblock_t *) buf;
sb = (struct ext2_super_block *) (buf+1024);
#ifdef WORDS_BIGENDIAN
if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(sb);
#endif
if ((ext2fs_be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) &&
(sb->s_magic == EXT2_SUPER_MAGIC) &&
(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
blocksize = EXT2_BLOCK_SIZE(sb);
blocknr = (blocksize == 1024) ? 2 : 1;
uuid_unparse(sb->s_uuid, jsb_buffer);
#ifdef DEBUG
fprintf(stdout, "Ext2 superblock header found.");
fprintf(stdout, "\tuuid=%s", jsb_buffer);
fprintf(stdout, "\tblocksize=%d", blocksize);
fprintf(stdout, "\tjournal data size %lu\n",(unsigned long) sb->s_blocks_count);
#endif
}
/* Next, read the journal superblock */
retval = read_journal_block(blocknr*blocksize,jsb_buffer, 1024, &got);
if (retval) goto errout;
jsb = (journal_superblock_t *) jsb_buffer;
if (ext2fs_be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) {
fprintf(stderr, "Journal superblock magic number invalid!\n");
return JOURNAL_ERROR ;
}
journal_superblock_to_cpu((__u32*)jsb_buffer);
blocksize = jsb->s_blocksize;
transaction = jsb->s_sequence;
maxlen = jsb->s_maxlen;
blocknr = jsb->s_first;
wrapflag = WRAP_UNDEF ;
pt_buff = malloc(maxlen * sizeof(journal_descriptor_tag_t));
pt = pt_buff ;
pt_count = 0;
if (pt_buff == NULL) {
fprintf(stderr,"Error: can't allocate %u Memory\n",maxlen * sizeof(journal_descriptor_tag_t));
goto errout;
}
ptl = (__u32*)(pt_buff + (maxlen * sizeof(journal_descriptor_tag_t)));
ptl_count = 0;
#ifdef DEBUG
fprintf(stdout, "Journal starts at block %u, transaction %u\n", blocknr, transaction);
#endif
while ( blocknr < maxlen ){
retval = read_journal_block(blocknr*blocksize, buf, blocksize, &got);
if (retval || got != blocksize)
//return JOURNAL_OPEN;
goto errout;
header = (journal_header_t *) buf;
magic = ext2fs_be32_to_cpu(header->h_magic);
sequence = ext2fs_be32_to_cpu(header->h_sequence);
blocktype = ext2fs_be32_to_cpu(header->h_blocktype);
if (magic != JFS_MAGIC_NUMBER) {
#ifdef DEBUG
// fprintf (stdout, "No magic number at block %u: skip this block\n", blocknr);
fprintf(stdout,"-");
#endif
if (jb_is_inodetable(buf)){
// printf("lost %d\n", blocknr);
ptl--;
*ptl = blocknr;
ptl_count ++;
}
if ( ! wrapflag ) wrapflag = WRAP_ON ;
blocknr++ ;
continue;
}
#ifdef DEBUG
fprintf (stdout, "\n%u (%s) T=%u JB=%u\n", blocktype, type_to_name(blocktype), sequence, blocknr);
#endif
if ( ! wrapflag ) wrapflag = WRAP_OFF ;
switch (blocktype) {
case JFS_DESCRIPTOR_BLOCK:
extract_descriptor_block(buf, jsb, &blocknr, blocksize, sequence, &wrapflag);
continue;
case JFS_COMMIT_BLOCK:
blocknr++;
continue;
case JFS_REVOKE_BLOCK:
blocknr++;
continue;
default:
fprintf (stderr, "Unexpected block type %u at block %u.\n", blocktype, blocknr);
return JOURNAL_OPEN;
}
}
#ifdef DEBUG
fprintf(stdout,"\nJournal init complete: %ld block copy\n",pt_count);
#endif
return JOURNAL_OPEN ;
errout:
fprintf(stderr,"Error: can't read journal\n");
if ( pt_buff ) {
free(pt_buff);
pt_buff = NULL;
}
return JOURNAL_ERROR ;
}
//get a list of all copy of blockbitmap from journal
int get_block_bitmap_list( journal_bitmap_tag_t **bitmap_list){
int count, sum, i, j;
char* tag_buf = NULL;
journal_bitmap_tag_t *fill_pointer ;
journal_descriptor_tag_t *list_pointer = pt_buff ;
count = sum = 0;
for (i = 0; i < pt_count ; i++, list_pointer++ )
for (j = 0 ; j < current_fs->group_desc_count; j++ )
#ifdef EXT2_FLAG_64BITS
if( list_pointer->f_blocknr == (blk64_t) ext2fs_block_bitmap_loc(current_fs, j))
#else
if (list_pointer->f_blocknr == (blk64_t) current_fs->group_desc[j].bg_block_bitmap)
#endif
count++ ;
if (count && (tag_buf = malloc((sizeof(journal_bitmap_tag_t) * count)))){
fill_pointer = (journal_bitmap_tag_t*)tag_buf ;
list_pointer = pt_buff ;
for ( i = 0 ; (i < pt_count && sum < count); i++ , list_pointer++ )
for ( j = 0; j < current_fs->group_desc_count; j++ )
#ifdef EXT2_FLAG_64BITS
if( list_pointer->f_blocknr == (blk64_t) ext2fs_block_bitmap_loc(current_fs, j)){
#else
if (list_pointer->f_blocknr == (blk64_t) current_fs->group_desc[j].bg_block_bitmap){
#endif
fill_pointer->blockgroup = (blk64_t) j;
fill_pointer->j_blocknr = list_pointer->j_blocknr;
fill_pointer->transaction = list_pointer->transaction;
fill_pointer++ ;
sum++;
}
list_pointer = (journal_descriptor_tag_t*) tag_buf;
sort_block_list( list_pointer,sum );
*bitmap_list = (journal_bitmap_tag_t*) tag_buf;
}
return sum;
}
//create and init the journal block bitmap
//return the count of all blockbitmap copies or 0
int init_block_bitmap_list(ext2fs_block_bitmap *d_bmap, __u32 t_after){
__u32 t;
jbbm.list = NULL;
jbbm.block_buf = NULL;
if (ext2fs_copy_bitmap(current_fs->block_map, d_bmap)){
fprintf(stderr,"Error: while duplicate bitmap\n");
return 0;
}
ext2fs_clear_block_bitmap(*d_bmap);
jbbm.count = get_block_bitmap_list( &jbbm.list);
jbbm.block_buf = malloc(current_fs->blocksize * 2);
if (! jbbm.block_buf){
fprintf(stderr,"Error: can't allocate Memory\n");
return 0;
}
if (jbbm.count){
jbbm.pointer = jbbm.list + jbbm.count - 1;
// jbbm.last_trans = jbbm.pointer->transaction;
jbbm.last_trans = 0;
jbbm.groups = current_fs->group_desc_count;
while ((jbbm.pointer > jbbm.list) && (get_trans_time(jbbm.pointer->transaction) >= t_after)) {
t = jbbm.pointer->transaction;
while (jbbm.pointer->transaction == t)
jbbm.pointer-- ;
}
jbbm.pointer++;
jbbm.first_trans = jbbm.pointer->transaction ;
jbbm.pointer = jbbm.list + jbbm.count - 1;
jbbm.blocksize = current_fs->blocksize;
jbbm.blocklen = current_fs->super->s_blocks_per_group >> 3 ;
jbbm.last_blocklen = (current_fs->super->s_blocks_count >> 3) % jbbm.blocklen;
if (!jbbm.last_blocklen)
jbbm.last_blocklen = jbbm.blocklen;
else
jbbm.last_blocklen += (current_fs->super->s_blocks_count % 8) ? 1 : 0 ;
}
return jbbm.count;
}
//destroy the journal block bitmap
void clear_block_bitmap_list(ext2fs_block_bitmap d_bmap){
if (jbbm.list)
free (jbbm.list);
if (jbbm.block_buf)
free (jbbm.block_buf);
jbbm.list = NULL;
jbbm.block_buf = NULL;
ext2fs_free_block_bitmap(d_bmap);
}
//produces a differential block bitmap for a transaction from the Journal
//return 1 if bitmap is differenzial ; an 2 if bitmap is empty
int next_block_bitmap(ext2fs_block_bitmap d_bmap){
struct ext2fs_struct_loc_generic_bitmap *fs_bitmap, *df_bitmap;
journal_bitmap_tag_t *p1;
journal_bitmap_tag_t *p2;
__u32 blockg, skip,i,len;
unsigned int got;
int retval;
char *diff_buf;
if (jbbm.pointer->transaction < jbbm.first_trans)
return 0;
fs_bitmap = (struct ext2fs_struct_loc_generic_bitmap*) current_fs->block_map;
df_bitmap = (struct ext2fs_struct_loc_generic_bitmap*) d_bmap;
ext2fs_clear_block_bitmap(d_bmap);
if (jbbm.last_trans == 0 ){
// last transaction compared with fs blockbitmap
jbbm.last_trans = jbbm.pointer->transaction;
p2 = jbbm.pointer;
while ((p2 >= jbbm.list) && (p2->transaction == jbbm.pointer->transaction)){
blockg = (__u32)p2->blockgroup;
skip = (jbbm.blocklen * blockg);
len = ((blockg + 1) == jbbm.groups) ? jbbm.last_blocklen : jbbm.blocklen;
retval = read_journal_block((__u32)p2->j_blocknr * jbbm.blocksize, jbbm.block_buf, jbbm.blocksize, &got);
if (retval || got != jbbm.blocksize){
fprintf(stderr,"Error: while reading journal\n");
goto errout;
}
for (i = 0 ; i < len ; i++){
*((df_bitmap->bitmap)+skip+i) = *(jbbm.block_buf + i) ^ *((fs_bitmap->bitmap)+skip+i) ;
}
p2--;
}
}
else{
//all other transactions compared with previous block copy
p1 = jbbm.pointer;
diff_buf = jbbm.block_buf + jbbm.blocksize ;
while ((p1->transaction >= jbbm.first_trans) && (p1->transaction == jbbm.pointer->transaction)){
p2 = p1 -1;
blockg = (__u32)p1->blockgroup;
skip = (jbbm.blocklen * blockg);
len = ((blockg + 1) == jbbm.groups) ? jbbm.last_blocklen : jbbm.blocklen;
while ((p2 > jbbm.list) && ((__u32)p2->blockgroup != blockg))
p2-- ;
retval = read_journal_block((__u32)p1->j_blocknr * jbbm.blocksize, jbbm.block_buf, jbbm.blocksize, &got);
if (retval || got != jbbm.blocksize){
fprintf(stderr,"Error: while reading journal\n");
goto errout;
}
if ((p2 == jbbm.list) && ((__u32)p2->blockgroup != blockg)){
//no previous block copy found, create difference to the entire block group
for (i = 0 ; i < len ; i++){
*((df_bitmap->bitmap)+skip+i) = *(jbbm.block_buf + i) ^ 0xFF ;
}
}
else{
retval = read_journal_block((__u32)p2->j_blocknr * jbbm.blocksize, diff_buf, jbbm.blocksize, &got);
if (retval || got != jbbm.blocksize){
fprintf(stderr,"Error: while reading journal\n");
goto errout;
}
//previous block copy is found, create difference to this copy
for (i = 0 ; i < len ; i++){
*((df_bitmap->bitmap)+skip+i) = *(jbbm.block_buf + i) ^ *(diff_buf + i ) ;
}
}
p1--;
}
jbbm.pointer = p1;
}
i = 0;
len = (fs_bitmap->end +1) >> 3;
while ((i < len) && (!(*(df_bitmap->bitmap +i))))
i++;
return ( i == len) ? 2 : 1 ;
errout:
return 0;
}
//read the next journal-lost inode block
int get_pool_block(char *buf){
int retval = 0;
int ret = 0;
unsigned int got ;
int blocksize = current_fs->blocksize;
if (ptl_count){
retval = read_journal_block(*ptl * blocksize ,(char*)buf,blocksize,&got);
if ((! retval) && (got == blocksize)){
ret = 1;
}
ptl_count--;
ptl++;
}
return ret;
}