www.pudn.com > testdisk-6.6.rar > DIR.C


/*

    File: dir.c

    Copyright (C) 1998-2006 Christophe GRENIER 
  
    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 
#endif
 
#ifdef HAVE_STRING_H
#include 
#endif
#ifdef HAVE_STDLIB_H
#include 
#endif
#ifdef HAVE_TIME_H
#include 
#endif
#ifdef HAVE_SYS_STAT_H
#include 
#endif
#include "types.h"
#include "common.h"
#include "fat.h"
#include "lang.h"
#include "fnctdsk.h"
#include "testdisk.h"
#include "intrf.h"
#include "dir.h"
#include "ext2_dir.h"
#include "fat_dir.h"
#include "ntfs_dir.h"
#include "rfs_dir.h"

static int dir_partition_aux(t_param_disk *disk_car, const t_partition *partition, t_dir_data *dir_data, const unsigned long int inode, const int first_time, char **current_cmd);
static long int dir_aff(t_param_disk *disk_car, const t_partition *partition, t_dir_data *dir_data, const t_file_data*dir_list, const unsigned long int inode, const int first_time, char**current_cmd);
static int copy_dir(t_param_disk *disk_car, const t_partition *partition, t_dir_data *dir_data, const unsigned long int inode);

static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
				"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
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 t_param_disk *disk_car, const t_partition *partition, const t_dir_data *dir_data, const t_file_data*dir_list)
{
  int test_date=0;
  const t_file_data *current_file;
  aff_part_rapport(disk_car,partition);
  if(dir_data!=NULL)
  {
    ecrit_rapport("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);
    ecrit_rapport("%7lu ",(unsigned long int)current_file->filestat.st_ino);
    ecrit_rapport("%s %5u  %5u   ", 
	str, (unsigned int)current_file->filestat.st_uid, (unsigned int)current_file->filestat.st_gid);
    ecrit_rapport("%7llu", (long long unsigned int)current_file->filestat.st_size);
    ecrit_rapport(" %s %s\n", datestr, current_file->name);
  }
  return test_date;
}

static long int dir_aff(t_param_disk *disk_car, const t_partition *partition, t_dir_data *dir_data, const t_file_data*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 t_file_data *current_file;
  const t_file_data *pos=dir_list;
  aff_copy(dir_data->window);
  wmove(dir_data->window,4,0);
  aff_part(dir_data->window,AFF_PART_ORDER,disk_car,partition);
  mvwaddstr(dir_data->window,5,0,"Use ");
  if(first_time==0)
  {
    if(has_colors())
      wbkgdset(dir_data->window,' ' | A_BOLD | COLOR_PAIR(0));
    waddstr(dir_data->window, "Left");
    if(has_colors())
      wbkgdset(dir_data->window,' ' | COLOR_PAIR(0));
    waddstr(dir_data->window," arrow to go back, ");
  }
  if(has_colors())
    wbkgdset(dir_data->window,' ' | A_BOLD | COLOR_PAIR(0));
  waddstr(dir_data->window,"Right");
  if(has_colors())
    wbkgdset(dir_data->window,' ' | COLOR_PAIR(0));
  waddstr(dir_data->window," arrow to change directory, ");
  if(dir_data->copy_file!=NULL)
  {
    if(has_colors())
      wbkgdset(dir_data->window,' ' | A_BOLD | COLOR_PAIR(0));
    waddstr(dir_data->window,"c");
    if(has_colors())
      wbkgdset(dir_data->window,' ' | COLOR_PAIR(0));
    waddstr(dir_data->window," to copy, ");
  }
  if(has_colors())
    wbkgdset(dir_data->window,' ' | A_BOLD | COLOR_PAIR(0));
  waddstr(dir_data->window,"q");
  if(has_colors())
    wbkgdset(dir_data->window,' ' | COLOR_PAIR(0));
  waddstr(dir_data->window," to quit");
  wmove(dir_data->window,6,0);
  wdoprintf(dir_data->window,"Directory %s\n",dir_data->current_directory);
  do
  {
    int i;
    int car;
    for(i=0,current_file=dir_list;(current_file!=NULL) && (inext,i++);
    wmove(dir_data->window, 8-1, 4);
    wclrtoeol(dir_data->window);
    if(offset>0)
      wdoprintf(dir_data->window, "Previous");
    for(i=offset;(current_file!=NULL) &&((i-offset)next)
    {
      struct tm		*tm_p;
      char str[11];
      char		datestr[80];
      wmove(dir_data->window, 8+i-offset, 0);
      wclrtoeol(dir_data->window);	/* before addstr for BSD compatibility */
      if(current_file==pos)
	wattrset(dir_data->window,A_STANDOUT);
      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(dir_data->window, "%s %5u %5u   ", 
	  str, (unsigned int)current_file->filestat.st_uid, (unsigned int)current_file->filestat.st_gid);
      wdoprintf(dir_data->window, "%7llu", (long long unsigned int)current_file->filestat.st_size);
      /* screen may overlap due to long filename */
      wdoprintf(dir_data->window, " %s %s", datestr, current_file->name);
      if(current_file==pos)
	wattroff(dir_data->window,A_STANDOUT);
    }
    /* Clear the last line, useful if overlapping */
    wmove(dir_data->window,8+i-offset,0);
    wclrtoeol(dir_data->window);
    wmove(dir_data->window, 8+INTER_DIR, 4);
    wclrtoeol(dir_data->window);
    if(current_file!=NULL)
      wdoprintf(dir_data->window, "Next");
    if(dir_list==NULL)
    {
      wmove(dir_data->window,8,0);
      wdoprintf(dir_data->window,"No file found, filesystem seems damaged.");
    }
    wrefresh(dir_data->window);
    /* Using gnome terminal under FC3, TERM=xterm, the screen is not always correct */
    wredrawln(dir_data->window,0,getmaxy(dir_data->window));	/* redrawwin def is boggus in pdcur24 */
    if(*current_cmd!=NULL)
      car='q';
    else
      car=wgetch(dir_data->window);
    wmove(dir_data->window,7,0);
    wclrtoeol(dir_data->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_numnext!=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;(iprev!=NULL);i++)
	  {
	    pos=pos->prev;
	    pos_num--;
	    if(pos_numnext!=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)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(dir_data->window,7,0);
		wclrtoeol(dir_data->window);
		if(has_colors())
		  wbkgdset(dir_data->window,' ' | A_BOLD | COLOR_PAIR(1));
		wdoprintf(dir_data->window,"Copying, please wait...");
		if(has_colors())
		  wbkgdset(dir_data->window,' ' | COLOR_PAIR(0));
		wrefresh(dir_data->window);
		if(LINUX_S_ISDIR(pos->filestat.st_mode)!=0)
		{
		  res=copy_dir(disk_car, partition, dir_data, (unsigned long int)pos->filestat.st_ino);
		}
		else if(LINUX_S_ISREG(pos->filestat.st_mode)!=0)
		{
		  res=dir_data->copy_file(disk_car, partition, dir_data, (unsigned long int)pos->filestat.st_ino);
		}
		wmove(dir_data->window,7,0);
		wclrtoeol(dir_data->window);
		if(res<0)
		{
		  if(has_colors())
		    wbkgdset(dir_data->window,' ' | A_BOLD | COLOR_PAIR(1));
		  wdoprintf(dir_data->window,"Copy failed!");
		}
		else
		{
		  if(has_colors())
		    wbkgdset(dir_data->window,' ' | A_BOLD | COLOR_PAIR(2));
		  if(res>0)
		    wdoprintf(dir_data->window,"Copy done! (Failed to copy some files)");
		  else
		    wdoprintf(dir_data->window,"Copy done!");
		}
		if(has_colors())
		  wbkgdset(dir_data->window,' ' | COLOR_PAIR(0));
	      }
	      dir_data->current_directory[current_directory_namelength]='\0';
	    }
	  }
	  break;
      }
    }
  } while(quit==0);
  return -1;
}

void dir_aff_entry(WINDOW *window, struct file_info *dir_info)
{
  struct tm		*tm_p;
  char str[11];
  char		datestr[80];
  if(dir_info->stat.st_mtime!=0)
  {
    tm_p = localtime(&dir_info->stat.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(dir_info->stat.st_mode,str);
  wdoprintf(window, "%s %5u %5u   ", 
      str, (unsigned int)dir_info->stat.st_uid, (unsigned int)dir_info->stat.st_gid);
  wdoprintf(window, "%7llu", (long long unsigned int)dir_info->stat.st_size);
  /* screen may overlap due to long filename */
  wdoprintf(window, " %s %s", datestr, dir_info->name);
}

void delete_list_file(t_file_data *file_list)
{
  t_file_data *current_file=file_list;
  while(current_file!=NULL)
  {
    t_file_data *next=current_file->next;
    free(current_file);
    current_file=next;
  }
}

int dir_partition_aff(t_param_disk *disk_car, const t_partition *partition, t_dir_data *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(t_param_disk *disk_car, const t_partition *partition, t_dir_data *dir_data, const unsigned long int inode, const int first_time, char**current_cmd)
{
  t_file_data *dir_list;
  long int new_inode;
  if(dir_data->debug>0)
    ecrit_rapport("\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;
}

static int copy_dir(t_param_disk *disk_car, const t_partition *partition, t_dir_data *dir_data, const unsigned long int inode)
{
  unsigned int file_failed=0;
  unsigned int file_copied=0;
  t_file_data *dir_list;
  unsigned int current_directory_namelength=strlen(dir_data->current_directory);
  t_file_data *current_file;
  if(dir_data->get_dir==NULL || dir_data->copy_file==NULL)
    return -1;
  ecrit_rapport("copy_dir %s\n",dir_data->current_directory);
  {
    char *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);
    free(dir_name);
  }
  dir_list=dir_data->get_dir(disk_car,partition,dir_data,inode);
//  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)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)
      {
	if((unsigned long int)current_file->filestat.st_ino!=inode &&
	    strcmp(current_file->name,"..")!=0 && strcmp(current_file->name,".")!=0)
	  copy_dir(disk_car, partition, dir_data, (unsigned long int)current_file->filestat.st_ino);
      }
      else if(LINUX_S_ISREG(current_file->filestat.st_mode)!=0)
      {
//	ecrit_rapport("copy_file %s\n",dir_data->current_directory);
	if(dir_data->copy_file(disk_car, partition, dir_data, (unsigned long int)current_file->filestat.st_ino))
	  file_failed++;
	else
	  file_copied++;
      }
      dir_data->current_directory[current_directory_namelength]='\0';
    }
  }
  delete_list_file(dir_list);
  return (file_copied>0?(file_failed>0?1:0):-1);
}

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;
}