/*
File: dir.c
Copyright (C) 1998-2007 Christophe GRENIER <grenier@cgsecurity.org>
This software 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 the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "types.h"
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#include "common.h"
#include "fat.h"
#include "lang.h"
#include "fnctdsk.h"
#include "testdisk.h"
#include "intrf.h"
#include "intrfn.h"
#include "dir.h"
#include "ext2_dir.h"
#include "fat_dir.h"
#include "ntfs_dir.h"
#include "rfs_dir.h"
#include "log.h"
const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static int dir_partition_aux(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const unsigned long int inode, const int first_time, char **current_cmd);
static long int dir_aff(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const file_data_t*dir_list, const unsigned long int inode, const int first_time, char**current_cmd);
static int copy_dir(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const file_data_t *dir);
static char ftypelet (unsigned int bits);
static char ftypelet (unsigned int bits)
{
#ifdef LINUX_S_ISBLK
if (LINUX_S_ISBLK (bits))
return 'b';
#endif
if (LINUX_S_ISCHR (bits))
return 'c';
if (LINUX_S_ISDIR (bits))
return 'd';
if (LINUX_S_ISREG (bits))
return '-';
#ifdef LINUX_S_ISFIFO
if (LINUX_S_ISFIFO (bits))
return 'p';
#endif
#ifdef LINUX_S_ISLNK
if (LINUX_S_ISLNK (bits))
return 'l';
#endif
#ifdef LINUX_S_ISSOCK
if (LINUX_S_ISSOCK (bits))
return 's';
#endif
#ifdef LINUX_S_ISMPC
if (LINUX_S_ISMPC (bits))
return 'm';
#endif
#ifdef LINUX_S_ISNWK
if (LINUX_S_ISNWK (bits))
return 'n';
#endif
#ifdef LINUX_S_ISDOOR
if (LINUX_S_ISDOOR (bits))
return 'D';
#endif
#ifdef LINUX_S_ISCTG
if (LINUX_S_ISCTG (bits))
return 'C';
#endif
#ifdef LINUX_S_ISOFD
if (LINUX_S_ISOFD (bits))
/* off line, with data */
return 'M';
#endif
#ifdef LINUX_S_ISOFL
/* off line, with no data */
if (LINUX_S_ISOFL (bits))
return 'M';
#endif
return '?';
}
void mode_string (const unsigned int mode, char *str)
{
str[0] = ftypelet(mode);
str[1] = mode & LINUX_S_IRUSR ? 'r' : '-';
str[2] = mode & LINUX_S_IWUSR ? 'w' : '-';
str[3] = mode & LINUX_S_IXUSR ? 'x' : '-';
str[4] = mode & LINUX_S_IRGRP ? 'r' : '-';
str[5] = mode & LINUX_S_IWGRP ? 'w' : '-';
str[6] = mode & LINUX_S_IXGRP ? 'x' : '-';
str[7] = mode & LINUX_S_IROTH ? 'r' : '-';
str[8] = mode & LINUX_S_IWOTH ? 'w' : '-';
str[9] = mode & LINUX_S_IXOTH ? 'x' : '-';
str[10]='\0';
#ifdef LINUX_S_ISUID
if (mode & LINUX_S_ISUID)
{
if (str[3] != 'x')
/* Set-uid, but not executable by owner. */
str[3] = 'S';
else
str[3] = 's';
}
#endif
#ifdef LINUX_S_ISGID
if (mode & LINUX_S_ISGID)
{
if (str[6] != 'x')
/* Set-gid, but not executable by group. */
str[6] = 'S';
else
str[6] = 's';
}
#endif
#ifdef LINUX_S_ISVTX
if (mode & LINUX_S_ISVTX)
{
if (str[9] != 'x')
/* Sticky, but not executable by others. */
str[9] = 'T';
else
str[9] = 't';
}
#endif
}
int dir_aff_log(const disk_t *disk_car, const partition_t *partition, const dir_data_t *dir_data, const file_data_t*dir_list)
{
int test_date=0;
const file_data_t *current_file;
log_partition(disk_car,partition);
if(dir_data!=NULL)
{
log_info("Directory %s\n",dir_data->current_directory);
}
for(current_file=dir_list;current_file!=NULL;current_file=current_file->next)
{
struct tm *tm_p;
char datestr[80];
char str[11];
if(current_file->filestat.st_mtime)
{
tm_p = localtime(¤t_file->filestat.st_mtime);
snprintf(datestr, sizeof(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);
/* FIXME: a check using current_file->name will be better */
if(1900+tm_p->tm_year>=2000 && 1900+tm_p->tm_year<=2010)
{
test_date=1;
}
} else {
strncpy(datestr, " ",sizeof(datestr));
}
mode_string(current_file->filestat.st_mode,str);
log_info("%7lu ",(unsigned long int)current_file->filestat.st_ino);
log_info("%s %5u %5u ",
str, (unsigned int)current_file->filestat.st_uid, (unsigned int)current_file->filestat.st_gid);
log_info("%7llu", (long long unsigned int)current_file->filestat.st_size);
log_info(" %s %s\n", datestr, current_file->name);
}
return test_date;
}
static long int dir_aff(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const file_data_t*dir_list, const unsigned long int inode, const int first_time, char **current_cmd)
{
/* Return value
* -1: quit
* 1: back
* other: new inode
* */
int quit=0;
int offset=0;
int pos_num=0;
const file_data_t *current_file;
const file_data_t *pos=dir_list;
WINDOW *window=(WINDOW*)dir_data->display;
aff_copy(window);
wmove(window,4,0);
aff_part(window,AFF_PART_ORDER,disk_car,partition);
mvwaddstr(window,5,0,"Use ");
if(first_time==0)
{
if(has_colors())
wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0));
waddstr(window, "Left");
if(has_colors())
wbkgdset(window,' ' | COLOR_PAIR(0));
waddstr(window," arrow to go back, ");
}
if(has_colors())
wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0));
waddstr(window,"Right");
if(has_colors())
wbkgdset(window,' ' | COLOR_PAIR(0));
waddstr(window," arrow to change directory, ");
if(dir_data->copy_file!=NULL)
{
if(has_colors())
wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0));
waddstr(window,"c");
if(has_colors())
wbkgdset(window,' ' | COLOR_PAIR(0));
waddstr(window," to copy, ");
}
if(has_colors())
wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0));
waddstr(window,"q");
if(has_colors())
wbkgdset(window,' ' | COLOR_PAIR(0));
waddstr(window," to quit");
wmove(window,6,0);
wdoprintf(window,"Directory %s\n",dir_data->current_directory);
do
{
int i;
int car;
for(i=0,current_file=dir_list;(current_file!=NULL) && (i<offset);current_file=current_file->next,i++);
for(i=offset;(current_file!=NULL) &&((i-offset)<INTER_DIR);i++,current_file=current_file->next)
{
struct tm *tm_p;
char str[11];
char datestr[80];
wmove(window, 8+i-offset, 0);
wclrtoeol(window); /* before addstr for BSD compatibility */
if(current_file==pos)
wattrset(window, A_REVERSE);
if(current_file->filestat.st_mtime!=0)
{
tm_p = localtime(¤t_file->filestat.st_mtime);
snprintf(datestr, sizeof(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);
/* May have to use %d instead of %e */
} else {
strncpy(datestr, " ",sizeof(datestr));
}
mode_string(current_file->filestat.st_mode,str);
wdoprintf(window, "%s %5u %5u ",
str, (unsigned int)current_file->filestat.st_uid, (unsigned int)current_file->filestat.st_gid);
wdoprintf(window, "%7llu", (long long unsigned int)current_file->filestat.st_size);
/* screen may overlap due to long filename */
wdoprintf(window, " %s %s", datestr, current_file->name);
if(current_file==pos)
wattroff(window, A_REVERSE);
}
wmove(window, 8-1, 51);
wclrtoeol(window);
if(offset>0)
wdoprintf(window, "Previous");
/* Clear the last line, useful if overlapping */
wmove(window,8+i-offset,0);
wclrtoeol(window);
wmove(window, 8+INTER_DIR, 51);
wclrtoeol(window);
if(current_file!=NULL)
wdoprintf(window, "Next");
if(dir_list==NULL)
{
wmove(window,8,0);
wdoprintf(window,"No file found, filesystem seems damaged.");
}
wrefresh(window);
/* Using gnome terminal under FC3, TERM=xterm, the screen is not always correct */
wredrawln(window,0,getmaxy(window)); /* redrawwin def is boggus in pdcur24 */
if(*current_cmd!=NULL)
car='q';
else
car=wgetch(window);
wmove(window,7,0);
wclrtoeol(window);
switch(car)
{
case key_ESC:
case 'q':
case 'M':
quit=1;
break;
case '-':
case KEY_LEFT:
if(first_time==0)
return 1;
break;
}
if(dir_list!=NULL)
{
switch(car)
{
case KEY_UP:
if(pos->prev!=NULL)
{
pos=pos->prev;
pos_num--;
}
if(pos_num<offset)
offset--;
break;
case KEY_DOWN:
if(pos->next!=NULL)
{
pos=pos->next;
pos_num++;
}
if(pos_num>=offset+INTER_DIR)
offset++;
break;
case 'p':
case 'P':
case '+':
case ' ':
case KEY_RIGHT:
case '\r':
case '\n':
case KEY_ENTER:
#ifdef PADENTER
case PADENTER:
#endif
if((pos!=NULL) && (LINUX_S_ISDIR(pos->filestat.st_mode)!=0))
{
unsigned long int new_inode=pos->filestat.st_ino;
if((new_inode!=inode) &&(strcmp(pos->name,".")!=0))
{
if(strcmp(pos->name,"..")==0)
return 1;
if(strlen(dir_data->current_directory)+1+strlen(pos->name)+1<=sizeof(dir_data->current_directory))
{
if(strcmp(dir_data->current_directory,"/"))
strcat(dir_data->current_directory,"/");
strcat(dir_data->current_directory,pos->name);
return (long int)new_inode;
}
}
}
break;
case KEY_PPAGE:
for(i=0;(i<INTER_DIR-1)&&(pos->prev!=NULL);i++)
{
pos=pos->prev;
pos_num--;
if(pos_num<offset)
offset--;
}
break;
case KEY_NPAGE:
for(i=0;(i<INTER_DIR-1)&&(pos->next!=NULL);i++)
{
pos=pos->next;
pos_num++;
if(pos_num>=offset+INTER_DIR)
offset++;
}
break;
case 'c':
if(dir_data->copy_file!=NULL)
{
unsigned int current_directory_namelength=strlen(dir_data->current_directory);
if(strcmp(pos->name,"..")!=0 &&
current_directory_namelength+1+strlen(pos->name)<sizeof(dir_data->current_directory)-1)
{
if(strcmp(dir_data->current_directory,"/"))
strcat(dir_data->current_directory,"/");
if(strcmp(pos->name,".")!=0)
strcat(dir_data->current_directory,pos->name);
if(dir_data->local_dir==NULL)
{
char *res;
if(LINUX_S_ISDIR(pos->filestat.st_mode)!=0)
res=ask_location("Are you sure you want to copy %s and any files below to the directory %s ? [Y/N]",dir_data->current_directory);
else
res=ask_location("Are you sure you want to copy %s to the directory %s ? [Y/N]",dir_data->current_directory);
// free(dir_data->local_dir);
dir_data->local_dir=res;
}
if(dir_data->local_dir!=NULL)
{
int res=-1;
wmove(window,7,0);
wclrtoeol(window);
if(has_colors())
wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(1));
wdoprintf(window,"Copying, please wait...");
if(has_colors())
wbkgdset(window,' ' | COLOR_PAIR(0));
wrefresh(window);
if(LINUX_S_ISDIR(pos->filestat.st_mode)!=0)
{
res=copy_dir(disk_car, partition, dir_data, pos);
}
else if(LINUX_S_ISREG(pos->filestat.st_mode)!=0)
{
res=dir_data->copy_file(disk_car, partition, dir_data, pos);
}
wmove(window,7,0);
wclrtoeol(window);
if(res < -1)
{
if(has_colors())
wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(1));
wdoprintf(window,"Copy failed!");
}
else
{
if(has_colors())
wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(2));
if(res < 0)
wdoprintf(window,"Copy done! (Failed to copy some files)");
else
wdoprintf(window,"Copy done!");
}
if(has_colors())
wbkgdset(window,' ' | COLOR_PAIR(0));
}
dir_data->current_directory[current_directory_namelength]='\0';
}
}
break;
}
}
} while(quit==0);
return -1;
}
void delete_list_file(file_data_t *file_list)
{
file_data_t *current_file=file_list;
while(current_file!=NULL)
{
file_data_t *next=current_file->next;
free(current_file);
current_file=next;
}
}
int dir_partition_aff(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const unsigned long int inode, char **current_cmd)
{
if(dir_data==NULL)
return -1;
return dir_partition_aux(disk_car,partition,dir_data,inode,1,current_cmd);
}
static int dir_partition_aux(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const unsigned long int inode, const int first_time, char**current_cmd)
{
file_data_t *dir_list;
long int new_inode;
if(dir_data->debug>0)
log_trace("\ndir_partition inode=%ld\n",inode);
dir_list=dir_data->get_dir(disk_car,partition,dir_data,inode);
dir_aff_log(disk_car, partition, dir_data, dir_list);
do
{
unsigned int current_directory_namelength=strlen(dir_data->current_directory);
new_inode=dir_aff(disk_car,partition,dir_data,dir_list,inode,first_time,current_cmd);
if(new_inode==0 || new_inode>1)
{
if(dir_partition_aux(disk_car, partition, dir_data, (unsigned long int)new_inode,0,current_cmd)<0)
{ /* quit */
delete_list_file(dir_list);
return -1;
}
/* back */
dir_data->current_directory[current_directory_namelength]='\0';
}
} while(new_inode==0 || new_inode>1);
delete_list_file(dir_list);
return new_inode;
}
/*
Returns
-2: no file copied
-1: failed to copy some files
0: all files has been copied
*/
static int copy_dir(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const file_data_t *dir)
{
file_data_t *dir_list;
unsigned int current_directory_namelength=strlen(dir_data->current_directory);
file_data_t *current_file;
char *dir_name;
int copy_bad=0;
int copy_ok=0;
if(dir_data->get_dir==NULL || dir_data->copy_file==NULL)
return -2;
dir_name=MALLOC(strlen(dir_data->local_dir)+strlen(dir_data->current_directory)+1);
strcpy(dir_name,dir_data->local_dir);
strcat(dir_name,dir_data->current_directory);
create_dir(dir_name,1);
dir_list=dir_data->get_dir(disk_car, partition,dir_data, (const unsigned long int)dir->filestat.st_ino);
// dir_aff_log(disk_car, partition, dir_data, dir_list);
for(current_file=dir_list;current_file!=NULL;current_file=current_file->next)
{
if(strlen(dir_data->current_directory)+1+strlen(current_file->name)<sizeof(dir_data->current_directory)-1)
{
if(strcmp(dir_data->current_directory,"/"))
strcat(dir_data->current_directory,"/");
strcat(dir_data->current_directory,current_file->name);
if(LINUX_S_ISDIR(current_file->filestat.st_mode)!=0)
{
int tmp=0;
if(current_file->filestat.st_ino != dir->filestat.st_ino &&
strcmp(current_file->name,"..")!=0 && strcmp(current_file->name,".")!=0)
tmp=copy_dir(disk_car, partition, dir_data, current_file);
if(tmp>=-1)
copy_ok=1;
if(tmp<0)
copy_bad=1;
}
else if(LINUX_S_ISREG(current_file->filestat.st_mode)!=0)
{
// log_trace("copy_file %s\n",dir_data->current_directory);
int tmp;
tmp=dir_data->copy_file(disk_car, partition, dir_data, current_file);
if(tmp==0)
copy_ok=1;
else
copy_bad=1;
}
dir_data->current_directory[current_directory_namelength]='\0';
}
}
delete_list_file(dir_list);
set_date(dir_name, dir->filestat.st_atime, dir->filestat.st_mtime);
free(dir_name);
return (copy_bad>0?(copy_ok>0?-1:-2):0);
}
FILE *create_file(const char *filename)
{
FILE *f_out;
f_out=fopen(filename,"wb");
if(!f_out)
{
create_dir(filename,0);
f_out=fopen(filename,"wb");
}
return f_out;
}
/**
* set_date - Set the file's date and time
* @pathname: Path and name of the file to alter
* @actime: Date and time to set
* @modtime: Date and time to set
*
* Give a file a particular date and time.
*
* Return: 1 Success, set the file's date and time
* 0 Error, failed to change the file's date and time
*/
int set_date(const char *pathname, time_t actime, time_t modtime)
{
#ifdef HAVE_UTIME
struct utimbuf ut;
if (!pathname)
return 0;
ut.actime = actime;
ut.modtime = modtime;
if (utime(pathname, &ut)) {
log_error("ERROR: Couldn't set the file's date and time for %s\n", pathname);
return 0;
}
return 1;
#else
return 0;
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1