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