blob: 52e075d1d5516baff0519c7bac87315506d55428 [file] [log] [blame]
Steve Kondik2111ad72013-07-07 12:07:44 -07001/**
2 * ntfswipe - Part of the Linux-NTFS project.
3 *
4 * Copyright (c) 2005 Anton Altaparmakov
5 * Copyright (c) 2002-2005 Richard Russon
6 * Copyright (c) 2004 Yura Pakhuchiy
7 *
8 * This utility will overwrite unused space on an NTFS volume.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program (in the main directory of the Linux-NTFS
22 * distribution in the file COPYING); if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include "config.h"
27
28#ifdef HAVE_STDIO_H
29#include <stdio.h>
30#endif
31#ifdef HAVE_ERRNO_H
32#include <errno.h>
33#endif
34#ifdef HAVE_STDARG_H
35#include <stdarg.h>
36#endif
37#ifdef HAVE_GETOPT_H
38#include <getopt.h>
39#endif
40#ifdef HAVE_STRING_H
41#include <string.h>
42#endif
43#ifdef HAVE_STDLIB_H
44#include <stdlib.h>
45#else
46#ifdef HAVE_MALLOC_H
47#include <malloc.h>
48#endif
49#endif
50#ifdef HAVE_UNISTD_H
51#include <unistd.h>
52#endif
53#ifdef HAVE_TIME_H
54#include <time.h>
55#endif
Steve Kondike68cb602016-08-28 00:45:36 -070056#ifdef HAVE_LIMITS_H
57#include <limits.h>
58#endif
Steve Kondik2111ad72013-07-07 12:07:44 -070059
60#include "ntfswipe.h"
61#include "types.h"
62#include "volume.h"
63#include "utils.h"
64#include "debug.h"
65#include "dir.h"
66#include "mst.h"
67/* #include "version.h" */
68#include "logging.h"
69#include "list.h"
70#include "mft.h"
71
72static const char *EXEC_NAME = "ntfswipe";
73static struct options opts;
74static unsigned long int npasses = 0;
75
76struct filename {
77 char *parent_name;
78 struct ntfs_list_head list; /* Previous/Next links */
79 ntfschar *uname; /* Filename in unicode */
80 int uname_len; /* and its length */
81 /* Allocated size (multiple of cluster size) */
82 s64 size_alloc;
83 s64 size_data; /* Actual size of data */
84 long long parent_mref;
85 FILE_ATTR_FLAGS flags;
86 time_t date_c; /* Time created */
87 time_t date_a; /* altered */
88 time_t date_m; /* mft record changed */
89 time_t date_r; /* read */
90 char *name; /* Filename in current locale */
91 FILE_NAME_TYPE_FLAGS name_space;
92 char padding[7]; /* Unused: padding to 64 bit. */
93};
94
95struct data {
96 struct ntfs_list_head list; /* Previous/Next links */
97 char *name; /* Stream name in current locale */
98 ntfschar *uname; /* Unicode stream name */
99 int uname_len; /* and its length */
100 int resident; /* Stream is resident */
101 int compressed; /* Stream is compressed */
102 int encrypted; /* Stream is encrypted */
103 /* Allocated size (multiple of cluster size) */
104 s64 size_alloc;
105 s64 size_data; /* Actual size of data */
106 /* Initialised size, may be less than data size */
107 s64 size_init;
108 VCN size_vcn; /* Highest VCN in the data runs */
109 runlist_element *runlist; /* Decoded data runs */
110 int percent; /* Amount potentially recoverable */
111 void *data; /* If resident, a pointer to the data */
112 char padding[4]; /* Unused: padding to 64 bit. */
113};
114
115struct ufile {
116 s64 inode; /* MFT record number */
117 time_t date; /* Last modification date/time */
118 struct ntfs_list_head name; /* A list of filenames */
119 struct ntfs_list_head data; /* A list of data streams */
120 char *pref_name; /* Preferred filename */
121 char *pref_pname; /* parent filename */
122 s64 max_size; /* Largest size we find */
123 int attr_list; /* MFT record may be one of many */
124 int directory; /* MFT record represents a directory */
125 MFT_RECORD *mft; /* Raw MFT record */
126 char padding[4]; /* Unused: padding to 64 bit. */
127};
128
129#define NPAT 22
130
131/* Taken from `shred' source */
132static const unsigned int patterns[NPAT] = {
133 0x000, 0xFFF, /* 1-bit */
134 0x555, 0xAAA, /* 2-bit */
135 0x249, 0x492, 0x6DB, 0x924, 0xB6D, 0xDB6, /* 3-bit */
136 0x111, 0x222, 0x333, 0x444, 0x666, 0x777,
137 0x888, 0x999, 0xBBB, 0xCCC, 0xDDD, 0xEEE /* 4-bit */
138};
139
140
141/**
142 * version - Print version information about the program
143 *
144 * Print a copyright statement and a brief description of the program.
145 *
146 * Return: none
147 */
148static void version(void)
149{
150 ntfs_log_info("\n%s v%s (libntfs-3g) - Overwrite the unused space on an NTFS "
151 "Volume.\n\n", EXEC_NAME, VERSION);
152 ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n");
153 ntfs_log_info("Copyright (c) 2004 Yura Pakhuchiy\n");
154 ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
155}
156
157/**
158 * usage - Print a list of the parameters to the program
159 *
160 * Print a list of the parameters and options for the program.
161 *
162 * Return: none
163 */
164static void usage(void)
165{
166 ntfs_log_info("\nUsage: %s [options] device\n"
167 " -i --info Show volume information (default)\n"
168 "\n"
169 " -d --directory Wipe directory indexes\n"
170 " -l --logfile Wipe the logfile (journal)\n"
171 " -m --mft Wipe mft space\n"
172 " -p --pagefile Wipe pagefile (swap space)\n"
173 " -t --tails Wipe file tails\n"
174 " -u --unused Wipe unused clusters\n"
Steve Kondik79165c32015-11-09 19:43:00 -0800175 " -U --unused-fast Wipe unused clusters (fast)\n"
Steve Kondik2111ad72013-07-07 12:07:44 -0700176 " -s --undel Wipe undelete data\n"
177 "\n"
178 " -a --all Wipe all unused space\n"
179 "\n"
180 " -c num --count num Number of times to write(default = 1)\n"
181 " -b list --bytes list List of values to write(default = 0)\n"
182 "\n"
183 " -n --no-action Do not write to disk\n"
184 " -f --force Use less caution\n"
185 " -q --quiet Less output\n"
186 " -v --verbose More output\n"
187 " -V --version Version information\n"
188 " -h --help Print this help\n\n",
189 EXEC_NAME);
190 ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
191}
192
193/**
194 * parse_list - Read a comma-separated list of numbers
195 * @list: The comma-separated list of numbers
196 * @result: Store the parsed list here (must be freed by caller)
197 *
198 * Read a comma-separated list of numbers and allocate an array of ints to store
199 * them in. The numbers can be in decimal, octal or hex.
200 *
201 * N.B. The caller must free the memory returned in @result.
202 * N.B. If the function fails, @result is not changed.
203 *
204 * Return: 0 Error, invalid string
205 * n Success, the count of numbers parsed
206 */
207static int parse_list(char *list, int **result)
208{
209 char *ptr;
210 char *end;
211 int i;
212 int count;
213 int *mem = NULL;
214
215 if (!list || !result)
216 return 0;
217
218 for (count = 0, ptr = list; ptr; ptr = strchr(ptr+1, ','))
219 count++;
220
221 mem = malloc((count+1) * sizeof(int));
222 if (!mem) {
223 ntfs_log_error("Couldn't allocate memory in parse_list().\n");
224 return 0;
225 }
226
227 memset(mem, 0xFF, (count+1) * sizeof(int));
228
229 for (ptr = list, i = 0; i < count; i++) {
230
231 end = NULL;
232 mem[i] = strtol(ptr, &end, 0);
233
234 if (!end || (end == ptr) || ((*end != ',') && (*end != 0))) {
235 ntfs_log_error("Invalid list '%s'\n", list);
236 free(mem);
237 return 0;
238 }
239
240 if ((mem[i] < 0) || (mem[i] > 255)) {
241 ntfs_log_error("Bytes must be in range 0-255.\n");
242 free(mem);
243 return 0;
244 }
245
246 ptr = end + 1;
247 }
248
249 ntfs_log_debug("Parsing list '%s' - ", list);
250 for (i = 0; i <= count; i++)
251 ntfs_log_debug("0x%02x ", mem[i]);
252 ntfs_log_debug("\n");
253
254 *result = mem;
255 return count;
256}
257
258/**
259 * parse_options - Read and validate the programs command line
260 *
261 * Read the command line, verify the syntax and parse the options.
262 * This function is very long, but quite simple.
263 *
264 * Return: 1 Success
265 * 0 Error, one or more problems
266 */
267static int parse_options(int argc, char *argv[])
268{
Steve Kondik79165c32015-11-09 19:43:00 -0800269 static const char *sopt = "-ab:c:dfh?ilmnpqtuUvVs";
Steve Kondik2111ad72013-07-07 12:07:44 -0700270 static struct option lopt[] = {
271 { "all", no_argument, NULL, 'a' },
272 { "bytes", required_argument, NULL, 'b' },
273 { "count", required_argument, NULL, 'c' },
274 { "directory", no_argument, NULL, 'd' },
275 { "force", no_argument, NULL, 'f' },
276 { "help", no_argument, NULL, 'h' },
277 { "info", no_argument, NULL, 'i' },
278 { "logfile", no_argument, NULL, 'l' },
279 { "mft", no_argument, NULL, 'm' },
280 { "no-action", no_argument, NULL, 'n' },
281 //{ "no-wait", no_argument, NULL, 0 },
282 { "pagefile", no_argument, NULL, 'p' },
283 { "quiet", no_argument, NULL, 'q' },
284 { "tails", no_argument, NULL, 't' },
285 { "unused", no_argument, NULL, 'u' },
Steve Kondik79165c32015-11-09 19:43:00 -0800286 { "unused-fast",no_argument, NULL, 'U' },
Steve Kondik2111ad72013-07-07 12:07:44 -0700287 { "undel", no_argument, NULL, 's' },
288 { "verbose", no_argument, NULL, 'v' },
289 { "version", no_argument, NULL, 'V' },
290 { NULL, 0, NULL, 0 }
291 };
292
293 int c = -1;
294 char *end;
295 int err = 0;
296 int ver = 0;
297 int help = 0;
298 int levels = 0;
299
300 opterr = 0; /* We'll handle the errors, thank you. */
301
302 opts.count = 1;
303
304 while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
305 switch (c) {
306 case 1: /* A non-option argument */
307 if (!opts.device) {
308 opts.device = argv[optind-1];
309 } else {
310 opts.device = NULL;
311 err++;
312 }
313 break;
314
315 case 'i':
316 opts.info++; /* and fall through */
317 case 'a':
318 opts.directory++;
319 opts.logfile++;
320 opts.mft++;
321 opts.pagefile++;
322 opts.tails++;
323 opts.unused++;
324 opts.undel++;
325 break;
326 case 'b':
327 if (!opts.bytes) {
328 if (!parse_list(optarg, &opts.bytes))
329 err++;
330 } else {
331 err++;
332 }
333 break;
334 case 'c':
335 if (opts.count == 1) {
336 end = NULL;
337 opts.count = strtol(optarg, &end, 0);
338 if (end && *end)
339 err++;
340 } else {
341 err++;
342 }
343 break;
344 case 'd':
345 opts.directory++;
346 break;
347 case 'f':
348 opts.force++;
349 break;
350 case 'h':
Steve Kondik2111ad72013-07-07 12:07:44 -0700351 help++;
352 break;
353 case 'l':
354 opts.logfile++;
355 break;
356 case 'm':
357 opts.mft++;
358 break;
359 case 'n':
360 opts.noaction++;
361 break;
362 case 'p':
363 opts.pagefile++;
364 break;
365 case 'q':
366 opts.quiet++;
367 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
368 break;
369 case 's':
370 opts.undel++;
371 break;
372 case 't':
373 opts.tails++;
374 break;
375 case 'u':
376 opts.unused++;
377 break;
Steve Kondik79165c32015-11-09 19:43:00 -0800378 case 'U':
379 opts.unused_fast++;
380 break;
Steve Kondik2111ad72013-07-07 12:07:44 -0700381 case 'v':
382 opts.verbose++;
383 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
384 break;
385 case 'V':
386 ver++;
387 break;
Steve Kondik79165c32015-11-09 19:43:00 -0800388 case '?':
389 if (strncmp (argv[optind-1], "--log-", 6) == 0) {
390 if (!ntfs_log_parse_option (argv[optind-1]))
391 err++;
392 break;
393 }
394 /* fall through */
Steve Kondik2111ad72013-07-07 12:07:44 -0700395 default:
396 if ((optopt == 'b') || (optopt == 'c')) {
397 ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]);
398 } else {
399 ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
400 }
401 err++;
402 break;
403 }
404 }
405
406 /* Make sure we're in sync with the log levels */
407 levels = ntfs_log_get_levels();
408 if (levels & NTFS_LOG_LEVEL_VERBOSE)
409 opts.verbose++;
410 if (!(levels & NTFS_LOG_LEVEL_QUIET))
411 opts.quiet++;
412
413 if (help || ver) {
414 opts.quiet = 0;
415 } else {
416 if (opts.device == NULL) {
417 if (argc > 1)
418 ntfs_log_error("You must specify exactly one device.\n");
419 err++;
420 }
421
422 if (opts.quiet && opts.verbose) {
423 ntfs_log_error("You may not use --quiet and --verbose at the same time.\n");
424 err++;
425 }
426
427 /*
428 if (opts.info && (opts.unused || opts.tails || opts.mft || opts.directory)) {
429 ntfs_log_error("You may not use any other options with --info.\n");
430 err++;
431 }
432 */
433
434 if ((opts.count < 1) || (opts.count > 100)) {
435 ntfs_log_error("The iteration count must be between 1 and 100.\n");
436 err++;
437 }
438
439 /* Create a default list */
440 if (!opts.bytes) {
441 opts.bytes = malloc(2 * sizeof(int));
442 if (opts.bytes) {
443 opts.bytes[0] = 0;
444 opts.bytes[1] = -1;
445 } else {
446 ntfs_log_error("Couldn't allocate memory for byte list.\n");
447 err++;
448 }
449 }
450
451 if (!opts.directory && !opts.logfile && !opts.mft &&
452 !opts.pagefile && !opts.tails && !opts.unused &&
Steve Kondik79165c32015-11-09 19:43:00 -0800453 !opts.unused_fast && !opts.undel) {
Steve Kondik2111ad72013-07-07 12:07:44 -0700454 opts.info = 1;
455 }
456 }
457
458 if (ver)
459 version();
460 if (help || err)
461 usage();
462
Steve Kondik79165c32015-11-09 19:43:00 -0800463 /* tri-state 0 : done, 1 : error, -1 : proceed */
464 return (err ? 1 : (help || ver ? 0 : -1));
Steve Kondik2111ad72013-07-07 12:07:44 -0700465}
466
467/**
468 * wipe_unused - Wipe unused clusters
469 * @vol: An ntfs volume obtained from ntfs_mount
470 * @byte: Overwrite with this value
471 * @act: Wipe, test or info
472 *
473 * Read $Bitmap and wipe any clusters that are marked as not in use.
474 *
475 * Return: >0 Success, the attribute was wiped
476 * 0 Nothing to wipe
477 * -1 Error, something went wrong
478 */
479static s64 wipe_unused(ntfs_volume *vol, int byte, enum action act)
480{
481 s64 i;
482 s64 total = 0;
483 s64 result = 0;
484 u8 *buffer = NULL;
485
486 if (!vol || (byte < 0))
487 return -1;
488
489 if (act != act_info) {
490 buffer = malloc(vol->cluster_size);
491 if (!buffer) {
492 ntfs_log_error("malloc failed\n");
493 return -1;
494 }
495 memset(buffer, byte, vol->cluster_size);
496 }
497
498 for (i = 0; i < vol->nr_clusters; i++) {
499 if (utils_cluster_in_use(vol, i)) {
500 //ntfs_log_verbose("cluster %lld is in use\n", i);
501 continue;
502 }
503
504 if (act == act_wipe) {
505 //ntfs_log_verbose("cluster %lld is not in use\n", i);
506 result = ntfs_pwrite(vol->dev, vol->cluster_size * i, vol->cluster_size, buffer);
507 if (result != vol->cluster_size) {
508 ntfs_log_error("write failed\n");
509 goto free;
510 }
511 }
512
513 total += vol->cluster_size;
514 }
515
516 ntfs_log_quiet("wipe_unused 0x%02x, %lld bytes\n", byte, (long long)total);
517free:
518 free(buffer);
519 return total;
520}
521
522/**
Steve Kondik79165c32015-11-09 19:43:00 -0800523 * wipe_unused_fast - Faster wipe unused clusters
524 * @vol: An ntfs volume obtained from ntfs_mount
525 * @byte: Overwrite with this value
526 * @act: Wipe, test or info
527 *
528 * Read $Bitmap and wipe any clusters that are marked as not in use.
529 *
530 * - read/write on a block basis (64 clusters, arbitrary)
531 * - skip of fully used block
532 * - skip non-used block already wiped
533 *
534 * Return: >0 Success, the attribute was wiped
535 * 0 Nothing to wipe
536 * -1 Error, something went wrong
537 */
538static s64 wipe_unused_fast(ntfs_volume *vol, int byte, enum action act)
539{
540 s64 i;
541 s64 total = 0;
542 s64 unused = 0;
543 s64 result;
544 u8 *buffer;
545 u8 *big_buffer;
546 u32 *u32_buffer;
547 u32 u32_bytes;
548 unsigned int blksize;
549 unsigned int j,k;
550 BOOL wipe_needed;
551
552 if (!vol || (byte < 0))
553 return -1;
554
555 big_buffer = (u8*)malloc(vol->cluster_size*64);
556 if (!big_buffer) {
557 ntfs_log_error("malloc failed\n");
558 return -1;
559 }
560
561 for (i = 0; i < vol->nr_clusters; i+=64) {
562 blksize = vol->nr_clusters - i;
563 if (blksize > 64)
564 blksize = 64;
565 /* if all clusters in this block are used, ignore the block */
566 result = 0;
567 for (j = 0; j < blksize; j++) {
568 if (utils_cluster_in_use(vol, i+j))
569 result++;
570 }
571 unused += (blksize - result) * vol->cluster_size;
572
573 if (result == blksize) {
574 continue;
575 }
576 /*
577 * if all unused clusters in this block are already wiped,
578 * ignore the block
579 */
580 if (ntfs_pread(vol->dev, vol->cluster_size * i,
581 vol->cluster_size * blksize, big_buffer)
582 != vol->cluster_size * blksize) {
583 ntfs_log_error("Read failed at cluster %lld\n",
584 (long long)i);
585 goto free;
586 }
587
588 result = 0;
589 wipe_needed = FALSE;
590 u32_bytes = (byte & 255)*0x01010101;
591 buffer = big_buffer;
592 for (j = 0; (j < blksize) && !wipe_needed; j++) {
593 u32_buffer = (u32*)buffer;
594 if (!utils_cluster_in_use(vol, i+j)) {
595 for (k = 0; (k < vol->cluster_size)
596 && (*u32_buffer++ == u32_bytes); k+=4) {
597 }
598 if (k < vol->cluster_size)
599 wipe_needed = TRUE;
600 }
601 buffer += vol->cluster_size;
602 }
603
604 if (!wipe_needed) {
605 continue;
606 }
607 /* else wipe unused clusters in the block */
608 buffer = big_buffer;
609
610 for (j = 0; j < blksize; j++) {
611 if (!utils_cluster_in_use(vol, i+j)) {
612 memset(buffer, byte, vol->cluster_size);
613 total += vol->cluster_size;
614 }
615 buffer += vol->cluster_size;
616 }
617
618 if ((act == act_wipe)
619 && (ntfs_pwrite(vol->dev, vol->cluster_size * i,
620 vol->cluster_size * blksize, big_buffer)
621 != vol->cluster_size * blksize)) {
622 ntfs_log_error("Write failed at cluster %lld\n",
623 (long long)i);
624 goto free;
625 }
626 }
627
628 ntfs_log_quiet("wipe_unused_fast 0x%02x, %lld bytes"
629 " already wiped, %lld more bytes wiped\n",
630 byte, (long long)(unused - total), (long long)total);
631free:
632 free(big_buffer);
633 return total;
634}
635
636/**
Steve Kondik2111ad72013-07-07 12:07:44 -0700637 * wipe_compressed_attribute - Wipe compressed $DATA attribute
638 * @vol: An ntfs volume obtained from ntfs_mount
639 * @byte: Overwrite with this value
640 * @act: Wipe, test or info
641 * @na: Opened ntfs attribute
642 *
643 * Return: >0 Success, the attribute was wiped
644 * 0 Nothing to wipe
645 * -1 Error, something went wrong
646 */
647static s64 wipe_compressed_attribute(ntfs_volume *vol, int byte,
648 enum action act, ntfs_attr *na)
649{
650 unsigned char *buf;
651 s64 size, offset, ret, wiped = 0;
652 le16 block_size_le;
653 u16 block_size;
654 VCN cur_vcn = 0;
655 runlist_element *rlc = na->rl;
656 s64 cu_mask = na->compression_block_clusters - 1;
657 runlist_element *restart = na->rl;
658
659 while (rlc->length) {
660 cur_vcn += rlc->length;
661 if ((cur_vcn & cu_mask) ||
662 (((rlc + 1)->length) && (rlc->lcn != LCN_HOLE))) {
663 rlc++;
664 continue;
665 }
666
667 if (rlc->lcn == LCN_HOLE) {
668 runlist_element *rlt;
669
670 offset = cur_vcn - rlc->length;
671 if (offset == (offset & (~cu_mask))) {
672 restart = rlc + 1;
673 rlc++;
674 continue;
675 }
676 offset = (offset & (~cu_mask))
677 << vol->cluster_size_bits;
678 rlt = rlc;
679 while ((rlt - 1)->lcn == LCN_HOLE) rlt--;
680 while (1) {
681 ret = ntfs_rl_pread(vol, restart,
682 offset - (restart->vcn
683 << vol->cluster_size_bits),
684 2, &block_size_le);
685 block_size = le16_to_cpu(block_size_le);
686 if (ret != 2) {
687 ntfs_log_verbose("Internal error\n");
688 ntfs_log_error("ntfs_rl_pread failed");
689 return -1;
690 }
691 if (block_size == 0) {
692 offset += 2;
693 break;
694 }
695 block_size &= 0x0FFF;
696 block_size += 3;
697 offset += block_size;
698 if (offset >= (((rlt->vcn) <<
699 vol->cluster_size_bits) - 2))
700 goto next;
701 }
702 size = (rlt->vcn << vol->cluster_size_bits) - offset;
703 } else {
704 size = na->allocated_size - na->data_size;
705 offset = (cur_vcn << vol->cluster_size_bits) - size;
706 }
707
708 if (size < 0) {
709 ntfs_log_verbose("Internal error\n");
710 ntfs_log_error("bug or damaged fs: we want "
711 "allocate buffer size %lld bytes",
712 (long long)size);
713 return -1;
714 }
715
716 if ((act == act_info) || (!size)) {
717 wiped += size;
718 if (rlc->lcn == LCN_HOLE)
719 restart = rlc + 1;
720 rlc++;
721 continue;
722 }
723
724 buf = malloc(size);
725 if (!buf) {
726 ntfs_log_verbose("Not enough memory\n");
727 ntfs_log_error("Not enough memory to allocate "
728 "%lld bytes",
729 (long long)size);
730 return -1;
731 }
732 memset(buf, byte, size);
733
734 ret = ntfs_rl_pwrite(vol, restart,
735 restart->vcn << vol->cluster_size_bits,
736 offset, size, buf);
737 free(buf);
738 if (ret != size) {
739 ntfs_log_verbose("Internal error\n");
740 ntfs_log_error("ntfs_rl_pwrite failed, offset %llu, "
741 "size %lld, vcn %lld",
742 (unsigned long long)offset,
743 (long long)size, (long long)rlc->vcn);
744 return -1;
745 }
746 wiped += ret;
747next:
748 if (rlc->lcn == LCN_HOLE)
749 restart = rlc + 1;
750 rlc++;
751 }
752
753 return wiped;
754}
755
756/**
757 * wipe_attribute - Wipe not compressed $DATA attribute
758 * @vol: An ntfs volume obtained from ntfs_mount
759 * @byte: Overwrite with this value
760 * @act: Wipe, test or info
761 * @na: Opened ntfs attribute
762 *
763 * Return: >0 Success, the attribute was wiped
764 * 0 Nothing to wipe
765 * -1 Error, something went wrong
766 */
767static s64 wipe_attribute(ntfs_volume *vol, int byte, enum action act,
768 ntfs_attr *na)
769{
770 unsigned char *buf;
771 s64 wiped;
772 s64 size;
773 u64 offset = na->data_size;
774
775 if (!offset)
776 return 0;
777 if (na->data_flags & ATTR_IS_ENCRYPTED)
778 offset = (((offset - 1) >> 10) + 1) << 10;
779 size = (vol->cluster_size - offset) % vol->cluster_size;
780
781 if (act == act_info)
782 return size;
783
784 buf = malloc(size);
785 if (!buf) {
786 ntfs_log_verbose("Not enough memory\n");
787 ntfs_log_error("Not enough memory to allocate %lld bytes",
788 (long long)size);
789 return -1;
790 }
791 memset(buf, byte, size);
792
793 wiped = ntfs_rl_pwrite(vol, na->rl, 0, offset, size, buf);
794 if (wiped == -1) {
795 ntfs_log_verbose("Internal error\n");
796 ntfs_log_error("Couldn't wipe tail");
797 }
798
799 free(buf);
800 return wiped;
801}
802
803/*
804 * Wipe a data attribute tail
805 *
806 * Return: >0 Success, the clusters were wiped
807 * 0 Nothing to wipe
808 * -1 Error, something went wrong
809 */
810
811static s64 wipe_attr_tail(ntfs_inode *ni, ntfschar *name, int namelen,
812 int byte, enum action act)
813{
814 ntfs_attr *na;
815 ntfs_volume *vol = ni->vol;
816 s64 wiped;
817
818 wiped = -1;
819 na = ntfs_attr_open(ni, AT_DATA, name, namelen);
820 if (!na) {
821 ntfs_log_error("Couldn't open $DATA attribute\n");
822 goto close_attr;
823 }
824
825 if (!NAttrNonResident(na)) {
826 ntfs_log_verbose("Resident $DATA attribute. Skipping.\n");
827 goto close_attr;
828 }
829
830 if (ntfs_attr_map_whole_runlist(na)) {
831 ntfs_log_verbose("Internal error\n");
832 ntfs_log_error("Can't map runlist (inode %lld)\n",
833 (long long)ni->mft_no);
834 goto close_attr;
835 }
836
837 if (na->data_flags & ATTR_COMPRESSION_MASK)
838 wiped = wipe_compressed_attribute(vol, byte, act, na);
839 else
840 wiped = wipe_attribute(vol, byte, act, na);
841
842 if (wiped == -1) {
843 ntfs_log_error(" (inode %lld)\n", (long long)ni->mft_no);
844 }
845
846close_attr:
847 ntfs_attr_close(na);
848 return (wiped);
849}
850
851/**
852 * wipe_tails - Wipe the file tails in all its data attributes
853 * @vol: An ntfs volume obtained from ntfs_mount
854 * @byte: Overwrite with this value
855 * @act: Wipe, test or info
856 *
857 * Disk space is allocated in clusters. If a file isn't an exact multiple of
858 * the cluster size, there is some slack space at the end. Wipe this space.
859 *
860 * Return: >0 Success, the clusters were wiped
861 * 0 Nothing to wipe
862 * -1 Error, something went wrong
863 */
864static s64 wipe_tails(ntfs_volume *vol, int byte, enum action act)
865{
866 s64 total = 0;
867 s64 nr_mft_records, inode_num;
868 ntfs_attr_search_ctx *ctx;
869 ntfs_inode *ni;
870 ATTR_RECORD *a;
871 ntfschar *name;
872
873 if (!vol || (byte < 0))
874 return -1;
875
876 nr_mft_records = vol->mft_na->initialized_size >>
877 vol->mft_record_size_bits;
878
Steve Kondike68cb602016-08-28 00:45:36 -0700879 /* Avoid getting fixup warnings on unitialized inodes */
880 NVolSetNoFixupWarn(vol);
881
Steve Kondik2111ad72013-07-07 12:07:44 -0700882 for (inode_num = FILE_first_user; inode_num < nr_mft_records;
883 inode_num++) {
884 s64 attr_wiped;
885 s64 wiped = 0;
886
887 ntfs_log_verbose("Inode %lld - ", (long long)inode_num);
888 ni = ntfs_inode_open(vol, inode_num);
889 if (!ni) {
Steve Kondike68cb602016-08-28 00:45:36 -0700890 if (opts.verbose)
891 ntfs_log_verbose("Could not open inode\n");
892 else
893 ntfs_log_verbose("\r");
Steve Kondik2111ad72013-07-07 12:07:44 -0700894 continue;
895 }
896
897 if (ni->mrec->base_mft_record) {
898 ntfs_log_verbose("Not base mft record. Skipping\n");
899 goto close_inode;
900 }
901
902 ctx = ntfs_attr_get_search_ctx(ni, (MFT_RECORD*)NULL);
903 if (!ctx) {
904 ntfs_log_error("Can't get a context, aborting\n");
905 ntfs_inode_close(ni);
906 goto close_abort;
907 }
908 while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, 0,
909 NULL, 0, ctx)) {
910 a = ctx->attr;
911
912 if (!ctx->al_entry || !ctx->al_entry->lowest_vcn) {
913 name = (ntfschar*)((u8*)a
914 + le16_to_cpu(a->name_offset));
915 attr_wiped = wipe_attr_tail(ni, name,
916 a->name_length, byte, act);
917 if (attr_wiped > 0)
918 wiped += attr_wiped;
919 }
920 }
921 ntfs_attr_put_search_ctx(ctx);
922 if (wiped) {
923 ntfs_log_verbose("Wiped %llu bytes\n",
924 (unsigned long long)wiped);
925 total += wiped;
926 } else
927 ntfs_log_verbose("Nothing to wipe\n");
928close_inode:
929 ntfs_inode_close(ni);
930 }
931close_abort :
Steve Kondike68cb602016-08-28 00:45:36 -0700932 NVolClearNoFixupWarn(vol);
Steve Kondik2111ad72013-07-07 12:07:44 -0700933 ntfs_log_quiet("wipe_tails 0x%02x, %lld bytes\n", byte,
934 (long long)total);
935 return total;
936}
937
938/**
939 * wipe_mft - Wipe the MFT slack space
940 * @vol: An ntfs volume obtained from ntfs_mount
941 * @byte: Overwrite with this value
942 * @act: Wipe, test or info
943 *
944 * MFT Records are 1024 bytes long, but some of this space isn't used. Wipe any
945 * unused space at the end of the record and wipe any unused records.
946 *
947 * Return: >0 Success, the clusters were wiped
948 * 0 Nothing to wipe
949 * -1 Error, something went wrong
950 */
951static s64 wipe_mft(ntfs_volume *vol, int byte, enum action act)
952{
953 // by considering the individual attributes we might be able to
954 // wipe a few more bytes at the attr's tail.
955 s64 nr_mft_records, i;
956 s64 total = 0;
957 s64 result = 0;
958 int size = 0;
959 MFT_RECORD *rec = NULL;
960
961 if (!vol || (byte < 0))
962 return -1;
963
964 rec = (MFT_RECORD*)malloc(vol->mft_record_size);
965 if (!rec) {
966 ntfs_log_error("malloc failed\n");
967 return -1;
968 }
969
970 nr_mft_records = vol->mft_na->initialized_size >>
971 vol->mft_record_size_bits;
972
973 for (i = 0; i < nr_mft_records; i++) {
974 if (utils_mftrec_in_use(vol, i)) {
975 result = ntfs_attr_mst_pread(vol->mft_na, vol->mft_record_size * i,
976 1, vol->mft_record_size, rec);
977 if (result != 1) {
978 ntfs_log_error("error attr mst read %lld\n",
979 (long long)i);
980 total = -1; // XXX just negate result?
981 goto free;
982 }
983
984 // We know that the end marker will only take 4 bytes
985 size = le32_to_cpu(rec->bytes_in_use) - 4;
986
Steve Kondike68cb602016-08-28 00:45:36 -0700987 if ((size <= 0) || (size > (int)vol->mft_record_size)) {
988 ntfs_log_error("Bad mft record %lld\n",
989 (long long)i);
990 total = -1;
991 goto free;
992 }
Steve Kondik2111ad72013-07-07 12:07:44 -0700993 if (act == act_info) {
994 //ntfs_log_info("mft %d\n", size);
995 total += size;
996 continue;
997 }
998
999 memset(((u8*) rec) + size, byte, vol->mft_record_size - size);
1000 } else {
1001 const u16 usa_offset =
1002 (vol->major_ver == 3) ? 0x0030 : 0x002A;
1003 const u32 usa_size = 1 +
1004 (vol->mft_record_size >> NTFS_BLOCK_SIZE_BITS);
1005 const u16 attrs_offset =
1006 ((usa_offset + usa_size) + 7) & ~((u16) 7);
1007 const u32 bytes_in_use = attrs_offset + 8;
1008
1009 if(usa_size > 0xFFFF || (usa_offset + usa_size) >
1010 (NTFS_BLOCK_SIZE - sizeof(u16)))
1011 {
1012 ntfs_log_error("%d: usa_size out of bounds "
1013 "(%u)\n", __LINE__, usa_size);
1014 total = -1;
1015 goto free;
1016 }
1017
1018 if (act == act_info) {
1019 total += vol->mft_record_size;
1020 continue;
1021 }
1022
1023 // Build the record from scratch
1024 memset(rec, 0, vol->mft_record_size);
1025
1026 // Common values
1027 rec->magic = magic_FILE;
1028 rec->usa_ofs = cpu_to_le16(usa_offset);
1029 rec->usa_count = cpu_to_le16((u16) usa_size);
Steve Kondike68cb602016-08-28 00:45:36 -07001030 rec->sequence_number = const_cpu_to_le16(0x0001);
Steve Kondik2111ad72013-07-07 12:07:44 -07001031 rec->attrs_offset = cpu_to_le16(attrs_offset);
1032 rec->bytes_in_use = cpu_to_le32(bytes_in_use);
1033 rec->bytes_allocated = cpu_to_le32(vol->mft_record_size);
Steve Kondike68cb602016-08-28 00:45:36 -07001034 rec->next_attr_instance = const_cpu_to_le16(0x0001);
Steve Kondik2111ad72013-07-07 12:07:44 -07001035
1036 // End marker.
Steve Kondike68cb602016-08-28 00:45:36 -07001037 *((le32*) (((u8*) rec) + attrs_offset)) = const_cpu_to_le32(0xFFFFFFFF);
Steve Kondik2111ad72013-07-07 12:07:44 -07001038 }
1039
1040 result = ntfs_attr_mst_pwrite(vol->mft_na, vol->mft_record_size * i,
1041 1, vol->mft_record_size, rec);
1042 if (result != 1) {
1043 ntfs_log_error("error attr mst write %lld\n",
1044 (long long)i);
1045 total = -1;
1046 goto free;
1047 }
1048
1049 if ((vol->mft_record_size * (i+1)) <= vol->mftmirr_na->allocated_size)
1050 {
1051 // We have to reduce the update sequence number, or else...
1052 u16 offset;
1053 le16 *usnp;
1054 offset = le16_to_cpu(rec->usa_ofs);
1055 usnp = (le16*) (((u8*) rec) + offset);
1056 *usnp = cpu_to_le16(le16_to_cpu(*usnp) - 1);
1057
1058 result = ntfs_attr_mst_pwrite(vol->mftmirr_na, vol->mft_record_size * i,
1059 1, vol->mft_record_size, rec);
1060 if (result != 1) {
1061 ntfs_log_error("error attr mst write %lld\n",
1062 (long long)i);
1063 total = -1;
1064 goto free;
1065 }
1066 }
1067
1068 total += vol->mft_record_size;
1069 }
1070
1071 ntfs_log_quiet("wipe_mft 0x%02x, %lld bytes\n", byte, (long long)total);
1072free:
1073 free(rec);
1074 return total;
1075}
1076
1077/**
1078 * wipe_index_allocation - Wipe $INDEX_ALLOCATION attribute
1079 * @vol: An ntfs volume obtained from ntfs_mount
1080 * @byte: Overwrite with this value
1081 * @act: Wipe, test or info
1082 * @naa: Opened ntfs $INDEX_ALLOCATION attribute
1083 * @nab: Opened ntfs $BITMAP attribute
1084 * @indx_record_size: Size of INDX record
1085 *
1086 * Return: >0 Success, the clusters were wiped
1087 * 0 Nothing to wipe
1088 * -1 Error, something went wrong
1089 */
1090static s64 wipe_index_allocation(ntfs_volume *vol, int byte, enum action act
1091 __attribute__((unused)), ntfs_attr *naa, ntfs_attr *nab,
1092 u32 indx_record_size)
1093{
1094 s64 total = 0;
1095 s64 wiped = 0;
1096 s64 offset = 0;
1097 s64 obyte = 0;
1098 u64 wipe_offset;
1099 s64 wipe_size;
1100 u8 obit = 0;
1101 u8 mask;
1102 u8 *bitmap;
1103 u8 *buf;
1104
1105 bitmap = malloc(nab->data_size);
1106 if (!bitmap) {
1107 ntfs_log_verbose("malloc failed\n");
1108 ntfs_log_error("Couldn't allocate %lld bytes",
1109 (long long)nab->data_size);
1110 return -1;
1111 }
1112
1113 if (ntfs_attr_pread(nab, 0, nab->data_size, bitmap)
1114 != nab->data_size) {
1115 ntfs_log_verbose("Internal error\n");
1116 ntfs_log_error("Couldn't read $BITMAP");
1117 total = -1;
1118 goto free_bitmap;
1119 }
1120
1121 buf = malloc(indx_record_size);
1122 if (!buf) {
1123 ntfs_log_verbose("malloc failed\n");
1124 ntfs_log_error("Couldn't allocate %u bytes",
1125 (unsigned int)indx_record_size);
1126 total = -1;
1127 goto free_bitmap;
1128 }
1129
1130 while (offset < naa->allocated_size) {
1131 mask = 1 << obit;
1132 if (bitmap[obyte] & mask) {
1133 INDEX_ALLOCATION *indx;
1134
1135 s64 ret = ntfs_rl_pread(vol, naa->rl,
1136 offset, indx_record_size, buf);
1137 if (ret != indx_record_size) {
1138 ntfs_log_verbose("ntfs_rl_pread failed\n");
1139 ntfs_log_error("Couldn't read INDX record");
1140 total = -1;
1141 goto free_buf;
1142 }
1143
1144 indx = (INDEX_ALLOCATION *) buf;
1145 if (ntfs_mst_post_read_fixup((NTFS_RECORD *)buf,
1146 indx_record_size))
1147 ntfs_log_error("damaged fs: mst_post_read_fixup failed");
1148
1149 if ((le32_to_cpu(indx->index.allocated_size) + 0x18) !=
1150 indx_record_size) {
1151 ntfs_log_verbose("Internal error\n");
1152 ntfs_log_error("INDX record should be %u bytes",
1153 (unsigned int)indx_record_size);
1154 total = -1;
1155 goto free_buf;
1156 }
1157
1158 wipe_offset = le32_to_cpu(indx->index.index_length) + 0x18;
1159 wipe_size = indx_record_size - wipe_offset;
1160 memset(buf + wipe_offset, byte, wipe_size);
1161 if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)indx,
1162 indx_record_size))
1163 ntfs_log_error("damaged fs: mst_pre_write_protect failed");
1164 if (opts.verbose > 1)
1165 ntfs_log_verbose("+");
1166 } else {
1167 wipe_size = indx_record_size;
1168 memset(buf, byte, wipe_size);
1169 if (opts.verbose > 1)
1170 ntfs_log_verbose("x");
1171 }
1172
1173 wiped = ntfs_rl_pwrite(vol, naa->rl, 0, offset, indx_record_size, buf);
1174 if (wiped != indx_record_size) {
1175 ntfs_log_verbose("ntfs_rl_pwrite failed\n");
1176 ntfs_log_error("Couldn't wipe tail of INDX record");
1177 total = -1;
1178 goto free_buf;
1179 }
1180 total += wipe_size;
1181
1182 offset += indx_record_size;
1183 obit++;
1184 if (obit > 7) {
1185 obit = 0;
1186 obyte++;
1187 }
1188 }
1189 if ((opts.verbose > 1) && (wiped != -1))
1190 ntfs_log_verbose("\n\t");
1191free_buf:
1192 free(buf);
1193free_bitmap:
1194 free(bitmap);
1195 return total;
1196}
1197
1198/**
1199 * get_indx_record_size - determine size of INDX record from $INDEX_ROOT
1200 * @nar: Opened ntfs $INDEX_ROOT attribute
1201 *
1202 * Return: >0 Success, return INDX record size
1203 * 0 Error, something went wrong
1204 */
1205static u32 get_indx_record_size(ntfs_attr *nar)
1206{
1207 u32 indx_record_size;
1208 le32 indx_record_size_le;
1209
1210 if (ntfs_attr_pread(nar, 8, 4, &indx_record_size_le) != 4) {
1211 ntfs_log_verbose("Couldn't determine size of INDX record\n");
1212 ntfs_log_error("ntfs_attr_pread failed");
1213 return 0;
1214 }
1215
1216 indx_record_size = le32_to_cpu(indx_record_size_le);
1217 if (!indx_record_size) {
1218 ntfs_log_verbose("Internal error\n");
1219 ntfs_log_error("INDX record should be 0");
1220 }
1221 return indx_record_size;
1222}
1223
1224/**
1225 * wipe_directory - Wipe the directory indexes
1226 * @vol: An ntfs volume obtained from ntfs_mount
1227 * @byte: Overwrite with this value
1228 * @act: Wipe, test or info
1229 *
1230 * Directories are kept in sorted B+ Trees. Index blocks may not be full. Wipe
1231 * the unused space at the ends of these blocks.
1232 *
1233 * Return: >0 Success, the clusters were wiped
1234 * 0 Nothing to wipe
1235 * -1 Error, something went wrong
1236 */
1237static s64 wipe_directory(ntfs_volume *vol, int byte, enum action act)
1238{
1239 s64 total = 0;
1240 s64 nr_mft_records, inode_num;
1241 ntfs_inode *ni;
1242 ntfs_attr *naa;
1243 ntfs_attr *nab;
1244 ntfs_attr *nar;
1245
1246 if (!vol || (byte < 0))
1247 return -1;
1248
1249 nr_mft_records = vol->mft_na->initialized_size >>
1250 vol->mft_record_size_bits;
1251
Steve Kondike68cb602016-08-28 00:45:36 -07001252 /* Avoid getting fixup warnings on unitialized inodes */
1253 NVolSetNoFixupWarn(vol);
1254
Steve Kondik2111ad72013-07-07 12:07:44 -07001255 for (inode_num = 5; inode_num < nr_mft_records; inode_num++) {
1256 u32 indx_record_size;
1257 s64 wiped;
1258
1259 ntfs_log_verbose("Inode %lld - ", (long long)inode_num);
1260 ni = ntfs_inode_open(vol, inode_num);
1261 if (!ni) {
1262 if (opts.verbose > 2)
1263 ntfs_log_verbose("Could not open inode\n");
1264 else
1265 ntfs_log_verbose("\r");
1266 continue;
1267 }
1268
1269 if (ni->mrec->base_mft_record) {
1270 if (opts.verbose > 2)
1271 ntfs_log_verbose("Not base mft record. Skipping\n");
1272 else
1273 ntfs_log_verbose("\r");
1274 goto close_inode;
1275 }
1276
1277 naa = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
1278 if (!naa) {
1279 if (opts.verbose > 2)
1280 ntfs_log_verbose("Couldn't open $INDEX_ALLOCATION\n");
1281 else
1282 ntfs_log_verbose("\r");
1283 goto close_inode;
1284 }
1285
1286 if (!NAttrNonResident(naa)) {
1287 ntfs_log_verbose("Resident $INDEX_ALLOCATION\n");
1288 ntfs_log_error("damaged fs: Resident $INDEX_ALLOCATION "
1289 "(inode %lld)\n", (long long)inode_num);
1290 goto close_attr_allocation;
1291 }
1292
1293 if (ntfs_attr_map_whole_runlist(naa)) {
1294 ntfs_log_verbose("Internal error\n");
1295 ntfs_log_error("Can't map runlist for $INDEX_ALLOCATION "
1296 "(inode %lld)\n", (long long)inode_num);
1297 goto close_attr_allocation;
1298 }
1299
1300 nab = ntfs_attr_open(ni, AT_BITMAP, NTFS_INDEX_I30, 4);
1301 if (!nab) {
1302 ntfs_log_verbose("Couldn't open $BITMAP\n");
1303 ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, "
1304 "but we can't open $BITMAP with same "
1305 "name (inode %lld)\n", (long long)inode_num);
1306 goto close_attr_allocation;
1307 }
1308
1309 nar = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4);
1310 if (!nar) {
1311 ntfs_log_verbose("Couldn't open $INDEX_ROOT\n");
1312 ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, but "
1313 "we can't open $INDEX_ROOT with same name"
1314 " (inode %lld)\n", (long long)inode_num);
1315 goto close_attr_bitmap;
1316 }
1317
1318 if (NAttrNonResident(nar)) {
1319 ntfs_log_verbose("Not resident $INDEX_ROOT\n");
1320 ntfs_log_error("damaged fs: Not resident $INDEX_ROOT "
1321 "(inode %lld)\n", (long long)inode_num);
1322 goto close_attr_root;
1323 }
1324
1325 indx_record_size = get_indx_record_size(nar);
1326 if (!indx_record_size) {
1327 ntfs_log_error(" (inode %lld)\n", (long long)inode_num);
1328 goto close_attr_root;
1329 }
1330
1331 wiped = wipe_index_allocation(vol, byte, act,
1332 naa, nab, indx_record_size);
1333 if (wiped == -1) {
1334 ntfs_log_error(" (inode %lld)\n",
1335 (long long)inode_num);
1336 goto close_attr_root;
1337 }
1338
1339 if (wiped) {
1340 ntfs_log_verbose("Wiped %llu bytes\n",
1341 (unsigned long long)wiped);
1342 total += wiped;
1343 } else
1344 ntfs_log_verbose("Nothing to wipe\n");
1345close_attr_root:
1346 ntfs_attr_close(nar);
1347close_attr_bitmap:
1348 ntfs_attr_close(nab);
1349close_attr_allocation:
1350 ntfs_attr_close(naa);
1351close_inode:
1352 ntfs_inode_close(ni);
1353 }
1354
Steve Kondike68cb602016-08-28 00:45:36 -07001355 NVolClearNoFixupWarn(vol);
Steve Kondik2111ad72013-07-07 12:07:44 -07001356 ntfs_log_quiet("wipe_directory 0x%02x, %lld bytes\n", byte,
1357 (long long)total);
1358 return total;
1359}
1360
1361/**
1362 * wipe_logfile - Wipe the logfile (journal)
1363 * @vol: An ntfs volume obtained from ntfs_mount
1364 * @byte: Overwrite with this value
1365 * @act: Wipe, test or info
1366 *
1367 * The logfile journals the metadata to give the volume fault-tolerance. If the
1368 * volume is in a consistent state, then this information can be erased.
1369 *
1370 * Return: >0 Success, the clusters were wiped
1371 * 0 Nothing to wipe
1372 * -1 Error, something went wrong
1373 */
1374static s64 wipe_logfile(ntfs_volume *vol, int byte, enum action act
1375 __attribute__((unused)))
1376{
1377 const int NTFS_BUF_SIZE2 = 8192;
1378 //FIXME(?): We might need to zero the LSN field of every single mft
1379 //record as well. (But, first try without doing that and see what
1380 //happens, since chkdsk might pickup the pieces and do it for us...)
1381 ntfs_inode *ni;
1382 ntfs_attr *na;
1383 s64 len, pos, count;
1384 char buf[NTFS_BUF_SIZE2];
1385 int eo;
1386
1387 /* We can wipe logfile only with 0xff. */
1388 byte = 0xff;
1389
1390 if (!vol || (byte < 0))
1391 return -1;
1392
1393 //ntfs_log_quiet("wipe_logfile(not implemented) 0x%02x\n", byte);
1394
1395 if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) {
1396 ntfs_log_debug("Failed to open inode FILE_LogFile.\n");
1397 return -1;
1398 }
1399
1400 if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) {
1401 ntfs_log_debug("Failed to open $FILE_LogFile/$DATA.\n");
1402 goto error_exit;
1403 }
1404
1405 /* The $DATA attribute of the $LogFile has to be non-resident. */
1406 if (!NAttrNonResident(na)) {
1407 ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n");
1408 errno = EIO;
1409 goto io_error_exit;
1410 }
1411
1412 /* Get length of $LogFile contents. */
1413 len = na->data_size;
1414 if (!len) {
1415 ntfs_log_debug("$LogFile has zero length, no disk write "
1416 "needed.\n");
1417 return 0;
1418 }
1419
1420 /* Read $LogFile until its end. We do this as a check for correct
1421 length thus making sure we are decompressing the mapping pairs
1422 array correctly and hence writing below is safe as well. */
1423 pos = 0;
1424 while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE2, buf)) > 0)
1425 pos += count;
1426
1427 if (count == -1 || pos != len) {
1428 ntfs_log_debug("Amount of $LogFile data read does not "
1429 "correspond to expected length!\n");
1430 if (count != -1)
1431 errno = EIO;
1432 goto io_error_exit;
1433 }
1434
1435 /* Fill the buffer with @byte's. */
1436 memset(buf, byte, NTFS_BUF_SIZE2);
1437
1438 /* Set the $DATA attribute. */
1439 pos = 0;
1440 while ((count = len - pos) > 0) {
1441 if (count > NTFS_BUF_SIZE2)
1442 count = NTFS_BUF_SIZE2;
1443
1444 if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) {
1445 ntfs_log_debug("Failed to set the $LogFile attribute "
1446 "value.\n");
1447 if (count != -1)
1448 errno = EIO;
1449 goto io_error_exit;
1450 }
1451
1452 pos += count;
1453 }
1454
1455 ntfs_attr_close(na);
1456 ntfs_inode_close(ni);
1457 ntfs_log_quiet("wipe_logfile 0x%02x, %lld bytes\n", byte,
1458 (long long)pos);
1459 return pos;
1460
1461io_error_exit:
1462 eo = errno;
1463 ntfs_attr_close(na);
1464 errno = eo;
1465error_exit:
1466 eo = errno;
1467 ntfs_inode_close(ni);
1468 errno = eo;
1469 return -1;
1470}
1471
1472/**
1473 * wipe_pagefile - Wipe the pagefile (swap space)
1474 * @vol: An ntfs volume obtained from ntfs_mount
1475 * @byte: Overwrite with this value
1476 * @act: Wipe, test or info
1477 *
1478 * pagefile.sys is used by Windows as extra virtual memory (swap space).
1479 * Windows recreates the file at bootup, so it can be wiped without harm.
1480 *
1481 * Return: >0 Success, the clusters were wiped
1482 * 0 Nothing to wipe
1483 * -1 Error, something went wrong
1484 */
1485static s64 wipe_pagefile(ntfs_volume *vol, int byte, enum action act
1486 __attribute__((unused)))
1487{
1488 // wipe completely, chkdsk doesn't do anything, booting writes header
1489 const int NTFS_BUF_SIZE2 = 4096;
1490 ntfs_inode *ni;
1491 ntfs_attr *na;
1492 s64 len, pos, count;
1493 char buf[NTFS_BUF_SIZE2];
1494 int eo;
1495
1496 if (!vol || (byte < 0))
1497 return -1;
1498
1499 //ntfs_log_quiet("wipe_pagefile(not implemented) 0x%02x\n", byte);
1500
1501 ni = ntfs_pathname_to_inode(vol, NULL, "pagefile.sys");
1502 if (!ni) {
1503 ntfs_log_debug("Failed to open inode of pagefile.sys.\n");
1504 return 0;
1505 }
1506
1507 if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) {
1508 ntfs_log_debug("Failed to open pagefile.sys/$DATA.\n");
1509 goto error_exit;
1510 }
1511
1512 /* The $DATA attribute of the pagefile.sys has to be non-resident. */
1513 if (!NAttrNonResident(na)) {
1514 ntfs_log_debug("pagefile.sys $DATA attribute is resident!?!\n");
1515 errno = EIO;
1516 goto io_error_exit;
1517 }
1518
1519 /* Get length of pagefile.sys contents. */
1520 len = na->data_size;
1521 if (!len) {
1522 ntfs_log_debug("pagefile.sys has zero length, no disk write "
1523 "needed.\n");
1524 return 0;
1525 }
1526
1527 memset(buf, byte, NTFS_BUF_SIZE2);
1528
1529 /* Set the $DATA attribute. */
1530 pos = 0;
1531 while ((count = len - pos) > 0) {
1532 if (count > NTFS_BUF_SIZE2)
1533 count = NTFS_BUF_SIZE2;
1534
1535 if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) {
1536 ntfs_log_debug("Failed to set the pagefile.sys "
1537 "attribute value.\n");
1538 if (count != -1)
1539 errno = EIO;
1540 goto io_error_exit;
1541 }
1542
1543 pos += count;
1544 }
1545
1546 ntfs_attr_close(na);
1547 ntfs_inode_close(ni);
1548 ntfs_log_quiet("wipe_pagefile 0x%02x, %lld bytes\n", byte,
1549 (long long)pos);
1550 return pos;
1551
1552io_error_exit:
1553 eo = errno;
1554 ntfs_attr_close(na);
1555 errno = eo;
1556error_exit:
1557 eo = errno;
1558 ntfs_inode_close(ni);
1559 errno = eo;
1560 return -1;
1561}
1562
1563/**
1564 * Part of ntfsprogs.
1565 * Modified: removed logging, signal handling, removed data.
1566 *
1567 * free_file - Release the resources used by a file object
1568 * \param file The unwanted file object
1569 *
1570 * This will free up the memory used by a file object and iterate through the
1571 * object's children, freeing their resources too.
1572 *
1573 * \return none
1574 */
1575static void free_file (struct ufile *file)
1576{
1577 struct ntfs_list_head *item = NULL, *tmp = NULL;
1578 struct filename *f = NULL;
1579 struct data *d = NULL;
1580
1581 if (file == NULL)
1582 return;
1583
1584 ntfs_list_for_each_safe(item, tmp, &(file->name)) {
1585 /* List of filenames */
1586
1587 f = ntfs_list_entry(item, struct filename, list);
1588 if (f->name != NULL)
1589 free(f->name);
1590 if (f->parent_name != NULL) {
1591 free(f->parent_name);
1592 }
1593 free(f);
1594 }
1595
1596 ntfs_list_for_each_safe(item, tmp, &(file->data)) {
1597 /* List of data streams */
1598
1599 d = ntfs_list_entry(item, struct data, list);
1600 if (d->name != NULL)
1601 free(d->name);
1602 if (d->runlist != NULL)
1603 free(d->runlist);
1604 free(d);
1605 }
1606
1607
1608 free(file->mft);
1609 free(file);
1610}
1611
1612/**
1613 * Fills the given buffer with one of predefined patterns.
1614 * \param pat_no Pass number.
1615 * \param buffer Buffer to be filled.
1616 * \param buflen Length of the buffer.
1617 */
1618static void fill_buffer (
1619 unsigned long int pat_no,
1620 unsigned char * const buffer,
1621 const size_t buflen,
1622 int * const selected )
1623 /*@requires notnull buffer @*/ /*@sets *buffer @*/
1624{
1625
1626 size_t i;
1627#if (!defined HAVE_MEMCPY) && (!defined HAVE_STRING_H)
1628 size_t j;
1629#endif
1630 unsigned int bits;
1631
1632 if ((buffer == NULL) || (buflen == 0))
1633 return;
1634
1635 /* De-select all patterns once every npasses calls. */
1636 if (pat_no % npasses == 0) {
1637 for (i = 0; i < NPAT; i++) {
1638 selected[i] = 0;
1639 }
1640 }
1641 pat_no %= npasses;
1642 /* double check for npasses >= NPAT + 3: */
1643 for (i = 0; i < NPAT; i++) {
1644 if (selected[i] == 0)
1645 break;
1646 }
1647 if (i >= NPAT) {
1648 for (i = 0; i < NPAT; i++) {
1649 selected[i] = 0;
1650 }
1651 }
1652
1653 /* The first, last and middle passess will be using a random pattern */
1654 if ((pat_no == 0) || (pat_no == npasses-1) || (pat_no == npasses/2)) {
1655#if (!defined __STRICT_ANSI__) && (defined HAVE_RANDOM)
1656 bits = (unsigned int)(random() & 0xFFF);
1657#else
1658 bits = (unsigned int)(rand() & 0xFFF);
1659#endif
1660 } else {
1661 /* For other passes, one of the fixed patterns is selected. */
1662 do {
1663#if (!defined __STRICT_ANSI__) && (defined HAVE_RANDOM)
1664 i = (size_t)(random() % NPAT);
1665#else
1666 i = (size_t)(rand() % NPAT);
1667#endif
1668 } while (selected[i] == 1);
1669 bits = opts.bytes[i];
1670 selected[i] = 1;
1671 }
1672
1673 buffer[0] = (unsigned char) bits;
1674 buffer[1] = (unsigned char) bits;
1675 buffer[2] = (unsigned char) bits;
1676 for (i = 3; i < buflen / 2; i *= 2) {
1677#ifdef HAVE_MEMCPY
1678 memcpy(buffer + i, buffer, i);
1679#elif defined HAVE_STRING_H
1680 strncpy((char *)(buffer + i), (char *)buffer, i);
1681#else
1682 for (j = 0; j < i; j++) {
1683 buffer[i+j] = buffer[j];
1684 }
1685#endif
1686 }
1687 if (i < buflen) {
1688#ifdef HAVE_MEMCPY
1689 memcpy(buffer + i, buffer, buflen - i);
1690#elif defined HAVE_STRING_H
1691 strncpy((char *)(buffer + i), (char *)buffer, buflen - i);
1692#else
1693 for (j=0; j<buflen - i; j++) {
1694 buffer[i+j] = buffer[j];
1695 }
1696#endif
1697 }
1698}
1699
1700/**
1701 * Destroys the specified record's filenames and data.
1702 *
1703 * \param nv The filesystem.
1704 * \param record The record (i-node number), which filenames & data
1705 * to destroy.
1706 * \return 0 in case of no errors, other values otherwise.
1707 */
1708static int destroy_record(ntfs_volume *nv, const s64 record,
1709 unsigned char * const buf)
1710{
1711 struct ufile *file = NULL;
1712 runlist_element *rl = NULL;
1713 ntfs_attr *mft = NULL;
1714
1715 ntfs_attr_search_ctx *ctx = NULL;
1716 int ret_wfs = 0;
1717 unsigned long int pass, i;
1718 s64 j;
1719 unsigned char * a_offset;
1720 int selected[NPAT];
1721
1722 file = (struct ufile *) malloc(sizeof(struct ufile));
1723 if (file == NULL) {
1724 return -1;
1725 }
1726
1727 NTFS_INIT_LIST_HEAD(&(file->name));
1728 NTFS_INIT_LIST_HEAD(&(file->data));
1729 file->inode = record;
1730
1731 file->mft = (MFT_RECORD*)malloc(nv->mft_record_size);
1732 if (file->mft == NULL) {
1733 free_file (file);
1734 return -1;
1735 }
1736
1737 mft = ntfs_attr_open(nv->mft_ni, AT_DATA, AT_UNNAMED, 0);
1738 if (mft == NULL) {
1739 free_file(file);
1740 return -2;
1741 }
1742
Steve Kondike68cb602016-08-28 00:45:36 -07001743 /* Avoid getting fixup warnings on unitialized inodes */
1744 NVolSetNoFixupWarn(nv);
Steve Kondik2111ad72013-07-07 12:07:44 -07001745 /* Read the MFT reocrd of the i-node */
1746 if (ntfs_attr_mst_pread(mft, nv->mft_record_size * record, 1LL,
1747 nv->mft_record_size, file->mft) < 1) {
1748
Steve Kondike68cb602016-08-28 00:45:36 -07001749 NVolClearNoFixupWarn(nv);
Steve Kondik2111ad72013-07-07 12:07:44 -07001750 ntfs_attr_close(mft);
1751 free_file(file);
1752 return -3;
1753 }
Steve Kondike68cb602016-08-28 00:45:36 -07001754 NVolClearNoFixupWarn(nv);
Steve Kondik2111ad72013-07-07 12:07:44 -07001755 ntfs_attr_close(mft);
1756 mft = NULL;
1757
1758 ctx = ntfs_attr_get_search_ctx(NULL, file->mft);
1759 if (ctx == NULL) {
1760 free_file(file);
1761 return -4;
1762 }
1763
1764 /* Wiping file names */
1765 while (1 == 1) {
1766
1767 if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE,
1768 0LL, NULL, 0, ctx) != 0) {
1769 break; /* None / no more of that type */
1770 }
1771 if (ctx->attr == NULL)
1772 break;
1773
1774 /* We know this will always be resident.
1775 Find the offset of the data, including the MFT record. */
1776 a_offset = ((unsigned char *) ctx->attr
1777 + le16_to_cpu(ctx->attr->value_offset));
1778
1779 for (pass = 0; pass < npasses; pass++) {
1780 fill_buffer(pass, a_offset,
1781 le32_to_cpu(ctx->attr->value_length),
1782 selected);
1783
1784 if ( !opts.noaction ) {
1785 if (ntfs_mft_records_write(nv,
1786 MK_MREF(record, 0), 1LL,
1787 ctx->mrec) != 0) {
1788 ret_wfs = -5;
1789 break;
1790 }
1791 /* Flush after each writing, if more than
1792 1 overwriting needs to be done. Allow I/O
1793 bufferring (efficiency), if just one
1794 pass is needed. */
1795 if (npasses > 1) {
1796 nv->dev->d_ops->sync(nv->dev);
1797 }
1798 }
1799
1800 }
1801
1802 /* Wiping file name length */
1803 for (pass = 0; pass < npasses; pass++) {
1804
1805 fill_buffer (pass, (unsigned char *)
1806 &(ctx->attr->value_length), sizeof(u32),
1807 selected);
1808
1809 if (!opts.noaction) {
1810 if (ntfs_mft_records_write(nv,
1811 MK_MREF(record, 0),
1812 1LL, ctx->mrec) != 0) {
1813 ret_wfs = -5;
1814 break;
1815 }
1816
1817 if (npasses > 1) {
1818 nv->dev->d_ops->sync(nv->dev);
1819 }
1820 }
1821 }
Steve Kondike68cb602016-08-28 00:45:36 -07001822 ctx->attr->value_length = const_cpu_to_le32(0);
Steve Kondik2111ad72013-07-07 12:07:44 -07001823 if (!opts.noaction) {
1824 if (ntfs_mft_records_write(nv, MK_MREF(record, 0),
1825 1LL, ctx->mrec) != 0) {
1826 ret_wfs = -5;
1827 break;
1828 }
1829 }
1830 }
1831
1832 ntfs_attr_reinit_search_ctx(ctx);
1833
1834 /* Wiping file data */
1835 while (1 == 1) {
1836 if (ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, 0LL,
1837 NULL, 0, ctx) != 0) {
1838 break; /* None / no more of that type */
1839 }
1840 if (ctx->attr == NULL)
1841 break;
1842
1843 if (ctx->attr->non_resident == 0) {
1844 /* attribute is resident (part of MFT record) */
1845 /* find the offset of the data, including the MFT record */
1846 a_offset = ((unsigned char *) ctx->attr
1847 + le16_to_cpu(ctx->attr->value_offset));
1848
1849 /* Wiping the data itself */
1850 for (pass = 0; pass < npasses; pass++) {
1851
1852 fill_buffer (pass, a_offset,
1853 le32_to_cpu(ctx->attr->value_length),
1854 selected);
1855
1856 if (!opts.noaction) {
1857 if (ntfs_mft_records_write(nv,
1858 MK_MREF(record, 0),
1859 1LL, ctx->mrec) != 0) {
1860 ret_wfs = -5;
1861 break;
1862 }
1863
1864 if (npasses > 1) {
1865 nv->dev->d_ops->sync(nv->dev);
1866 }
1867 }
1868 }
1869
1870 /* Wiping data length */
1871 for (pass = 0; pass < npasses; pass++) {
1872
1873 fill_buffer(pass, (unsigned char *)
1874 &(ctx->attr->value_length),
1875 sizeof(u32), selected);
1876
1877 if (!opts.noaction) {
1878 if (ntfs_mft_records_write(nv,
1879 MK_MREF(record, 0),
1880 1LL, ctx->mrec) != 0) {
1881 ret_wfs = -5;
1882 break;
1883 }
1884
1885 if (npasses > 1) {
1886 nv->dev->d_ops->sync(nv->dev);
1887 }
1888 }
1889 }
Steve Kondike68cb602016-08-28 00:45:36 -07001890 ctx->attr->value_length = const_cpu_to_le32(0);
Steve Kondik2111ad72013-07-07 12:07:44 -07001891 if ( !opts.noaction ) {
1892 if (ntfs_mft_records_write(nv,
1893 MK_MREF(record, 0),
1894 1LL, ctx->mrec) != 0) {
1895 ret_wfs = -5;
1896 break;
1897 }
1898 }
1899 } else {
1900 /* Non-resident here */
1901
1902 rl = ntfs_mapping_pairs_decompress(nv,
1903 ctx->attr, NULL);
1904 if (rl == NULL) {
1905 continue;
1906 }
1907
1908 if (rl[0].length <= 0) {
1909 continue;
1910 }
1911
1912 for (i = 0; (rl[i].length > 0) && (ret_wfs == 0); i++) {
1913 if (rl[i].lcn == -1) {
1914 continue;
1915 }
1916 for (j = rl[i].lcn;
1917 (j < rl[i].lcn + rl[i].length)
1918 && (ret_wfs == 0); j++) {
1919
1920 if (utils_cluster_in_use(nv, j) != 0)
1921 continue;
1922 for (pass = 0;
1923 pass < npasses;
1924 pass++) {
1925
1926 fill_buffer(pass, buf,
1927 (size_t) nv->cluster_size,
1928 selected);
1929 if (!opts.noaction) {
1930 if (ntfs_cluster_write(
1931 nv, j, 1LL,
1932 buf) < 1) {
1933 ret_wfs = -5;
1934 break;
1935 }
1936
1937 if (npasses > 1) {
1938 nv->dev->d_ops->sync
1939 (nv->dev);
1940 }
1941 }
1942 }
1943 }
1944 }
1945
1946 /* Wipe the data length here */
1947 for (pass = 0; pass < npasses; pass++) {
1948 fill_buffer(pass, (unsigned char *)
1949 &(ctx->attr->lowest_vcn),
1950 sizeof(VCN), selected);
1951 fill_buffer(pass, (unsigned char *)
1952 &(ctx->attr->highest_vcn),
1953 sizeof(VCN), selected);
1954 fill_buffer(pass, (unsigned char *)
1955 &(ctx->attr->allocated_size),
1956 sizeof(s64), selected);
1957 fill_buffer(pass, (unsigned char *)
1958 &(ctx->attr->data_size),
1959 sizeof(s64), selected);
1960 fill_buffer(pass, (unsigned char *)
1961 &(ctx->attr->initialized_size),
1962 sizeof(s64), selected);
1963 fill_buffer(pass, (unsigned char *)
1964 &(ctx->attr->compressed_size),
1965 sizeof(s64), selected);
1966
1967 if ( !opts.noaction ) {
1968 if (ntfs_mft_records_write(nv,
1969 MK_MREF (record, 0),
1970 1LL, ctx->mrec) != 0) {
1971 ret_wfs = -5;
1972 break;
1973 }
1974
1975 if (npasses > 1) {
1976 nv->dev->d_ops->sync(nv->dev);
1977 }
1978 }
1979 }
Steve Kondike68cb602016-08-28 00:45:36 -07001980 ctx->attr->lowest_vcn = const_cpu_to_sle64(0);
1981 ctx->attr->highest_vcn = const_cpu_to_sle64(0);
1982 ctx->attr->allocated_size = const_cpu_to_sle64(0);
1983 ctx->attr->data_size = const_cpu_to_sle64(0);
1984 ctx->attr->initialized_size = const_cpu_to_sle64(0);
1985 ctx->attr->compressed_size = const_cpu_to_sle64(0);
Steve Kondik2111ad72013-07-07 12:07:44 -07001986 if (!opts.noaction) {
1987 if (ntfs_mft_records_write(nv,
1988 MK_MREF (record, 0),
1989 1LL, ctx->mrec) != 0) {
1990 ret_wfs = -5;
1991 break;
1992 }
1993 }
1994 } /* end of resident check */
1995 } /* end of 'wiping file data' loop */
1996
1997 ntfs_attr_put_search_ctx(ctx);
1998 free_file(file);
1999
2000 return ret_wfs;
2001}
2002
2003/**
2004 * Starts search for deleted inodes and undelete data on the given
2005 * NTFS filesystem.
2006 * \param FS The filesystem.
2007 * \return 0 in case of no errors, other values otherwise.
2008 */
2009static int wipe_unrm(ntfs_volume *nv)
2010{
2011 int ret_wfs = 0, ret;
2012 ntfs_attr *bitmapattr = NULL;
2013 s64 bmpsize, size, nr_mft_records, i, j, k;
2014 unsigned char b;
2015 unsigned char * buf = NULL;
2016
2017#define MYBUF_SIZE 8192
2018 unsigned char *mybuf;
2019#define MINIM(x, y) ( ((x)<(y))?(x):(y) )
2020
2021 mybuf = (unsigned char *) malloc(MYBUF_SIZE);
2022 if (mybuf == NULL) {
2023 return -1;
2024 }
2025
2026 buf = (unsigned char *) malloc(nv->cluster_size);
2027 if (buf == NULL) {
2028 free (mybuf);
2029 return -1;
2030 }
2031
2032 bitmapattr = ntfs_attr_open(nv->mft_ni, AT_BITMAP, AT_UNNAMED, 0);
2033 if (bitmapattr == NULL) {
2034 free (buf);
2035 free (mybuf);
2036 return -2;
2037 }
2038 bmpsize = bitmapattr->initialized_size;
2039
2040 nr_mft_records = nv->mft_na->initialized_size
2041 >> nv->mft_record_size_bits;
2042
2043 /* just like ntfsundelete; detects i-node numbers fine */
2044 for (i = 0; (i < bmpsize) && (ret_wfs==0); i += MYBUF_SIZE) {
2045
2046 /* read a part of the file bitmap */
2047 size = ntfs_attr_pread(bitmapattr, i,
2048 MINIM((bmpsize - i), MYBUF_SIZE), mybuf);
2049 if (size < 0)
2050 break;
2051
2052 /* parse each byte of the just-read part of the bitmap */
2053 for (j = 0; (j < size) && (ret_wfs==0); j++) {
2054 b = mybuf[j];
2055 /* parse each bit of the byte Bit 1 means 'in use'. */
2056 for (k = 0; (k < CHAR_BIT) && (ret_wfs==0);
2057 k++, b>>=1) {
2058 /* (i+j)*8+k is the i-node bit number */
2059 if (((i+j)*CHAR_BIT+k) >= nr_mft_records) {
2060 goto done;
2061 }
2062 if ((b & 1) != 0) {
2063 /* i-node is in use, skip it */
2064 continue;
2065 }
2066 /* wiping the i-node here: */
2067 ret = destroy_record (nv,
2068 (i+j)*CHAR_BIT+k, buf);
2069 if (ret != 0) {
2070 ret_wfs = ret;
2071 }
2072 }
2073 }
2074 }
2075done:
2076 ntfs_attr_close(bitmapattr);
2077 free(buf);
2078 free(mybuf);
2079
2080 ntfs_log_quiet("wipe_undelete\n");
2081 return ret_wfs;
2082}
2083
2084
2085
2086/**
2087 * print_summary - Tell the user what we are about to do
2088 *
2089 * List the operations about to be performed. The output will be silenced by
2090 * the --quiet option.
2091 *
2092 * Return: none
2093 */
2094static void print_summary(void)
2095{
2096 int i;
2097
2098 if (opts.noaction)
2099 ntfs_log_quiet("%s is in 'no-action' mode, it will NOT write to disk."
2100 "\n\n", EXEC_NAME);
2101
2102 ntfs_log_quiet("%s is about to wipe:\n", EXEC_NAME);
2103 if (opts.unused)
2104 ntfs_log_quiet("\tunused disk space\n");
Steve Kondik79165c32015-11-09 19:43:00 -08002105 if (opts.unused_fast)
2106 ntfs_log_quiet("\tunused disk space (fast)\n");
Steve Kondik2111ad72013-07-07 12:07:44 -07002107 if (opts.tails)
2108 ntfs_log_quiet("\tfile tails\n");
2109 if (opts.mft)
2110 ntfs_log_quiet("\tunused mft areas\n");
2111 if (opts.directory)
2112 ntfs_log_quiet("\tunused directory index space\n");
2113 if (opts.logfile)
2114 ntfs_log_quiet("\tthe logfile (journal)\n");
2115 if (opts.pagefile)
2116 ntfs_log_quiet("\tthe pagefile (swap space)\n");
2117 if (opts.undel)
2118 ntfs_log_quiet("\tundelete data\n");
2119
2120 ntfs_log_quiet("\n%s will overwrite these areas with: ", EXEC_NAME);
2121 if (opts.bytes) {
2122 for (i = 0; opts.bytes[i] >= 0; i++)
2123 ntfs_log_quiet("0x%02x ", opts.bytes[i]);
2124 }
2125 ntfs_log_quiet("\n");
2126
2127 if (opts.count > 1)
2128 ntfs_log_quiet("%s will repeat these operations %d times.\n", EXEC_NAME, opts.count);
2129 ntfs_log_quiet("\n");
2130}
2131
2132/**
2133 * main - Begin here
2134 *
2135 * Start from here.
2136 *
2137 * Return: 0 Success, the program worked
2138 * 1 Error, something went wrong
2139 */
2140int main(int argc, char *argv[])
2141{
2142 ntfs_volume *vol;
2143 int result = 1;
2144 int flags = 0;
Steve Kondik79165c32015-11-09 19:43:00 -08002145 int res;
Steve Kondik2111ad72013-07-07 12:07:44 -07002146 int i, j;
2147 enum action act = act_info;
2148
2149 ntfs_log_set_handler(ntfs_log_handler_outerr);
2150
Steve Kondik79165c32015-11-09 19:43:00 -08002151 res = parse_options(argc, argv);
2152 if (res >= 0)
2153 return (res);
Steve Kondik2111ad72013-07-07 12:07:44 -07002154
2155 utils_set_locale();
2156
2157 if (!opts.info)
2158 print_summary();
2159
2160 if (opts.info || opts.noaction)
2161 flags = NTFS_MNT_RDONLY;
2162 if (opts.force)
2163 flags |= NTFS_MNT_RECOVER;
2164
2165 vol = utils_mount_volume(opts.device, flags);
2166 if (!vol)
2167 goto free;
2168
2169 if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force)
2170 goto umount;
2171
2172 if (opts.info) {
2173 act = act_info;
2174 opts.count = 1;
2175 } else if (opts.noaction) {
2176 act = act_test;
2177 } else {
2178 act = act_wipe;
2179 }
2180
2181 /* Even if the output it quieted, you still get 5 seconds to abort. */
2182 if ((act == act_wipe) && !opts.force) {
2183 ntfs_log_quiet("\n%s will begin in 5 seconds, press CTRL-C to abort.\n", EXEC_NAME);
2184 sleep(5);
2185 }
2186
2187 for (i = 0; opts.bytes[i] >= 0; i++) {
2188 npasses = i+1;
2189 }
2190 if (npasses == 0) {
2191 npasses = opts.count;
2192 }
2193#ifdef HAVE_TIME_H
2194 srandom(time(NULL));
2195#else
2196 /* use a pointer as a pseudorandom value */
2197 srandom((int)vol + npasses);
2198#endif
2199 ntfs_log_info("\n");
2200 for (i = 0; i < opts.count; i++) {
2201 int byte;
2202 s64 total = 0;
2203 s64 wiped = 0;
2204
2205 for (j = 0; byte = opts.bytes[j], byte >= 0; j++) {
2206
2207 if (opts.directory) {
2208 wiped = wipe_directory(vol, byte, act);
2209 if (wiped < 0)
2210 goto umount;
2211 else
2212 total += wiped;
2213 }
2214
2215 if (opts.tails) {
2216 wiped = wipe_tails(vol, byte, act);
2217 if (wiped < 0)
2218 goto umount;
2219 else
2220 total += wiped;
2221 }
2222
2223 if (opts.logfile) {
2224 wiped = wipe_logfile(vol, byte, act);
2225 if (wiped < 0)
2226 goto umount;
2227 else
2228 total += wiped;
2229 }
2230
2231 if (opts.mft) {
2232 wiped = wipe_mft(vol, byte, act);
2233 if (wiped < 0)
2234 goto umount;
2235 else
2236 total += wiped;
2237 }
2238
2239 if (opts.pagefile) {
2240 wiped = wipe_pagefile(vol, byte, act);
2241 if (wiped < 0)
2242 goto umount;
2243 else
2244 total += wiped;
2245 }
2246
Steve Kondik79165c32015-11-09 19:43:00 -08002247 if (opts.unused || opts.unused_fast) {
2248 if (opts.unused_fast)
2249 wiped = wipe_unused_fast(vol, byte,
2250 act);
2251 else
2252 wiped = wipe_unused(vol, byte, act);
Steve Kondik2111ad72013-07-07 12:07:44 -07002253 if (wiped < 0)
2254 goto umount;
2255 else
2256 total += wiped;
2257 }
2258
2259 if (opts.undel) {
2260 wiped = wipe_unrm(vol);
2261 if (wiped != 0)
2262 goto umount;
2263 /*
2264 else
2265 total += wiped;
2266 */
2267 }
2268
2269 if (act == act_info)
2270 break;
2271 }
2272
Steve Kondike68cb602016-08-28 00:45:36 -07002273 if (opts.noaction || opts.info)
2274 ntfs_log_info("%lld bytes would be wiped"
2275 " (excluding undelete data)\n",
2276 (long long)total);
2277 else
2278 ntfs_log_info("%lld bytes were wiped"
2279 " (excluding undelete data)\n",
2280 (long long)total);
Steve Kondik2111ad72013-07-07 12:07:44 -07002281 }
2282 result = 0;
2283umount:
2284 ntfs_umount(vol, FALSE);
2285free:
2286 if (opts.bytes)
2287 free(opts.bytes);
2288 return result;
2289}