296 lines
7.3 KiB
C
296 lines
7.3 KiB
C
/*
|
|
*----------------------------------------------------------------------
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* <jeang3nie@HitchHiker-Linux.org> 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 <ctype.h> // isprint
|
|
#include <errno.h> // errno
|
|
#include <stdint.h> // uint8_t
|
|
#include <libgen.h> // basename
|
|
#include <stdio.h> // fopen, fclose, fseek, ftell, fflush, fwrite
|
|
#include <stdlib.h> // exit, system
|
|
#include <string.h> // strcomp
|
|
#include <unistd.h> // 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]);
|
|
}
|
|
}
|