/* *---------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you * retain this notice you can do whatever you want with this stuff. If * we meet some day, and you think this stuff is worth it, you can buy * me a beer in return. * --------------------------------------------------------------------- * ______ _______ _ _________ * ( __ \ ( ___ )( ( /|( )\__ __/ * | ( \ )| ( ) || \ ( ||/ ) ( * | | ) || | | || \ | | | | * | | | || | | || (\ \) | | | * | | ) || | | || | \ | | | * | (__/ )| (___) || ) \ | | | * (______/ (_______)|/ )_) )_( * * _______ _______ _ _________ _______ * ( ____ )( ___ )( \ /|\__ __/( ____ \ * | ( )|| ( ) || \ ( | ) ( | ( |/ * | (____)|| (___) || \ | | | | | | * | _____)| ___ || (\ \) | | | | | * | ( | ( ) || | \ | | | | | * | ) | ) ( || ) \ |___) (___| (____|\ * |/ |/ \||/ \_)\_______/(_______/ * */ #define _XOPEN_SOURCE #include // isprint #include // errno #include // uint8_t #include // basename #include // fopen, fclose, fseek, ftell, fflush, fwrite #include // exit, system #include // strcomp #include // getopt static const char *__progname; const int slice = 4096; // read/write 4k at a time uint8_t Sflag; // sync uint8_t uflag; // unlink uint8_t vflag; // verbose uint8_t wflag; // wipe uint8_t zflag; // zero uint8_t pass = 0; uint8_t cpass = 1; // current pass uint8_t tpass = 0; static void usage() { fprintf(stderr, "usage: %s [-hnSuvz] [FILE1]... [FILE2]...\n", __progname); } unsigned long int _findsize(char file[]) { // opening the file in read mode FILE *fp = fopen(file, "r"); // checking if the file exist or not if (fp == NULL) { perror("fopen"); exit(EXIT_FAILURE); } // seek to the file end fseek(fp, 0L, SEEK_END); // calculating the size of the file unsigned long int fsize = ftell(fp); // closing the file fclose(fp); return fsize; } void progress(int i) { unsigned int c; if (i < 10) fprintf(stderr, " %i%% [", i); else if (i < 100) fprintf(stderr, " %i%% [", i); else fprintf(stderr, " %i%% [", i); for (c = 0; c <= (i / 2); c++) { fprintf(stderr, "#"); } while (c <= 50) { fprintf(stderr, "-"); c++; } fprintf(stderr, "]"); fprintf(stderr, "\r"); fflush(stderr); } int _shredit(char *file, char *dev, unsigned long int _size) { char buf[slice]; // store our data in a buffer FILE *dp; // the file stream for reading unsigned long int done; // number of bytes written uint8_t percent; // percentage finished unsigned long int _tsize = (_size); char *data; // data type /* hide the cursor */ if (vflag) system("tput civis"); /* Open the appropriate device file for reading */ if (strcmp(dev, "r") == 0) { dp = fopen("/dev/urandom", "r"); data = "random"; } else if (strcmp(dev, "z") == 0) { dp = fopen("/dev/zero", "r"); data = "000000"; } else { // function called incorrectly (shutting up the compiler) exit(1); } /* opening the device file failed */ if (dp == NULL) { perror("fopen"); exit(EXIT_FAILURE); } /* Open our file for writing */ FILE *fp = fopen(file, "w"); /* opening the file failed */ if (fp == NULL) { perror("fopen"); exit(EXIT_FAILURE); } while (_size > slice) { if (fread(&buf, 1, slice, dp) != slice) { perror("fread"); exit(EXIT_FAILURE); } if (fwrite(&buf, 1, slice, fp) != slice) { perror("fwrite"); exit(EXIT_FAILURE); } if (vflag) { done = _tsize - _size; percent = done * 100 / _tsize; fprintf(stderr, "%s: %s: pass %i/%i (%s)", __progname, file, cpass, tpass, data); progress(percent); } _size -= slice; } /* write the remaning bytes */ if (fread(&buf, 1, _size, dp) != _size) { perror("fread"); exit(EXIT_FAILURE); } if (fwrite(&buf, 1, _size, fp) != _size) { perror("fwrite"); exit(EXIT_FAILURE); } if (vflag) { done = _tsize - slice; fprintf(stderr, "%s: %s: pass %i/%i (%s)", __progname, file, cpass, tpass, data); progress(100); fprintf(stderr, "\n"); } /* flush and sync to disk */ if (Sflag) { if (vflag) fprintf(stderr, "Syncing to disk\n"); if (fflush(fp) != 0) { perror("fflush"); exit(EXIT_FAILURE); } if (fsync(fileno(fp)) != 0) { perror("fsync"); exit(EXIT_FAILURE); } } /* Put our cursor back */ if (vflag) system("tput cvvis"); // close streams if (fclose(dp) != 0) { perror("fclose"); exit(EXIT_FAILURE); } if (fclose(fp) != 0) { perror("fclose"); exit(EXIT_FAILURE); } return 0; } int _unlinkit(char *file) { if (unlink(file) == 0) { if (vflag) fprintf(stderr, "unlink: %s\n", file); return 0; } else { perror("unlink"); exit(EXIT_FAILURE); } } long parselong(char *buf) { errno = 0; char *endptr; int num = strtol(optarg, &endptr, 10); if (errno != 0) { perror("Error parsing integer from input: "); exit(EXIT_FAILURE); } else if (num == 0 && endptr == optarg) { fprintf(stderr, "Error: invalid input: %s\n", optarg); exit(EXIT_FAILURE); } else if (*endptr != '\0') { fprintf(stderr, "Error: invalid input: %s\n", optarg); exit(EXIT_FAILURE); } return num; } int main(int argc, char *argv[]) { unsigned int index; unsigned int c; __progname = basename(argv[0]); while ((c = getopt(argc, argv, "hSuvwzn:")) != -1) switch (c) { case 'h': usage(); return 0; break; case 'n': pass = parselong(optarg); break; case 'S': Sflag = 1; break; case 'u': uflag = 1; break; case 'v': vflag = 1; break; case 'w': wflag = 1; break; case 'z': zflag = 1; break; case '?': if (optopt == 'n') fprintf(stderr, "Option -%c requires an argument.\n", optopt); else if (isprint(optopt)) { usage(); exit(1); } default: usage(); } // no arguments supplied if (argv[optind] == NULL) { fprintf(stderr, "Mandatory argument(s) missing\n"); exit(1); } for (index = optind; index < argc; index++) { /* get the file size */ unsigned long int _size = _findsize(argv[index]); /* number of passes */ if (pass == 0) pass = 3; cpass = 1; // reset current pass to 1 for each file if (zflag) tpass = (pass + 1); // zero adds an extra pass to total else tpass = (pass); while (cpass <= pass) { _shredit(argv[index], "r", _size); cpass++; } /* final pass with zeros to cover our tracks somewhat */ if (zflag) { _shredit(argv[index], "z", _size); } /* remove the file */ _unlinkit(argv[index]); } }