1 #include "main.h"
2 #include "utf8.h"
3 #include <stdarg.h>
4
5 // $Id$
6
7 /**
8 *
9 * display.cpp:
10 * Manages user output.
11 * All output is in UTF-8.
12 *
13 * If opt_escape8 is set, then non-ASCII UTF-8 characters are turned
14 * into U+XXXX notation.
15 *
16 * NOTE WITH MINGW GCC-4.3.0:
17 * You will get a warning from the format. IGNORE IT.
18 * See http://lists.gnu.org/archive/html/qemu-devel/2009-01/msg01979.html
19 *
20 * All output is threadsafe.
21 */
22
23 /****************************************************************
24 ** Support routines
25 ****************************************************************/
26
set_outfilename(std::string outfilename)27 void display::set_outfilename(std::string outfilename)
28 {
29 myoutstream.open(outfilename.c_str(),std::ios::out|std::ios::binary);
30 if(!myoutstream.is_open()){
31 fatal_error("%s: Cannot open: ",opt_outfilename.c_str());
32 }
33 out = &myoutstream;
34 }
35
writeln(std::ostream * os,const std::string & str)36 void display::writeln(std::ostream *os,const std::string &str)
37 {
38 lock();
39 (*os) << str;
40 if (opt_zero){
41 (*os) << '\000';
42 }
43 else {
44 (*os) << std::endl;
45 }
46 os->flush();
47 unlock();
48 }
49
50 /* special handling for vasprintf under mingw.
51 * Sometimes the function prototype is missing. Sometimes the entire function is missing!
52 */
53 #if defined(HAVE_VASPRINTF) && defined(MINGW)
54 extern "C" {
55 int vasprintf(char **ret,const char *fmt,va_list ap);
56 }
57 #endif
58
59 /*
60 * If we don't have vasprintf, bring it in.
61 */
62 /*
63 #if !defined(HAVE_VASPRINTF)
64 extern "C" {
65 //
66 // We do not have vasprintf.
67 // We have determined that vsnprintf() does not perform properly on windows.
68 // So we just allocate a huge buffer and then strdup() and hope!
69 //
70 int vasprintf(char **ret,const char *fmt,va_list ap)
71 {
72 // Figure out how long the result will be
73 char buf[65536];
74 int size = vsnprintf(buf,sizeof(buf),fmt,ap);
75 if(size<0) return size;
76 // Now allocate the memory
77 *ret = (char *)strdup(buf);
78 return size;
79 }
80 }
81 #endif
82 */
83
status(const char * fmt,...)84 void display::status(const char *fmt,...)
85 {
86 va_list(ap);
87 va_start(ap,fmt);
88
89 char *ret = 0;
90 if(vasprintf(&ret,fmt,ap) < 0){
91 (*out) << progname << ": " << strerror(errno);
92 exit(EXIT_FAILURE);
93 }
94 writeln(out,ret);
95 free(ret);
96 va_end(ap);
97 }
98
error(const char * fmt,...)99 void display::error(const char *fmt,...)
100 {
101 if(opt_silent) return;
102 va_list(ap);
103 va_start(ap,fmt);
104 char *ret = 0;
105 if (vasprintf(&ret,fmt,ap) < 0) {
106 (*out) << progname << ": " << strerror(errno);
107 exit(EXIT_FAILURE);
108 }
109 lock();
110 std::cerr << progname << ": " << ret << std::endl;
111 unlock();
112 va_end(ap);
113 }
114
115
try_msg(void)116 void display::try_msg(void)
117 {
118 writeln(&std::cerr,std::string("Try `") + progname + " -h` for more information.");
119 }
120
121
fatal_error(const char * fmt,...)122 void display::fatal_error(const char *fmt,...)
123 {
124 va_list(ap);
125 va_start(ap,fmt);
126
127 char *ret = 0;
128 if(vasprintf(&ret,fmt,ap) < 0){
129 (*out) << progname << ": " << strerror(errno);
130 exit(EXIT_FAILURE);
131 }
132 writeln(&std::cerr,progname + ": " + ret);
133 free(ret);
134 va_end(ap);
135 exit(1);
136 }
137
internal_error(const char * fmt,...)138 void display::internal_error(const char *fmt,...)
139 {
140 va_list(ap);
141 va_start(ap,fmt);
142
143 char *ret = 0;
144 if(vasprintf(&ret,fmt,ap) < 0){
145 (*out) << progname << ": " << strerror(errno);
146 exit(EXIT_FAILURE);
147 }
148 writeln(&std::cerr,ret);
149 writeln(&std::cerr,progname+": Internal error. Contact developer!");
150 va_end(ap);
151 free(ret);
152 exit(1);
153 }
154
print_debug(const char * fmt,...)155 void display::print_debug(const char *fmt, ... )
156 {
157 #ifdef __DEBUG
158 va_list(ap);
159 va_start(ap,fmt);
160 char *ret = 0;
161 if(vasprintf(&ret,fmt,ap) < 0){
162 (*out) << progname << ": " << strerror(errno);
163 exit(EXIT_FAILURE);
164 }
165 writeln(&std::cerr,std::string("DEBUG:" ) + ret);
166 free(ret);
167 va_end(ap);
168 #endif
169 }
170 /**
171 * output the string, typically a fn, optionally performing unicode escaping
172 */
173
fmt_filename(const std::string & fn) const174 std::string display::fmt_filename(const std::string &fn) const
175 {
176 if(opt_unicode_escape){
177 return global::escape_utf8(fn);
178 } else {
179 return fn; // assumed to be utf8
180 }
181 }
182
183
184 #ifdef _WIN32
fmt_filename(const std::wstring & fn) const185 std::string display::fmt_filename(const std::wstring &fn) const
186 {
187 if(opt_unicode_escape){
188 return global::escape_utf8(global::make_utf8(fn));
189 } else {
190 return global::make_utf8(fn);
191 }
192 }
193 #endif
194
195
196
error_filename(const std::string & fn,const char * fmt,...)197 void display::error_filename(const std::string &fn,const char *fmt, ...)
198 {
199 if(opt_silent) return;
200
201 va_list(ap);
202 va_start(ap,fmt);
203 char *ret = 0;
204 if(vasprintf(&ret,fmt,ap) < 0){
205 (*out) << progname << ": " << strerror(errno);
206 exit(EXIT_FAILURE);
207 }
208 if(dfxml){
209 lock();
210 dfxml->push("fileobject");
211 dfxml->xmlout("filename",fn);
212 dfxml->xmlout("error",ret);
213 dfxml->pop();
214 unlock();
215 } else {
216 writeln(&std::cerr,fmt_filename(fn) + ": " + ret);
217 }
218 free(ret);
219 va_end(ap);
220 }
221
222 #ifdef _WIN32
error_filename(const std::wstring & fn,const char * fmt,...)223 void display::error_filename(const std::wstring &fn,const char *fmt, ...)
224 {
225 if(opt_silent) return;
226
227 va_list(ap);
228 va_start(ap,fmt);
229 char *ret = 0;
230 if(vasprintf(&ret,fmt,ap) < 0){
231 (*out) << progname << ": " << strerror(errno);
232 exit(EXIT_FAILURE);
233 }
234 writeln(&std::cerr,fmt_filename(fn) + ": " + ret);
235 free(ret);
236 va_end(ap);
237 }
238 #endif
239
clear_realtime_stats()240 void display::clear_realtime_stats()
241 {
242 lock();
243 std::cerr << "\r";
244 for(int i=0;i<LINE_LENGTH;i++){
245 std::cerr << ' ';
246 }
247 std::cerr << "\r";
248 unlock();
249 }
250
251 // At least one user has suggested changing update_display() to
252 // use human readable units (e.g. GB) when displaying the updates.
253 // The problem is that once the display goes above 1024MB, there
254 // won't be many updates. The counter doesn't change often enough
255 // to indicate progress. Using MB is a reasonable compromise.
256
display_realtime_stats(const file_data_hasher_t * fdht,const hash_context_obj * hc,time_t elapsed)257 void display::display_realtime_stats(const file_data_hasher_t *fdht, const hash_context_obj *hc,time_t elapsed)
258 {
259 uint64_t mb_read=0;
260
261 /*
262 * This is the only place where filenames are shortened.
263 * So do it explicitly here.
264 *
265 */
266
267 std::stringstream ss;
268
269 std::string fn = fdht->file_name;
270 if (fn.size() > MAX_FILENAME_LENGTH){
271 /* Shorten from the middle */
272 size_t half = (MAX_FILENAME_LENGTH)/2;
273 fn = fn.substr(0,half) + "..." + fn.substr(fn.size()-half,half);
274 }
275
276 ss << fmt_filename(fn) << " ";
277
278 // If we've read less than one MB, then the computed value for mb_read
279 // will be zero. Later on we may need to divide the total file size,
280 // total_megs, by mb_read. Dividing by zero can create... problems
281
282 if (hc->read_len < ONE_MEGABYTE){
283 mb_read = 1;
284 }
285 else {
286 mb_read = hc->read_len / ONE_MEGABYTE;
287 }
288
289 if (fdht->stat_megs()==0 || opt_estimate==false) {
290 ss << mb_read << "MB done. Unable to estimate remaining time.";
291 }
292 else {
293 // Estimate the number of seconds using only integer math.
294 //
295 // We now compute the number of bytes read per second and then
296 // use that to determine how long the whole file should take.
297 // By subtracting the number of elapsed seconds from that, we should
298 // get a good estimate of how many seconds remain.
299
300 uint64_t seconds = (fdht->stat_bytes / (fdht->file_bytes / elapsed)) - elapsed;
301
302 // We don't care if the remaining time is more than one day.
303 // If you're hashing something that big, to quote the movie Jaws:
304 //
305 // "We're gonna need a bigger boat."
306 uint64_t hour = seconds / 3600;
307 seconds -= (hour * 3600);
308
309 uint64_t min = seconds/60;
310 seconds -= min * 60;
311
312 ss << mb_read << "MB of " << fdht->stat_megs() << "MB done, ";
313 char msg[64];
314 snprintf(msg,sizeof(msg),"%02" PRIu64":%02" PRIu64":%02" PRIu64" left", hour, min, seconds);
315 ss << msg;
316 }
317 ss << "\r";
318 lock();
319 std::cerr << ss.str() << "\r";
320 unlock();
321 }
322
323
display_banner_if_needed()324 void display::display_banner_if_needed()
325 {
326 if(this->dfxml!=0) return; // output is in DFXML; no banner
327 lock();
328 if(banner_displayed==false){
329 (*out) << utf8_banner;
330 banner_displayed=true;
331 }
332 unlock();
333 }
334
dfxml_write_hashes(std::string hex_hashes[],int indent)335 void file_data_hasher_t::dfxml_write_hashes(std::string hex_hashes[],int indent)
336 {
337 for(int i=0;i<NUM_ALGORITHMS;i++){
338 if(hashes[i].inuse){
339 for(int j=0;j<indent;j++){
340 this->dfxml_hash << ' ';
341 }
342 this->dfxml_hash << "<hashdigest type='" << makeupper(hashes[i].name) << "'>"
343 << hex_hashes[i] <<"</hashdigest>\n";
344 }
345 }
346 }
347
348
compute_dfxml(bool known_hash,const hash_context_obj * hc)349 void file_data_hasher_t::compute_dfxml(bool known_hash,const hash_context_obj *hc)
350 {
351 int indent=0;
352 if(hc && this->ocb->piecewise_size>0){
353 this->dfxml_hash << "<byte_run file_offset='" << hc->read_offset << "'"
354 << " len='" << hc->read_len << "'> \n";
355 indent=2;
356 }
357
358 this->dfxml_write_hashes(this->hash_hex,indent);
359 if(known_hash){
360 this->dfxml_hash << "<matched>1</matched>";
361 }
362 if(hc && this->ocb->piecewise_size){
363 this->dfxml_hash << "</byte_run>\n";
364 }
365 }
366
367 /**
368 * Return the number of entries in the hashlist that have used==0
369 * Optionally display them, optionally with additional output.
370 */
compute_unused(bool show_display,std::string annotation)371 uint64_t display::compute_unused(bool show_display, std::string annotation)
372 {
373 uint64_t count=0;
374
375 /* This is the only place we iterate through known.
376 * We make a list of all the filenames so we can get out of the lock fast.
377 */
378
379 std::vector<std::string> filelist;
380
381 lock();
382 for(hashlist::const_iterator i = known.begin(); i != known.end(); i++){
383 if((*i)->matched_file_number==0){
384 count++;
385 if (show_display || opt_verbose >= MORE_VERBOSE) {
386 filelist.push_back((*i)->file_name);
387 }
388 }
389 }
390 unlock();
391 for(std::vector<std::string>::const_iterator i = filelist.begin(); i!= filelist.end(); i++){
392 std::string line = *i + annotation;
393 writeln(out,line);
394 }
395 return count;
396 }
397
398
399 /**
400 * perform an audit
401 */
402
403
audit_check()404 int display::audit_check()
405 {
406 /* Count the number of unused */
407 match.unused = compute_unused(false,": Known file not used");
408 return (0 == this->match.unused &&
409 0 == this->match.unknown &&
410 0 == this->match.moved);
411 }
412
413
414
display_audit_results()415 void display::display_audit_results()
416 {
417 if (audit_check()==0) {
418 status("%s: Audit failed", progname.c_str());
419 set_return_code(status_t::status_EXIT_FAILURE);
420 }
421 else {
422 status("%s: Audit passed", progname.c_str());
423 }
424
425 if (opt_verbose) {
426 if(opt_verbose >= MORE_VERBOSE){
427 status(" Input files examined: %" PRIu64, this->match.total);
428 status(" Known files expecting: %" PRIu64, this->match.expect);
429 }
430 status(" Files matched: %" PRIu64, this->match.exact);
431 status("Files partially matched: %" PRIu64, this->match.partial);
432 status(" Files moved: %" PRIu64, this->match.moved);
433 status(" New files found: %" PRIu64, this->match.unknown);
434 status(" Known files not found: %" PRIu64, this->match.unused);
435 }
436 }
437
438
fmt_size(const file_data_t * fdt) const439 std::string display::fmt_size(const file_data_t *fdt) const
440 {
441 if (opt_display_size) {
442 std::stringstream ss;
443 ss.width(10);
444 ss.fill(' ');
445 ss << fdt->file_bytes;
446 ss.width(1);
447 ss << (opt_csv ? ", " : " ");
448 return ss.str();
449 }
450 return std::string();
451 }
452
453
454 // The old display_match_result from md5deep
md5deep_display_match_result(file_data_hasher_t * fdht,const hash_context_obj * hc)455 void display::md5deep_display_match_result(file_data_hasher_t *fdht,
456 const hash_context_obj *hc)
457 {
458 lock();
459 const file_data_t *fs = known.find_hash(opt_md5deep_mode_algorithm,
460 fdht->hash_hex[opt_md5deep_mode_algorithm],
461 fdht->file_name,
462 fdht->file_number);
463 unlock();
464
465 int known_hash = fs ? 1 : 0;
466 if ((known_hash && opt_mode_match) || (!known_hash && opt_mode_match_neg)) {
467 if(dfxml){
468 fdht->compute_dfxml(opt_show_matched || known_hash,hc);
469 return ;
470 }
471
472 std::stringstream line;
473
474 line << fmt_size(fdht);
475 if (opt_display_hash) {
476 line << fdht->hash_hex[opt_md5deep_mode_algorithm];
477 if (opt_csv) {
478 line << ",";
479 } else if (opt_asterisk) {
480 line << " *";
481 } else {
482 line << " ";
483 }
484 }
485
486 if (opt_show_matched) {
487 if (known_hash && (opt_mode_match)) {
488 line << fdht->file_name << " matched " << fs->file_name;
489 }
490 else {
491 line << fdht->file_name << " does NOT match";
492 }
493 }
494 else{
495 line << fdht->file_name;
496 if(piecewise_size) {
497 line << " offset " << hc->read_offset << "-" << hc->read_offset + hc->read_len - 1;
498 }
499 }
500 writeln(out,line.str());
501 }
502 }
503
504 /* The original display_match_result from md5deep.
505 * This should probably be merged with the function above.
506 * This function is very similar to audit_update(), which follows
507 */
display_match_result(file_data_hasher_t * fdht,const hash_context_obj * hc)508 void display::display_match_result(file_data_hasher_t *fdht,const hash_context_obj *hc)
509 {
510 file_data_t *matched_fdt = NULL;
511 int should_display = (primary_match_neg == primary_function);
512
513 lock(); // protects the search and the printing
514 hashlist::searchstatus_t m = known.search(fdht,
515 &matched_fdt,
516 opt_case_sensitive);
517 unlock();
518
519 std::stringstream line1;
520
521 switch(m){
522 // If only the name is different, it's still really a match
523 // as far as we're concerned.
524 case hashlist::status_file_name_mismatch:
525 case hashlist::status_match:
526 should_display = (primary_match_neg != primary_function);
527 break;
528
529 case hashlist::status_file_size_mismatch:
530 line1 << fmt_filename(fdht) << ": Hash collision with " << fmt_filename(matched_fdt);
531 writeln(&std::cerr,line1.str());
532 break;
533
534 case hashlist::status_partial_match:
535 line1 << fmt_filename(fdht) << ": partial hash match with " << fmt_filename(matched_fdt);
536 writeln(&std::cerr,line1.str());
537 break;
538
539 default:
540 break;
541 }
542 if (should_display) {
543 std::stringstream line2;
544
545 if (opt_display_hash){
546 display_hash_simple(fdht,hc);
547 }
548 else {
549 line2 << fmt_filename(fdht);
550 if (opt_show_matched && primary_match == primary_function) {
551 line2 << " matches ";
552 if (NULL == matched_fdt) {
553 line2 << "(unknown file)";
554 } else {
555 line2 << fmt_filename(matched_fdt);
556 }
557 }
558 writeln(out,line2.str());
559 }
560 }
561 }
562
563
564 /**
565 * Called after every file is hashed by display_hash
566 * when primary_function==primary_audit
567 * Records every file seen in the 'seen' structure, referencing the 'known' structure.
568 */
569
audit_update(file_data_hasher_t * fdht)570 int display::audit_update(file_data_hasher_t *fdht)
571 {
572 file_data_t *matched_fdht=0;
573 lock(); // protects the search and the printing
574 hashlist::searchstatus_t m = known.search(fdht,
575 &matched_fdht,
576 opt_case_sensitive);
577 unlock();
578 std::string line;
579 switch(m){
580 case hashlist::status_match:
581 case hashlist::searchstatus_ok:
582 this->match.exact++;
583 if (opt_verbose >= INSANELY_VERBOSE) {
584 line = fmt_filename(fdht) + ": Ok";
585 }
586 break;
587 case hashlist::status_no_match:
588 this->match.unknown++;
589 if (opt_verbose >= MORE_VERBOSE) {
590 line = fmt_filename(fdht) + ": No match";
591 }
592 break;
593 case hashlist::status_file_name_mismatch:
594 this->match.moved++;
595 if (opt_verbose >= MORE_VERBOSE) {
596 line = fmt_filename(fdht) + ": Moved from " + fmt_filename(matched_fdht);
597 }
598 break;
599 case hashlist::status_partial_match:
600 case hashlist::status_file_size_mismatch:
601 this->match.partial++;
602 // We only record the hash collision if it wasn't anything else.
603 // At the same time, however, a collision is such a significant
604 // event that we print it no matter what.
605 line = fmt_filename(fdht) + ": Hash collision with " + fmt_filename(matched_fdht);
606 }
607 if(line.size()>0){
608 writeln(&std::cout,line);
609 }
610 return FALSE;
611 }
612
613 /**
614 * Examines the hash table and determines if any known hashes have not
615 * been used or if any input files did not match the known hashes. If
616 * requested, displays any unused known hashes. Returns a status variable
617 */
finalize_matching()618 void display::finalize_matching()
619 {
620 /* Could the total matched */
621 lock();
622 uint64_t total_matched = known.total_matched();
623 unlock();
624
625 if (total_matched != known_size())
626 {
627 // were there any unmatched?
628 return_code.add(status_t::STATUS_UNUSED_HASHES);
629 }
630
631 // RBF - Implement check for Input Did not Match
632 /*
633 if (this->match.unknown)
634 {
635 return_code.add(status_t::STATUS_INPUT_DID_NOT_MATCH);
636 }
637 */
638
639 if (mode_not_matched)
640 {
641 // should we display those that were not matched?
642 compute_unused(true,"");
643 }
644 }
645
646
647 mutex_t display::portable_gmtime_mutex;
648
portable_gmtime(struct tm * my_time,const timestamp_t * t)649 struct tm *display::portable_gmtime(struct tm *my_time,const timestamp_t *t)
650 {
651 memset(my_time,0,sizeof(*my_time)); // clear it out
652 #ifdef HAVE_GMTIME_R
653 gmtime_r(t,my_time);
654 return my_time;
655 #endif
656 #ifdef HAVE__GMTIME64
657 // This is not threadsafe, hence the lock
658 portable_gmtime_mutex.lock();
659 *my_time = *_gmtime64(t);
660 portable_gmtime_mutex.unlock();
661
662 //we tried this:
663 //_gmtime64_s(&fdht->timestamp,&my_time);
664 //but it had problems on mingw64
665 return my_time;
666 #endif
667 #if !defined(HAVE__GMTIME64) && !defined(HAVE_GMTIME_R)
668 #error This program requires either _gmtime64 or gmtime_r for compilation
669 #endif
670 }
671
672 /* The old display_hash from the md5deep program, with modifications
673 * to build the line before outputing it.
674 *
675 * needs hasher because of triage mode
676 */
677
md5deep_display_hash(file_data_hasher_t * fdht,const hash_context_obj * hc)678 void display::md5deep_display_hash(file_data_hasher_t *fdht,const hash_context_obj *hc)
679 {
680 /**
681 * We can't call display_size here because we don't know if we're
682 * going to display *anything* yet. If we're in matching mode, we
683 * have to evaluate if there was a match first.
684 */
685 if (opt_mode_match || opt_mode_match_neg){
686 md5deep_display_match_result(fdht,hc);
687 return;
688 }
689
690 if(this->dfxml){
691 fdht->compute_dfxml(opt_show_matched,hc);
692 return;
693 }
694
695 std::stringstream line;
696
697 if (mode_triage) {
698 line << fdht->triage_info << "\t";
699 }
700 line << fmt_size(fdht) << fdht->hash_hex[opt_md5deep_mode_algorithm];
701
702 if (mode_quiet){
703 line << " ";
704 }
705 else {
706 if ((fdht->ocb->piecewise_size) || (fdht->is_stdin()==false)) {
707 if (mode_timestamp) {
708 struct tm my_time;
709 portable_gmtime(&my_time,&fdht->ctime);
710 char time_str[MAX_TIME_STRING_LENGTH];
711
712 // The format is four digit year, two digit month,
713 // two digit hour, two digit minute, two digit second
714 strftime(time_str, sizeof(time_str), "%Y:%m:%d:%H:%M:%S", &my_time);
715 line << (opt_csv ? ",":" ") << time_str;
716 }
717 if (opt_csv) {
718 line << ",";
719 } else if (opt_asterisk) {
720 line << " *";
721 } else if (mode_triage) {
722 line << "\t";
723 } else {
724 line << " ";
725 }
726 line << fmt_filename(fdht);
727 }
728 }
729 if(hc && fdht->ocb->piecewise_size > 0){
730 uint64_t len = (hc->read_offset+hc->read_len-1);
731 if(hc->read_offset==0 && hc->read_len==0) len=0;
732 line << " offset " << hc->read_offset << "-" << len;
733 }
734 writeln(out,line.str());
735 }
736
737
738 /*
739 * Externally called to display a simple hash
740 */
display_hash_simple(file_data_hasher_t * fdht,const hash_context_obj * hc)741 void display::display_hash_simple(file_data_hasher_t *fdht,
742 const hash_context_obj *hc)
743 {
744 if (this->dfxml)
745 {
746 fdht->compute_dfxml(opt_show_matched,hc);
747 return;
748 }
749
750 /* Old comment:
751 * In piecewise mode the size of each 'file' is the size
752 * of the block it came from. This is important when doing an
753 * audit in piecewise mode. In all other cases we use the
754 * total number of bytes from the file we *actually* read
755 *
756 * New comment:
757 * read_len is always correct.
758 * In piecewise mode its the size of the piece,
759 * In normal mode it's the size of the file.
760 *
761 * NOTE: Ignore the warning in the format when running on mingw with GCC-4.3.0
762 * see http://lists.gnu.org/archive/html/qemu-devel/2009-01/msg01979.html
763 *
764 */
765
766 display_banner_if_needed();
767 std::stringstream line;
768
769 line << hc->read_len << ",";
770
771 for (int i = 0 ; i < NUM_ALGORITHMS ; ++i)
772 {
773 if (hashes[i].inuse)
774 {
775 line << fdht->hash_hex[i] << ",";
776 }
777 }
778 line << fmt_filename(fdht);
779 if(fdht->ocb->piecewise_size > 0){
780 std::stringstream ss;
781 uint64_t len = (hc->read_offset+hc->read_len-1);
782 if(hc->read_offset==0 && hc->read_len==0) len=0;
783 line << " offset " << hc->read_offset << "-" << len;
784 }
785 writeln(out,line.str());
786 }
787
788
789 /**
790 * Called by hash() in hash.c when the hashing operation is complete.
791 * Display the hash and perform any auditing steps.
792 */
display_hash(file_data_hasher_t * fdht,const hash_context_obj * hc)793 void display::display_hash(file_data_hasher_t *fdht,const hash_context_obj *hc)
794 {
795 switch (primary_function) {
796 case primary_compute:
797 display_hash_simple(fdht,hc);
798 break;
799 case primary_match:
800 case primary_match_neg:
801 display_match_result(fdht,hc);
802 break;
803 case primary_audit:
804 audit_update(fdht);
805 break;
806 }
807 }
808
809
810
dfxml_startup(int argc,char ** argv)811 void display::dfxml_startup(int argc,char **argv)
812 {
813 if(dfxml){
814 lock();
815 dfxml->push("dfxml",
816 "\n xmlns='http://www.forensicswiki.org/wiki/Category:Digital_Forensics_XML' "
817 "\n xmlns:deep='http://md5deep.sourceforge.net/md5deep/' "
818 "\n xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
819 "\n xmlns:dc='http://purl.org/dc/elements/1.1/' "
820 "version='1.0'");
821 dfxml->push("deep:configuration");
822 dfxml->push("algorithms");
823 for(int i=0;i<NUM_ALGORITHMS;i++){
824 dfxml->make_indent();
825 dfxml->printf("<algorithm name='%s' enabled='%d'/>\n",
826 hashes[i].name.c_str(),hashes[i].inuse);
827 }
828 dfxml->pop(); // algorithms
829 dfxml->pop(); // configuration
830 dfxml->push("metadata", "");
831 dfxml->xmlout("dc:type","Hash List","",false);
832 dfxml->pop();
833 dfxml->add_DFXML_creator(PACKAGE_NAME,PACKAGE_VERSION,XML::make_command_line(argc,argv));
834 unlock();
835 }
836 }
837
dfxml_timeout(const std::string & tag,const timestamp_t & val)838 void display::dfxml_timeout(const std::string &tag,const timestamp_t &val)
839 {
840 char buf[256];
841 struct tm tm;
842 strftime(buf,sizeof(buf),"%Y-%m-%dT%H:%M:%SZ",portable_gmtime(&tm,&val));
843 dfxml->xmlout(tag,buf);
844 }
845
dfxml_write(file_data_hasher_t * fdht)846 void display::dfxml_write(file_data_hasher_t *fdht)
847 {
848 if(dfxml){
849 std::string attrs;
850 if(opt_verbose && fdht->workerid>=0){
851 std::stringstream ss;
852 ss << "workerid='" << fdht->workerid << "'";
853 attrs = ss.str();
854 }
855 lock();
856 dfxml->push("fileobject",attrs);
857 dfxml->xmlout("filename",fdht->file_name);
858 dfxml->xmlout("filesize",(int64_t)fdht->stat_bytes);
859 if(fdht->mtime) dfxml_timeout("mtime",fdht->mtime);
860 if(fdht->ctime) dfxml_timeout("ctime",fdht->ctime);
861 if(fdht->atime) dfxml_timeout("atime",fdht->atime);
862 dfxml->writexml(fdht->dfxml_hash.str());
863 dfxml->pop();
864 unlock();
865 }
866 }
867
dfxml_shutdown()868 void display::dfxml_shutdown()
869 {
870 if(dfxml){
871 lock();
872 dfxml->add_rusage();
873 dfxml->pop(); // outermost
874 dfxml->close();
875 delete dfxml;
876 dfxml = 0;
877 unlock();
878 }
879 }
880
881