149 lines
3.4 KiB
C
149 lines
3.4 KiB
C
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
|
|
void eat(int fd) {
|
|
char buf[0x10000];
|
|
|
|
off_t pos = lseek(fd, 0, SEEK_CUR);
|
|
errno = 0; // no errors :-)
|
|
|
|
off_t eatpos = 0;
|
|
|
|
while (1) {
|
|
ssize_t s = -1;
|
|
|
|
while (s == -1) {
|
|
errno = 0;
|
|
s = read(fd, buf, sizeof(buf));
|
|
|
|
if (s == -1 && pos != -1) { // position unspecified in case of error
|
|
fprintf(stderr, "munchcat: read(): %s\n", strerror(errno));
|
|
usleep(100 * 1000); // save some cpu if theres error loop forever
|
|
lseek(fd, pos + s, SEEK_SET);
|
|
}
|
|
}
|
|
|
|
if (s == 0)
|
|
break;
|
|
|
|
{
|
|
char *p = buf;
|
|
ssize_t ps = s;
|
|
|
|
while (ps > 0) {
|
|
errno = 0;
|
|
ssize_t wrc = write(STDOUT_FILENO, p, ps);
|
|
|
|
if (wrc == -1) {
|
|
fprintf(stderr, "munchcat: write(): %s\n", strerror(errno));
|
|
usleep(100 * 1000); // save some cpu if theres error loop forever
|
|
} else {
|
|
ps -= wrc;
|
|
p += wrc;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pos != -1) {
|
|
off_t endpos = (pos + s) & ~(off_t)0xFFFF;
|
|
if (endpos > eatpos) {
|
|
errno = 0;
|
|
fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, eatpos, endpos - eatpos);
|
|
}
|
|
eatpos = endpos;
|
|
pos += s;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (pos != -1 && eatpos < pos) {
|
|
errno = 0;
|
|
fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, eatpos, pos - eatpos);
|
|
}
|
|
*/
|
|
|
|
ftruncate(fd, 0);
|
|
}
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
const char *files[256] = { "/dev/stdin", 0 };
|
|
|
|
{
|
|
bool optp = true;
|
|
|
|
for (const char **p = argv + 1, **f = files; *p != NULL; ++p) {
|
|
|
|
if ((*p)[0] != '-' || ((*p)[0] == '-' && (*p)[1] == '\0') || !optp) {
|
|
|
|
const char *file = *p;
|
|
|
|
if (!strcmp(*p, "-"))
|
|
file = "/dev/stdin";
|
|
|
|
if ((f - files) < sizeof(files) / sizeof(*files) - 1) {
|
|
*f++ = file;
|
|
} else {
|
|
fprintf(stderr, "munchcat: too many inputs\n");
|
|
return 1;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(*p, "--")) {
|
|
|
|
optp = false;
|
|
|
|
} else if (!strcmp(*p, "--help")) {
|
|
|
|
fprintf(stdout,
|
|
"Usage: munchcat [OPTION]... [FILE]...\n"
|
|
"Concatenate FILE(s) to standard output, EATING them in the process.\n"
|
|
"\n"
|
|
"With no FILE, or when FILE is -, read standard input.\n"
|
|
"\n"
|
|
" --help display this help and exit\n"
|
|
"\n"
|
|
"Examples:\n"
|
|
" munchcat the-entire-internet.sqlite Eat the entire internet.\n"
|
|
" munchcat /mnt/a/big.gz > /mnt/b/big.gz Move a file from one disk to another,\n"
|
|
" freeing up space as you go.\n"
|
|
"\n"
|
|
"WARNING: After processing by this program any files used as input will have\n"
|
|
"their contents destroyed. Kimapr.NET cannot be held liable for any damages\n"
|
|
"created by inappropriate usage of the munchcat program.\n"
|
|
"\n"
|
|
"WARNING: Content destruction will not occur if your filesystem does not support\n"
|
|
"sparse files. Regardless of sparse file support this program is not a\n"
|
|
"substitute for a proper secure erase mechanism. Kimapr.NET cannot be held\n"
|
|
"liable for any data leaks or other damage resulting from inappropriate usage of\n"
|
|
"the munchcat program for erasure of sensitive data.\n"
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const char **p = files; *p != NULL; ++p) {
|
|
int fd = open(*p, O_RDWR, 0);
|
|
|
|
if (fd == -1) {
|
|
fd = open(*p, O_RDONLY, 0);
|
|
}
|
|
|
|
if (fd == -1) {
|
|
fprintf(stderr, "munchcat: %s: %s\n", *p, strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
eat(fd);
|
|
}
|
|
|
|
return 0;
|
|
}
|