/* wipeit.c - Marcus Fritzsch - 20040309 - 0.1.6 */ /***history * 20040309 0031: start of development (0.1.0) * 20040312 0309: first working build (0.1.1) * 20040312 1654: added switches -a and -f (0.1.2) * 20040313 0249: fixed some minor bugs and help msg (0.1.3) * 20040315 0036: fixed bugs * altered handling of --ask and --force * started thinking of more verbose output ;o) * altered handling of ask and force option flags (0.1.4) * 20040315 2150: changed file handling from streams to fd's (0.1.5) * 20040507 2226: remove those termios thingies, cleanup (0.1.6) */ /***TODO * 20040312 1702: add reasonable comments! */ /* general headers */ #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include /* ************************************* some macros and defines */ #define VER "0.1.5" #define PGM "wipeit" #define FALSE ((signed char)0) #define TRUE ((signed char)1) #define BUFS 65536 /* 386+ Processors do this very fast!! */ #ifndef USR # define USR "m" #endif #ifndef MCH # define MCH "localhost" #endif /* critical error --> warn + exit */ #define wpt_cerror_m \ { \ perror (PGM); \ exit (EXIT_FAILURE); \ } /* ... */ #define wpt_perror_m perror (PGM); /* error through fprintf */ #define wpt_error_m(x) fprintf (stderr, "%s: %s\n", PGM, (x)) /* get opt-bits from misc */ /* 20040315: '<=' for verbose option */ #define optset_m(x) (((wpt_opts.misc&(x)) <= (x)) && ((wpt_opts.misc&(x)) > 0)) /* get verbose status */ #define optverbose_m (wpt_opts.misc & (OPTVERB1|OPTVERB2|OPTVERB3)) /* get times option */ #define opttimes_m (wpt_opts.times) /* options in wpt_opts.misc */ #define OPTVERB1 (1) #define OPTVERB2 (OPTVERB1 + 1) #define OPTVERB3 (OPTVERB2 + 1) #define OPTASK (1<<2) #define OPTFORCE (1<<3) /* ************************************* static variables */ static int errcnt; static char * errmsg[] = { "could not open file", "could not find file", "file is not a regular file", "file is not writeable", 0 /*@null@*/ }; /* ************************************* structs and enums */ typedef enum WPT_ERRT { NOTOPN = 0, /* could not open file */ NOTFND, /* could not find file */ NOTREG, /* file is not a regular file */ NOTWRT /* file is not writeable */ } wpt_err_t; typedef enum WPT_TYPE { WPT_TFRND = 1, /* fast random */ WPT_TRND, /* random (/dev/random) */ WPT_TZERO, /* all zero */ WPT_TZRND1, /* zero-frnd-zero-frnd... */ WPT_TZRND2 /* randomly switch between zero and frnd */ } wpt_type_t; typedef struct WPT_FILES { struct WPT_FINF { char * name; struct stat stat; } * files; /* filenames + stats */ size_t count; /* length of files */ } wpt_files_t; /* I do not need to typedef it, I only need it ones! */ struct WPT_OPTS { unsigned short times; /* times overwrites */ wpt_type_t type; /* how to wipe */ wpt_files_t files; /* files to wipe, last one is { 0, 0 }! */ /* 0000 0 0 00 * | force | ask | verbose 1-3 */ unsigned char misc; } wpt_opts; /* ************************************* prototypes */ void wpt_file_err (wpt_err_t err, const char * nam); void free (void * ptr); /*@modifies ptr@*/ void * wpt_malloc (size_t size); void * wpt_realloc (void * ptr, size_t size); int wpt_open (const char * name); void wpt_close (int f); void wpt_frnd (char * ptr, int sz); void wpt_rnd (char * ptr, int sz); void wpt_zero (char * ptr, int sz); void wpt_zrnd1 (char * ptr, int sz); void wpt_zrnd2 (char * ptr, int sz); int wpt_ask (const char * name); void wpt_set_term (); void wpt_wipe (struct WPT_FINF * finf); void wpt_help (); void wpt_version (); int wpt_get_stat (struct WPT_FINF * ptr); void wpt_parse_opts (int * argcp, char ** argv); void wpt_init (char ** argv); /* XXX: why do i have to do this?! */ extern int fsync (int); /* ************************************* routines */ void wpt_file_err (wpt_err_t err, const char * nam) { char msgbuf [1000]; snprintf (msgbuf, 999, "%s '%s'", errmsg[err], nam); if (errno != 0) perror (msgbuf); else fprintf (stderr, "%s\n", msgbuf); } void * wpt_malloc (size_t size) { void * t; if ((t = malloc (size)) != NULL) /*@out@*/ return t; wpt_cerror_m; return NULL; /*@null@*/ } void * wpt_realloc (void * ptr, size_t size) { if (size != 0) { if ((ptr = realloc (ptr, size)) != NULL) return ptr; } else { free (ptr); return NULL; /*@null@*/ } wpt_cerror_m; return NULL; /*@null@*/ } int wpt_open (const char * name) { int t; if ((t = open (name, O_WRONLY)) == -1) { wpt_file_err (NOTOPN, name); return -1; } flock (t, LOCK_EX); return t; } void wpt_close (int f) { flock (f, LOCK_UN); close (f); } void wpt_frnd (char * ptr, int sz) { int i; time_t t = time (NULL) & 0xFF; for (i = 0; i < sz; i++) ptr[i] = (rand () & 0xFF) ^ t; } void wpt_rnd (char * ptr, int sz) { /* ptr has to be allocated and will be filled with sz random-bytes */ int rf; int r; if ((rf = open ("/dev/random", O_RDONLY)) == -1) { fprintf (stderr, "could not open '/dev/random', using FRND\n"); wpt_frnd (ptr, sz); return; } flock (rf, LOCK_EX); r = read (rf, ptr, sz); flock (rf, LOCK_UN); close (rf); if (r != sz) { fprintf (stderr, "could not read from '/dev/random', using FRND\n"); wpt_frnd (ptr, sz); return; } } void wpt_zero (char * ptr, int sz) { /* ptr has to be allocated and will be filled with sz 0s */ memset (ptr, 0, sz); } void wpt_zrnd1 (char * ptr, int sz) { static int c = 0; if (c & 1) wpt_zero (ptr, sz); else wpt_frnd (ptr, sz); ++c; } void wpt_zrnd2 (char * ptr, int sz) { if (rand () & 1) wpt_zero (ptr, sz); else wpt_frnd (ptr, sz); } int wpt_ask (const char * name) { char * i = "\0\0\0"; fprintf (stderr, "Ready to wipe file '%s' (yes/no): ", name); i = fgets (i, 4, stdin); if (i == NULL) return FALSE; if (i [2] == '\n') i [2] = '\0'; if (strcasecmp (i, "yes") == 0) return TRUE; return FALSE; } void wpt_wipe (struct WPT_FINF * finf) { int i, j, c; int fil; void (*fptr)(char*, int); char buf[BUFS]; if (optset_m(OPTASK)) { if (wpt_ask (finf->name) == FALSE) return; } if ((fil = wpt_open (finf->name)) == -1) return; switch (wpt_opts.type) { case WPT_TRND: fptr = wpt_rnd; break; case WPT_TFRND: fptr = wpt_frnd; break; case WPT_TZERO: fptr = wpt_zero; break; case WPT_TZRND1: fptr = wpt_zrnd1; break; case WPT_TZRND2: fptr = wpt_zrnd2; break; default: fptr = wpt_zrnd2; } if (optset_m(OPTVERB2)) fprintf (stderr, "wiping %s: ", finf->name); for (c = 0; c < wpt_opts.times; c++) { if (optset_m(OPTVERB2)) fprintf (stderr, "%d ", c+1); lseek (fil, 0, 0); for (i = j = 0; i < finf->stat.st_size; i += j) { j = finf->stat.st_size - i; (*fptr) (buf, j > BUFS ? (j = BUFS) : j); write (fil, buf, j); } fsync (fil); } if (optset_m(OPTVERB2)) fprintf (stderr, "done\n"); wpt_close (fil); unlink (finf->name); } void wpt_help () { fprintf (stderr, "%s file1 file2 file3 ...\n\n", PGM); fprintf (stderr, "Overview of all options\n"); fprintf (stderr, "--verbose=N or -vN verbose, N might be 1..3 and is not mandatory\n"); fprintf (stderr, "--method=X or -m X method, X might be one of the following:\n" " RND or 1 random data (/dev/random)\n" " FRND or 2 fast random data (rand ())\n" " ZERO or 3 all zero bytes\n" " ZRND1 or 4 rnd+zero+rnd+zero...\n" " ZRND2 or 5 randomly switch zero and fast rnd data\n"); fprintf (stderr, "--count=N or -c N overwrite file N times\n"); fprintf (stderr, "--help or -h this help message\n"); fprintf (stderr, "--ask or -a ask before wiping anything\n"); fprintf (stderr, "--force or -f do _not_ ask before wiping\n"); fprintf (stderr, "--version show version and build information\n"); fprintf (stderr, "\ndefault values are: -a -c 7 -m ZRND2\n"); fprintf (stderr, "\nNOTE: use this program with caution, files wiped will be lost!!!\n\n"); exit (EXIT_SUCCESS); } void wpt_version () { fprintf (stderr,"%s %s build on %s at %s by %s@%s\n", __FILE__, VER, __DATE__, __TIME__, USR, MCH); exit (EXIT_SUCCESS); } int wpt_get_stat (struct WPT_FINF * ptr) { struct stat my_stat; int ret; ret = stat (ptr->name, &my_stat); if (ret == -1) { switch (errno) { case ENOTDIR: case ELOOP: case ENOMEM: case ENAMETOOLONG: case ENOENT: perror (ptr->name); break; default: wpt_file_err (NOTFND, ptr->name); } return FALSE; } if (!S_ISREG(my_stat.st_mode)) { wpt_file_err (NOTREG, ptr->name); return FALSE; } if ((my_stat.st_mode & S_IWUSR) == 0) { wpt_file_err (NOTWRT, ptr->name); return FALSE; } ptr->stat = my_stat; return TRUE; } void wpt_parse_opts (int * argcp, char ** argv) { int c, start; while (1) { int index, i, l, tmp; static struct option long_options[] = { { "count", 1, 0, 'c' }, { "method", 1, 0, 'm' }, { "help", 0, 0, 'h' }, { "version", 0, 0, '@' }, { "verbose", 2, 0, 'v' }, { "ask", 0, 0, 'a' }, { "force", 0, 0, 'f' }, { 0, 0, 0, 0 } }; c = getopt_long (*argcp, argv, "c:m:v::h@af", long_options, &index); if (c == -1) break; switch (c) { case 'c': wpt_opts.times = atoi (optarg); if (wpt_opts.times == 0) { if (strcmp (optarg, "NSA") == 0) wpt_opts.times = 7; else if (strcmp (optarg, "paranoid") == 0) wpt_opts.times = 35; else { wpt_error_m("error on processing option 'c'"); exit (2); } } break; case 'm': for (i = 0, l = strlen (optarg); i < l; i++) toupper (optarg[i]); if (strcmp (optarg, "FRND") == 0) wpt_opts.type = WPT_TFRND; else if (strcmp (optarg, "RND") == 0) wpt_opts.type = WPT_TRND; else if (strcmp (optarg, "ZERO") == 0) wpt_opts.type = WPT_TZERO; else if (strcmp (optarg, "ZRND1") == 0) wpt_opts.type = WPT_TZRND1; else if (strcmp (optarg, "ZRND2") == 0) wpt_opts.type = WPT_TZRND2; else if (isdigit (optarg [0])) wpt_opts.type = (wpt_type_t) (atoi (optarg) > 5 ? 5 : atoi (optarg)); else { wpt_error_m("error on processing option 'm'"); wpt_opts.type = WPT_TZRND2; } break; case 'h': wpt_help (); break; case '@': wpt_version (); break; case 'v': if (optarg) { tmp = atoi (optarg); if (tmp > 3) tmp = 3; wpt_opts.misc |= tmp; } else wpt_opts.misc |= 1; break; case 'a': wpt_opts.misc |= OPTASK; break; case 'f': wpt_opts.misc |= OPTFORCE; break; default: fprintf (stderr, "%s: unknown option %s use -h for help\n", PGM, argv [optind]); break; } } if (optset_m(OPTASK) && optset_m(OPTFORCE)) { wpt_opts.misc &= ~OPTFORCE; fprintf (stderr, "you specified an -a and -f option, every -f will be ignored!\n"); } start = optind; c = 0; wpt_opts.files.files = NULL; while (optind < *argcp) { wpt_opts.files.files = (struct WPT_FINF*) wpt_realloc ( wpt_opts.files.files, (c+1) * sizeof (struct WPT_FINF)); wpt_opts.files.files [c].name = (char*) wpt_malloc (strlen ( argv[optind]) + 1); /* strcpy: length is known and allocated */ strcpy (wpt_opts.files.files [c].name, argv[optind]); if (wpt_get_stat (&wpt_opts.files.files [c]) == FALSE) { free (wpt_opts.files.files [c].name); wpt_opts.files.files [c].name = NULL; } ++optind; ++c; wpt_opts.files.count = c; } if (wpt_opts.files.count == 0) { fprintf (stderr, "no files specified, use -h for help\n"); exit (EXIT_FAILURE); } } void wpt_init (char ** argv) { srand (time (NULL) + getpid ()); wpt_opts.times = 7; wpt_opts.type = WPT_TZRND2; wpt_opts.files.files = NULL; wpt_opts.files.count = 0; wpt_opts.misc = (unsigned char) 0; errcnt = 0; } int main (int argc, char ** argv) { int i; wpt_init (argv); wpt_parse_opts (&argc, argv); for (i = 0; i < wpt_opts.files.count; i++) { if (wpt_opts.files.files[i].name != NULL) { wpt_wipe (&wpt_opts.files.files[i]); free (wpt_opts.files.files[i].name); } } free (wpt_opts.files.files); return EXIT_SUCCESS; }