/* File: dir.c Copyright (C) 1998-2007 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" #ifdef HAVE_UTIME_H #include #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) && (inext,i++); for(i=offset;(current_file!=NULL) &&((i-offset)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_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(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)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 }