/*
# The software provided here is released by the National
# Institute of Standards and Technology (NIST), an agency of
# the U.S. Department of Commerce, Gaithersburg MD 20899,
# USA.  The software bears no warranty, either expressed or
# implied. NIST does not assume legal liability nor
# responsibility for a User's use of the software or the
# results of such use.
# 
# Please note that within the United States, copyright
# protection, under Section 105 of the United States Code,
# Title 17, is not available for any work of the United
# States Government and/or for any works created by United
# States Government employees. User acknowledges that this
# software contains work which was created by NIST employees
# and is therefore in the public domain and not subject to
# copyright.  The User may use, distribute, or incorporate
# this software provided the User acknowledges this via an
# explicit acknowledgment of NIST-related contributions to
# the User's work. User also agrees to acknowledge, via an
# explicit acknowledgment, that any modifications or
# alterations have been made to this software before
# redistribution.
*/
# define O_LARGEFILE 0
# include <stdlib.h>
# include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
# include <time.h>



# include <sys/ioctl.h>
# include <linux/fs.h>

off_t get_drive_size (int fd)
{
	off_t sectors;
	int	code;


	code = ioctl (fd,BLKGETSIZE,&sectors);
	/*
	printf ("D R I V E    S I Z E: %lld\n",(long long) sectors);
	*/

	if (code) {
		printf("ioctl(BLKGETSIZE) status\t= %d\n", code);
		return (off_t) 0;
	}
	return sectors;
}

show_progress (time_t from, long long at, long long n)
{
	long long	one_pc = n/100,pc;
	static int first = 1;
	time_t		till; /* time program finished (actually time log_close called) */
	unsigned long	et, /* elapsed time (program run time: seconds of wall clock) */
			tmin, /* total minutes running */
			min, /* minutes for elapsed time after whole hours deducted */
			sec, /* seconds of elapsed run time after hours & minutes */
			hours; /* hours of elapsed time */

   

	if (((at%one_pc) == 0) && ((at/one_pc) < 9)){
		pc = at/one_pc;
		time(&till); /* get current time */
		et = till - from; /* elapsed time in seconds */
		tmin = et/60; /* elapsed time in minutes */
		sec = et%60; /* fraction of last minute in seconds */
		hours = tmin/60; /* hours elapsed time */
		min = tmin%60; /* fraction of last hour in whole minutes */
		printf ("%3lld %% at sector %12lld of %12lld ",pc,at,n);
		printf ("elapsed time %lu:%lu:%lu\n",hours,min,sec);
	}
	else if ((at%(one_pc*10)) == 0){
		pc = at/(one_pc);
		time(&till); /* get current time */
		et = till - from; /* elapsed time in seconds */
		tmin = et/60; /* elapsed time in minutes */
		sec = et%60; /* fraction of last minute in seconds */
		hours = tmin/60; /* hours elapsed time */
		min = tmin%60; /* fraction of last hour in whole minutes */
		printf ("%3lld %% at sector %12lld of %12lld ",pc,at,n);
		printf ("elapsed time %lu:%lu:%lu\n",hours,min,sec);
	}
	return;
}


/*****************************************************************
Compute elapsed time and close a log file
*****************************************************************/
void log_close (FILE *log, /* log file */
		time_t from /* time program started running */)
{
	time_t		till; /* time program finished (actually time log_close called) */
	unsigned long	et, /* elapsed time (program run time: seconds of wall clock) */
			tmin, /* total minutes running */
			min, /* minutes for elapsed time after whole hours deducted */
			sec, /* seconds of elapsed run time after hours & minutes */
			hours; /* hours of elapsed time */

	time(&till); /* get current time */
	et = till - from; /* elapsed time in seconds */
	tmin = et/60; /* elapsed time in minutes */
	sec = et%60; /* fraction of last minute in seconds */
	hours = tmin/60; /* hours elapsed time */
	min = tmin%60; /* fraction of last hour in whole minutes */
   
	fprintf (log,"run start %s",ctime(&from));
	fprintf (log,"run finish %s",ctime(&till));

	fprintf (log,"elapsed time %lu:%lu:%lu\n",hours,min,sec);
}

/***** Author: Dr. James R. Lyle, NIST/SDCT/SQG ****/

/*
CMD: ransum TTT HHH UUU /dev/sdb DF fff
Case: TTT
Host: HHH
User: UUU
Device: /dev/sdb
Label: DF
Log file: fff

Analyze a drive to determine what areas have been overwritten.
Assume the following steps:
	1. diskwipe
	2. do something
	3. ransum
The ransum output identifies areas on the drive that have changed.
The output is a list of sector ranges classified as either
overwritten or unchanged.
*/

char *sccs_id = "@(#) ransum.c Version 1.3 created 03/29/13 at 13:34:55";

int init (int np, char *p[],FILE *f)
{
	long long test;
	int	i;

	if (np != 7){
		f = stderr;
		fprintf (f,"Missing or extra parameter\n");
		fprintf (f,"CMD: ");
		for (i = 0 ; i < np; i++) fprintf (f," %s",p[i]);
		fprintf (f,"\n");
		fprintf (f,"Usage: ransum case host user {device|-} label {log_file|-}\n");
		exit (1);
	}
	if (f == NULL){
		printf ("%s Failed to open log file: %s\n",p[0],p[6]);
		exit (1);
	}
	fprintf (f,"Starting Analysis ...\n");
	 printf ("Output to %s\n",p[6][0] == '-'?"stdout":p[6]);
	if (sizeof(test) != 8){
		f = stderr;
		fprintf (f,"Data type too small ");
		fprintf (f,"long long is %lu\n",sizeof(test));
		fprintf (f,"long is %lu\n",sizeof(long));
		exit(1);
	}
	if (sccs_id[0] == '%') fprintf (f,"Development version of %s ",p[0]);
	fprintf (f,"%s\n",sccs_id);
	fprintf (f,"Compiled with gcc version %s on %s at %s\n",
		__VERSION__,__DATE__,__TIME__);
	fprintf (f,"CMD: ");
	for (i = 0 ; i < np; i++) fprintf (f," %s",p[i]);
	fprintf (f,"\n");

	fprintf (f,"Case: %s\n",p[1]);
	fprintf (f,"Host: %s\n",p[2]);
	fprintf (f,"User: %s\n",p[3]);
	fprintf (f,"Device: %s%s\n",p[4],(p[4][0] == '-')?" (stdin)":"");
	fprintf (f,"Label: %s\n",p[5]);
	fprintf (f,"Output: %s\n",p[6][0] == '-'?"stdout":p[6]);
	fflush (f);
	return 0;
}

# define NL 300
# define FILL_START 27

main (int np, char *p[]){

	long long test, count[256],ascii_count[256],total = 0,sectors,more,
		n_printable = 0,at = 0,drive_size;
	int	k,i,n,nnl,seen = 0,line = 0, max_line = 60;
	int	trace = 0;
	int	alt = 0,printable,input;
	int is_other_fill;
	unsigned char buff[512],fill_byte,other_fill,fill_seen;
	unsigned int fill_value;
	FILE *f = stdout;
	time_t		from; /* time program started */
	enum state_is {init_state, fill_state, wipe_state} state = init_state, this_sector;
	typedef struct {
		enum state_is	state;
		long long	from;
		long long	to; } range_rec;
	range_rec fill_list[NL];
	long long n_list = 0, n_other_fill = 0;

	if (np == 7){
		if (p[6][0] == '-') f = stdout;
		else f = fopen (p[6],"w");
	}
	else f = stderr;

	init (np,p,f);
	input = 0;
	if (p[4][0] != '-'){ /* input is a file to open */
		input = open (p[4],O_RDONLY);
		if (input == -1){
			fprintf (stderr,"Unable to open file %s: ",p[4]);
			perror(NULL);
			exit(1);
		}
	}
	time(&from); /* get current time */
	drive_size = (long long) get_drive_size (input);
	for (i = 0; i < 256; i++) count[i] = 0;
	for (i = 0; i < 256; i++) ascii_count[i] = 0;
	sscanf (p[5],"%2x",&fill_value);
	fill_byte = fill_value;
	while (n=read (input,buff,512)){
		printable = 0;
		this_sector = fill_state;
		for (i = FILL_START; i < n; i++){
			if ((buff[i] != fill_byte)) {
				this_sector = wipe_state;
				break;
			}
		}
		if (this_sector != state){
			if (state != init_state){
				fill_list[n_list].to = at - 1;
				n_list++;
				if (n_list >= NL){
					fprintf (stderr,"Too many fill/wipe runs at sector %lld\n",at);
					exit(1);
				}
			}
			state = this_sector;
			fill_list[n_list].state = this_sector;
			fill_list[n_list].from = at;
		}
		if (state == wipe_state){
			is_other_fill = 1;
			for (i=0; i < 24 ; i++){
				if ((i==5) || ( i==9)){
					if (buff[i] != '/'){
						is_other_fill = 0;
						break;
					}
				}
				else if (i == 12){
					if (buff[i] != ' '){
						is_other_fill = 0;
						break;
					}
				}
				else if (!isdigit(buff[i])){
					is_other_fill = 0;
					break;
				}
			}
			if (is_other_fill){
				other_fill = buff[FILL_START];
				for (i = FILL_START; i < 512; i++){
					if (buff[i] != other_fill){
						is_other_fill = 0;
						break;
					}
				}
			}
			if (is_other_fill){
				n_other_fill++;
				fill_seen = other_fill;
			}
		}
		at++;
		show_progress (from,at,drive_size);
	}
	fill_list[n_list].to = at - 1;
	n_list++;
	fprintf (f,"Run List\n");
	fprintf (f,"%12s %16s %10s\n","First Sector","Last Sector","State");
	for (i = 0; i < n_list; i++){
		fprintf (f,"%12lld -- %12lld   %s\n",
			fill_list[i].from, fill_list[i].to, (fill_list[i].state == wipe_state)?"Overwritten":"Unchanged");
	}
	if (n_other_fill){
		fprintf (f,"WARNING at least one other fill value (%02X) seen %lld times\n",fill_seen,n_other_fill);
		fprintf (f,"Entered label (%s) may be incorrect\n",p[5]);
	}
	fprintf (f,"End List\n");
	log_close (f,from);
	fprintf (f,"... done\n");
}
