/* Secure FILL - by van Hauser / [THC], vh@reptile.rug.ac.be
 *
 * Secure FILL overwrites all available free diskspace by creating a file,
 * wiping all free diskspace it gets and finally deleting the file.
 * The option -s does 2 overwritings.
 * The option -s twice does a real security wipe for 39 times, flushing
 * the caches after every write. The wipe technique was proposed by Peter
 * Gutmann at Usenix '96 and includes 10 random overwrites plus 29 special
 * defined characters. Take a look at the paper of him, it's really worth
 * your time.
 *
 * Advice : use always the options -ssv
 *
 * Read the manual for limitations.
 * Don't make this program suid/sgid !
 * Compiles clean on OpenBSD, Linux, Solaris and AIX
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <signal.h>

#define DIR_SEPERATOR '/'
#define FLUSH sync();

char *VERSION = "v1.8 by van Hauser / THC  <vh@reptile.rug.ac.be>  http://r3wt.base.org";
char *prog;
int verbose = 0;
char newname[1024];
int file;
FILE *f;

void help() {
    printf("%s %s\n\n",prog, VERSION);
    printf("Syntax: %s [-ssv] directory\n\n", prog);
    printf("%s does a secure overwrite of the free diskspace area in the directory.\n", prog);
    printf("Option -ss does real secure overwrite (39 times). -v is verbose mode.\n");
    exit(1);
}

int smash_it(char *filename, int mode) 
{
    unsigned char write_modes[27][3] = {
        {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"},
        {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"},
        {"\x33\x33\x33"}, {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"},
        {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"}, {"\xaa\xaa\xaa"},
        {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"}, {"\xdd\xdd\xdd"}, {"\xee\xee\xee"},
        {"\xff\xff\xff"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"},
        {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"}
    };

    unsigned char std_array[3] = "\xff\xff\xff";
    int turn;
    unsigned char ch;

/* open the file, get the filesize, calculate the numbers of needed writings */
    if (verbose) printf("Creating %s ", filename);
    if ((file = open(filename, O_RDWR | O_EXCL | O_CREAT, 00600 )) < 0)
        return 1;
    if ((f = fdopen(file, "r+b")) == NULL)
        return 1;

/* do the overwriting stuff */
    if (mode != 0) { 
        for (turn=0; turn<=35; turn++) {
            rewind(f);
            if ((mode == 1) && (turn > 0)) break;
            if ((turn>=5) && (turn<=31)) 
               do {} while (fwrite(&write_modes[turn-5][0], 1, 3, f) == 3);
            else {
               do {
                   ch = (unsigned char) (256.0*rand()/(RAND_MAX+1.0));
               } while (fwrite(&ch, 1, 1, f) == 1);
            }
            fflush(f);
            fsync(file);
            FLUSH   
            if (verbose)
                printf("*");
        }
        rewind(f); 
    }

    do {} while (fwrite(std_array, 1, 3, f) == 3);

    if (verbose)
        printf("*");

/* Soft Flush -> Write buffered data */
    fflush(f);
    fsync(file);
    (void) fclose(f);
/* Hard Flush -> Force cached data to be written to disk - the defines above! */
    FLUSH
    if ((file = open(filename, O_WRONLY | O_TRUNC)) >= 0)
	close(file);

    return 0;
}

void cleanup(int signo) {
    fprintf(stderr,"Terminated by signal. Clean exit.\n");
    if (f) {
        fflush(f);
        fsync(file);
        (void) fclose(f);
        if (unlink(newname) != 0 )
    	    fprintf (stderr,"Warning: Can't remove %s!\n",newname);
    }
    if (file >=0)
        close(file);
    FLUSH
    fflush(stdout);
    fflush(stderr);
    exit(1);
}

int main (int argc, char *argv[])
{
    int result;
    int secure = 0;
    int loop;
    int turn;
    int counter;
    struct stat filestat;

    if (getuid() != 0)
        fprintf(stderr,"Warning: you are not root. You might not be able to wipe the whole filesystem.\n");

    prog = argv[0];
    if (argc==1)
        help();

    while (1) {
        result = getopt(argc, argv, "sSvV");
        if (result<0) break;
        switch (result) {
	    case 's' :
            case 'S' : secure++;
                       break;
	    case 'v' :
            case 'V' : verbose++;
                       break;
            default :  help();        
        }
    }
    loop = optind;
    if (loop >= argc)
	help();
    (void) setvbuf(stdout, NULL, _IONBF, 0);

    do {
       strncpy(newname, argv[loop], sizeof(newname));    
       if (opendir(newname) == NULL) {
           fprintf(stderr,"%s : not a directory\n",newname);
       } else {

/* Generate random unique name for tempfile */
	    srand(getpid()+getuid());

            if (newname[strlen(newname)-1] != DIR_SEPERATOR) {
               result = strlen(newname);
               newname[result] = DIR_SEPERATOR;
               newname[result+1] = 0;
	    }    

	    turn = 0; result = 0;
            (void)strcat(newname, "oooooooo.ooo");
            result = stat(newname, &filestat);

            while ((result >= 0)&&(turn<=250)) {
                for (counter=strlen(newname)-1; (newname[counter] != DIR_SEPERATOR); counter--) 
                    if (newname[counter] != '.') 
                       newname[counter] = 97+(int) (27.0*rand()/(RAND_MAX+1.0));
		result = stat(newname, &filestat);
		turn++;
            };
            if (result>=0) fprintf(stderr,"%s : couldn't find a free filename\n",argv[loop]);
            else {
               if (verbose)
                   printf("Wiping now ...\n");

               signal(SIGINT, cleanup);
               signal(SIGTERM, cleanup);
               signal(SIGHUP, cleanup);

               result = smash_it(newname, secure);
               switch (result) {
                   case 0 : if (verbose) printf(" Finished\n");
                            break;
                   case 1 : fprintf(stderr,"%s : no write permission. ", argv[loop]);
			    perror("");
                            break;
                   default: fprintf(stderr,"Unknown error\n");
               }
               (void) unlink(newname);
            }
       }

       loop++;
    } while (loop<argc);
}
