/***************************************************************************
* 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
#ifdef HAVE_ERRNO_H
#include
#endif
#include "inode.h"
#include "ring_buf.h"
#include "extent_db.h"
#include "block.h"
extern ext2_filsys current_fs;
extern time_t now_time ;
// read real fs inode 128++
int intern_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode, int bufsize)
{
int retval;
retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize);
if (retval) {
fprintf(stderr,"Error %d while reading inode %u\n",retval, ino);
return 1;
}
return 0;
}
// read real fs inode 128
int intern_read_inode(ext2_ino_t ino, struct ext2_inode * inode)
{
int retval;
retval = ext2fs_read_inode(current_fs, ino, inode);
if (retval) {
fprintf(stderr,"Error %d while reading inode %u\n",retval, ino);
return 1;
}
return 0;
}
#ifdef WORDS_BIGENDIAN
// On my current version of libext2 the extra time fields ar not bigendian corrected
// We want this solved temporarily here with this function
static void le_to_cpu_swap_extra_time(struct ext2_inode_large *inode, char *inode_buf){
//inode->i_pad1 = ext2fs_le16_to_cpu(((struct ext2_inode_large *))inode_buf->i_pad1);
inode->i_ctime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_ctime_extra);
inode->i_mtime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_mtime_extra );
inode->i_atime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_atime_extra );
inode->i_crtime = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_crtime );
inode->i_crtime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_crtime_extra );
//inode->i_version_hi = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_version_hi );
}
#endif
//subfunction for dump_inode_extra
static void dump_xattr_string(FILE *out, const char *str, int len)
{
int printable = 0;
int i;
// check: is string "printable enough?"
for (i = 0; i < len; i++)
if (isprint(str[i]))
printable++;
if (printable <= len*7/8)
printable = 0;
for (i = 0; i < len; i++)
if (printable)
fprintf(out, isprint(str[i]) ? "%c" : "\\%03o",
(unsigned char)str[i]);
else
fprintf(out, "%02x ", (unsigned char)str[i]);
}
//print Blocks of inode (ext4)
static void local_dump_extents(FILE *f, const char *prefix, struct ext2_inode * inode,
int flags, int logical_width, int physical_width)
{
ext2_extent_handle_t handle;
struct ext2fs_extent extent;
struct ext2_extent_info info;
int op = EXT2_EXTENT_ROOT;
unsigned int printed = 0;
errcode_t errcode;
errcode = local_ext2fs_extent_open(current_fs, *inode, &handle);
if (errcode)
return;
if (flags & DUMP_EXTENT_TABLE)
fprintf(f, "Level Entries %*s %*s Length Flags\n",
(logical_width*2)+3, "Logical",
(physical_width*2)+3, "Physical");
else
fprintf(f, "%sEXTENTS:\n%s", prefix, prefix);
while (1) {
errcode = ext2fs_extent_get(handle, op, &extent);
if (errcode)
break;
op = EXT2_EXTENT_NEXT;
if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
continue;
if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
if ((flags & DUMP_LEAF_EXTENTS) == 0)
continue;
}
errcode = ext2fs_extent_get_info(handle, &info);
if (errcode)
continue;
if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) {
if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
continue;
if (flags & DUMP_EXTENT_TABLE) {
fprintf(f, "%2d/%2d %3d/%3d %11llu - %11llu "
"%11llu%14s %6u\n",
info.curr_level, info.max_depth,
info.curr_entry, info.num_entries,
extent.e_lblk,
extent.e_lblk + (extent.e_len - 1),
extent.e_pblk,
"", extent.e_len);
continue;
}
fprintf(f, "%s(NODE #%d, %lld-%lld, blk %lld)",
printed ? ", " : "",
info.curr_entry,
extent.e_lblk,
extent.e_lblk + (extent.e_len - 1),
extent.e_pblk);
printed = 1;
continue;
}
if (flags & DUMP_EXTENT_TABLE) {
fprintf(f, "%2d/%2d %3d/%3d %11llu - %11llu %11llu - %11llu %6u %s\n",
info.curr_level, info.max_depth,
info.curr_entry, info.num_entries,
extent.e_lblk,
extent.e_lblk + (extent.e_len - 1),
extent.e_pblk,
extent.e_pblk + (extent.e_len - 1),
extent.e_len,
extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT ?
"Uninit" : "");
continue;
}
if (extent.e_len == 0)
continue;
else if (extent.e_len == 1)
fprintf(f,
"%s(%lld%s): %lld",
printed ? ", " : "",
extent.e_lblk,
extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT ?
" [uninit]" : "",
extent.e_pblk);
else
fprintf(f,
"%s(%lld-%lld%s): %lld-%lld",
printed ? ", " : "",
extent.e_lblk,
extent.e_lblk + (extent.e_len - 1),
extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT ?
" [uninit]" : "",
extent.e_pblk,
extent.e_pblk + (extent.e_len - 1));
printed = 1;
}
if (printed)
fprintf(f, "\n\n");
local_ext2fs_extent_free(handle);
}
//print extended attribute of Inode
static void dump_inode_extra(FILE *out,
const char *prefix EXT2FS_ATTR((unused)),
ext2_ino_t inode_num EXT2FS_ATTR((unused)),
struct ext2_inode_large *inode)
{
struct ext2_ext_attr_entry *entry;
__u32 *magic;
char *start, *end;
unsigned int storage_size;
fprintf(out, "Size of extra inode fields: %u\n", inode->i_extra_isize);
if (inode->i_extra_isize > EXT2_INODE_SIZE(current_fs->super) -
EXT2_GOOD_OLD_INODE_SIZE) {
fprintf(stderr, "invalid inode->i_extra_isize (%u)\n",
inode->i_extra_isize);
return;
}
storage_size = EXT2_INODE_SIZE(current_fs->super) -
EXT2_GOOD_OLD_INODE_SIZE -
inode->i_extra_isize;
magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize);
if (*magic == EXT2_EXT_ATTR_MAGIC) {
fprintf(out, "Extended attributes stored in inode body: \n");
end = (char *) inode + EXT2_INODE_SIZE(current_fs->super);
start = (char *) magic + sizeof(__u32);
entry = (struct ext2_ext_attr_entry *) start;
while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
struct ext2_ext_attr_entry *next =
EXT2_EXT_ATTR_NEXT(entry);
if (entry->e_value_size > storage_size ||
(char *) next >= end) {
fprintf(out, "invalid EA entry in inode\n");
return;
}
fprintf(out, " ");
dump_xattr_string(out, EXT2_EXT_ATTR_NAME(entry),
entry->e_name_len);
fprintf(out, " = \"");
dump_xattr_string(out, start + entry->e_value_offs,
entry->e_value_size);
fprintf(out, "\" (%u)\n", entry->e_value_size);
entry = next;
}
}
}
//subfunction for dump_blocks
static void finish_range(struct list_blocks_struct *lb)
{
if (lb->first_block == 0)
return;
if (lb->first)
lb->first = 0;
else
fprintf(lb->f, ", ");
if (lb->first_block == lb->last_block)
fprintf(lb->f, "(%lld):%u",
(long long)lb->first_bcnt, lb->first_block);
else
fprintf(lb->f, "(%lld-%lld):%u-%u",
(long long)lb->first_bcnt, (long long)lb->last_bcnt,
lb->first_block, lb->last_block);
lb->first_block = 0;
}
//subfunction for dump_blocks
static int list_blocks_proc(ext2_filsys fs EXT2FS_ATTR((unused)),
blk64_t *block64nr, e2_blkcnt_t blockcnt,
blk64_t ref_block EXT2FS_ATTR((unused)),
int ref_offset EXT2FS_ATTR((unused)),
void *private)
{
blk_t blocknr = (blk_t) *block64nr;
struct list_blocks_struct *lb = (struct list_blocks_struct *) private;
lb->total++;
if (blockcnt >= 0) {
//* See if we can add on to the existing range (if it exists)
if (lb->first_block &&
(lb->last_block+1 == blocknr) &&
(lb->last_bcnt+1 == blockcnt)) {
lb->last_block = blocknr;
lb->last_bcnt = blockcnt;
return 0;
}
//* Start a new range.
finish_range(lb);
lb->first_block = lb->last_block = blocknr;
lb->first_bcnt = lb->last_bcnt = blockcnt;
return 0;
}
//* Not a normal block. Always force a new range.
finish_range(lb);
if (lb->first)
lb->first = 0;
else
fprintf(lb->f, ", ");
if (blockcnt == -1)
fprintf(lb->f, "(IND):%u", blocknr);
else if (blockcnt == -2)
fprintf(lb->f, "(DIND):%u", blocknr);
else if (blockcnt == -3)
fprintf(lb->f, "(TIND):%u", blocknr);
return 0;
}
// print the Datablocks from Inode (ext3)
static void dump_blocks(FILE *f, const char *prefix, struct ext2_inode *inode)
{
struct list_blocks_struct lb;
fprintf(f, "%sBLOCKS:\n%s", prefix, prefix);
lb.total = 0;
lb.first_block = 0;
lb.f = f;
lb.first = 1;
// ext2fs_block_iterate2(current_fs, inode, BLOCK_FLAG_READ_ONLY, NULL,
local_block_iterate3(current_fs, *inode, BLOCK_FLAG_READ_ONLY, NULL,
list_blocks_proc, (void *)&lb);
finish_range(&lb);
if (lb.total)
fprintf(f, "\n%sTOTAL: %lld\n", prefix, (long long)lb.total);
fprintf(f,"\n");
}
//print the contents of inode
void dump_inode(FILE *out, const char *prefix,
ext2_ino_t inode_num, struct ext2_inode *inode,
int do_dump_blocks)
{
const char *i_type;
char frag, fsize;
int os = current_fs->super->s_creator_os;
struct ext2_inode_large *large_inode;
int is_large_inode = 0;
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
is_large_inode = 1;
large_inode = (struct ext2_inode_large *) inode;
// blockhex(stdout,large_inode,0,256);
if (LINUX_S_ISDIR(inode->i_mode)) i_type = "directory";
else if (LINUX_S_ISREG(inode->i_mode)) i_type = "regular";
else if (LINUX_S_ISLNK(inode->i_mode)) i_type = "symlink";
else if (LINUX_S_ISBLK(inode->i_mode)) i_type = "block special";
else if (LINUX_S_ISCHR(inode->i_mode)) i_type = "character special";
else if (LINUX_S_ISFIFO(inode->i_mode)) i_type = "FIFO";
else if (LINUX_S_ISSOCK(inode->i_mode)) i_type = "socket";
else i_type = "bad type";
fprintf(out, "%sInode: %u Type: %s ", prefix, inode_num, i_type);
fprintf(out, "%sMode: %04o Flags: 0x%x ",
prefix, inode->i_mode & 0777, inode->i_flags);
#ifdef FILE_ATTR
#include
if (do_dump_blocks && inode->i_flags) {
fprintf(out,"[");
print_flags(out, inode->i_flags, 0);
fprintf(out,"]\n");
}
else
#endif
fprintf(out,"\n");
if (is_large_inode && large_inode->i_extra_isize >= 24) {
fprintf(out, "%sGeneration: %u Version: 0x%08x:%08x\n",
prefix, inode->i_generation, large_inode->i_version_hi,
inode->osd1.linux1.l_i_version);
} else {
fprintf(out, "%sGeneration: %u Version: 0x%08x\n", prefix,
inode->i_generation, inode->osd1.linux1.l_i_version);
}
fprintf(out, "%sUser: %5d Group: %5d Size: ",
prefix, inode_uid(*inode), inode_gid(*inode));
if (LINUX_S_ISREG(inode->i_mode)) {
unsigned long long i_size = (inode->i_size |
((unsigned long long)inode->i_size_high << 32));
fprintf(out, "%llu\n", i_size);
} else
fprintf(out, "%d\n", inode->i_size);
if (os == EXT2_OS_HURD)
fprintf(out,
"%sFile ACL: %d Directory ACL: %d Translator: %d\n",
prefix,
inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_file_acl : 0,
inode->osd1.hurd1.h_i_translator);
else
fprintf(out, "%sFile ACL: %llu Directory ACL: %d\n",
prefix,
inode->i_file_acl | ((long long)
(inode->osd2.linux2.l_i_file_acl_high) << 32),
LINUX_S_ISDIR(inode->i_mode) ? inode->i_file_acl : 0);
if (os == EXT2_OS_LINUX)
fprintf(out, "%sLinks: %d Blockcount: %llu\n",
prefix, inode->i_links_count,
(((unsigned long long)
inode->osd2.linux2.l_i_blocks_hi << 32)) +
inode->i_blocks);
else
fprintf(out, "%sLinks: %d Blockcount: %u\n",
prefix, inode->i_links_count, inode->i_blocks);
switch (os) {
case EXT2_OS_HURD:
frag = inode->osd2.hurd2.h_i_frag;
fsize = inode->osd2.hurd2.h_i_fsize;
break;
default:
frag = fsize = 0;
}
fprintf(out, "%sFragment: Address: %d Number: %d Size: %d\n",
prefix, inode->i_faddr, frag, fsize);
if (is_large_inode && large_inode->i_extra_isize >= 24) {
fprintf(out, "%s ctime: %10lu:%010lu -- %s", prefix,
(long unsigned int)inode->i_ctime, (long unsigned int)large_inode->i_ctime_extra,
time_to_string(inode->i_ctime));
fprintf(out, "%s atime: %10lu:%010lu -- %s", prefix,
(long unsigned int)inode->i_atime, (long unsigned int)large_inode->i_atime_extra,
time_to_string(inode->i_atime));
fprintf(out, "%s mtime: %10lu:%010lu -- %s", prefix,
(long unsigned int)inode->i_mtime, (long unsigned int)large_inode->i_mtime_extra,
time_to_string(inode->i_mtime));
fprintf(out, "%scrtime: %10lu:%010lu -- %s", prefix,
(long unsigned int)large_inode->i_crtime, (long unsigned int)large_inode->i_crtime_extra,
time_to_string(large_inode->i_crtime));
} else {
fprintf(out, "%sctime: %10lu -- %s", prefix, (long unsigned int)inode->i_ctime,
time_to_string(inode->i_ctime));
fprintf(out, "%satime: %10lu -- %s", prefix, (long unsigned int)inode->i_atime,
time_to_string(inode->i_atime));
fprintf(out, "%smtime: %10lu -- %s", prefix, (long unsigned int)inode->i_mtime,
time_to_string(inode->i_mtime));
}
if (inode->i_dtime)
fprintf(out, "%sdtime: %10lu -- %s", prefix, (long unsigned int)inode->i_dtime,
time_to_string(inode->i_dtime));
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
dump_inode_extra(out, prefix, inode_num,
(struct ext2_inode_large *) inode);
if (LINUX_S_ISLNK(inode->i_mode) && ext2fs_inode_data_blocks(current_fs,inode) == 0)
fprintf(out, "%sFast_link_dest: %.*s\n\n", prefix,
(int) inode->i_size, (char *)inode->i_block);
else if (LINUX_S_ISBLK(inode->i_mode) || LINUX_S_ISCHR(inode->i_mode)) {
int major, minor;
const char *devnote;
if (inode->i_block[0]) {
major = (inode->i_block[0] >> 8) & 255;
minor = inode->i_block[0] & 255;
devnote = "";
} else {
major = (inode->i_block[1] & 0xfff00) >> 8;
minor = ((inode->i_block[1] & 0xff) |
((inode->i_block[1] >> 12) & 0xfff00));
devnote = "(New-style) ";
}
fprintf(out, "%sDevice major/minor number: %02d:%02d (hex %02x:%02x)\n\n",
devnote, major, minor, major, minor);
} else if (do_dump_blocks && !(inode->i_dtime)) {
if (inode->i_flags & EXT4_EXTENTS_FL)
local_dump_extents(out, prefix, inode,
DUMP_LEAF_EXTENTS | DUMP_EXTENT_TABLE, 11, 11);
else
dump_blocks(out, prefix, inode);
}
}
//calculate the position of inode in FS
blk_t get_inode_pos(struct ext2_super_block *es ,struct inode_pos_struct *pos, ext2_ino_t inode_nr,int flag){
//FIXME blk64_t
__u32 inode_group, group_offset, inodes_per_block, inode_offset;
blk_t inode_block;
pos->size = EXT2_INODE_SIZE(current_fs->super);
inode_group = ((inode_nr - 1) / es->s_inodes_per_group);
group_offset = ((inode_nr - 1) % es->s_inodes_per_group);
inodes_per_block = (current_fs->blocksize / pos->size );
#ifdef EXT2_FLAG_64BITS
inode_block = (blk_t) ext2fs_inode_table_loc(current_fs,inode_group) + (group_offset / inodes_per_block);
#else
inode_block = current_fs->group_desc[inode_group].bg_inode_table + (group_offset / inodes_per_block);
#endif
inode_offset = ((group_offset % inodes_per_block) * pos->size );
if (flag)
printf("Inode %u is at group %u, block %u, offset %u\n", inode_nr, inode_group, inode_block, inode_offset);
pos->block = inode_block;
pos->offset = inode_offset;
return inode_block;
};
// get journalinode from transactionnumber
int get_transaction_inode(ext2_ino_t inode_nr, __u32 transaction_nr, struct ext2_inode_large *inode){
struct inode_pos_struct pos;
__u32 journal_block;
blk_t block_nr;
struct ext2_inode_large *inode_buf;
char *buf = NULL;
int retval = 0;
unsigned int got;
int blocksize = current_fs->blocksize;
block_nr = get_inode_pos(current_fs->super, &pos , inode_nr, 0);
journal_block = get_journal_blocknr(block_nr, transaction_nr);
if (! journal_block){
fprintf(stdout,"No journalblock found for inode %lu by transaction %lu\n",
(long unsigned int)inode_nr,(long unsigned int)transaction_nr);
retval = -1;
}
else {
buf =(char*) malloc(blocksize);
if(buf){
retval = read_journal_block(journal_block * blocksize ,buf,blocksize,&got);
if ((! retval) && (got == blocksize)){
inode_buf = (struct ext2_inode_large *)(buf + pos.offset);
#ifdef WORDS_BIGENDIAN
memset(inode, 0, pos.size);
ext2fs_swap_inode_full(current_fs, inode, inode_buf, 0, pos.size);
if ((pos.size > EXT2_GOOD_OLD_INODE_SIZE) && (inode->i_extra_isize >= 24)
&& (ext2fs_le32_to_cpu(inode_buf->i_crtime ) != inode->i_crtime)){
//FIXME: On my current version of libext2 the extra time fields ar not bigendian corrected
// We solved this temporarily here with this function
le_to_cpu_swap_extra_time(inode,(char*)inode_buf);
}
#else
memcpy(inode, inode_buf, pos.size);
#endif
}
free(buf);
}
}
return retval;
}
//function for dump_inode_list
void print_j_inode(struct ext2_inode_large *inode , ext2_ino_t inode_nr , __u32 transaction , int flag){
fprintf(stdout,"\nDump Inode %d from journal transaction %d\n",inode_nr,transaction);
dump_inode(stdout, "",inode_nr, (struct ext2_inode *)inode, flag);
return ;
}
//print the contents of all copy of inode in the journal
void dump_inode_list(struct ring_buf* buf, int flag){
r_item* item;
int i, count;
__u32 time_stamp;
if (!buf ) return;
item = r_first(buf);
count = r_get_count(buf);
for (i = 0; i < count ; i++){
print_j_inode(item->inode , buf->nr , item->transaction.start, flag);
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(buf->nr, (struct ext2_inode*)item->inode, &(item->transaction),time_stamp);
}
item = r_next(item);
}
return;
}
// return the last undeleted inode in journal
r_item* get_last_undel_inode(struct ring_buf* buf){
r_item* item;
int i, count;
// __u32 generation;
if (!buf ) return NULL;
item = r_last(buf);
count = r_get_count(buf);
for (i = 0; i< count; i++){
// if ( !i ) generation = item->inode->i_generation;
if (item->inode->i_dtime) {
// buf->del_flag = 1;
item = r_prev(item);
}
else {
// if (item->inode->i_generation != generation)
// buf->reuse_flag = 1;
#ifdef DEBUG
printf("UD-Inode %d\n",item->transaction.start);
#endif
if((! item->inode->i_links_count)&&(item->inode->i_ctime)
&&(item->inode->i_mode))
item->inode->i_links_count = 1 ;
return item;
}
}
return NULL;
}
// return the last undelete inode in journal after -> <-before
r_item* get_undel_inode(struct ring_buf* buf, __u32 after, __u32 before){
r_item* item;
int i, count;
// __u32 generation;
if (!buf) return NULL;
item = r_last(buf);
count = r_get_count(buf);
for (i = 0; i< count; i++){
// if ( !i ) generation = item->inode->i_generation;
if ((item->inode->i_dtime) && (item->inode->i_dtime < after) )
return NULL;
if ((item->inode->i_ctime >= before ) || (item->inode->i_dtime)) {
// if (item->inode->i_dtime)
// buf->del_flag = 1 ;
item = r_prev(item);
continue;
}
// if (item->inode->i_generation != generation)
// buf->reuse_flag = 1;
if((! item->inode->i_links_count)&&(item->inode->i_ctime)&&(item->inode->i_mode))
item->inode->i_links_count = 1 ;
#ifdef DEBUG
printf("UTD-Inode %d\n",item->transaction.start);
#endif
return item;
}
return NULL;
}
//fill all inode found in the Journal in the inode-ringbuffer
struct ring_buf* get_j_inode_list(struct ext2_super_block *es, ext2_ino_t inode_nr){
struct inode_pos_struct pos;
blk_t block;
char *inode_buf = NULL ;
struct ext2_inode *inode_pointer;
struct ring_buf *buf = NULL;
r_item *item = NULL;
// struct ext2_inode_large *inode = NULL;
int count, retval = 0;
unsigned int got;
off_t offset;
char *journal_tag_buf = NULL;
journal_descriptor_tag_t *block_list;
__u32 ctime = 1;
__u32 same_size = 1;
__u32 same_block_count = 0;
__u16 same_link_count = 0;
if ((inode_nr > es->s_inodes_count) || (inode_nr == 0))
{
printf(" unknown ERROR: bad inode number found %d \n", inode_nr );
return NULL;
}
block = get_inode_pos(es , &pos , inode_nr, 0);
inode_buf = malloc(pos.size);
if (!inode_buf) {
fprintf(stderr,"Error: can not allocate memory for buffer\n");
goto errout;
}
count = get_block_list_count(block) ;
#ifdef DEBUG
fprintf(stdout,"\n\nINODE_RING : %ld/%ld/%ld : ",inode_nr, pos.block, pos.offset);
#endif
if(! count) {
// no inode block found
// then we will load the oginal inode from the filesystem
// we will hope, the file has not change for a long time
buf = ring_new(pos.size,inode_nr);
if (buf)
item = r_item_add(buf);
if ( ! item)
{
fprintf(stderr,"Error: can not allocate memory for inode\n");
goto errout;
}
if ( ext2fs_read_inode_full(current_fs, inode_nr, (struct ext2_inode*) item->inode, pos.size))
goto errout;
item->transaction.start = item->transaction.end = 0;
#ifdef DEBUG
fprintf(stdout,"*;");
#endif
}
else {
// read and fill the journal inode
journal_tag_buf = (void *)malloc((sizeof(journal_descriptor_tag_t) * count));
if (!journal_tag_buf) {
fprintf(stderr,"Error: while allocate Memory for blocklist\n");
goto errout;
}
block_list = (journal_descriptor_tag_t *) journal_tag_buf;
count = get_block_list(block_list, block, count);
buf = ring_new(pos.size,inode_nr);
if ((! buf) || (! count)) goto errout;
for (;count > 0;count-- , block_list++ ){
offset = (block_list->j_blocknr * current_fs->blocksize) + pos.offset ;
retval = read_journal_block( offset , inode_buf , pos.size , &got);
if (retval) {
fprintf(stderr,"Error: while read Inode %d from journal transaction %d\n", inode_nr, block_list->transaction);
goto errout;
}
inode_pointer = (struct ext2_inode*) inode_buf ;
//FIXME: check more bad Inode
if (get_inode_mode_type(ext2fs_le16_to_cpu(inode_pointer->i_mode)) == ' '){
#ifdef DEBUG
fprintf(stdout,"!%ld;",block_list->transaction);
#endif
continue;
}
//Correct d_time anomalies if directory delete process not completed BUG: #18730
if ((LINUX_S_ISDIR(ext2fs_le16_to_cpu(inode_pointer->i_mode))) &&
ext2fs_le32_to_cpu(inode_pointer->i_dtime) && ext2fs_le32_to_cpu(inode_pointer->i_blocks) &&
ext2fs_le32_to_cpu(inode_pointer->i_size) && (!ext2fs_le16_to_cpu(inode_pointer->i_links_count)) &&
ext2fs_le32_to_cpu(inode_pointer->i_ctime) != ext2fs_le32_to_cpu(inode_pointer->i_dtime)){
if ( buf->count && LINUX_S_ISDIR(item->inode->i_mode) && item->inode->i_blocks){
continue;
}
else{
inode_pointer->i_dtime = 0;
}
}
//inode with the same ctime + the same size and links and <= block_count, skipped
if (ext2fs_le32_to_cpu(inode_pointer->i_ctime) == ctime){
if (item) {
if ((ext2fs_le32_to_cpu(inode_pointer->i_size) == same_size) &&
(ext2fs_le16_to_cpu(inode_pointer->i_links_count) == same_link_count) &&
(ext2fs_le32_to_cpu(inode_pointer->i_blocks) <= same_block_count)){
item->transaction.end = block_list->transaction;
#ifdef DEBUG
fprintf(stdout,"-;");
#endif
continue;
}
}
}
item = r_item_add(buf);
if ( ! item)
{
fprintf(stderr,"Error: can not allocate memory for inode\n");
goto errout;
}
#ifdef WORDS_BIGENDIAN
memset(item->inode, 0, pos.size);
ext2fs_swap_inode_full(current_fs,
(struct ext2_inode_large *) item->inode,
(struct ext2_inode_large *) inode_buf,
0, pos.size);
if ((pos.size > EXT2_GOOD_OLD_INODE_SIZE) && (item->inode->i_extra_isize >= 24)
&& (ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_crtime) != item->inode->i_crtime)){
//FIXME: On my current version of libext2 the extra time fields ar not bigendian corrected
// We solved this temporarily here with this function
le_to_cpu_swap_extra_time(item->inode,(char*)inode_buf);
}
#else
memcpy(item->inode, inode_buf, pos.size);
#endif
#ifdef DEBUG
fprintf(stdout,"%ld;",block_list->transaction);
#endif
item->transaction.start = block_list->transaction;
item->transaction.end = block_list->transaction;
ctime = item->inode->i_ctime;
same_size = item->inode->i_size;
same_block_count = item->inode->i_blocks;
same_link_count = item->inode->i_links_count;
}
// check for the real filesystem inode
// if not delete or newer than the last journal copy, we add also this inode
if ( ext2fs_read_inode_full(current_fs, inode_nr, (struct ext2_inode*)inode_buf, pos.size))
goto errout;
inode_pointer = (struct ext2_inode*) inode_buf ;
if((((! inode_pointer->i_dtime) && inode_pointer->i_ctime) || (inode_pointer->i_ctime > ctime)) &&
(inode_pointer->i_ctime < (__u32) now_time)){
item = r_item_add(buf);
if ( ! item){
fprintf(stderr,"Error: can not allocate memory for inode\n");
goto errout;
}
memcpy(item->inode, inode_buf, pos.size);
item->transaction.start = item->transaction.end = 0;
#ifdef DEBUG
fprintf(stdout,"*;");
#endif
}
} // else-tree ? count==0
if (buf->count == 0)
goto errout;
if ( inode_buf ) {
free(inode_buf);
inode_buf = NULL;
}
if ( journal_tag_buf ) {
free(journal_tag_buf);
journal_tag_buf = NULL;
}
r_begin(buf);
#ifdef DEBUG
fprintf(stdout," %ld\n",buf->count);
#endif
return buf;
errout:
if ( inode_buf ) {
free(inode_buf);
inode_buf = NULL;
}
if ( journal_tag_buf ) {
free(journal_tag_buf);
journal_tag_buf = NULL;
}
if ( buf ) {
ring_del(buf);
buf = NULL;
}
#ifdef DEBUG
fprintf(stdout,"\n");
#endif
return NULL;
}
// get the first Journal Inode by time_stamp
int read_time_match_inode( ext2_ino_t inode_nr, struct ext2_inode* inode_buf, __u32 time_stamp){
struct ring_buf* i_ring;
r_item* item;
int retval = 1;
i_ring = get_j_inode_list(current_fs->super, inode_nr);
if (i_ring) {
item = r_last(i_ring);
while ((item->inode->i_ctime > time_stamp) && ( item != r_first(i_ring)))
item = r_prev(item);
//FIXME
memcpy(inode_buf,item->inode,EXT2_GOOD_OLD_INODE_SIZE);
ring_del(i_ring);
retval= 0;
}
return retval;
}
// get the first Journal Inode by transaction
int read_journal_inode( ext2_ino_t inode_nr, struct ext2_inode* inode_buf, __u32 transaction){
struct ring_buf* i_ring;
r_item* item;
int retval = 1;
i_ring = get_j_inode_list(current_fs->super, inode_nr);
if (i_ring) {
item = r_first(i_ring);
while ((item->transaction.start < transaction) && ( item != r_last(i_ring)))
item = r_next(item);
memcpy(inode_buf,item->inode,EXT2_GOOD_OLD_INODE_SIZE);
ring_del(i_ring);
retval= 0;
}
return retval;
}
//create a new inode
struct ext2_inode_large* new_inode(){
struct ext2_inode_large *inode;
__u32 a_time;
time_t help_time;
struct ext3_extent_header *p_extent_header;
time( &help_time );
a_time = (__u32) help_time;
inode = malloc(EXT2_INODE_SIZE(current_fs->super));
if (! inode )
return NULL;
memset(inode, 0 , EXT2_INODE_SIZE(current_fs->super));
inode->i_mode = LINUX_S_IFREG | LINUX_S_IRUSR | LINUX_S_IWUSR | LINUX_S_IRGRP | LINUX_S_IROTH ;
inode->i_ctime = inode->i_mtime = inode->i_atime = a_time;
inode->i_links_count = 1 ;
if (current_fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) {
inode->i_flags = EXT4_EXTENTS_FL ;
p_extent_header = (struct ext3_extent_header*) inode->i_block;
p_extent_header->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC) ;
p_extent_header->eh_max = ext2fs_cpu_to_le16(4);
}
return inode;
}
//add extent to inode
int inode_add_extent(struct ext2_inode_large* inode , struct extent_area* ea, __u32* last, int flag ){
int ret = 0;
struct ext3_extent_idx *idx;
struct ext3_extent_header *header;
struct ext3_extent *extent;
struct ext3_extent *extent_new;
__u64 i_size;
if ((!ea ) || (!ea->blocknr))
return 0;
header = (struct ext3_extent_header*) inode->i_block;
if (flag){
// flag == 1 ; add extent index
if (ext2fs_le16_to_cpu(header->eh_entries) >= ext2fs_le16_to_cpu(header->eh_max)){
fprintf(stderr," Error: can not add a extent to inode\n");
return 0;
}
idx = (struct ext3_extent_idx*) (header + (ext2fs_le16_to_cpu(header->eh_entries) + 1));
if (! header->eh_entries)
header->eh_depth = ext2fs_cpu_to_le16(ea->depth) +1;
idx->ei_leaf = ext2fs_cpu_to_le32(ea->blocknr);
idx->ei_leaf_hi = 0;
idx->ei_unused = 0;
idx->ei_block = ext2fs_cpu_to_le32(ea-> l_start);
header->eh_entries = ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(header->eh_entries) + 1 );
ret = 1;
}
else{
// flag == 0 : add or attach a extent entry
if (! ext2fs_le16_to_cpu(header->eh_entries))
header->eh_entries = ext2fs_cpu_to_le16(1);
extent = (struct ext3_extent*) (header + (ext2fs_le16_to_cpu(header->eh_entries)+1));
// new
if(!(ext2fs_le32_to_cpu(extent->ee_start))){
extent->ee_start = ext2fs_cpu_to_le32(ea->start_b);
extent->ee_len = ext2fs_cpu_to_le16(ea->len);
ret = 1 ;
}
else{
// attach
if (ea->start_b == (ext2fs_le32_to_cpu(extent->ee_start) + ext2fs_le16_to_cpu(extent->ee_len))){
extent->ee_len = ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(extent->ee_len)+ ea->len);
ret = 1;
}
}
if ((! ret) && (ext2fs_le16_to_cpu(header->eh_entries) < ext2fs_le16_to_cpu(header->eh_max))){
// new entry
header->eh_entries = ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(header->eh_entries) + 1 );
extent_new = (struct ext3_extent*) (header + (ext2fs_le16_to_cpu(header->eh_entries)+1));
// extent->ee_start_hi
extent_new->ee_start = ext2fs_cpu_to_le32(ea->start_b);
extent_new->ee_len = ext2fs_cpu_to_le16(ea->len);
extent_new->ee_block = ext2fs_cpu_to_le32(ext2fs_le32_to_cpu(extent->ee_block)+ext2fs_le16_to_cpu(extent->ee_len));
ret = 1 ;
}
}
if (ret){
i_size = (unsigned long long)(inode->i_size | ((unsigned long long)inode->i_size_high << 32));
i_size += ea->size;
inode->i_size = i_size & 0xffffffff ;
inode->i_size_high = i_size >> 32 ;
inode->i_blocks += (ea->b_count * (current_fs->blocksize / 512)) ;
*last = (ea->end_b) ? ea->end_b : 0 ;
// blockhex (stdout, (void*) inode, 0, 128);
}
else
fprintf(stderr," Error: can not add a extent to inode\n");
return ret;
}
//add a block to inode, (ext3 only the first 12 blocks)
int inode_add_block(struct ext2_inode_large* inode , blk_t blk ){
int i = 0 ;
unsigned long long i_size;
struct ext3_extent *extent;
struct ext3_extent_header *header;
if (! (inode->i_flags & EXT4_EXTENTS_FL)){
//ext3
while ((i < EXT2_N_BLOCKS) && inode->i_block[i] )
i++;
if ( i >= EXT2_NDIR_BLOCKS){
// printf("faulty Block %u as i_block %d \n", i,blk);
//indirect blocks
return 0;
}
inode->i_block[i] = blk;
}
else{
//ext4
header = (struct ext3_extent_header*) inode->i_block;
if (! ext2fs_le16_to_cpu(header->eh_entries)) {
header->eh_entries = ext2fs_cpu_to_le16(1);
extent = (struct ext3_extent*) (header + (ext2fs_le16_to_cpu(header->eh_entries)));
extent->ee_start = ext2fs_cpu_to_le32(blk);
}
else
extent = (struct ext3_extent*) (header + (ext2fs_le16_to_cpu(header->eh_entries)));
extent->ee_len = ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(extent->ee_len) +1);
}
i_size = (unsigned long long)(inode->i_size | ((unsigned long long)inode->i_size_high << 32));
i_size += current_fs->blocksize;
inode->i_size = i_size & 0xffffffff ;
inode->i_size_high = i_size >> 32 ;
inode->i_blocks += (current_fs->blocksize / 512);
return 1;
}
//add the ext3 indirect Blocks to the inode
int inode_add_meta_block(struct ext2_inode_large* inode , blk_t blk, blk_t *last, blk_t *next, char *buf ){
blk_t block_count = 0;
int i = 0;
__u64 i_size = 0;
int ret = 0;
if (! (inode->i_flags & EXT4_EXTENTS_FL)){
while ((i < EXT2_N_BLOCKS) && inode->i_block[i] )
i++;
switch (i){
case EXT2_IND_BLOCK :
ret = get_ind_block_len(buf, &block_count, last, next, &i_size);
break;
case EXT2_DIND_BLOCK :
ret = get_dind_block_len(buf, &block_count, last, next, &i_size);
break;
case EXT2_TIND_BLOCK :
ret = get_tind_block_len(buf, &block_count, last, next, &i_size);
break;
default:
// printf("faulty Block %u as indirekter_block %d \n", i,blk);
return 0;
break;
}
if (ret && i_size){
i_size += (((__u64)inode->i_size_high << 32)| inode->i_size);
inode->i_size = i_size & 0xffffffff ;
inode->i_size_high = i_size >> 32 ;
inode->i_blocks += (block_count * (current_fs->blocksize / 512));
inode->i_block[ i ] = blk;
}
else
*next = 0;
return ret;
}
else{
// printf("ERROR: ext3 indirect block %u ; but is a ext4_inode\n", blk);
//FIXME ext4
}
return 0;
}