http://linux-ntfs.sourceforge.net/snapshots/ntfsprogs-200309071734.tar.bz2
[ntfsprogs.git] / ntfsprogs / ntfswipe.c
1 /**
2  * ntfswipe - Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2002-2003 Richard Russon
5  *
6  * This utility will overwrite usused space on an NTFS volume.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program (in the main directory of the Linux-NTFS
20  * distribution in the file COPYING); if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <stdarg.h>
29 #include <getopt.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33
34 #include "ntfswipe.h"
35 #include "types.h"
36 #include "volume.h"
37 #include "utils.h"
38 #include "debug.h"
39
40 static const char *EXEC_NAME = "ntfswipe";
41 static struct options opts;
42
43 GEN_PRINTF (Eprintf, stderr, NULL,          FALSE)
44 GEN_PRINTF (Vprintf, stdout, &opts.verbose, TRUE)
45 GEN_PRINTF (Qprintf, stdout, &opts.quiet,   FALSE)
46
47 /**
48  * version - Print version information about the program
49  *
50  * Print a copyright statement and a brief description of the program.
51  *
52  * Return:  none
53  */
54 void version (void)
55 {
56         printf ("\n%s v%s - Overwrite the unused space on an NTFS Volume.\n\n",
57                 EXEC_NAME, VERSION);
58         printf ("Copyright (c)\n");
59         printf ("    2002-2003 Richard Russon\n");
60         printf ("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
61 }
62
63 /**
64  * usage - Print a list of the parameters to the program
65  *
66  * Print a list of the parameters and options for the program.
67  *
68  * Return:  none
69  */
70 void usage (void)
71 {
72         printf ("\nUsage: %s [options] device\n"
73                 "    -i       --info        Show volume information (default)\n"
74                 "\n"
75                 "    -d       --directory   Wipe directory indexes\n"
76                 "    -l       --logfile     Wipe the logfile (journal)\n"
77                 "    -m       --mft         Wipe mft space\n"
78                 "    -p       --pagefile    Wipe pagefile (swap space)\n"
79                 "    -t       --tails       Wipe file tails\n"
80                 "    -u       --unused      Wipe unused clusters\n"
81                 "\n"
82                 "    -a       --all         Wipe all unused space\n"
83                 "\n"
84                 "    -c num   --count num   Number of times to write (default = 1)\n"
85                 "    -b list  --bytes list  List of values to write (default = 0)\n"
86                 "\n"
87                 "    -n       --no-action   Do not write to disk\n"
88                 "    -f       --force       Use less caution\n"
89                 "    -q       --quiet       Less output\n"
90                 "    -v       --verbose     More output\n"
91                 "    -V       --version     Version information\n"
92                 "    -h       --help        Print this help\n\n",
93                 EXEC_NAME);
94         printf ("%s%s\n", ntfs_bugs, ntfs_home);
95 }
96
97 /**
98  * parse_list - Read a comma-separated list of numbers
99  * @list:    The comma-separated list of numbers
100  * @result:  Store the parsed list here (must be freed by caller)
101  *
102  * Read a comma-separated list of numbers and allocate an array of ints to store
103  * them in.  The numbers can be in decimal, octal or hex.
104  *
105  * N.B.  The caller must free the memory returned in @result.
106  * N.B.  If the function fails, @result is not changed.
107  *
108  * Return:  0  Error, invalid string
109  *          n  Success, the count of numbers parsed
110  */
111 int parse_list (const char *list, int **result)
112 {
113         const char *ptr;
114         char *end;
115         int i;
116         int count;
117         int *mem = NULL;
118
119         if (!list || !result)
120                 return 0;
121
122         for (count = 0, ptr = list; ptr; ptr = strchr (ptr+1, ','))
123                 count++;
124
125         mem = malloc ((count+1) * sizeof (int));
126         if (!mem) {
127                 Eprintf ("Couldn't allocate memory in parse_list().\n");
128                 return 0;
129         }
130
131         memset (mem, 0xFF, (count+1) * sizeof (int));
132
133         for (ptr = list, i = 0; i < count; i++) {
134
135                 end = NULL;
136                 mem[i] = strtol (ptr, &end, 0);
137
138                 if (!end || (end == ptr) || ((*end != ',') && (*end != 0))) {
139                         Eprintf ("Invalid list '%s'\n", list);
140                         free (mem);
141                         return 0;
142                 }
143
144                 if ((mem[i] < 0) || (mem[i] > 255)) {
145                         Eprintf ("Bytes must be in range 0-255.\n");
146                         free (mem);
147                         return 0;
148                 }
149
150                 ptr = end + 1;
151         }
152
153         Dprintf ("Parsing list '%s' - ", list);
154         for (i = 0; i <= count; i++)
155                 Dprintf ("0x%02x ", mem[i]);
156         Dprintf ("\n");
157
158         *result = mem;
159         return count;
160 }
161
162 /**
163  * parse_options - Read and validate the programs command line
164  *
165  * Read the command line, verify the syntax and parse the options.
166  * This function is very long, but quite simple.
167  *
168  * Return:  1 Success
169  *          0 Error, one or more problems
170  */
171 int parse_options (int argc, char *argv[])
172 {
173         static const char *sopt = "-ab:c:dfh?ilmnpqtuvV";
174         static const struct option lopt[] = {
175                 { "all",        no_argument,            NULL, 'a' },
176                 { "bytes",      required_argument,      NULL, 'b' },
177                 { "count",      required_argument,      NULL, 'c' },
178                 { "directory",  no_argument,            NULL, 'd' },
179                 { "force",      no_argument,            NULL, 'f' },
180                 { "help",       no_argument,            NULL, 'h' },
181                 { "info",       no_argument,            NULL, 'i' },
182                 { "logfile",    no_argument,            NULL, 'l' },
183                 { "mft",        no_argument,            NULL, 'm' },
184                 { "no-action",  no_argument,            NULL, 'n' },
185                 { "pagefile",   no_argument,            NULL, 'p' },
186                 { "quiet",      no_argument,            NULL, 'q' },
187                 { "tails",      no_argument,            NULL, 't' },
188                 { "unused",     no_argument,            NULL, 'u' },
189                 { "verbose",    no_argument,            NULL, 'v' },
190                 { "version",    no_argument,            NULL, 'V' },
191                 { NULL,         0,                      NULL, 0   }
192         };
193
194         char c = -1;
195         char *end;
196         int err  = 0;
197         int ver  = 0;
198         int help = 0;
199
200         opterr = 0; /* We'll handle the errors, thank you. */
201
202         opts.count = 1;
203
204         while ((c = getopt_long (argc, argv, sopt, lopt, NULL)) != -1) {
205                 switch (c) {
206                 case 1: /* A non-option argument */
207                         if (!opts.device) {
208                                 opts.device = argv[optind-1];
209                         } else {
210                                 opts.device = NULL;
211                                 err++;
212                         }
213                         break;
214
215                 case 'i':
216                         opts.info++;            /* and fall through */
217                 case 'a':
218                         opts.directory++;
219                         opts.logfile++;
220                         opts.mft++;
221                         opts.pagefile++;
222                         opts.tails++;
223                         opts.unused++;
224                         break;
225                 case 'b':
226                         if (!opts.bytes) {
227                                 if (!parse_list (argv[optind-1], &opts.bytes))
228                                         err++;
229                         } else {
230                                 err++;
231                         }
232                         break;
233                 case 'c':
234                         if (opts.count == 1) {
235                                 end = NULL;
236                                 opts.count = strtol (optarg, &end, 0);
237                                 if (end && *end)
238                                         err++;
239                         } else {
240                                 err++;
241                         }
242                         break;
243                 case 'd':
244                         opts.directory++;
245                         break;
246                 case 'f':
247                         opts.force++;
248                         break;
249                 case 'h':
250                 case '?':
251                         help++;
252                         break;
253                 case 'l':
254                         opts.logfile++;
255                         break;
256                 case 'm':
257                         opts.mft++;
258                         break;
259                 case 'n':
260                         opts.noaction++;
261                         break;
262                 case 'p':
263                         opts.pagefile++;
264                         break;
265                 case 'q':
266                         opts.quiet++;
267                         break;
268                 case 't':
269                         opts.tails++;
270                         break;
271                 case 'u':
272                         opts.unused++;
273                         break;
274                 case 'v':
275                         opts.verbose++;
276                         break;
277                 case 'V':
278                         ver++;
279                         break;
280                 default:
281                         if ((optopt == 'b') || (optopt == 'c')) {
282                                 Eprintf ("Option '%s' requires an argument.\n", argv[optind-1]);
283                         } else {
284                                 Eprintf ("Unknown option '%s'.\n", argv[optind-1]);
285                         }
286                         err++;
287                         break;
288                 }
289         }
290
291         if (help || ver) {
292                 opts.quiet = 0;
293         } else {
294                 if (opts.device == NULL) {
295                         if (argc > 1)
296                                 Eprintf ("You must specify exactly one device.\n");
297                         err++;
298                 }
299
300                 if (opts.quiet && opts.verbose) {
301                         Eprintf ("You may not use --quiet and --verbose at the same time.\n");
302                         err++;
303                 }
304
305                 /*
306                 if (opts.info && (opts.unused || opts.tails || opts.mft || opts.directory)) {
307                         Eprintf ("You may not use any other options with --info.\n");
308                         err++;
309                 }
310                 */
311
312                 if ((opts.count < 1) || (opts.count > 100)) {
313                         Eprintf ("The iteration count must be between 1 and 100.\n");
314                         err++;
315                 }
316
317                 /* Create a default list */
318                 if (!opts.bytes) {
319                         opts.bytes = malloc (2 * sizeof (int));
320                         if (opts.bytes) {
321                                 opts.bytes[0] =  0;
322                                 opts.bytes[1] = -1;
323                         } else {
324                                 Eprintf ("Couldn't allocate memory for byte list.\n");
325                                 err++;
326                         }
327                 }
328
329                 if (!opts.directory && !opts.logfile && !opts.mft &&
330                     !opts.pagefile && !opts.tails && !opts.unused) {
331                         opts.info = 1;
332                 }
333         }
334
335         if (ver)
336                 version();
337         if (help || err)
338                 usage();
339
340         return (!err && !help && !ver);
341 }
342
343
344 /**
345  * wipe_unused - Wipe unused clusters
346  * @vol:   An ntfs volume obtained from ntfs_mount
347  * @byte:  Overwrite with this value
348  *
349  * Read $Bitmap and wipe any clusters that are marked as not in use.
350  *
351  * Return:  1  Success, the clusters were wiped
352  *          0  Error, something went wrong
353  */
354 int wipe_unused (ntfs_volume *vol, int byte, enum action act)
355 {
356         if (!vol || (byte < 0))
357                 return 0;
358
359         Qprintf ("wipe_unused 0x%02x\n", byte);
360         return 1;
361 }
362
363 /**
364  * wipe_tails - Wipe the file tails
365  * @vol:   An ntfs volume obtained from ntfs_mount
366  * @byte:  Overwrite with this value
367  *
368  * Disk space is allocated in clusters.  If a file isn't an exact multiple of
369  * the cluster size, there is some slack space at the end.  Wipe this space.
370  *
371  * Return:  1  Success, the clusters were wiped
372  *          0  Error, something went wrong
373  */
374 int wipe_tails (ntfs_volume *vol, int byte, enum action act)
375 {
376         if (!vol || (byte < 0))
377                 return 0;
378
379         Qprintf ("wipe_tails 0x%02x\n", byte);
380         return 1;
381 }
382
383 /**
384  * wipe_mft - Wipe the MFT slack space
385  * @vol:   An ntfs volume obtained from ntfs_mount
386  * @byte:  Overwrite with this value
387  *
388  * MFT Records are 1024 bytes long, but some of this space isn't used.  Wipe any
389  * unused space at the end of the record and wipe any unused records.
390  *
391  * Return:  1  Success, the clusters were wiped
392  *          0  Error, something went wrong
393  */
394 int wipe_mft (ntfs_volume *vol, int byte, enum action act)
395 {
396         if (!vol || (byte < 0))
397                 return 0;
398
399         Qprintf ("wipe_mft 0x%02x\n", byte);
400         return 1;
401 }
402
403 /**
404  * wipe_directory - Wipe the directory indexes
405  * @vol:   An ntfs volume obtained from ntfs_mount
406  * @byte:  Overwrite with this value
407  *
408  * Directories are kept in sorted B+ Trees.  Index blocks may not be full.  Wipe
409  * the unused space at the ends of these blocks.
410  *
411  * Return:  1  Success, the clusters were wiped
412  *          0  Error, something went wrong
413  */
414 int wipe_directory (ntfs_volume *vol, int byte, enum action act)
415 {
416         if (!vol || (byte < 0))
417                 return 0;
418
419         Qprintf ("wipe_directory 0x%02x\n", byte);
420         return 1;
421 }
422
423 /**
424  * wipe_logfile - Wipe the logfile (journal)
425  * @vol:   An ntfs volume obtained from ntfs_mount
426  * @byte:  Overwrite with this value
427  *
428  * The logfile journals the metadata to give the volume fault-tolerance.  If the
429  * volume is in a consistant state, then this information can be erased.
430  *
431  * Return:  1  Success, the clusters were wiped
432  *          0  Error, something went wrong
433  */
434 int wipe_logfile (ntfs_volume *vol, int byte, enum action act)
435 {
436         if (!vol || (byte < 0))
437                 return 0;
438
439         Qprintf ("wipe_logfile 0x%02x\n", byte);
440         return 1;
441 }
442
443 /**
444  * wipe_pagefile - Wipe the pagefile (swap space)
445  * @vol:   An ntfs volume obtained from ntfs_mount
446  * @byte:  Overwrite with this value
447  *
448  * pagefile.sys is used by Windows as extra virtual memory (swap space).
449  * Windows recreates the file at bootup, so it can be wiped without harm.
450  *
451  * Return:  1  Success, the clusters were wiped
452  *          0  Error, something went wrong
453  */
454 int wipe_pagefile (ntfs_volume *vol, int byte, enum action act)
455 {
456         if (!vol || (byte < 0))
457                 return 0;
458
459         Qprintf ("wipe_pagefile 0x%02x\n", byte);
460         return 1;
461 }
462
463
464 /**
465  * ntfs_info - Display information about the NTFS Volume
466  * @vol:  An ntfs volume obtained from ntfs_mount
467  *
468  * Tell the user how much could be cleaned up.  List the number of free
469  * clusters, MFT records, etc.
470  *
471  * Return:  1  Success, displayed some info
472  *          0  Error, something went wrong
473  */
474 int ntfs_info (ntfs_volume *vol)
475 {
476         u8 *buffer;
477
478         if (!vol)
479                 return 0;
480
481         Qprintf ("ntfs_info\n");
482
483         Qprintf ("\n");
484
485         Qprintf ("Cluster size = %u\n", vol->cluster_size);
486         Qprintf ("Volume size = %lld clusters\n", vol->nr_clusters);
487         Qprintf ("Volume size = %lld bytes\n", vol->nr_clusters * vol->cluster_size);
488         Qprintf ("Volume size = %lld MiB\n", vol->nr_clusters * vol->cluster_size / (1024*1024)); /* round up? */
489
490         Qprintf ("\n");
491
492         // move back bufsize
493         buffer = malloc (vol->mft_record_size);
494         if (!buffer)
495                 return 0;
496
497         Qprintf ("cluster\n");
498         //Qprintf ("allocated_size = %lld\n", vol->lcnbmp_na->allocated_size);
499         Qprintf ("data_size = %lld\n", vol->lcnbmp_na->data_size);
500         //Qprintf ("initialized_size = %lld\n", vol->lcnbmp_na->initialized_size);
501
502         {
503         u64 offset;
504         u64 size = vol->lcnbmp_na->allocated_size;
505         int bufsize = vol->mft_record_size;
506         u64 use = 0;
507         u64 not = 0;
508         int i, j;
509
510         for (offset = 0; offset < size; offset += bufsize) {
511
512                 if ((offset + bufsize) > size)
513                         bufsize = size - offset;
514
515                 if (ntfs_attr_pread (vol->lcnbmp_na, offset, bufsize, buffer) < bufsize) {
516                         Eprintf ("error\n");
517                         return 0;
518                 }
519
520                 for (i = 0; i < bufsize; i++) {
521                         for (j = 0; j < 8; j++) {
522                                 if ((((offset+i)*8) + j) >= vol->nr_clusters)
523                                         goto done;
524                                 if (buffer[i] & (1 << j)) {
525                                         //printf ("*");
526                                         use++;
527                                 } else {
528                                         //printf (".");
529                                         not++;
530                                 }
531                         }
532                 }
533         }
534 done:
535
536         Qprintf ("cluster use %lld, not %lld, total %lld\n", use, not, use+not);
537         Qprintf ("\n");
538
539         }
540
541         {
542         u8 *bitmap;
543         u64 bmpoff;
544         u64 bmpsize = vol->mftbmp_na->data_size;
545         int bmpbufsize = 512;
546         int i, j;
547         u64 use = 0, not = 0;
548
549         bitmap = malloc (bmpbufsize);
550         if (!bitmap)
551                 return 0;
552
553         printf ("mft has %lld records\n", vol->nr_mft_records);
554
555         //Qprintf ("allocated_size = %lld\n", vol->mftbmp_na->allocated_size);
556         Qprintf ("data_size = %lld\n", vol->mftbmp_na->data_size);
557         //Qprintf ("initialized_size = %lld\n", vol->mftbmp_na->initialized_size);
558
559         printf ("bmpsize = %lld\n", bmpsize);
560         for (bmpoff = 0; bmpoff < bmpsize; bmpoff += bmpbufsize) {
561                 if ((bmpoff + bmpbufsize) > bmpsize)
562                         bmpbufsize = bmpsize - bmpoff;
563
564                 //printf ("bmpbufsize = %d\n", bmpbufsize);
565
566                 if (ntfs_attr_pread (vol->mftbmp_na, bmpoff, bmpbufsize, bitmap) < bmpbufsize) {
567                         Eprintf ("error\n");
568                         return 0;
569                 }
570
571                 for (i = 0; i < bmpbufsize; i++) {
572                         for (j = 0; j < 8; j++) {
573                                 if ((((bmpoff+i)*8) + j) >= vol->nr_mft_records)
574                                         goto bmpdone;
575                                 if (bitmap[i] & (1 << j)) {
576                                         //printf ("*");
577                                         use++;
578                                 } else {
579                                         //printf (".");
580                                         not++;
581                                 }
582                         }
583                 }
584         }
585
586 bmpdone:
587         printf ("mft\n");
588         printf ("use %lld, not %lld, total %lld\n", use, not, use+not);
589
590         free (bitmap);
591         }
592
593
594         /*
595          * wipe_unused - volume = n clusters, u unused (%age & MB)
596          *      $Bitmap
597          *      vol->lcnbmp_na
598          *
599          * wipe_tails - volume = n files, total tail slack space
600          *      $MFT, $DATA
601          *      vol->mft_na
602          *
603          * wipe_mft - volume = n mft records, u unused, s total slack space
604          *      $MFT, $BITMAP
605          *      vol->mftbmp_na
606          *
607          * wipe_directory - volume has d dirs, t total slack space
608          *      $MFT, $INDEX_ROOT, $INDEX_ALLOC, $BITMAP
609          *
610          * wipe_logfile - logfile is <size>
611          *      $MFT, $DATA
612          *
613          * wipe_pagefile - pagefile is <size>
614          *      $MFT, $DATA
615          */
616
617         free (buffer);
618 #if 0
619         ntfs_inode *inode;
620         ntfs_attr *attr;
621
622         inode = ntfs_inode_open (vol, 6);       /* $Bitmap */
623         if (!inode)
624                 return 0;
625
626         attr = ntfs_attr_open (inode, AT_DATA, NULL, 0);
627         if (!attr)
628                 return 0;
629
630         ntfs_attr_pread
631
632         ntfs_attr_close (attr);
633         ntfs_inode_close (inode);
634 #endif
635
636         return 1;
637 }
638
639
640 /**
641  * print_summary - Tell the user what we are about to do
642  *
643  * List the operations about to be performed.  The output will be silenced by
644  * the --quiet option.
645  *
646  * Return:  none
647  */
648 void print_summary (void)
649 {
650         int i;
651
652         if (opts.noaction)
653                 Qprintf ("%s is in 'no-action' mode, it will NOT write to disk."
654                          "\n\n", EXEC_NAME);
655
656         Qprintf ("%s is about to wipe:\n", EXEC_NAME);
657         if (opts.unused)
658                 Qprintf ("\tunused disk space\n");
659         if (opts.tails)
660                 Qprintf ("\tfile tails\n");
661         if (opts.mft)
662                 Qprintf ("\tunused mft areas\n");
663         if (opts.directory)
664                 Qprintf ("\tunused directory index space\n");
665         if (opts.logfile)
666                 Qprintf ("\tthe logfile (journal)\n");
667         if (opts.pagefile)
668                 Qprintf ("\tthe pagefile (swap space)\n");
669
670         Qprintf ("\n%s will overwrite these areas with: ", EXEC_NAME);
671         if (opts.bytes) {
672                 for (i = 0; opts.bytes[i] >= 0; i++)
673                         Qprintf ("0x%02x ", opts.bytes[i]);
674         }
675         Qprintf ("\n");
676
677         if (opts.count > 1)
678                 Qprintf ("%s will repeat these operations %d times.\n", EXEC_NAME, opts.count);
679         Qprintf ("\n");
680 }
681
682 /**
683  * main - Begin here
684  *
685  * Start from here.
686  *
687  * Return:  0  Success, the program worked
688  *          1  Error, something went wrong
689  */
690 int main (int argc, char *argv[])
691 {
692         ntfs_volume *vol;
693         int result = 1;
694         int flags = 0;
695         int i, j;
696         enum action act = act_info;
697
698         if (!parse_options (argc, argv))
699                 return 1;
700
701         utils_set_locale();
702
703         if (!opts.info)
704                 print_summary();
705
706         if (opts.info || opts.noaction)
707                 flags = MS_RDONLY;
708
709         vol = utils_mount_volume (opts.device, flags, opts.force);
710         if (!vol)
711                 goto free;
712
713         if (vol->flags & VOLUME_IS_DIRTY) {
714                 Qprintf ("Volume is dirty.\n");
715                 if (!opts.force) {
716                         Eprintf ("Run chkdsk and try again, or use the --force option.\n");
717                         goto umount;
718                 }
719                 Qprintf ("Forced to continue.\n");
720         }
721
722         if (opts.info) {
723                 act = act_info;
724                 opts.count = 1;
725         } else if (opts.noaction) {
726                 act = act_test;
727         } else {
728                 act = act_wipe;
729         }
730
731         /* Even if the output it quieted, you still get 5 seconds to abort. */
732         if ((act == act_wipe) && !opts.force) {
733                 Qprintf ("\n%s will begin in 5 seconds, press CTRL-C to abort.\n", EXEC_NAME);
734                 sleep (5);
735         }
736
737         if (0)
738         {
739                 int i = 0;
740                 runlist_element *rl = vol->mft_na->rl;
741                 printf ("________________________________________________________________________________\n\n");
742                 for (; rl->length > 0; rl++, i++) {
743                         printf ("%4d %lld,%lld,%lld\n", i, rl->vcn, rl->lcn, rl->length);
744                 }
745                 printf ("%4d %lld,%lld,%lld\n", i, rl->vcn, rl->lcn, rl->length);
746                 return 0;
747         }
748
749         printf ("\n");
750         for (i = 0; i < opts.count; i++) {
751                 int byte;
752                 s64 total = 0;
753                 s64 wiped = 0;
754
755                 for (j = 0; byte = opts.bytes[j], byte >= 0; j++) {
756
757                         if (opts.directory) {
758                                 wiped = wipe_directory (vol, byte, act);
759                                 if (wiped < 0)
760                                         goto umount;
761                                 else
762                                         total += wiped;
763                         }
764
765                         if (opts.tails) {
766                                 wiped = wipe_tails (vol, byte, act);
767                                 if (wiped < 0)
768                                         goto umount;
769                                 else
770                                         total += wiped;
771                         }
772
773                         if (opts.logfile) {
774                                 wiped = wipe_logfile (vol, byte, act);
775                                 if (wiped < 0)
776                                         goto umount;
777                                 else
778                                         total += wiped;
779                         }
780
781                         if (opts.mft) {
782                                 wiped = wipe_mft (vol, byte, act);
783                                 if (wiped < 0)
784                                         goto umount;
785                                 else
786                                         total += wiped;
787                         }
788
789                         if (opts.pagefile) {
790                                 wiped = wipe_pagefile (vol, byte, act);
791                                 if (wiped < 0)
792                                         goto umount;
793                                 else
794                                         total += wiped;
795                         }
796
797                         if (opts.unused) {
798                                 wiped = wipe_unused (vol, byte, act);
799                                 if (wiped < 0)
800                                         goto umount;
801                                 else
802                                         total += wiped;
803                         }
804
805                         if (act == act_info)
806                                 break;
807                 }
808         }
809
810         result = 0;
811 umount:
812         ntfs_umount (vol, FALSE);
813 free:
814         if (opts.bytes)
815                 free (opts.bytes);
816         return result;
817 }
818
819