1 // Scalpel Copyright (C) 2005-11 by Golden G. Richard III and
2 // 2007-11 by Vico Marziale.
3 //
4 // Written by Golden G. Richard III and Vico Marziale.
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // General Public License for more details.
15 
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 // 02110-1301, USA.
20 //
21 // Thanks to Kris Kendall, Jesse Kornblum, et al for their work
22 // on Foremost.  Foremost 0.69 was used as the starting point for
23 // Scalpel, in 2005.
24 //
25 
26 #include "scalpel.h"
27 
28 // globals defined in scalpel.h
29 
30 // signal has been caught by signal handler
31 int signal_caught;
32 
33 // current wildcard character
34 char wildcard;
35 
36 // width of tty, for progress bar
37 int ttywidth;
38 
39 char *__scalpel__progname;
40 
usage()41 void usage() {
42 
43   printf(
44 	 "Scalpel carves files or data fragments from a disk image based on a set of\n"
45 	 "file carving patterns, which include headers, footers, and other information.\n\n"
46 
47 	 "Usage: scalpel [-b] [-c <config file>] [-d] [-e] [-h] [-i <file>]\n"
48 	 "[-n] [-o <outputdir>] [-O] [-p] [-q <clustersize>] [-r]\n"
49 
50 	 /*	 "[-s] [-m <blockmap file>] [-M <blocksize>] [-n] [-o <outputdir>]\n" */
51 	 /*	 "[-O] [-p] [-q <clustersize>] [-r] [-s <num>] [-u <blockmap file>]\n" */
52 
53 	 "[-v] [-V] <imgfile> [<imgfile>] ...\n\n"
54 
55 
56 
57 	 "Options:\n"
58 
59 	 "-b  Carve files even if defined footers aren't discovered within\n"
60 	 "    maximum carve size for file type [foremost 0.69 compat mode].\n"
61 
62 	 "-c  Choose configuration file.\n"
63 
64 	 "-d  Generate header/footer database; will bypass certain optimizations\n"
65 	 "    and discover all footers, so performance suffers.  Doesn't affect\n"
66 	 "    the set of files carved.  **EXPERIMENTAL**\n"
67 
68 	 "-e  Do nested header/footer matching, to deal with structured files that may\n"
69 	 "    contain embedded files of the same type.  Applicable only to\n"
70 	 "    FORWARD / NEXT patterns.\n"
71 
72 	 "-h  Print this help message and exit.\n"
73 
74 	 "-i  Read names of disk images from specified file.  Note that minimal parsing of\n"
75 	 "    the pathnames is performed and they should be formatted to be compliant C\n"
76 	 "    strings; e.g., under Windows, backslashes must be properly quoted, etc.\n"
77 
78 	 /*
79 
80 	 "-m  Use and update carve coverage blockmap file.  If the blockmap file does\n"
81 	 "    not exist, it is created. For new blockmap files, 512 bytes is used as\n"
82 	 "    a default blocksize unless the -M option overrides this value. In the\n"
83 	 "    blockmap file, the first 32bit unsigned int in the file identifies the\n"
84 	 "    block size.  Thereafter each 32bit unsigned int entry in the blockmap\n"
85 	 "    file corresponds to one block in the image file.  Each entry counts how\n"
86 	 "    many carved files contain this block. Requires more system resources.\n"
87 	 "    This feature is currently experimental.\n"
88 
89 	 "-M  Set blocksize for new coverage blockmap file.\n"
90 
91 	 */
92 
93 
94 	 "-n  Don't add extensions to extracted files.\n"
95 
96 	 "-o  Set output directory for carved files.\n"
97 
98 	 "-O  Don't organize carved files by type. Default is to organize carved files\n"
99 	 "    into subdirectories.\n"
100 
101 	 "-p  Perform image file preview; audit log indicates which files\n"
102 	 "    would have been carved, but no files are actually carved.  Useful for\n"
103 	 "    indexing file or data fragment locations or supporting in-place file\n"
104 	 "    carving.\n"
105 
106 	 "-q  Carve only when header is cluster-aligned.\n"
107 
108 	 "-r  Find only first of overlapping headers/footers [foremost 0.69 compat mode].\n"
109 
110 	 /*
111 
112 	  "-s  Skip num bytes in each disk image before carving.\n"
113 
114 
115 	 "-u  Use (but don't update) carve coverage blockmap file when carving.\n"
116 	 "    Carve only sections of the image whose entries in the blockmap are 0.\n"
117 	 "    These areas are treated as contiguous regions.  This feature is\n"
118 	 "    currently experimental.\n"
119 
120 	 */
121 
122 	 "-V  Print copyright information and exit.\n"
123 
124 	 "-v  Verbose mode.\n"
125 	  );
126 }
127 
128 
129 // signal handler, sets global variable 'signal_caught' which is
130 // checked periodically during carve operations.  Allows clean
131 // shutdown.
catch_alarm(int signum)132 void catch_alarm(int signum) {
133   signal_caught = signum;
134   signal(signum, catch_alarm);
135 
136 #ifdef __DEBUG
137   fprintf(stderr, "\nCaught signal: %s.\n", (char *)strsignal(signum));
138 #endif
139 
140   fprintf(stderr, "\nKill signal detected. Cleaning up...\n");
141 }
142 
143 
144 int
extractSearchSpecData(struct scalpelState * state,struct SearchSpecLine * s,char ** tokenarray)145 extractSearchSpecData(struct scalpelState *state,
146 		      struct SearchSpecLine *s, char **tokenarray) {
147 
148   int err = 0;
149 
150   // process one line from config file:
151   //     token[0] = suffix
152   //     token[1] = case sensitive?
153   //     token[2] = maximum carve size
154   //     token[3] = begintag
155   //     token[4] = endtag
156   //     token[5] = search type (optional)
157 
158   s->suffix = (char *)malloc(MAX_SUFFIX_LENGTH * sizeof(char));
159   checkMemoryAllocation(state, s->suffix, __LINE__, __FILE__, "s->suffix");
160   s->begin = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
161   checkMemoryAllocation(state, s->begin, __LINE__, __FILE__, "s->begin");
162   s->end = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
163   checkMemoryAllocation(state, s->end, __LINE__, __FILE__, "s->end");
164   s->begintext = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
165   checkMemoryAllocation(state, s->begintext, __LINE__, __FILE__, "s->begintext");
166   s->endtext = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
167   checkMemoryAllocation(state, s->endtext, __LINE__, __FILE__, "s->endtext");
168 
169   if(!strncasecmp(tokenarray[0],
170 		  SCALPEL_NOEXTENSION_SUFFIX,
171 		  strlen(SCALPEL_NOEXTENSION_SUFFIX))) {
172     s->suffix[0] = SCALPEL_NOEXTENSION;
173     s->suffix[1] = 0;
174   }
175   else {
176     memcpy(s->suffix, tokenarray[0], MAX_SUFFIX_LENGTH);
177   }
178 
179   // case sensitivity check
180   s->casesensitive = (!strncasecmp(tokenarray[1], "y", 1) ||
181 		      !strncasecmp(tokenarray[1], "yes", 3));
182 
183   //#ifdef _WIN32
184   //    s->length = _atoi64(tokenarray[2]);
185   //#else
186   //  s->length = atoull(tokenarray[2]);
187   //#endif
188 
189 
190   char split[MAX_STRING_LENGTH];
191   char *maxcarvelength;
192 
193   strcpy(split, tokenarray[2]);
194   maxcarvelength = strchr(split, ':');
195   if(!maxcarvelength) {
196     s->minlength = 0;
197     s->length = strtoull(split, 0, 10);
198   }
199   else {
200     *maxcarvelength = 0;
201     maxcarvelength++;
202     s->minlength = strtoull(split, 0, 10);
203     s->length = strtoull(maxcarvelength, 0, 10);
204   }
205 
206   // determine search type for this needle
207   s->searchtype = SEARCHTYPE_FORWARD;
208   if(!strncasecmp(tokenarray[5], "REVERSE", strlen("REVERSE"))) {
209     s->searchtype = SEARCHTYPE_REVERSE;
210   }
211   else if(!strncasecmp(tokenarray[5], "NEXT", strlen("NEXT"))) {
212     s->searchtype = SEARCHTYPE_FORWARD_NEXT;
213   }
214   // FORWARD is the default, but OK if the user defines it explicitly
215   else if(!strncasecmp(tokenarray[5], "FORWARD", strlen("FORWARD"))) {
216     s->searchtype = SEARCHTYPE_FORWARD;
217   }
218 
219   // regular expressions must be handled separately
220 
221   if(isRegularExpression(tokenarray[3])) {
222 
223 #ifdef GPU_THREADING
224 		// GPU execution does not support regex needles.
225 			fprintf(stderr, "ERROR: GPU search for regex headers is not supported!\n");
226 			fprintf(stderr, "Please modify the config file for non-regex headers only.\n");
227 			exit(1);
228 #endif
229 
230     // copy RE, zap leading/training '/' and prepare for regular expression compilation
231     s->beginisRE = 1;
232     strcpy(s->begin, tokenarray[3]);
233     strcpy(s->begintext, tokenarray[3]);
234     s->beginlength = strlen(tokenarray[3]);
235     s->begin[s->beginlength] = 0;
236     // compile regular expression
237     err = regncomp(&(s->beginstate.re), s->begin+1, s->beginlength-2,
238 		   REG_EXTENDED | (REG_ICASE * (!s->casesensitive)));
239     if (err) {
240       return SCALPEL_ERROR_BAD_HEADER_REGEX;
241     }
242   }
243   else {
244     // non-regular expression header
245     s->beginisRE = 0;
246     strcpy(s->begintext, tokenarray[3]);
247     s->beginlength = translate(tokenarray[3]);
248     memcpy(s->begin, tokenarray[3], s->beginlength);
249     init_bm_table(s->begin, s->beginstate.bm_table, s->beginlength,
250 		  s->casesensitive);
251   }
252 
253   if(isRegularExpression(tokenarray[4])) {
254 
255 #ifdef GPU_THREADING
256 		// GPU execution does not support regex needles.
257 			fprintf(stderr, "ERROR: GPU search for regex footers is not supported!\n");
258 			fprintf(stderr, "Please modify the config file for non-regex footers only.\n");
259 			exit(1);
260 #endif
261 
262     // copy RE, zap leading/training '/' and prepare for for regular expression compilation
263     s->endisRE = 1;
264     strcpy(s->end, tokenarray[4]);
265     strcpy(s->endtext, tokenarray[4]);
266     s->endlength = strlen(tokenarray[4]);
267     s->end[s->endlength] = 0;
268     // compile regular expression
269     err = regncomp(&(s->endstate.re), s->end+1, s->endlength-2,
270 		   REG_EXTENDED | (REG_ICASE * (!s->casesensitive)));
271 
272     if(err) {
273       return SCALPEL_ERROR_BAD_FOOTER_REGEX;
274     }
275   }
276   else {
277     s->endisRE = 0;
278     strcpy(s->endtext, tokenarray[4]);
279     s->endlength = translate(tokenarray[4]);
280     memcpy(s->end, tokenarray[4], s->endlength);
281     init_bm_table(s->end, s->endstate.bm_table, s->endlength, s->casesensitive);
282   }
283 
284   return SCALPEL_OK;
285 }
286 
287 
288 
processSearchSpecLine(struct scalpelState * state,char * buffer,int lineNumber)289 int processSearchSpecLine(struct scalpelState *state, char *buffer, int lineNumber) {
290 
291   char *buf = buffer;
292   char *token;
293   char **tokenarray = (char **)malloc(6 * sizeof(char[MAX_STRING_LENGTH + 1]));
294   int i = 0, err = 0, len = strlen(buffer);
295 
296   checkMemoryAllocation(state, tokenarray, __LINE__, __FILE__, "tokenarray");
297 
298   // murder CTRL-M (0x0d) characters
299   //  if(buffer[len - 2] == 0x0d && buffer[len - 1] == 0x0a) {
300   if (len >= 2 && buffer[len - 2] == 0x0d && buffer[len - 1] == 0x0a) {
301     buffer[len - 2] = buffer[len - 1];
302     buffer[len - 1] = buffer[len];
303   }
304 
305   buf = (char *)skipWhiteSpace(buf);
306   token = strtok(buf, " \t\n");
307 
308   // lines beginning with # are comments
309   if(token == NULL || token[0] == '#') {
310     return SCALPEL_OK;
311   }
312 
313   // allow wildcard to be changed
314   if(!strncasecmp(token, "wildcard", 9)) {
315     if((token = strtok(NULL, " \t\n")) != NULL) {
316       translate(token);
317     }
318     else {
319       fprintf(stdout,
320 	      "Warning: Empty wildcard in configuration file line %d. Ignoring.\n",
321 	      lineNumber);
322       return SCALPEL_OK;
323     }
324 
325     if(strlen(token) > 1) {
326       fprintf(stderr, "Warning: Wildcard can only be one character,"
327 	      " but you specified %d characters.\n"
328 	      "         Using the first character, \"%c\", as the wildcard.\n",
329 	      (int)strlen(token), token[0]);
330     }
331 
332     wildcard = token[0];
333     return SCALPEL_OK;
334   }
335 
336   while (token && (i < NUM_SEARCH_SPEC_ELEMENTS)) {
337     tokenarray[i] = token;
338     i++;
339     token = strtok(NULL, " \t\n");
340   }
341 
342   switch (NUM_SEARCH_SPEC_ELEMENTS - i) {
343   case 2:
344     tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 1] = (char *)"";
345     tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 2] = (char *)"";
346     break;
347   case 1:
348     tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 1] = (char *)"";
349     break;
350   case 0:
351     break;
352   default:
353     fprintf(stderr,
354 	    "\nERROR: In line %d of the configuration file, expected %d tokens,\n"
355 	    "       but instead found only %d.\n",
356 	    lineNumber, NUM_SEARCH_SPEC_ELEMENTS, i);
357     return SCALPEL_ERROR_NO_SEARCH_SPEC;
358     break;
359 
360   }
361 
362   if((err =
363       extractSearchSpecData(state, &(state->SearchSpec[state->specLines]),
364 			    tokenarray))) {
365     switch (err) {
366     case SCALPEL_ERROR_BAD_HEADER_REGEX:
367       fprintf(stderr,
368 	      "\nERROR: In line %d of the configuration file, bad regular expression for header.\n",
369 	      lineNumber);
370       break;
371     case SCALPEL_ERROR_BAD_FOOTER_REGEX:
372       fprintf(stderr,
373 	      "\nERROR: In line %d of the configuration file, bad regular expression for footer.\n",
374 	      lineNumber);
375       break;
376 
377     default:
378       fprintf(stderr,
379 	      "\nERROR: Unknown error on line %d of the configuration file.\n",
380 	      lineNumber);
381     }
382   }
383   state->specLines++;
384   return SCALPEL_OK;
385 }
386 
387 
388 // process configuration file
readSearchSpecFile(struct scalpelState * state)389 int readSearchSpecFile(struct scalpelState *state) {
390 
391   int lineNumber = 0, status;
392   FILE *f;
393 
394   char *buffer =
395     (char *)malloc((NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH + 1) *
396 		   sizeof(char));
397   checkMemoryAllocation(state, buffer, __LINE__, __FILE__, "buffer");
398 
399   f = fopen(state->conffile, "r");
400   if(f == NULL) {
401     fprintf(stderr,
402 	    "ERROR: Couldn't open configuration file:\n%s -- %s\n",
403 	    state->conffile, strerror(errno));
404     free(buffer);
405     return SCALPEL_ERROR_FATAL_READ;
406   }
407 
408   while (fgets(buffer, NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH, f)) {
409     lineNumber++;
410 
411     if(state->specLines > MAX_FILE_TYPES) {
412       fprintf(stderr, "Your conf file contains too many file types.\n");
413       fprintf(stderr,
414 	      "This version was compiled with MAX_FILE_TYPES == %d.\n",
415 	      MAX_FILE_TYPES);
416       fprintf(stderr, "Increase MAX_FILE_TYPES, recompile, and try again.\n");
417       free(buffer);
418       return SCALPEL_ERROR_TOO_MANY_TYPES;
419     }
420 
421     if((status =
422 	processSearchSpecLine(state, buffer, lineNumber)) != SCALPEL_OK) {
423       free(buffer);
424       return status;
425     }
426   }
427 
428   // add an empty object to the end of the list as a marker
429 
430   state->SearchSpec[state->specLines].suffix = NULL;
431   state->SearchSpec[state->specLines].casesensitive = 0;
432   state->SearchSpec[state->specLines].length = 0;
433   state->SearchSpec[state->specLines].begin = NULL;
434   state->SearchSpec[state->specLines].beginlength = 0;
435   state->SearchSpec[state->specLines].end = NULL;
436   state->SearchSpec[state->specLines].endlength = 0;
437 
438   // GGRIII: offsets field is uninitialized--it doesn't
439   // matter, since we won't use this entry.
440 
441   fclose(f);
442   free(buffer);
443   return SCALPEL_OK;
444 }
445 
446 // Register the signal-handler that will write to the audit file and
447 // close it if we catch a SIGINT or SIGTERM
registerSignalHandlers()448 void registerSignalHandlers() {
449   if(signal(SIGINT, catch_alarm) == SIG_IGN) {
450     signal(SIGINT, SIG_IGN);
451   }
452   if(signal(SIGTERM, catch_alarm) == SIG_IGN) {
453     signal(SIGTERM, SIG_IGN);
454   }
455 
456 #ifndef _WIN32
457   // *****GGRIII:  is this problematic?
458   // From foremost 0.69:
459   /* Note: I haven't found a way to get notified of
460      console resize events in Win32.  Right now the statusbar
461      will be too long or too short if the user decides to resize
462      their console window while foremost runs.. */
463 
464   //    signal(SIGWINCH, setttywidth);
465 #endif
466 }
467 
468 // initialize state variable and copy command line arguments
initializeState(char ** argv,struct scalpelState * state)469 void initializeState(char **argv, struct scalpelState *state) {
470 
471   char **argvcopy = argv;
472   int sss;
473   int i;
474 
475   // Allocate memory for state
476   state->imagefile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
477   checkMemoryAllocation(state, state->imagefile, __LINE__, __FILE__,
478 			"state->imagefile");
479   state->inputFileList = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
480   checkMemoryAllocation(state, state->inputFileList, __LINE__, __FILE__,
481 			"state->inputFileList");
482   state->conffile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
483   checkMemoryAllocation(state, state->conffile, __LINE__, __FILE__,
484 			"state->conffile");
485   state->outputdirectory = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
486   checkMemoryAllocation(state, state->conffile, __LINE__, __FILE__,
487 			"state->outputdirectory");
488   state->invocation = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
489   checkMemoryAllocation(state, state->invocation, __LINE__, __FILE__,
490 			"state->invocation");
491 
492   // GGRIII: memory allocation made more sane, because we're storing
493   // more information in Scalpel than foremost had to, for each file
494   // type.
495   sss = (MAX_FILE_TYPES + 1) * sizeof(struct SearchSpecLine);
496   state->SearchSpec = (struct SearchSpecLine *)malloc(sss);
497   checkMemoryAllocation(state, state->SearchSpec, __LINE__, __FILE__,
498 			"state->SearchSpec");
499   state->specLines = 0;
500 
501   // GGRIII: initialize header/footer offset data, carved file count,
502   // et al.  The header/footer database is re-initialized in "dig.c"
503   // after each image file is processed (numfilestocarve and
504   // organizeDirNum are not). Storage for the header/footer offsets
505   // will be reallocated as needed.
506 
507   for(i = 0; i < MAX_FILE_TYPES; i++) {
508     state->SearchSpec[i].offsets.headers = 0;
509     state->SearchSpec[i].offsets.footers = 0;
510     state->SearchSpec[i].offsets.numheaders = 0;
511     state->SearchSpec[i].offsets.numfooters = 0;
512     state->SearchSpec[i].offsets.headerstorage = 0;
513     state->SearchSpec[i].offsets.footerstorage = 0;
514     state->SearchSpec[i].numfilestocarve = 0;
515     state->SearchSpec[i].organizeDirNum = 0;
516   }
517 
518   state->fileswritten = 0;
519   state->skip = 0;
520   state->organizeMaxFilesPerSub = MAX_FILES_PER_SUBDIRECTORY;
521   state->modeVerbose = FALSE;
522   state->modeNoSuffix = FALSE;
523   state->useInputFileList = FALSE;
524   state->carveWithMissingFooters = FALSE;
525   state->noSearchOverlap = FALSE;
526   state->generateHeaderFooterDatabase = FALSE;
527   state->updateCoverageBlockmap = FALSE;
528   state->useCoverageBlockmap = FALSE;
529   state->coverageblocksize = 0;
530   state->blockAlignedOnly = FALSE;
531   state->organizeSubdirectories = TRUE;
532   state->previewMode = FALSE;
533   state->handleEmbedded = FALSE;
534   state->auditFile = NULL;
535 
536   // default values for output directory, config file, wildcard character,
537   // coverage blockmap directory
538   strncpy(state->outputdirectory, SCALPEL_DEFAULT_OUTPUT_DIR,
539 	  strlen(SCALPEL_DEFAULT_OUTPUT_DIR));
540   strncpy(state->conffile, SCALPEL_DEFAULT_CONFIG_FILE, MAX_STRING_LENGTH);
541   state->coveragefile = state->outputdirectory;
542   wildcard = SCALPEL_DEFAULT_WILDCARD;
543   signal_caught = 0;
544   state->invocation[0] = 0;
545 
546   // copy the invocation string into the state
547   do {
548     strncat(state->invocation,
549 	    *argvcopy, MAX_STRING_LENGTH - strlen(state->invocation));
550     strncat(state->invocation,
551 	    " ", MAX_STRING_LENGTH - strlen(state->invocation));
552     ++argvcopy;
553   }
554   while (*argvcopy);
555 
556   registerSignalHandlers();
557 }
558 
559 // parse command line arguments
processCommandLineArgs(int argc,char ** argv,struct scalpelState * state)560 void processCommandLineArgs(int argc, char **argv, struct scalpelState *state) {
561   char i;
562   int numopts = 1;
563 
564   while ((i = getopt(argc, argv, "behvVu:ndpq:rc:o:s:i:m:M:O")) != -1) {
565     numopts++;
566     switch (i) {
567 
568     case 'V':
569       fprintf(stdout, SCALPEL_COPYRIGHT_STRING);
570       exit(1);
571 
572     case 'h':
573       usage();
574       exit(1);
575 
576     case 's':
577       numopts++;
578       state->skip = strtoull(optarg, NULL, 10);
579       fprintf(stdout,
580 	      "Skipping the first %lld bytes of each image file.\n",
581 	      state->skip);
582       break;
583 
584     case 'c':
585       numopts++;
586       strncpy(state->conffile, optarg, MAX_STRING_LENGTH);
587       break;
588 
589     case 'd':
590       state->generateHeaderFooterDatabase = TRUE;
591       break;
592 
593     case 'e':
594       state->handleEmbedded = TRUE;
595       break;
596 
597       /*
598 
599     case 'm':
600       numopts++;
601       state->updateCoverageBlockmap = TRUE;
602       state->useCoverageBlockmap = TRUE;
603       state->coveragefile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
604       checkMemoryAllocation(state, state->coveragefile, __LINE__,
605 			    __FILE__, "state->coveragefile");
606       strncpy(state->coveragefile, optarg, MAX_STRING_LENGTH);
607       break;
608 
609     case 'u':
610       numopts++;
611       state->useCoverageBlockmap = TRUE;
612       state->coveragefile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
613       checkMemoryAllocation(state, state->coveragefile, __LINE__,
614 			    __FILE__, "state->coveragefile");
615       strncpy(state->coveragefile, optarg, MAX_STRING_LENGTH);
616       break;
617 
618     case 'M':
619       numopts++;
620       state->coverageblocksize = strtoul(optarg, NULL, 10);
621       if(state->coverageblocksize <= 0) {
622 	fprintf(stderr,
623 		"\nERROR: Invalid blocksize for -M command line option.\n");
624 	exit(1);
625       }
626       break;
627 
628       */
629 
630     case 'o':
631       numopts++;
632       strncpy(state->outputdirectory, optarg, MAX_STRING_LENGTH);
633       break;
634 
635     case 'O':
636       state->organizeSubdirectories = FALSE;
637       break;
638 
639     case 'p':
640       state->previewMode = TRUE;
641       break;
642 
643     case 'b':
644       state->carveWithMissingFooters = TRUE;
645       break;
646 
647     case 'i':
648       state->useInputFileList = TRUE;
649       state->inputFileList = optarg;
650       break;
651 
652     case 'n':
653       state->modeNoSuffix = TRUE;
654       fprintf(stdout, "Extracting files without filename extensions.\n");
655       break;
656 
657     case 'q':
658       numopts++;
659       state->blockAlignedOnly = TRUE;
660       state->alignedblocksize = strtoul(optarg, NULL, 10);
661       if(state->alignedblocksize <= 0) {
662 	fprintf(stderr,
663 		"\nERROR: Invalid blocksize for -q command line option.\n");
664 	exit(1);
665       }
666       break;
667 
668     case 'r':
669       state->noSearchOverlap = TRUE;
670       break;
671 
672     case 'v':
673       state->modeVerbose = TRUE;
674       break;
675 
676     default:
677       exit(1);
678     }
679   }
680 
681   // check for incompatible options
682 
683   if((state->useInputFileList || argc - numopts > 1) &&
684      (state->updateCoverageBlockmap || state->useCoverageBlockmap)) {
685 
686 
687     fprintf(stderr, "%d %d\n", argc, numopts);
688 
689     fprintf(stderr,
690 	    "\nCoverage blockmaps can be processed only if a single image filename is\n"
691 	    "specified on the command line.\n");
692     exit(1);
693   }
694 }
695 
696 // full pathnames for all files used
convertFileNames(struct scalpelState * state)697 void convertFileNames(struct scalpelState *state) {
698 
699   char fn[MAX_STRING_LENGTH]; // should be [PATH_MAX +1] from limits.h
700 
701   if(realpath(state->outputdirectory, fn)) {
702     strncpy(state->outputdirectory, fn, MAX_STRING_LENGTH);
703   }
704   else {
705     //		perror("realpath");
706   }
707 
708   if(realpath(state->conffile, fn)) {
709     strncpy(state->conffile, fn, MAX_STRING_LENGTH);
710   }
711   else {
712     //		perror("realpath");
713   }
714 
715 }
716 
717 
718 // GGRIII: for each file, build header/footer offset database first,
719 // then carve files based on this database.  Need to clear the
720 // header/footer offset database after processing of each file.
721 
digAllFiles(char ** argv,struct scalpelState * state)722 void digAllFiles(char **argv, struct scalpelState *state) {
723 
724   int i = 0, j = 0;
725   FILE *listoffiles = NULL;
726 
727   if(state->useInputFileList) {
728     fprintf(stdout, "Batch mode: reading list of images from %s.\n",
729 	    state->inputFileList);
730     listoffiles = fopen(state->inputFileList, "r");
731     if(listoffiles == NULL) {
732       fprintf(stderr, "Couldn't open file:\n%s -- %s\n",
733 	      (*(state->inputFileList) ==
734 	       '\0') ? "<blank>" : state->inputFileList, strerror(errno));
735       handleError(state, SCALPEL_ERROR_FATAL_READ);
736       exit(-1);
737     }
738     j = 0;
739     do {
740       j++;
741 
742       if(fgets(state->imagefile, MAX_STRING_LENGTH, listoffiles) == NULL) {
743 	if (!feof(listoffiles)) {
744 		fprintf(stderr,
745 			"Error reading line %d of %s. Skipping line.\n",
746 			j, state->inputFileList);
747 		}
748 	continue;
749       }
750       if(state->imagefile[strlen(state->imagefile) - 1] == '\n') {
751 	state->imagefile[strlen(state->imagefile) - 1] = '\x00';
752       }
753 
754       // GGRIII: this function now *only* builds the header/footer
755       // database.  Carving is handled afterward, in carveImageFile().
756 
757       if((i = digImageFile(state)) != SCALPEL_OK) {
758 	handleError(state, i);
759 		continue;
760       }
761       else {
762 	// GGRIII: "digging" is now complete and header/footer database
763 	// has been built.  The function carveImageFile() performs
764 	// extraction of files based on this database.
765 
766 	if((i = carveImageFile(state)) != SCALPEL_OK) {
767 	  handleError(state, i);
768 	}
769       }
770     }
771     while (!feof(listoffiles));
772     fclose(listoffiles);
773   }
774   else {
775     do {
776       state->imagefile = *argv;
777 
778       // GGRIII: this function now *only* builds the header/footer
779       // database.  Carving is handled afterward, in carveImageFile().
780 
781       if((i = digImageFile(state))) {
782 	handleError(state, i);
783 	continue;
784       }
785       else {
786 	// GGRIII: "digging" is now complete and header/footer database
787 	// has been built.  The function carveImageFile() performs extraction
788 	// of files based on this database.
789 
790 	if((i = carveImageFile(state))) {
791 	  handleError(state, i);
792 	}
793       }
794       ++argv;
795     }
796     while (*argv);
797   }
798 }
799 
main(int argc,char ** argv)800 int main(int argc, char **argv) {
801 
802   time_t starttime = time(0);
803   struct scalpelState state;
804 
805 
806   ///////// JUST  A TEST FOR WIN32 TSK LINKAGE ///////////
807   //      TSK_TCHAR *imgs=_TSK_T("c:\\128MB.dd");
808   //      TSK_IMG_INFO *img = tsk_img_open(_TSK_T("raw"), 1, (const TSK_TCHAR **)&imgs);
809   //      printf("TSK size is %d bytes\n", (int)img->size);
810   ///////// END JUST  A TEST FOR WIN32 TSK LINKAGE ///////////
811 
812 
813   if(ldiv(SIZE_OF_BUFFER, SCALPEL_BLOCK_SIZE).rem != 0) {
814     fprintf(stderr, SCALPEL_SIZEOFBUFFER_PANIC_STRING);
815     exit(-1);
816   }
817 
818 #ifndef __GLIBC__
819   setProgramName(argv[0]);
820 #endif
821 
822   fprintf(stdout, SCALPEL_BANNER_STRING);
823 
824   initializeState(argv, &state);
825   processCommandLineArgs(argc, argv, &state);
826   convertFileNames(&state);
827 
828   // read configuration file
829   int err;
830   if((err = readSearchSpecFile(&state))) {
831     // problem with config file
832     handleError(&state, err);
833     exit(-1);
834   }
835 
836   setttywidth();
837 
838   argv += optind;
839   if(*argv != NULL || state.useInputFileList) {
840     // prepare audit file and make sure output directory is empty.
841     int err;
842     if((err = openAuditFile(&state))) {
843       handleError(&state, err);
844       exit(-1);
845     }
846 
847   	// Initialize the backing store of buffer to read-in, process image data.
848   	init_store();
849 
850   	// Initialize threading model for cpu or gpu search.
851   	init_threading_model(&state);
852 
853 
854     digAllFiles(argv, &state);
855     closeAuditFile(state.auditFile);
856   }
857   else {
858     usage();
859     fprintf(stdout, "\nERROR: No image files specified.\n\n");
860   }
861 
862 #ifdef _WIN32
863   fprintf(stdout,
864 	  "\nScalpel is done, files carved = %I64u, elapsed  = %ld secs.\n",
865 	  state.fileswritten, (int)time(0) - starttime);
866 #else
867   fprintf(stdout,
868 	  "\nScalpel is done, files carved = %llu, elapsed  = %ld secs.\n",
869 	  state.fileswritten, (int)time(0) - starttime);
870 #endif
871 
872   return 0;
873 }
874