blob: 49e245f754f6af426b7b16a1ca97229cb3b1c6e4 [file] [log] [blame]
Alexandre Lisionddd731e2014-01-31 11:50:08 -05001// Copyright (C) 2010 David Sugar, Tycho Softworks.
2//
3// This file is part of GNU uCommon C++.
4//
5// GNU uCommon C++ is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published
7// by the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// GNU uCommon C++ is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>.
17
18#include <ucommon/secure.h>
19#include <sys/stat.h>
20
21using namespace UCOMMON_NAMESPACE;
22
23static shell::flagopt helpflag('h',"--help", _TEXT("display this list"));
24static shell::flagopt althelp('?', NULL, NULL);
25static shell::numericopt blocks('b', "--blocksize", _TEXT("size of i/o blocks in k (1-x)"), "size k", 1);
26static shell::flagopt follow('F', "--follow", _TEXT("follow symlinks"));
27static shell::numericopt passes('p', "--passes", _TEXT("passes with randomized data (0-x)"), "count", 1);
28static shell::flagopt renamefile('n', "--rename", _TEXT("rename file randomly"));
29static shell::flagopt recursive('R', "--recursive", _TEXT("recursive directory scan"));
30static shell::flagopt altrecursive('r', NULL, NULL);
31static shell::flagopt truncflag('t', "--truncate", _TEXT("decompose file by truncation"));
32static shell::flagopt verbose('v', "--verbose", _TEXT("show active status"));
33
34static int exit_code = 0;
35static const char *argv0 = "scrub";
36
37static void report(const char *path, int code)
38{
39 const char *err = _TEXT("i/o error");
40
41 switch(code) {
42 case EACCES:
43 case EPERM:
44 err = _TEXT("permission denied");
45 break;
46 case EROFS:
47 err = _TEXT("read-only file system");
48 break;
49 case ENODEV:
50 case ENOENT:
51 err = _TEXT("no such file or directory");
52 break;
53 case ENOTDIR:
54 err = _TEXT("not a directory");
55 break;
56 case ENOTEMPTY:
57 err = _TEXT("directory not empty");
58 break;
59 case ENOSPC:
60 err = _TEXT("no space left on device");
61 break;
62 case EBADF:
63 case ENAMETOOLONG:
64 err = _TEXT("bad file path");
65 break;
66 case EBUSY:
67 case EINPROGRESS:
68 err = _TEXT("file or directory busy");
69 break;
70 case EINTR:
71 err = _TEXT("operation interupted");
72 break;
73#ifdef ELOOP
74 case ELOOP:
75 err = _TEXT("too many sym links");
76 break;
77#endif
78 }
79
80 if(!code) {
81 if(is(verbose))
82 shell::printf("%s\n", _TEXT(" removed"));
83 return;
84 }
85
86 if(is(verbose))
87 shell::printf(" - %s\n", err);
88 else
89 shell::errexit(1, "*** %s: %s: %s\n", argv0, path, err);
90
91 exit_code = 1;
92}
93
94static void scrub(const char *path)
95{
96 fsys_t fs;
97 fsys::fileinfo_t ino;
98 unsigned char block[1024];
99 unsigned long count;
100 fsys::offset_t pos = 0l;
101 unsigned dots = 0;
102 unsigned pass = 0;
103
104 if(is(verbose))
105 shell::printf("%s", path);
106
107 int err = fsys::info(path, &ino);
108
109 if(err == ENOENT || fsys::is_link(path)) {
110 report(path, fsys::unlink(path));
111 return;
112 }
113
114 if(fsys::is_dir(&ino)) {
115 report(path, dir::remove(path));
116 return;
117 }
118
119 if(err == ENOENT || !ino.st_size || fsys::is_sys(&ino) || fsys::is_dev(&ino)) {
120 report(path, fsys::erase(path));
121 return;
122 }
123
124 count = (ino.st_size + 1023l) / 1024;
125 count /= (fsys::offset_t)(*blocks);
126 count *= (fsys::offset_t)(*blocks);
127
128 fs.open(path, fsys::REWRITE);
129 if(!is(fs)) {
130 report(path, fs.err());
131 return;
132 }
133
134 while(count--) {
135 while(++dots > 16384) {
136 dots = 0;
137 if(is(verbose))
138 shell::printf(".");
139 }
140 pass = 0;
141
142repeat:
143 fs.seek(pos);
144 if(fs.err()) {
145 report(path, fs.err());
146 fs.close();
147 return;
148 }
149 if(pass < (unsigned)(*passes)) {
150 Random::fill(block, 1024);
151 fs.write(block, 1024);
152 if(fs.err()) {
153 report(path, fs.err());
154 fs.close();
155 return;
156 }
157 if(++pass < (unsigned)(*passes))
158 goto repeat;
159 }
160
161 // we followup with a zero fill always as it is friendly for many
162 // virtual machine image formats which can later re-pack unused disk
163 // space, and if no random passes are specified, we at least do this.
164
165 if(pass)
166 fs.seek(pos);
167
168 memset(block, 0, sizeof(block));
169 fs.write(block, 1024);
170 if(fs.err()) {
171 report(path, fs.err());
172 fs.close();
173 return;
174 }
175
176 pos += 1024l;
177 }
178
179 while(is(truncflag) && pos > 0l) {
180 pos -= 1024l * (fsys::offset_t)*blocks;
181 fs.trunc(pos);
182 if(fs.err()) {
183 report(path, fs.err());
184 fs.close();
185 return;
186 }
187 }
188
189 report(path, dir::remove(path));
190
191 fs.close();
192
193}
194
195static void scan(String path, bool top = true)
196{
197 char filename[128];
198 String filepath;
199 dir_t dir(path);
200
201 while(is(dir) && dir.read(filename, sizeof(filename))) {
202 if(*filename == '.' && (filename[1] == '.' || !filename[1]))
203 continue;
204
205 filepath = str(path) + str("/") + str(filename);
206 if(fsys::is_dir(filepath)) {
207 if(is(follow) || is(recursive) || is(altrecursive)) {
208 if(fsys::is_link(filepath) && !is(follow))
209 scrub(filepath);
210 else
211 scan(filepath, false);
212 }
213 else
214 scrub(filepath);
215 }
216 else
217 scrub(filepath);
218 }
219 scrub(path);
220}
221
222PROGRAM_MAIN(argc, argv)
223{
224 shell::bind("scrub");
225 shell args(argc, argv);
226 argv0 = args.argv0();
227 unsigned count = 0;
228
229 if(*blocks < 1)
230 shell::errexit(2, "*** %s: blocksize: %ld: %s\n",
231 argv0, *blocks, _TEXT("must be greater than zero"));
232
233 if(*passes < 0)
234 shell::errexit(2, "*** %s: passes: %ld: %s\n",
235 argv0, *passes, _TEXT("negative passes invalid"));
236
237 argv0 = args.argv0();
238
239 if(is(helpflag) || is(althelp)) {
240 printf("%s\n", _TEXT("Usage: scrub [options] path..."));
241 printf("%s\n\n", _TEXT("Securely erase files"));
242 printf("%s\n", _TEXT("Options:"));
243 shell::help();
244 printf("\n%s\n", _TEXT("Report bugs to dyfet@gnu.org"));
245 PROGRAM_EXIT(0);
246 }
247
248 if(!args())
249 PROGRAM_EXIT(0);
250
251 secure::init();
252
253 while(count < args()) {
254 if(fsys::is_dir(args[count]))
255 scan(str(args[count++]));
256 else
257 scrub(args[count++]);
258 }
259
260 PROGRAM_EXIT(exit_code);
261}
262