~nabijaczleweli/voreutils

d6ea570385ff22ce8bab260920f6bcb19e55363c — наб 25 days ago 305ccd6
Add shred (lol)
8 files changed, 222 insertions(+), 5 deletions(-)

M Makefile
M README.md
A cmd/shred.cpp
M include/vore-file
A man/shred.1
M man/stdbuf.1
M tests/sha1sum/test
A tests/shred
M Makefile => Makefile +0 -1
@@ 21,7 21,6 @@ LIBDIR ?= $(OUTDIR)lib/
MANDIR ?= $(OUTDIR)man/
HTMLMANDIR ?= $(OUTDIR)man-html/
VOREUTILS_LIB_PREFIX ?= /usr/lib/voreutils/
VOREUTILS_LIB_PREFIX ?= /usr/lib/x86_64-linux-gnu/voreutils/


AS_NEEDED := -Wl,--as-needed

M README.md => README.md +2 -1
@@ 83,7 83,7 @@ GNU coreutils provide the following 105 binaries, according to `dpkg -L coreutil
  [x] /usr/bin/sha256sum
  [x] /usr/bin/sha384sum
  [x] /usr/bin/sha512sum
  [ ] /usr/bin/shred
  [x] /usr/bin/shred – a perfunctory novote
  [ ] /usr/bin/shuf
  [ ] /usr/bin/sort
  [ ] /usr/bin/split


@@ 128,6 128,7 @@ notably `VOREUTILS_{VERSION,DATE}`, which are derived from git HEAD by default,
`OUTDIR` (and `{CMD,MAN,HTMLMAN}DIR`) where artifacts land, and
`OBJDIR` where intermediate objects land; these can all be set independently,
`SYMLINK`, if set to "y", will link binary altnames together symbolically.
`VOREUTILS_LIB_PREFIX` (`/usr/lib/voreutils/`) is the location of `libstdbuf`.

## Organisation
Who knows yet!

A cmd/shred.cpp => cmd/shred.cpp +119 -0
@@ 0,0 1,119 @@
// SPDX-License-Identifier: 0BSD


#include <errno.h>
#include <unistd.h>
#include <vore-file>
#include <vore-getopt>
#include <vore-optarg>
#include <vore-size>


using namespace std::literals;


#define USAGE "usage: %s [-fuvzx] [-s size] [--random-source=path] [--remove[=how]] [-n iterations] file...\n"


int main(int argc, char * const * argv) {
	std::optional<off_t> size;
	bool force{}, remove{}, verbose{};
	for(auto && [arg, val] : vore::opt::get{argc,
	                                        argv,
	                                        "fuvs:zn:x",
	                                        {{"force", no_argument, nullptr, 'f'},
	                                         {"remove", optional_argument, nullptr, 'u'},
	                                         {"verbose", no_argument, nullptr, 'v'},
	                                         {"size", required_argument, nullptr, 's'},
	                                         {"zero", no_argument, nullptr, 'z'},
	                                         {"random-source", required_argument, nullptr, 'r'},
	                                         {"iterations", required_argument, nullptr, 'n'},
	                                         {"exact", no_argument, nullptr, 'x'}}})
		switch(arg) {
			case 's':
				if(!(size = vore::parse_size<off_t>(argv[0], val)))
					return 1;
				break;
			case 'f':
				force = true;
				break;
			case 'u':
				remove = true;
				break;
			case 'v':
				verbose = true;
				break;
			case 'z':
			case 'r':
			case 'n':
			case 'x':
				break;
			default:
				std::fprintf(stderr, USAGE, argv[0]);
				return 1;
		}
	if(!argv[optind]) {
		std::fprintf(stderr, USAGE, argv[0]);
		return 1;
	}

	bool err{};
	for(auto file : vore::opt::args{argv + optind}) {
		vore::file::fd<true> fd{file, O_WRONLY | O_CLOEXEC};
		if(force && fd == -1 && errno == EACCES) {
			chmod(file, 0200);
			vore::file::fd<true>{file, O_WRONLY | O_CLOEXEC}.swap(fd);
		}
		if(fd == -1) {
			std::fprintf(stderr, "%s: %s: %s\n", argv[0], file, std::strerror(errno));
			err = true;
			continue;
		}
		if(fcntl(fd, F_GETFL) & O_APPEND) {
			std::fprintf(stderr, "%s: %s: append-only\n", argv[0], file);
			err = true;
			continue;
		}

		auto lastpos = lseek(fd, 0, SEEK_END);
		if(lastpos == -1) {
			std::fprintf(stderr, "%s: %s: can't seek\n", argv[0], file);
			err = true;
			continue;
		}
		lseek(fd, 0, SEEK_SET);
		std::size_t destpos = size.value_or(lastpos);

		if(verbose)
			std::fprintf(stderr, "%s: %s: zeroing\n", argv[0], file);


		auto wr = [&](int ret) {
			if(ret == -1) {
				std::fprintf(stderr, "%s: %s: %s\n", argv[0], file, std::strerror(errno));
				err = true;
				return false;
			} else
				return true;
		};
		std::uint8_t buf[64 * 1024]{};
		while(destpos > sizeof(buf)) {
			if(!wr(write(fd, buf, sizeof(buf))))
				continue;
			destpos -= sizeof(buf);
		}
		if(!wr(write(fd, buf, destpos)))
			continue;
		wr(fdatasync(fd));

		if(remove && file != "-"sv) {
			if(verbose)
				std::fprintf(stderr, "%s: %s: removing\n", argv[0], file);
			if(unlink(file) == -1) {
				std::fprintf(stderr, "%s: unlink %s: %s\n", argv[0], file, std::strerror(errno));
				err = true;
			}
		}
	}
	return err;
}

M include/vore-file => include/vore-file +7 -2
@@ 51,6 51,11 @@ namespace vore::file {

			constexpr operator int() const noexcept { return this->desc; }

			constexpr void swap(fd & oth) noexcept {
				std::swap(this->desc, oth.desc);
				std::swap(this->opened, oth.opened);
			}

		private:
			int desc    = -1;
			bool opened = false;


@@ 81,7 86,7 @@ namespace vore::file {
					}
				}

				this->stream = fopen(path, opts);
				this->stream = std::fopen(path, opts);
				this->opened = this->stream;
			}



@@ 90,7 95,7 @@ namespace vore::file {

			~FILE() {
				if(this->opened)
					fclose(this->stream);
					std::fclose(this->stream);
			}

			constexpr operator ::FILE *() const noexcept { return this->stream; }

A man/shred.1 => man/shred.1 +68 -0
@@ 0,0 1,68 @@
.\" SPDX-License-Identifier: 0BSD
.\"
.Dd
.Dt SHRED 1
.Os
.
.Sh NAME
.Nm shred
.Nd zero and unlink files
.Sh SYNOPSIS
.Nm
.Op Fl fvzx
.Op Fl s Ar size
.Op Fl -random-source Ns = Ns Ar path
.Op Fl u Ns | Ns Fl -remove Ns Op Ns = Ns Ar how
.Op Fl n Ar iterations
.Ar file Ns …
.
.Sh DESCRIPTION
Writes NUL bytes to end of
.Ar file Ns s Pq standard output if Qq Sy - ,
then removes them if
.Fl u
is specified.
.Pp
Don't use this.
.Pp
If you think you need this: don't.
Secure-TRIM flash media, single-pass
.Nm dd Ar if= Ns Pa /dev/zero
over magnetic media.
Or just burn it.
You may be tempted to use this on regular files: don't.
Just remove them instead of making more copies by overwriting them again.
.
.Sh OPTIONS
.Bl -tag -compact -width "-i, --ignore-garbage"
.It Fl f , -force
Change mode to
.Li 0200 Pq write for owner
if opening
.Ar file
failed with
.Er EACCESS .
.It Fl u
.It Fl -remove Ns Op = Ns Ar how
Remove
.Ar file
after writing to it.
.It Fl v , -verbose
Log status to the standard error stream.
.It Fl s , -size Ns = Ns Ar size
Write
.Ar size
NULs instead of the file length.
Same format as
.Xr stdbuf 1 .
.It Fl z , -zero
.It Fl -random-source Ns = Ns Ar path
.It Fl n , -iterations Ns = Ns Ar count
.It Fl x , -exact
Ignored.
.El
.
.Sh HISTORY
Originates from the GNU system, where it purports to
.D1 Overwrite the specified FILE(s) repeatedly, in order to make it harder for even very expensive hardware probing to recover the data.
This is laughable, of course.

M man/stdbuf.1 => man/stdbuf.1 +9 -0
@@ 105,3 105,12 @@ supports a simplified
.Sy 0 Ns | Ns Sy L Ns | Ns Sy B Ns | Ns Ar size Ns Op Sy kmG
buffer size spec
.Pq where Sy B No means Dv BUFSIZ .
.Pp
.Nx 7.1
includes a similar facility natively in its
.Lb libc
as
.Ev STDBUF Ns Oo Ar n Oc Ns = Ns Sy U Ns | Ns Sy L Ns | Ns Sy F Ns Op Ar size ;
the example above could be re-written as
.Dl Nm tail Fl f Pa /var/log/messages | Ev STDBUF Ns Ar 1 Ns = Ns Ar L Nm grep Li 'DPT=445' | Nm cat Fl n
.\" TODO: actually do this on NetBSD?

M tests/sha1sum/test => tests/sha1sum/test +1 -1
@@ 1,6 1,6 @@
#!/bin/sh
# SPDX-License-Identifier: 0BSD

exit

# rm -rf a* ../all-*; mkdir a
# for i in $(seq 0 255); do

A tests/shred => tests/shred +16 -0
@@ 0,0 1,16 @@
#!/bin/sh
# SPDX-License-Identifier: 0BSD

tmpdir="$(mktemp -dt "shred.XXXXXXXXXX")/"
shred="${CMDDIR}shred"


echo "What a damn joke." > "${tmpdir}file"

"${shred}" "${tmpdir}file" 2>&3
printf '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' | cmp - "${tmpdir}file" 2>&3 || echo "shred: file wrong"

"${shred}" -s 32 "${tmpdir}file" 2>&3
printf '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' | cmp - "${tmpdir}file" 2>&3 || echo "shred: file -s32 wrong"

# who cares