1/* shred.c - Overwrite a file to securely delete
2 *
3 * Copyright 2014 Rob Landley <rob@landley.net>
4 *
5 * No standard
6
7USE_SHRED(NEWTOY(shred, "<1zxus#<1n#<1o#<0f", TOYFLAG_USR|TOYFLAG_BIN))
8
9config SHRED
10  bool "shred"
11  default y
12  help
13    usage: shred [-fuz] [-n COUNT] [-s SIZE] FILE...
14
15    Securely delete a file by overwriting its contents with random data.
16
17    -f        Force (chmod if necessary)
18    -n COUNT  Random overwrite iterations (default 1)
19    -o OFFSET Start at OFFSET
20    -s SIZE   Use SIZE instead of detecting file size
21    -u        unlink (actually delete file when done)
22    -x        Use exact size (default without -s rounds up to next 4k)
23    -z        zero at end
24
25    Note: data journaling filesystems render this command useless, you must
26    overwrite all free space (fill up disk) to erase old data on those.
27*/
28
29#define FOR_shred
30#include "toys.h"
31
32GLOBALS(
33  long offset;
34  long iterations;
35  long size;
36
37  int ufd;
38)
39
40void shred_main(void)
41{
42  char **try;
43
44  if (!(toys.optflags & FLAG_n)) TT.iterations++;
45  TT.ufd = xopenro("/dev/urandom");
46
47  // We don't use loopfiles() here because "-" isn't stdin, and want to
48  // respond to files we can't open via chmod.
49
50  for (try = toys.optargs; *try; try++) {
51    off_t pos = 0, len = TT.size;
52    int fd = open(*try, O_RDWR), iter = 0, throw;
53
54    // do -f chmod if necessary
55    if (fd == -1 && (toys.optflags & FLAG_f)) {
56      chmod(*try, 0600);
57      fd = open(*try, O_RDWR);
58    }
59    if (fd == -1) {
60      perror_msg_raw(*try);
61      continue;
62    }
63
64    // determine length
65    if (!len) len = fdlength(fd);
66    if (len<1) {
67      error_msg("%s: needs -s", *try);
68      close(fd);
69      continue;
70    }
71
72    // Loop through, writing to this file
73    for (;;) {
74      // Advance to next -n or -z?
75
76      if (pos >= len) {
77        pos = -1;
78        if (++iter == TT.iterations && (toys.optargs && FLAG_z)) {
79          memset(toybuf, 0, sizeof(toybuf));
80          continue;
81        }
82        if (iter >= TT.iterations) break;
83      }
84
85      if (pos < TT.offset) {
86        if (TT.offset != lseek(fd, TT.offset, SEEK_SET)) {
87          perror_msg_raw(*try);
88          break;
89        }
90        pos = TT.offset;
91      }
92
93      // Determine length, read random data if not zeroing, write.
94
95      throw = sizeof(toybuf);
96      if (toys.optflags & FLAG_x)
97        if (len-pos < throw) throw = len-pos;
98
99      if (iter != TT.iterations) xread(TT.ufd, toybuf, throw);
100      if (throw != writeall(fd, toybuf, throw)) perror_msg_raw(*try);
101      pos += throw;
102    }
103    if (toys.optflags & FLAG_u)
104      if (unlink(*try)) perror_msg("unlink '%s'", *try);
105  }
106}
107