/***************************************************************************
* 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 . *
***************************************************************************/
//header util.h
#include
#include
#if HAVE_UNISTD_H
#include
#endif
#include
//#include
#include "ext2fsP.h"
#include
#include
#include "dir_list.h"
#include "util.h"
#include "inode.h"
#include "block.h"
#define DIRENT_MIN_LENGTH 12
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, "%12lld/", 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);
retval = read_time_match_inode( ino, &inode, ls->time_stamp);
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, "%12d", inode.i_size);
else
fprintf(stdout, "%12llu", 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 directory data error\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;
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);
}
#ifdef WORDS_BIGENDIAN
//---------------------------------------------------------------
/*
* This function checks to see whether or not a potential deleted
* directory entry looks valid. Returns 1 if the deleted entry looks valid,
* zero if not valid.
*
* only from convert_dir_block() on bigendian
*/
static int local_validate_entry2(char *p , char *end, unsigned int offset, unsigned int final_offset)
{
struct ext2_dir_entry_2 *dirent;
__u16 rec_len = 0;
while ((offset < final_offset) &&
(offset <= (unsigned int) (end - p - DIRENT_MIN_LENGTH))) {
dirent = (struct ext2_dir_entry_2 *)(p + offset);
rec_len = ext2fs_swab16(dirent->rec_len);
if ((rec_len == 0 ) || (rec_len == 65535 ))
rec_len = current_fs->blocksize ;
offset += rec_len;
if ((rec_len < 8) || ((rec_len % 4) != 0) || (((unsigned) dirent->name_len +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;
char real_len;
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;
dirent->inode = ext2fs_swab32(dirent->inode);
dirent->rec_len = ext2fs_swab16(dirent->rec_len);
dirent->name_len = ext2fs_swab16(dirent->name_len);
name_len = dirent->name_len;
if (flags & EXT2_DIRBLOCK_V2_STRUCT)
dirent->name_len = ext2fs_swab16(dirent->name_len);
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;
real_len = ((name_len & 0xFF) + 11) & ~3;
if ((rec_len > real_len) && ((real_len + DIRENT_MIN_LENGTH) <= rec_len)){
// p += ((name_len & 0xFF) + 11) & ~3;
//FIXME
unsigned int offset = real_len;
while (offset < rec_len && !local_validate_entry2(p , end , offset, rec_len))
offset += 4;
p += offset;
}
else
p += rec_len;
}
return retval;
}
#endif
/*
* 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,
blk64_t *blocknr,
e2_blkcnt_t blockcnt,
blk64_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, i;
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;
if (bmap)
ext2fs_mark_generic_bitmap(bmap, *blocknr);
i=0;
while ((!(ctx->buf[i++])) && (i<8)) ;
if (i==8)
return 0; //Interior nodes of an htree which the full length of a data block
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 (ctx->flags & SKIP_HTREE){
if ((! *(__u32*)(ctx->buf + (offset+size))) &&
(*(ctx->buf + (offset+size+5)) == 0x08) && ( !(*(ctx->buf + (offset+size+7))))){
ctx->flags &= ~SKIP_HTREE;
break;
}
}
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
// function current not used
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;
// not testet flags = (inode->i_flags & EXT2_INDEX_FL) ? SKIP_HTREE : 0 ;
retval = local_dir_iterate3(current_fs,ino, inode, flags,0, list_dir_proc, &ls);
fprintf(stdout, "\n");
if (retval)
fprintf(stderr,"Error: %d inode or directory data error\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) memset(d_inode, 0 , current_fs->super->s_inode_size);
i_list = (struct ring_buf*) get_j_inode_list(current_fs->super, ino);
if (! i_list) return NULL;
//FIXME
if (imap){
ext2fs_mark_generic_bitmap(imap,ino);
// printf("mark inode %10u\n",ino);
}
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;
flags = (inode->i_flags & EXT2_INDEX_FL) ? SKIP_HTREE : 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-NR %d can not found file: %s/%s\n", retval, path, name);
}
}
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;
int recursion = 0;
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 {
#ifdef DEBUG
printf(">>%s>>\n",dir->pathname);
#endif
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",(long unsigned int)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){
//FIXME search for lost dir
recursion = 1;
if (flag & LOST_DIR_SEARCH)
recursion = check_find_dir(des_dir,lp->inode_nr,dir->pathname,lp->filename);
//recursion for directory
if(recursion) {
lookup_local(des_dir, d_list, t_after, t_before, flag);
#ifdef DEBUG
printf("<<%s<<\n",dir->pathname);
#endif
}
}
else{
//function for all files apart from dir
switch (flag & REC_FILTER){
case LIST_ALL :
printf("--- %lu %c%s%s%s%c\n",(long unsigned int)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 ,__u32 time_stamp)
{
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;
ls.time_stamp = time_stamp;
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;
flags |= (inode->i_flags & EXT2_INDEX_FL) ? SKIP_HTREE : 0 ;
retval = local_dir_iterate3(current_fs,ino, inode, flags,0, list_dir_proc, &ls);
fprintf(stdout, "\n");
if (retval)
fprintf(stderr,"Error %d inode or journal directory data error\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;
}