/* $Id: fatback.c,v 1.24 2001/05/30 15:44:10 harbourn Exp $
* At this point, this is just a test driver for
* all of the various modules. Hopefully someday, this will
* be a real program (Grin).
*/
#ifdef VERSION
static char *fatback_version = VERSION;
#else
static char *fatback_version = "(Unknown)";
#endif /*VERSION*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "getopt.h"
#include <assert.h>
#include <string.h>
#include "input.h"
#include "output.h"
#include "mbr.h"
#include "util.h"
#include "vbr.h"
#include "fat.h"
#include "dirtree.h"
#include "interface.h"
#include "lfn.h"
#include "vars.h"
static void print_dirtree(dirent_t *);
static void print_help(char *);
static void print_ver(void);
static void print_shortver(void);
static char *gen_opts(struct option *);
static char *str_cons(char *, char);
static char *output; /* used in auto undelete mode */
enum { /* flags for undel_partition() */
PART_ONLY = 0x01,
INTERACTIVE = 0x02
};
int main(int argc, char *argv[])
{
int num_partitions;
int flags = INTERACTIVE;
int opt, opt_index;
int partition = -1;
char *logname = NULL;
int input_type = DFILE;
char *opt_string;
static struct option long_opts[] =
{
{"output", 1, 0, 'o'},
{"log", 1, 0, 'l'},
{"auto", 0, 0, 'a'},
{"verbose", 0, 0, 'v'},
{"partition", 1, 0, 'p'},
{"delprefix", 1, 0, 'd'},
{"single", 0, 0, 's'},
{"sectsize", 1, 0, 'z'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'V'},
#ifdef HAVE_MMAP
{"mmap", 0, 0, 'm'},
#endif /*HAVE_MMAP*/
{0, 0, 0, 0}
};
if (argc == 1) {
print_help(argv[0]);
return 0;
}
/* set some default values */
set_fbvar("loglevel", 5);
set_fbvar("verbose", "off");
set_fbvar("sectsize", 512);
set_fbvar("prompt", "fatback>");
set_fbvar("deleted_prefix", "?");
/* generate an opt string based upon the long_opts[] array */
opt_string = gen_opts(long_opts);
/* parse the command line options */
while ((opt = getopt_long(argc, argv, opt_string, long_opts, &opt_index)) > 0) {
switch (opt) {
case 'o':
output = optarg;
break;
case 'l':
logname = optarg;
break;
case 'a':
flags &= !INTERACTIVE;
break;
case 'v':
set_fbvar("verbose", "on");
break;
case 'p':
partition = atoi(optarg);
break;
case 'd':
set_fbvar("deleted_prefix", optarg);
break;
case 's':
flags |= PART_ONLY;
break;
case 'z':
set_fbvar("sectsize", atol(optarg));
break;
case 'h':
print_help(argv[0]);
return 0;
break;
case 'V':
print_ver();
return 0;
break;
#ifdef HAVE_MMAP
case 'm':
input_type = MMAP;
break;
#endif /*HAVE_MMAP*/
default:
break;
}
}
/* Disable output buffering. this isnt essential,
* but for the '.' tic marks to show as the file system
* is being parsed, it is essential. */
setbuf(stdout, NULL);
if (!argv[optind]) {
printf("Error: No input file specified. Type '%s --help' for usage\n", argv[0]);
return -1;
}
if (!(flags & INTERACTIVE) && !output) {
printf("Auto undelete mode requires an output directory to be specified. (-o [dir])\n");
return -1;
}
/* initialize audit logging and input handeling*/
audit_init(logname, argv);
if (input_init(input_type, argv[optind]) < 0)
return -1;
/* if the user forced a single partition, or if
* we are unable to find valid partition tables,
* just jump into undeleteing a single partition
*/
num_partitions = map_partitions();
if (num_partitions == 0)
flags |= PART_ONLY;
if (partition < 0 && num_partitions < 2)
partition = num_partitions;
if (partition > num_partitions) {
display(NORMAL, "Partition number specified is out of range,"
" only %d partitions detected\n", num_partitions);
return -1;
}
if ((flags & INTERACTIVE) && partition < 0)
partition_menu(num_partitions, flags);
else
undel_partition(partition, flags);
/* if the user is in an interactive session, and
* did we were able to find partition tables,
* then give them a menu, or jump to the partition
* they specified on the command line.
*/
/* wrap things up */
input_close();
audit_close();
return 0;
}
/*
* Process a single partition. both interactively
* and automatically.
*/
int undel_partition(int pnum, int flags)
{
struct part_range_s *prange = NULL;
vbr_t myvbr;
unsigned long num_fat_entries;
clust_t *clusts;
off_t rdir_loc;
dirent_t *root_dir;
fbvar_t *verbose_var;
unsigned verbose;
/* we start out by building a vbr structure for
* this partition.
*/
if (flags & INTERACTIVE) {
printf("Parsing file system.\n");
ticmarker();
}
if (flags & PART_ONLY)
myvbr = build_vbr(0);
else {
if ((prange = get_prange(pnum - 1)) == 0)
return 0;
myvbr = build_vbr(prange->start);
}
if (!myvbr)
return 0;
/* audit log all information in the vbr */
log_vbr(myvbr);
/* quit this partition if it is not a FAT fs */
switch (get_fs_type(myvbr)) {
case VBR_FAT12:
case VBR_FAT16:
case VBR_FAT32:
break;
default:
return 0;
}
/* find out where our root directory is as well as build a
* virtual image of the FAT table */
ticmarker();
if (flags & PART_ONLY) {
rdir_loc = get_root_loc(0, myvbr);
clusts = build_fat(&num_fat_entries, 0, myvbr);
} else {
rdir_loc = get_root_loc(prange->start, myvbr);
clusts = build_fat(&num_fat_entries, prange->start, myvbr);
}
myvbr->fat_entries = num_fat_entries;
display(VERBOSE, "Rood dir location: %lu\n", rdir_loc);
if (!clusts) {
display(VERBOSE, "Unable to read FAT in partition\n");
return 0;
}
ticmarker();
/* create a directory entry stucture for the root and
* fill it with the essential data */
root_dir = emalloc(sizeof *root_dir);
root_dir->parent = NULL;
root_dir->child = NULL;
root_dir->next = NULL;
root_dir->lfn_list = NULL;
root_dir->lfn = NULL;
root_dir->filename = "root";
root_dir->attrs |= ATTR_DIR;
root_dir->child = build_tree(clusts,
num_fat_entries,
rdir_loc, myvbr,
root_dir);
ticmarker();
/* piece together all long file name fragments and
* associate them with there corresponding directory
* entries.
*/
cat_lfn_tree(root_dir);
lfn_assoc_tree(root_dir);
unichoke_tree(root_dir);
ticmarker();
printf(" (Done)\n");
/* if the session is interactive, then just jump to the
* command interpreter. However, if its automatic mode,
* then build and execute fixed commands. */
interface_init(root_dir, clusts, myvbr);
if (flags & INTERACTIVE)
return process_commands();
else {
char *cmd_first = "cp -d -R *"; /* the "canned" command. it
* is the copy command with
* the only-deleted and
* recursive flags on. */
char *cmd_total;
cmd_total = emalloc(strlen(cmd_first) + strlen(output) + 1);
/* add the output directory to the command line. */
sprintf(cmd_total, "%s %s", cmd_first, output);
exec_line(cmd_total);
return 0;
}
}
/*
* print out a directory tree.
* only used in debugging
*/
static void print_dirtree(dirent_t *ent_list)
{
dirent_t *ent;
for (ent = ent_list; ent; ent = ent->next) {
printf("%s\n", ent->lfn ? ent->lfn : ent->filename);
if (ent->child)
print_dirtree(ent->child);
}
}
/*
* Display the help screen
*/
static void print_help(char *cmd)
{
printf("Usage: %s [FILE] -l [LOG] [OPTION]...\n"
"Undelete files from FAT filesystems.\n", cmd);
printf("Fatback v"); print_ver();
printf("(c) 2000-2001 DoD Computer Forensics Lab\n");
printf(" -o, --output=DIR specifies a directory to place output files\n"
" -a, --auto auto undelete mode. non-interactively\n"
" recovers all deleted files\n"
" -l, --log=LOGFILE specifies a file to audit log to.\n"
" -v, --verbose display extra information to the screen.\n"
" -p, --partition=PNUM go directly to PNUM partition\n"
" -d, --delprefix=PREFIX use PREFIX to signify deleted files instead\n"
" of the default \"?\"\n"
" -s, --single force into single partition mode\n"
" -z, --sectsize=SIZE adjust the sector size. default is 512\n"
#ifdef HAVE_MMAP
" -m, --mmap use mmap() file I/O for improved performance\n"
#endif /*HAVE_MMAP*/
" -h, --help display this help screen\n"
"Report bugs to <harbourn@dcfl.gov>\n");
}
/*
* Display the program version
*/
static void print_ver(void)
{
printf("%s\n", fatback_version);
}
/*
* Generate an option string for the getopt() function
* given an array of stuct option's
*/
static char *gen_opts(struct option *options)
{
char *retval = NULL;
int i;
assert(options);
/* loop through all the structures in options[] */
for (i = 0; options[i].val != '\0'; i++) {
if (options[i].val) {
char *tmp;
/* create the string retval if needed */
if (!retval) {
retval = emalloc(1);
*retval = '\0';
}
/* create a new string that is the old
* string plus the new character */
tmp = retval;
retval = str_cons(retval, options[i].val);
free(tmp);
/* if the option takes an argument, add
* the ':' character to the string */
if (options[i].has_arg) {
tmp = retval;
retval = str_cons(retval, ':');
free(tmp);
}
}
}
return retval;
}
/*
* Create a new string that is a concatenation of
* str and chr.
*/
static char *str_cons(char *str, char chr)
{
char *retval = NULL;
int len;
assert(str);
len = strlen(str);
retval = emalloc(len + 2);
strcpy(retval, str);
retval[len] = chr;
retval[len + 1] = '\0';
return retval;
}
syntax highlighted by Code2HTML, v. 0.9.1