summaryrefslogtreecommitdiff
Diffstat
-rwxr-xr-xAndroid.mk138
-rwxr-xr-xAndroid.mk.bak89
-rwxr-xr-xCREDITS20
-rwxr-xr-xINSTALL200
-rwxr-xr-xMakefile715
-rwxr-xr-xMakefile.am106
-rwxr-xr-xMakefile.in955
-rwxr-xr-xNEWS4
-rwxr-xr-xREADME80
-rwxr-xr-xTODO.ntfsprogs126
-rwxr-xr-xaclocal.m47091
-rwxr-xr-xcompile21
-rwxr-xr-xconfig.guess292
-rw-r--r--[-rwxr-xr-x]config.h165
-rwxr-xr-xconfig.h.in110
-rwxr-xr-xconfig.log3290
-rwxr-xr-xconfig.status1220
-rwxr-xr-xconfig.sub104
-rwxr-xr-xconfigure26448
-rwxr-xr-xconfigure.ac291
-rwxr-xr-xdepcomp87
-rwxr-xr-xinclude/Makefile.in181
-rwxr-xr-xinclude/fuse-lite/Makefile.in112
-rwxr-xr-xinclude/fuse-lite/fuse.h61
-rwxr-xr-xinclude/fuse-lite/fuse_common.h49
-rwxr-xr-xinclude/fuse-lite/fuse_kernel.h6
-rwxr-xr-xinclude/fuse-lite/fuse_lowlevel.h20
-rwxr-xr-xinclude/ntfs-3g/Makefile.am4
-rwxr-xr-xinclude/ntfs-3g/Makefile.in163
-rwxr-xr-xinclude/ntfs-3g/acls.h3
-rwxr-xr-xinclude/ntfs-3g/attrib.h50
-rwxr-xr-xinclude/ntfs-3g/cache.h7
-rwxr-xr-xinclude/ntfs-3g/compat.h6
-rwxr-xr-xinclude/ntfs-3g/compress.h6
-rwxr-xr-xinclude/ntfs-3g/debug.h4
-rwxr-xr-xinclude/ntfs-3g/device.h18
-rwxr-xr-xinclude/ntfs-3g/device_io.h21
-rwxr-xr-xinclude/ntfs-3g/dir.h11
-rwxr-xr-xinclude/ntfs-3g/layout.h32
-rwxr-xr-xinclude/ntfs-3g/lcnalloc.h1
-rwxr-xr-xinclude/ntfs-3g/logging.h3
-rwxr-xr-xinclude/ntfs-3g/mst.h3
-rwxr-xr-xinclude/ntfs-3g/ntfstime.h12
-rwxr-xr-xinclude/ntfs-3g/param.h72
-rwxr-xr-xinclude/ntfs-3g/realpath.h24
-rwxr-xr-xinclude/ntfs-3g/runlist.h3
-rwxr-xr-xinclude/ntfs-3g/security.h20
-rwxr-xr-xinclude/ntfs-3g/types.h8
-rwxr-xr-xinclude/ntfs-3g/unistr.h13
-rwxr-xr-xinclude/ntfs-3g/volume.h78
-rwxr-xr-xinclude/ntfs-3g/xattrs.h75
-rwxr-xr-xinstall-sh5
-rwxr-xr-xlibfuse-lite/Makefile.am3
-rwxr-xr-xlibfuse-lite/Makefile.in188
-rwxr-xr-xlibfuse-lite/fuse.c418
-rwxr-xr-xlibfuse-lite/fuse_kern_chan.c2
-rwxr-xr-xlibfuse-lite/fuse_lowlevel.c53
-rwxr-xr-xlibfuse-lite/fuse_opt.c20
-rwxr-xr-xlibfuse-lite/fuse_session.c12
-rwxr-xr-xlibfuse-lite/fusermount.c97
-rwxr-xr-xlibfuse-lite/helper.c21
-rwxr-xr-xlibfuse-lite/mount.c486
-rwxr-xr-xlibfuse-lite/mount_util.c246
-rwxr-xr-xlibfuse-lite/mount_util.h11
-rwxr-xr-xlibntfs-3g/Makefile.am15
-rwxr-xr-xlibntfs-3g/Makefile.in420
-rwxr-xr-xlibntfs-3g/acls.c219
-rwxr-xr-xlibntfs-3g/attrib.c1205
-rwxr-xr-xlibntfs-3g/bootsect.c4
-rwxr-xr-xlibntfs-3g/cache.c7
-rwxr-xr-xlibntfs-3g/compress.c962
-rwxr-xr-xlibntfs-3g/device.c252
-rwxr-xr-xlibntfs-3g/dir.c374
-rwxr-xr-xlibntfs-3g/efs.c217
-rwxr-xr-xlibntfs-3g/index.c30
-rwxr-xr-xlibntfs-3g/inode.c46
-rwxr-xr-xlibntfs-3g/lcnalloc.c36
-rw-r--r--libntfs-3g/libntfs-3g.pc10
-rw-r--r--libntfs-3g/libntfs-3g.script.so2
-rwxr-xr-xlibntfs-3g/logfile.c23
-rwxr-xr-xlibntfs-3g/logging.c24
-rwxr-xr-xlibntfs-3g/mft.c12
-rwxr-xr-xlibntfs-3g/mst.c24
-rwxr-xr-xlibntfs-3g/object_id.c9
-rwxr-xr-xlibntfs-3g/realpath.c103
-rwxr-xr-xlibntfs-3g/reparse.c67
-rwxr-xr-xlibntfs-3g/runlist.c62
-rwxr-xr-xlibntfs-3g/security.c345
-rwxr-xr-xlibntfs-3g/unistr.c244
-rwxr-xr-xlibntfs-3g/unix_io.c19
-rwxr-xr-xlibntfs-3g/volume.c365
-rwxr-xr-xlibntfs-3g/win32_io.c708
-rwxr-xr-xlibntfs-3g/xattrs.c791
-rwxr-xr-xlibtool9301
-rwxr-xr-xltmain.sh8439
-rwxr-xr-xm4/libtool.m47377
-rwxr-xr-xm4/ltoptions.m4368
-rwxr-xr-xm4/ltsugar.m4123
-rwxr-xr-xm4/ltversion.m423
-rwxr-xr-xm4/lt~obsolete.m492
-rwxr-xr-xmissing49
-rwxr-xr-xntfsprogs/Makefile.am154
-rwxr-xr-xntfsprogs/Makefile.in1199
-rwxr-xr-xntfsprogs/attrdef.c168
-rwxr-xr-xntfsprogs/attrdef.h7
-rwxr-xr-xntfsprogs/boot.c268
-rwxr-xr-xntfsprogs/boot.h7
-rwxr-xr-xntfsprogs/cluster.c118
-rwxr-xr-xntfsprogs/cluster.h39
-rwxr-xr-xntfsprogs/list.h194
-rw-r--r--ntfsprogs/mkntfs.8290
-rwxr-xr-xntfsprogs/mkntfs.8.in290
-rwxr-xr-xntfsprogs/mkntfs.c5177
-rw-r--r--ntfsprogs/ntfscat.8136
-rwxr-xr-xntfsprogs/ntfscat.8.in136
-rwxr-xr-xntfsprogs/ntfscat.c440
-rwxr-xr-xntfsprogs/ntfscat.h46
-rwxr-xr-xntfsprogs/ntfsck.c883
-rw-r--r--ntfsprogs/ntfsclone.8391
-rwxr-xr-xntfsprogs/ntfsclone.8.in391
-rwxr-xr-xntfsprogs/ntfsclone.c2701
-rw-r--r--ntfsprogs/ntfscluster.8124
-rwxr-xr-xntfsprogs/ntfscluster.8.in124
-rwxr-xr-xntfsprogs/ntfscluster.c563
-rwxr-xr-xntfsprogs/ntfscluster.h63
-rw-r--r--ntfsprogs/ntfscmp.877
-rwxr-xr-xntfsprogs/ntfscmp.8.in77
-rwxr-xr-xntfsprogs/ntfscmp.c1012
-rw-r--r--ntfsprogs/ntfscp.8111
-rwxr-xr-xntfsprogs/ntfscp.8.in111
-rwxr-xr-xntfsprogs/ntfscp.c590
-rwxr-xr-xntfsprogs/ntfsdecrypt.c1436
-rwxr-xr-xntfsprogs/ntfsdump_logfile.c779
-rw-r--r--ntfsprogs/ntfsfix.881
-rwxr-xr-xntfsprogs/ntfsfix.8.in81
-rwxr-xr-xntfsprogs/ntfsfix.c1657
-rw-r--r--ntfsprogs/ntfsinfo.889
-rwxr-xr-xntfsprogs/ntfsinfo.8.in89
-rwxr-xr-xntfsprogs/ntfsinfo.c2384
-rw-r--r--ntfsprogs/ntfslabel.8118
-rwxr-xr-xntfsprogs/ntfslabel.8.in118
-rwxr-xr-xntfsprogs/ntfslabel.c458
-rw-r--r--ntfsprogs/ntfsls.8172
-rwxr-xr-xntfsprogs/ntfsls.8.in172
-rwxr-xr-xntfsprogs/ntfsls.c717
-rwxr-xr-xntfsprogs/ntfsmftalloc.c368
-rwxr-xr-xntfsprogs/ntfsmove.c923
-rwxr-xr-xntfsprogs/ntfsmove.h46
-rw-r--r--ntfsprogs/ntfsprogs.869
-rwxr-xr-xntfsprogs/ntfsprogs.8.in69
-rw-r--r--ntfsprogs/ntfsresize.8326
-rwxr-xr-xntfsprogs/ntfsresize.8.in326
-rwxr-xr-xntfsprogs/ntfsresize.c4497
-rwxr-xr-xntfsprogs/ntfstruncate.c809
-rw-r--r--ntfsprogs/ntfsundelete.8324
-rwxr-xr-xntfsprogs/ntfsundelete.8.in324
-rwxr-xr-xntfsprogs/ntfsundelete.c2490
-rwxr-xr-xntfsprogs/ntfsundelete.h112
-rwxr-xr-xntfsprogs/ntfswipe.c2131
-rwxr-xr-xntfsprogs/ntfswipe.h54
-rwxr-xr-xntfsprogs/sd.c607
-rwxr-xr-xntfsprogs/sd.h11
-rwxr-xr-xntfsprogs/utils.c1184
-rwxr-xr-xntfsprogs/utils.h137
-rwxr-xr-xprog.IAB1071
-rwxr-xr-xprog.IAD5
-rwxr-xr-xprog.IMB466
-rwxr-xr-xprog.IMD2
-rwxr-xr-xprog.PFI2
-rwxr-xr-xprog.PO1
-rwxr-xr-xprog.PR14
-rwxr-xr-xprog.PRI219
-rwxr-xr-xprog.PS979
-rwxr-xr-xprog.SearchResults3
-rwxr-xr-xprog.WK39
-rwxr-xr-xsrc/Makefile.am86
-rwxr-xr-xsrc/Makefile.in938
-rwxr-xr-xsrc/lowntfs-3g.c1538
-rw-r--r--src/ntfs-3g.8448
-rwxr-xr-xsrc/ntfs-3g.8.in217
-rwxr-xr-xsrc/ntfs-3g.c1357
-rw-r--r--src/ntfs-3g.probe.881
-rwxr-xr-xsrc/ntfs-3g.probe.8.in4
-rwxr-xr-xsrc/ntfs-3g.probe.c9
-rw-r--r--src/ntfs-3g.secaudit.8184
-rwxr-xr-xsrc/ntfs-3g.secaudit.8.in15
-rw-r--r--src/ntfs-3g.usermap.896
-rwxr-xr-xsrc/ntfs-3g_common.c745
-rwxr-xr-xsrc/ntfs-3g_common.h185
-rwxr-xr-xsrc/secaudit.c673
-rwxr-xr-xsrc/secaudit.h32
-rwxr-xr-xsrc/usermap.c3
-rw-r--r--[-rwxr-xr-x]stamp-h10
193 files changed, 80411 insertions, 44563 deletions
diff --git a/ntfsprogs/ntfswipe.c b/ntfsprogs/ntfswipe.c
new file mode 100755
index 0000000..e432897
--- a/dev/null
+++ b/ntfsprogs/ntfswipe.c
@@ -0,0 +1,2131 @@
+/**
+ * ntfswipe - Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2005 Anton Altaparmakov
+ * Copyright (c) 2002-2005 Richard Russon
+ * Copyright (c) 2004 Yura Pakhuchiy
+ *
+ * This utility will overwrite unused space on an NTFS volume.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#else
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "ntfswipe.h"
+#include "types.h"
+#include "volume.h"
+#include "utils.h"
+#include "debug.h"
+#include "dir.h"
+#include "mst.h"
+/* #include "version.h" */
+#include "logging.h"
+#include "list.h"
+#include "mft.h"
+
+static const char *EXEC_NAME = "ntfswipe";
+static struct options opts;
+static unsigned long int npasses = 0;
+
+struct filename {
+ char *parent_name;
+ struct ntfs_list_head list; /* Previous/Next links */
+ ntfschar *uname; /* Filename in unicode */
+ int uname_len; /* and its length */
+ /* Allocated size (multiple of cluster size) */
+ s64 size_alloc;
+ s64 size_data; /* Actual size of data */
+ long long parent_mref;
+ FILE_ATTR_FLAGS flags;
+ time_t date_c; /* Time created */
+ time_t date_a; /* altered */
+ time_t date_m; /* mft record changed */
+ time_t date_r; /* read */
+ char *name; /* Filename in current locale */
+ FILE_NAME_TYPE_FLAGS name_space;
+ char padding[7]; /* Unused: padding to 64 bit. */
+};
+
+struct data {
+ struct ntfs_list_head list; /* Previous/Next links */
+ char *name; /* Stream name in current locale */
+ ntfschar *uname; /* Unicode stream name */
+ int uname_len; /* and its length */
+ int resident; /* Stream is resident */
+ int compressed; /* Stream is compressed */
+ int encrypted; /* Stream is encrypted */
+ /* Allocated size (multiple of cluster size) */
+ s64 size_alloc;
+ s64 size_data; /* Actual size of data */
+ /* Initialised size, may be less than data size */
+ s64 size_init;
+ VCN size_vcn; /* Highest VCN in the data runs */
+ runlist_element *runlist; /* Decoded data runs */
+ int percent; /* Amount potentially recoverable */
+ void *data; /* If resident, a pointer to the data */
+ char padding[4]; /* Unused: padding to 64 bit. */
+};
+
+struct ufile {
+ s64 inode; /* MFT record number */
+ time_t date; /* Last modification date/time */
+ struct ntfs_list_head name; /* A list of filenames */
+ struct ntfs_list_head data; /* A list of data streams */
+ char *pref_name; /* Preferred filename */
+ char *pref_pname; /* parent filename */
+ s64 max_size; /* Largest size we find */
+ int attr_list; /* MFT record may be one of many */
+ int directory; /* MFT record represents a directory */
+ MFT_RECORD *mft; /* Raw MFT record */
+ char padding[4]; /* Unused: padding to 64 bit. */
+};
+
+#define NPAT 22
+
+/* Taken from `shred' source */
+static const unsigned int patterns[NPAT] = {
+ 0x000, 0xFFF, /* 1-bit */
+ 0x555, 0xAAA, /* 2-bit */
+ 0x249, 0x492, 0x6DB, 0x924, 0xB6D, 0xDB6, /* 3-bit */
+ 0x111, 0x222, 0x333, 0x444, 0x666, 0x777,
+ 0x888, 0x999, 0xBBB, 0xCCC, 0xDDD, 0xEEE /* 4-bit */
+};
+
+
+/**
+ * version - Print version information about the program
+ *
+ * Print a copyright statement and a brief description of the program.
+ *
+ * Return: none
+ */
+static void version(void)
+{
+ ntfs_log_info("\n%s v%s (libntfs-3g) - Overwrite the unused space on an NTFS "
+ "Volume.\n\n", EXEC_NAME, VERSION);
+ ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n");
+ ntfs_log_info("Copyright (c) 2004 Yura Pakhuchiy\n");
+ ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
+}
+
+/**
+ * usage - Print a list of the parameters to the program
+ *
+ * Print a list of the parameters and options for the program.
+ *
+ * Return: none
+ */
+static void usage(void)
+{
+ ntfs_log_info("\nUsage: %s [options] device\n"
+ " -i --info Show volume information (default)\n"
+ "\n"
+ " -d --directory Wipe directory indexes\n"
+ " -l --logfile Wipe the logfile (journal)\n"
+ " -m --mft Wipe mft space\n"
+ " -p --pagefile Wipe pagefile (swap space)\n"
+ " -t --tails Wipe file tails\n"
+ " -u --unused Wipe unused clusters\n"
+ " -s --undel Wipe undelete data\n"
+ "\n"
+ " -a --all Wipe all unused space\n"
+ "\n"
+ " -c num --count num Number of times to write(default = 1)\n"
+ " -b list --bytes list List of values to write(default = 0)\n"
+ "\n"
+ " -n --no-action Do not write to disk\n"
+ " -f --force Use less caution\n"
+ " -q --quiet Less output\n"
+ " -v --verbose More output\n"
+ " -V --version Version information\n"
+ " -h --help Print this help\n\n",
+ EXEC_NAME);
+ ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
+}
+
+/**
+ * parse_list - Read a comma-separated list of numbers
+ * @list: The comma-separated list of numbers
+ * @result: Store the parsed list here (must be freed by caller)
+ *
+ * Read a comma-separated list of numbers and allocate an array of ints to store
+ * them in. The numbers can be in decimal, octal or hex.
+ *
+ * N.B. The caller must free the memory returned in @result.
+ * N.B. If the function fails, @result is not changed.
+ *
+ * Return: 0 Error, invalid string
+ * n Success, the count of numbers parsed
+ */
+static int parse_list(char *list, int **result)
+{
+ char *ptr;
+ char *end;
+ int i;
+ int count;
+ int *mem = NULL;
+
+ if (!list || !result)
+ return 0;
+
+ for (count = 0, ptr = list; ptr; ptr = strchr(ptr+1, ','))
+ count++;
+
+ mem = malloc((count+1) * sizeof(int));
+ if (!mem) {
+ ntfs_log_error("Couldn't allocate memory in parse_list().\n");
+ return 0;
+ }
+
+ memset(mem, 0xFF, (count+1) * sizeof(int));
+
+ for (ptr = list, i = 0; i < count; i++) {
+
+ end = NULL;
+ mem[i] = strtol(ptr, &end, 0);
+
+ if (!end || (end == ptr) || ((*end != ',') && (*end != 0))) {
+ ntfs_log_error("Invalid list '%s'\n", list);
+ free(mem);
+ return 0;
+ }
+
+ if ((mem[i] < 0) || (mem[i] > 255)) {
+ ntfs_log_error("Bytes must be in range 0-255.\n");
+ free(mem);
+ return 0;
+ }
+
+ ptr = end + 1;
+ }
+
+ ntfs_log_debug("Parsing list '%s' - ", list);
+ for (i = 0; i <= count; i++)
+ ntfs_log_debug("0x%02x ", mem[i]);
+ ntfs_log_debug("\n");
+
+ *result = mem;
+ return count;
+}
+
+/**
+ * parse_options - Read and validate the programs command line
+ *
+ * Read the command line, verify the syntax and parse the options.
+ * This function is very long, but quite simple.
+ *
+ * Return: 1 Success
+ * 0 Error, one or more problems
+ */
+static int parse_options(int argc, char *argv[])
+{
+ static const char *sopt = "-ab:c:dfh?ilmnpqtuvVs";
+ static struct option lopt[] = {
+ { "all", no_argument, NULL, 'a' },
+ { "bytes", required_argument, NULL, 'b' },
+ { "count", required_argument, NULL, 'c' },
+ { "directory", no_argument, NULL, 'd' },
+ { "force", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { "info", no_argument, NULL, 'i' },
+ { "logfile", no_argument, NULL, 'l' },
+ { "mft", no_argument, NULL, 'm' },
+ { "no-action", no_argument, NULL, 'n' },
+ //{ "no-wait", no_argument, NULL, 0 },
+ { "pagefile", no_argument, NULL, 'p' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "tails", no_argument, NULL, 't' },
+ { "unused", no_argument, NULL, 'u' },
+ { "undel", no_argument, NULL, 's' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c = -1;
+ char *end;
+ int err = 0;
+ int ver = 0;
+ int help = 0;
+ int levels = 0;
+
+ opterr = 0; /* We'll handle the errors, thank you. */
+
+ opts.count = 1;
+
+ while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
+ switch (c) {
+ case 1: /* A non-option argument */
+ if (!opts.device) {
+ opts.device = argv[optind-1];
+ } else {
+ opts.device = NULL;
+ err++;
+ }
+ break;
+
+ case 'i':
+ opts.info++; /* and fall through */
+ case 'a':
+ opts.directory++;
+ opts.logfile++;
+ opts.mft++;
+ opts.pagefile++;
+ opts.tails++;
+ opts.unused++;
+ opts.undel++;
+ break;
+ case 'b':
+ if (!opts.bytes) {
+ if (!parse_list(optarg, &opts.bytes))
+ err++;
+ } else {
+ err++;
+ }
+ break;
+ case 'c':
+ if (opts.count == 1) {
+ end = NULL;
+ opts.count = strtol(optarg, &end, 0);
+ if (end && *end)
+ err++;
+ } else {
+ err++;
+ }
+ break;
+ case 'd':
+ opts.directory++;
+ break;
+ case 'f':
+ opts.force++;
+ break;
+ case 'h':
+ case '?':
+ if (strncmp (argv[optind-1], "--log-", 6) == 0) {
+ if (!ntfs_log_parse_option (argv[optind-1]))
+ err++;
+ break;
+ }
+ help++;
+ break;
+ case 'l':
+ opts.logfile++;
+ break;
+ case 'm':
+ opts.mft++;
+ break;
+ case 'n':
+ opts.noaction++;
+ break;
+ case 'p':
+ opts.pagefile++;
+ break;
+ case 'q':
+ opts.quiet++;
+ ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
+ break;
+ case 's':
+ opts.undel++;
+ break;
+ case 't':
+ opts.tails++;
+ break;
+ case 'u':
+ opts.unused++;
+ break;
+ case 'v':
+ opts.verbose++;
+ ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
+ break;
+ case 'V':
+ ver++;
+ break;
+ default:
+ if ((optopt == 'b') || (optopt == 'c')) {
+ ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]);
+ } else {
+ ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
+ }
+ err++;
+ break;
+ }
+ }
+
+ /* Make sure we're in sync with the log levels */
+ levels = ntfs_log_get_levels();
+ if (levels & NTFS_LOG_LEVEL_VERBOSE)
+ opts.verbose++;
+ if (!(levels & NTFS_LOG_LEVEL_QUIET))
+ opts.quiet++;
+
+ if (help || ver) {
+ opts.quiet = 0;
+ } else {
+ if (opts.device == NULL) {
+ if (argc > 1)
+ ntfs_log_error("You must specify exactly one device.\n");
+ err++;
+ }
+
+ if (opts.quiet && opts.verbose) {
+ ntfs_log_error("You may not use --quiet and --verbose at the same time.\n");
+ err++;
+ }
+
+ /*
+ if (opts.info && (opts.unused || opts.tails || opts.mft || opts.directory)) {
+ ntfs_log_error("You may not use any other options with --info.\n");
+ err++;
+ }
+ */
+
+ if ((opts.count < 1) || (opts.count > 100)) {
+ ntfs_log_error("The iteration count must be between 1 and 100.\n");
+ err++;
+ }
+
+ /* Create a default list */
+ if (!opts.bytes) {
+ opts.bytes = malloc(2 * sizeof(int));
+ if (opts.bytes) {
+ opts.bytes[0] = 0;
+ opts.bytes[1] = -1;
+ } else {
+ ntfs_log_error("Couldn't allocate memory for byte list.\n");
+ err++;
+ }
+ }
+
+ if (!opts.directory && !opts.logfile && !opts.mft &&
+ !opts.pagefile && !opts.tails && !opts.unused &&
+ !opts.undel) {
+ opts.info = 1;
+ }
+ }
+
+ if (ver)
+ version();
+ if (help || err)
+ usage();
+
+ return (!err && !help && !ver);
+}
+
+/**
+ * wipe_unused - Wipe unused clusters
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ *
+ * Read $Bitmap and wipe any clusters that are marked as not in use.
+ *
+ * Return: >0 Success, the attribute was wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_unused(ntfs_volume *vol, int byte, enum action act)
+{
+ s64 i;
+ s64 total = 0;
+ s64 result = 0;
+ u8 *buffer = NULL;
+
+ if (!vol || (byte < 0))
+ return -1;
+
+ if (act != act_info) {
+ buffer = malloc(vol->cluster_size);
+ if (!buffer) {
+ ntfs_log_error("malloc failed\n");
+ return -1;
+ }
+ memset(buffer, byte, vol->cluster_size);
+ }
+
+ for (i = 0; i < vol->nr_clusters; i++) {
+ if (utils_cluster_in_use(vol, i)) {
+ //ntfs_log_verbose("cluster %lld is in use\n", i);
+ continue;
+ }
+
+ if (act == act_wipe) {
+ //ntfs_log_verbose("cluster %lld is not in use\n", i);
+ result = ntfs_pwrite(vol->dev, vol->cluster_size * i, vol->cluster_size, buffer);
+ if (result != vol->cluster_size) {
+ ntfs_log_error("write failed\n");
+ goto free;
+ }
+ }
+
+ total += vol->cluster_size;
+ }
+
+ ntfs_log_quiet("wipe_unused 0x%02x, %lld bytes\n", byte, (long long)total);
+free:
+ free(buffer);
+ return total;
+}
+
+/**
+ * wipe_compressed_attribute - Wipe compressed $DATA attribute
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ * @na: Opened ntfs attribute
+ *
+ * Return: >0 Success, the attribute was wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_compressed_attribute(ntfs_volume *vol, int byte,
+ enum action act, ntfs_attr *na)
+{
+ unsigned char *buf;
+ s64 size, offset, ret, wiped = 0;
+ le16 block_size_le;
+ u16 block_size;
+ VCN cur_vcn = 0;
+ runlist_element *rlc = na->rl;
+ s64 cu_mask = na->compression_block_clusters - 1;
+ runlist_element *restart = na->rl;
+
+ while (rlc->length) {
+ cur_vcn += rlc->length;
+ if ((cur_vcn & cu_mask) ||
+ (((rlc + 1)->length) && (rlc->lcn != LCN_HOLE))) {
+ rlc++;
+ continue;
+ }
+
+ if (rlc->lcn == LCN_HOLE) {
+ runlist_element *rlt;
+
+ offset = cur_vcn - rlc->length;
+ if (offset == (offset & (~cu_mask))) {
+ restart = rlc + 1;
+ rlc++;
+ continue;
+ }
+ offset = (offset & (~cu_mask))
+ << vol->cluster_size_bits;
+ rlt = rlc;
+ while ((rlt - 1)->lcn == LCN_HOLE) rlt--;
+ while (1) {
+ ret = ntfs_rl_pread(vol, restart,
+ offset - (restart->vcn
+ << vol->cluster_size_bits),
+ 2, &block_size_le);
+ block_size = le16_to_cpu(block_size_le);
+ if (ret != 2) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("ntfs_rl_pread failed");
+ return -1;
+ }
+ if (block_size == 0) {
+ offset += 2;
+ break;
+ }
+ block_size &= 0x0FFF;
+ block_size += 3;
+ offset += block_size;
+ if (offset >= (((rlt->vcn) <<
+ vol->cluster_size_bits) - 2))
+ goto next;
+ }
+ size = (rlt->vcn << vol->cluster_size_bits) - offset;
+ } else {
+ size = na->allocated_size - na->data_size;
+ offset = (cur_vcn << vol->cluster_size_bits) - size;
+ }
+
+ if (size < 0) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("bug or damaged fs: we want "
+ "allocate buffer size %lld bytes",
+ (long long)size);
+ return -1;
+ }
+
+ if ((act == act_info) || (!size)) {
+ wiped += size;
+ if (rlc->lcn == LCN_HOLE)
+ restart = rlc + 1;
+ rlc++;
+ continue;
+ }
+
+ buf = malloc(size);
+ if (!buf) {
+ ntfs_log_verbose("Not enough memory\n");
+ ntfs_log_error("Not enough memory to allocate "
+ "%lld bytes",
+ (long long)size);
+ return -1;
+ }
+ memset(buf, byte, size);
+
+ ret = ntfs_rl_pwrite(vol, restart,
+ restart->vcn << vol->cluster_size_bits,
+ offset, size, buf);
+ free(buf);
+ if (ret != size) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("ntfs_rl_pwrite failed, offset %llu, "
+ "size %lld, vcn %lld",
+ (unsigned long long)offset,
+ (long long)size, (long long)rlc->vcn);
+ return -1;
+ }
+ wiped += ret;
+next:
+ if (rlc->lcn == LCN_HOLE)
+ restart = rlc + 1;
+ rlc++;
+ }
+
+ return wiped;
+}
+
+/**
+ * wipe_attribute - Wipe not compressed $DATA attribute
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ * @na: Opened ntfs attribute
+ *
+ * Return: >0 Success, the attribute was wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_attribute(ntfs_volume *vol, int byte, enum action act,
+ ntfs_attr *na)
+{
+ unsigned char *buf;
+ s64 wiped;
+ s64 size;
+ u64 offset = na->data_size;
+
+ if (!offset)
+ return 0;
+ if (na->data_flags & ATTR_IS_ENCRYPTED)
+ offset = (((offset - 1) >> 10) + 1) << 10;
+ size = (vol->cluster_size - offset) % vol->cluster_size;
+
+ if (act == act_info)
+ return size;
+
+ buf = malloc(size);
+ if (!buf) {
+ ntfs_log_verbose("Not enough memory\n");
+ ntfs_log_error("Not enough memory to allocate %lld bytes",
+ (long long)size);
+ return -1;
+ }
+ memset(buf, byte, size);
+
+ wiped = ntfs_rl_pwrite(vol, na->rl, 0, offset, size, buf);
+ if (wiped == -1) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("Couldn't wipe tail");
+ }
+
+ free(buf);
+ return wiped;
+}
+
+/*
+ * Wipe a data attribute tail
+ *
+ * Return: >0 Success, the clusters were wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+
+static s64 wipe_attr_tail(ntfs_inode *ni, ntfschar *name, int namelen,
+ int byte, enum action act)
+{
+ ntfs_attr *na;
+ ntfs_volume *vol = ni->vol;
+ s64 wiped;
+
+ wiped = -1;
+ na = ntfs_attr_open(ni, AT_DATA, name, namelen);
+ if (!na) {
+ ntfs_log_error("Couldn't open $DATA attribute\n");
+ goto close_attr;
+ }
+
+ if (!NAttrNonResident(na)) {
+ ntfs_log_verbose("Resident $DATA attribute. Skipping.\n");
+ goto close_attr;
+ }
+
+ if (ntfs_attr_map_whole_runlist(na)) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("Can't map runlist (inode %lld)\n",
+ (long long)ni->mft_no);
+ goto close_attr;
+ }
+
+ if (na->data_flags & ATTR_COMPRESSION_MASK)
+ wiped = wipe_compressed_attribute(vol, byte, act, na);
+ else
+ wiped = wipe_attribute(vol, byte, act, na);
+
+ if (wiped == -1) {
+ ntfs_log_error(" (inode %lld)\n", (long long)ni->mft_no);
+ }
+
+close_attr:
+ ntfs_attr_close(na);
+ return (wiped);
+}
+
+/**
+ * wipe_tails - Wipe the file tails in all its data attributes
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ *
+ * Disk space is allocated in clusters. If a file isn't an exact multiple of
+ * the cluster size, there is some slack space at the end. Wipe this space.
+ *
+ * Return: >0 Success, the clusters were wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_tails(ntfs_volume *vol, int byte, enum action act)
+{
+ s64 total = 0;
+ s64 nr_mft_records, inode_num;
+ ntfs_attr_search_ctx *ctx;
+ ntfs_inode *ni;
+ ATTR_RECORD *a;
+ ntfschar *name;
+
+ if (!vol || (byte < 0))
+ return -1;
+
+ nr_mft_records = vol->mft_na->initialized_size >>
+ vol->mft_record_size_bits;
+
+ for (inode_num = FILE_first_user; inode_num < nr_mft_records;
+ inode_num++) {
+ s64 attr_wiped;
+ s64 wiped = 0;
+
+ ntfs_log_verbose("Inode %lld - ", (long long)inode_num);
+ ni = ntfs_inode_open(vol, inode_num);
+ if (!ni) {
+ ntfs_log_verbose("Could not open inode\n");
+ continue;
+ }
+
+ if (ni->mrec->base_mft_record) {
+ ntfs_log_verbose("Not base mft record. Skipping\n");
+ goto close_inode;
+ }
+
+ ctx = ntfs_attr_get_search_ctx(ni, (MFT_RECORD*)NULL);
+ if (!ctx) {
+ ntfs_log_error("Can't get a context, aborting\n");
+ ntfs_inode_close(ni);
+ goto close_abort;
+ }
+ while (!ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, 0,
+ NULL, 0, ctx)) {
+ a = ctx->attr;
+
+ if (!ctx->al_entry || !ctx->al_entry->lowest_vcn) {
+ name = (ntfschar*)((u8*)a
+ + le16_to_cpu(a->name_offset));
+ attr_wiped = wipe_attr_tail(ni, name,
+ a->name_length, byte, act);
+ if (attr_wiped > 0)
+ wiped += attr_wiped;
+ }
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ if (wiped) {
+ ntfs_log_verbose("Wiped %llu bytes\n",
+ (unsigned long long)wiped);
+ total += wiped;
+ } else
+ ntfs_log_verbose("Nothing to wipe\n");
+close_inode:
+ ntfs_inode_close(ni);
+ }
+close_abort :
+ ntfs_log_quiet("wipe_tails 0x%02x, %lld bytes\n", byte,
+ (long long)total);
+ return total;
+}
+
+/**
+ * wipe_mft - Wipe the MFT slack space
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ *
+ * MFT Records are 1024 bytes long, but some of this space isn't used. Wipe any
+ * unused space at the end of the record and wipe any unused records.
+ *
+ * Return: >0 Success, the clusters were wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_mft(ntfs_volume *vol, int byte, enum action act)
+{
+ // by considering the individual attributes we might be able to
+ // wipe a few more bytes at the attr's tail.
+ s64 nr_mft_records, i;
+ s64 total = 0;
+ s64 result = 0;
+ int size = 0;
+ MFT_RECORD *rec = NULL;
+
+ if (!vol || (byte < 0))
+ return -1;
+
+ rec = (MFT_RECORD*)malloc(vol->mft_record_size);
+ if (!rec) {
+ ntfs_log_error("malloc failed\n");
+ return -1;
+ }
+
+ nr_mft_records = vol->mft_na->initialized_size >>
+ vol->mft_record_size_bits;
+
+ for (i = 0; i < nr_mft_records; i++) {
+ if (utils_mftrec_in_use(vol, i)) {
+ result = ntfs_attr_mst_pread(vol->mft_na, vol->mft_record_size * i,
+ 1, vol->mft_record_size, rec);
+ if (result != 1) {
+ ntfs_log_error("error attr mst read %lld\n",
+ (long long)i);
+ total = -1; // XXX just negate result?
+ goto free;
+ }
+
+ // We know that the end marker will only take 4 bytes
+ size = le32_to_cpu(rec->bytes_in_use) - 4;
+
+ if (act == act_info) {
+ //ntfs_log_info("mft %d\n", size);
+ total += size;
+ continue;
+ }
+
+ memset(((u8*) rec) + size, byte, vol->mft_record_size - size);
+ } else {
+ const u16 usa_offset =
+ (vol->major_ver == 3) ? 0x0030 : 0x002A;
+ const u32 usa_size = 1 +
+ (vol->mft_record_size >> NTFS_BLOCK_SIZE_BITS);
+ const u16 attrs_offset =
+ ((usa_offset + usa_size) + 7) & ~((u16) 7);
+ const u32 bytes_in_use = attrs_offset + 8;
+
+ if(usa_size > 0xFFFF || (usa_offset + usa_size) >
+ (NTFS_BLOCK_SIZE - sizeof(u16)))
+ {
+ ntfs_log_error("%d: usa_size out of bounds "
+ "(%u)\n", __LINE__, usa_size);
+ total = -1;
+ goto free;
+ }
+
+ if (act == act_info) {
+ total += vol->mft_record_size;
+ continue;
+ }
+
+ // Build the record from scratch
+ memset(rec, 0, vol->mft_record_size);
+
+ // Common values
+ rec->magic = magic_FILE;
+ rec->usa_ofs = cpu_to_le16(usa_offset);
+ rec->usa_count = cpu_to_le16((u16) usa_size);
+ rec->sequence_number = cpu_to_le16(0x0001);
+ rec->attrs_offset = cpu_to_le16(attrs_offset);
+ rec->bytes_in_use = cpu_to_le32(bytes_in_use);
+ rec->bytes_allocated = cpu_to_le32(vol->mft_record_size);
+ rec->next_attr_instance = cpu_to_le16(0x0001);
+
+ // End marker.
+ *((le32*) (((u8*) rec) + attrs_offset)) = cpu_to_le32(0xFFFFFFFF);
+ }
+
+ result = ntfs_attr_mst_pwrite(vol->mft_na, vol->mft_record_size * i,
+ 1, vol->mft_record_size, rec);
+ if (result != 1) {
+ ntfs_log_error("error attr mst write %lld\n",
+ (long long)i);
+ total = -1;
+ goto free;
+ }
+
+ if ((vol->mft_record_size * (i+1)) <= vol->mftmirr_na->allocated_size)
+ {
+ // We have to reduce the update sequence number, or else...
+ u16 offset;
+ le16 *usnp;
+ offset = le16_to_cpu(rec->usa_ofs);
+ usnp = (le16*) (((u8*) rec) + offset);
+ *usnp = cpu_to_le16(le16_to_cpu(*usnp) - 1);
+
+ result = ntfs_attr_mst_pwrite(vol->mftmirr_na, vol->mft_record_size * i,
+ 1, vol->mft_record_size, rec);
+ if (result != 1) {
+ ntfs_log_error("error attr mst write %lld\n",
+ (long long)i);
+ total = -1;
+ goto free;
+ }
+ }
+
+ total += vol->mft_record_size;
+ }
+
+ ntfs_log_quiet("wipe_mft 0x%02x, %lld bytes\n", byte, (long long)total);
+free:
+ free(rec);
+ return total;
+}
+
+/**
+ * wipe_index_allocation - Wipe $INDEX_ALLOCATION attribute
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ * @naa: Opened ntfs $INDEX_ALLOCATION attribute
+ * @nab: Opened ntfs $BITMAP attribute
+ * @indx_record_size: Size of INDX record
+ *
+ * Return: >0 Success, the clusters were wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_index_allocation(ntfs_volume *vol, int byte, enum action act
+ __attribute__((unused)), ntfs_attr *naa, ntfs_attr *nab,
+ u32 indx_record_size)
+{
+ s64 total = 0;
+ s64 wiped = 0;
+ s64 offset = 0;
+ s64 obyte = 0;
+ u64 wipe_offset;
+ s64 wipe_size;
+ u8 obit = 0;
+ u8 mask;
+ u8 *bitmap;
+ u8 *buf;
+
+ bitmap = malloc(nab->data_size);
+ if (!bitmap) {
+ ntfs_log_verbose("malloc failed\n");
+ ntfs_log_error("Couldn't allocate %lld bytes",
+ (long long)nab->data_size);
+ return -1;
+ }
+
+ if (ntfs_attr_pread(nab, 0, nab->data_size, bitmap)
+ != nab->data_size) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("Couldn't read $BITMAP");
+ total = -1;
+ goto free_bitmap;
+ }
+
+ buf = malloc(indx_record_size);
+ if (!buf) {
+ ntfs_log_verbose("malloc failed\n");
+ ntfs_log_error("Couldn't allocate %u bytes",
+ (unsigned int)indx_record_size);
+ total = -1;
+ goto free_bitmap;
+ }
+
+ while (offset < naa->allocated_size) {
+ mask = 1 << obit;
+ if (bitmap[obyte] & mask) {
+ INDEX_ALLOCATION *indx;
+
+ s64 ret = ntfs_rl_pread(vol, naa->rl,
+ offset, indx_record_size, buf);
+ if (ret != indx_record_size) {
+ ntfs_log_verbose("ntfs_rl_pread failed\n");
+ ntfs_log_error("Couldn't read INDX record");
+ total = -1;
+ goto free_buf;
+ }
+
+ indx = (INDEX_ALLOCATION *) buf;
+ if (ntfs_mst_post_read_fixup((NTFS_RECORD *)buf,
+ indx_record_size))
+ ntfs_log_error("damaged fs: mst_post_read_fixup failed");
+
+ if ((le32_to_cpu(indx->index.allocated_size) + 0x18) !=
+ indx_record_size) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("INDX record should be %u bytes",
+ (unsigned int)indx_record_size);
+ total = -1;
+ goto free_buf;
+ }
+
+ wipe_offset = le32_to_cpu(indx->index.index_length) + 0x18;
+ wipe_size = indx_record_size - wipe_offset;
+ memset(buf + wipe_offset, byte, wipe_size);
+ if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)indx,
+ indx_record_size))
+ ntfs_log_error("damaged fs: mst_pre_write_protect failed");
+ if (opts.verbose > 1)
+ ntfs_log_verbose("+");
+ } else {
+ wipe_size = indx_record_size;
+ memset(buf, byte, wipe_size);
+ if (opts.verbose > 1)
+ ntfs_log_verbose("x");
+ }
+
+ wiped = ntfs_rl_pwrite(vol, naa->rl, 0, offset, indx_record_size, buf);
+ if (wiped != indx_record_size) {
+ ntfs_log_verbose("ntfs_rl_pwrite failed\n");
+ ntfs_log_error("Couldn't wipe tail of INDX record");
+ total = -1;
+ goto free_buf;
+ }
+ total += wipe_size;
+
+ offset += indx_record_size;
+ obit++;
+ if (obit > 7) {
+ obit = 0;
+ obyte++;
+ }
+ }
+ if ((opts.verbose > 1) && (wiped != -1))
+ ntfs_log_verbose("\n\t");
+free_buf:
+ free(buf);
+free_bitmap:
+ free(bitmap);
+ return total;
+}
+
+/**
+ * get_indx_record_size - determine size of INDX record from $INDEX_ROOT
+ * @nar: Opened ntfs $INDEX_ROOT attribute
+ *
+ * Return: >0 Success, return INDX record size
+ * 0 Error, something went wrong
+ */
+static u32 get_indx_record_size(ntfs_attr *nar)
+{
+ u32 indx_record_size;
+ le32 indx_record_size_le;
+
+ if (ntfs_attr_pread(nar, 8, 4, &indx_record_size_le) != 4) {
+ ntfs_log_verbose("Couldn't determine size of INDX record\n");
+ ntfs_log_error("ntfs_attr_pread failed");
+ return 0;
+ }
+
+ indx_record_size = le32_to_cpu(indx_record_size_le);
+ if (!indx_record_size) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("INDX record should be 0");
+ }
+ return indx_record_size;
+}
+
+/**
+ * wipe_directory - Wipe the directory indexes
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ *
+ * Directories are kept in sorted B+ Trees. Index blocks may not be full. Wipe
+ * the unused space at the ends of these blocks.
+ *
+ * Return: >0 Success, the clusters were wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_directory(ntfs_volume *vol, int byte, enum action act)
+{
+ s64 total = 0;
+ s64 nr_mft_records, inode_num;
+ ntfs_inode *ni;
+ ntfs_attr *naa;
+ ntfs_attr *nab;
+ ntfs_attr *nar;
+
+ if (!vol || (byte < 0))
+ return -1;
+
+ nr_mft_records = vol->mft_na->initialized_size >>
+ vol->mft_record_size_bits;
+
+ for (inode_num = 5; inode_num < nr_mft_records; inode_num++) {
+ u32 indx_record_size;
+ s64 wiped;
+
+ ntfs_log_verbose("Inode %lld - ", (long long)inode_num);
+ ni = ntfs_inode_open(vol, inode_num);
+ if (!ni) {
+ if (opts.verbose > 2)
+ ntfs_log_verbose("Could not open inode\n");
+ else
+ ntfs_log_verbose("\r");
+ continue;
+ }
+
+ if (ni->mrec->base_mft_record) {
+ if (opts.verbose > 2)
+ ntfs_log_verbose("Not base mft record. Skipping\n");
+ else
+ ntfs_log_verbose("\r");
+ goto close_inode;
+ }
+
+ naa = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
+ if (!naa) {
+ if (opts.verbose > 2)
+ ntfs_log_verbose("Couldn't open $INDEX_ALLOCATION\n");
+ else
+ ntfs_log_verbose("\r");
+ goto close_inode;
+ }
+
+ if (!NAttrNonResident(naa)) {
+ ntfs_log_verbose("Resident $INDEX_ALLOCATION\n");
+ ntfs_log_error("damaged fs: Resident $INDEX_ALLOCATION "
+ "(inode %lld)\n", (long long)inode_num);
+ goto close_attr_allocation;
+ }
+
+ if (ntfs_attr_map_whole_runlist(naa)) {
+ ntfs_log_verbose("Internal error\n");
+ ntfs_log_error("Can't map runlist for $INDEX_ALLOCATION "
+ "(inode %lld)\n", (long long)inode_num);
+ goto close_attr_allocation;
+ }
+
+ nab = ntfs_attr_open(ni, AT_BITMAP, NTFS_INDEX_I30, 4);
+ if (!nab) {
+ ntfs_log_verbose("Couldn't open $BITMAP\n");
+ ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, "
+ "but we can't open $BITMAP with same "
+ "name (inode %lld)\n", (long long)inode_num);
+ goto close_attr_allocation;
+ }
+
+ nar = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4);
+ if (!nar) {
+ ntfs_log_verbose("Couldn't open $INDEX_ROOT\n");
+ ntfs_log_error("damaged fs: $INDEX_ALLOCATION is present, but "
+ "we can't open $INDEX_ROOT with same name"
+ " (inode %lld)\n", (long long)inode_num);
+ goto close_attr_bitmap;
+ }
+
+ if (NAttrNonResident(nar)) {
+ ntfs_log_verbose("Not resident $INDEX_ROOT\n");
+ ntfs_log_error("damaged fs: Not resident $INDEX_ROOT "
+ "(inode %lld)\n", (long long)inode_num);
+ goto close_attr_root;
+ }
+
+ indx_record_size = get_indx_record_size(nar);
+ if (!indx_record_size) {
+ ntfs_log_error(" (inode %lld)\n", (long long)inode_num);
+ goto close_attr_root;
+ }
+
+ wiped = wipe_index_allocation(vol, byte, act,
+ naa, nab, indx_record_size);
+ if (wiped == -1) {
+ ntfs_log_error(" (inode %lld)\n",
+ (long long)inode_num);
+ goto close_attr_root;
+ }
+
+ if (wiped) {
+ ntfs_log_verbose("Wiped %llu bytes\n",
+ (unsigned long long)wiped);
+ total += wiped;
+ } else
+ ntfs_log_verbose("Nothing to wipe\n");
+close_attr_root:
+ ntfs_attr_close(nar);
+close_attr_bitmap:
+ ntfs_attr_close(nab);
+close_attr_allocation:
+ ntfs_attr_close(naa);
+close_inode:
+ ntfs_inode_close(ni);
+ }
+
+ ntfs_log_quiet("wipe_directory 0x%02x, %lld bytes\n", byte,
+ (long long)total);
+ return total;
+}
+
+/**
+ * wipe_logfile - Wipe the logfile (journal)
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ *
+ * The logfile journals the metadata to give the volume fault-tolerance. If the
+ * volume is in a consistent state, then this information can be erased.
+ *
+ * Return: >0 Success, the clusters were wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_logfile(ntfs_volume *vol, int byte, enum action act
+ __attribute__((unused)))
+{
+ const int NTFS_BUF_SIZE2 = 8192;
+ //FIXME(?): We might need to zero the LSN field of every single mft
+ //record as well. (But, first try without doing that and see what
+ //happens, since chkdsk might pickup the pieces and do it for us...)
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ s64 len, pos, count;
+ char buf[NTFS_BUF_SIZE2];
+ int eo;
+
+ /* We can wipe logfile only with 0xff. */
+ byte = 0xff;
+
+ if (!vol || (byte < 0))
+ return -1;
+
+ //ntfs_log_quiet("wipe_logfile(not implemented) 0x%02x\n", byte);
+
+ if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) {
+ ntfs_log_debug("Failed to open inode FILE_LogFile.\n");
+ return -1;
+ }
+
+ if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) {
+ ntfs_log_debug("Failed to open $FILE_LogFile/$DATA.\n");
+ goto error_exit;
+ }
+
+ /* The $DATA attribute of the $LogFile has to be non-resident. */
+ if (!NAttrNonResident(na)) {
+ ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n");
+ errno = EIO;
+ goto io_error_exit;
+ }
+
+ /* Get length of $LogFile contents. */
+ len = na->data_size;
+ if (!len) {
+ ntfs_log_debug("$LogFile has zero length, no disk write "
+ "needed.\n");
+ return 0;
+ }
+
+ /* Read $LogFile until its end. We do this as a check for correct
+ length thus making sure we are decompressing the mapping pairs
+ array correctly and hence writing below is safe as well. */
+ pos = 0;
+ while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE2, buf)) > 0)
+ pos += count;
+
+ if (count == -1 || pos != len) {
+ ntfs_log_debug("Amount of $LogFile data read does not "
+ "correspond to expected length!\n");
+ if (count != -1)
+ errno = EIO;
+ goto io_error_exit;
+ }
+
+ /* Fill the buffer with @byte's. */
+ memset(buf, byte, NTFS_BUF_SIZE2);
+
+ /* Set the $DATA attribute. */
+ pos = 0;
+ while ((count = len - pos) > 0) {
+ if (count > NTFS_BUF_SIZE2)
+ count = NTFS_BUF_SIZE2;
+
+ if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) {
+ ntfs_log_debug("Failed to set the $LogFile attribute "
+ "value.\n");
+ if (count != -1)
+ errno = EIO;
+ goto io_error_exit;
+ }
+
+ pos += count;
+ }
+
+ ntfs_attr_close(na);
+ ntfs_inode_close(ni);
+ ntfs_log_quiet("wipe_logfile 0x%02x, %lld bytes\n", byte,
+ (long long)pos);
+ return pos;
+
+io_error_exit:
+ eo = errno;
+ ntfs_attr_close(na);
+ errno = eo;
+error_exit:
+ eo = errno;
+ ntfs_inode_close(ni);
+ errno = eo;
+ return -1;
+}
+
+/**
+ * wipe_pagefile - Wipe the pagefile (swap space)
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @byte: Overwrite with this value
+ * @act: Wipe, test or info
+ *
+ * pagefile.sys is used by Windows as extra virtual memory (swap space).
+ * Windows recreates the file at bootup, so it can be wiped without harm.
+ *
+ * Return: >0 Success, the clusters were wiped
+ * 0 Nothing to wipe
+ * -1 Error, something went wrong
+ */
+static s64 wipe_pagefile(ntfs_volume *vol, int byte, enum action act
+ __attribute__((unused)))
+{
+ // wipe completely, chkdsk doesn't do anything, booting writes header
+ const int NTFS_BUF_SIZE2 = 4096;
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ s64 len, pos, count;
+ char buf[NTFS_BUF_SIZE2];
+ int eo;
+
+ if (!vol || (byte < 0))
+ return -1;
+
+ //ntfs_log_quiet("wipe_pagefile(not implemented) 0x%02x\n", byte);
+
+ ni = ntfs_pathname_to_inode(vol, NULL, "pagefile.sys");
+ if (!ni) {
+ ntfs_log_debug("Failed to open inode of pagefile.sys.\n");
+ return 0;
+ }
+
+ if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) {
+ ntfs_log_debug("Failed to open pagefile.sys/$DATA.\n");
+ goto error_exit;
+ }
+
+ /* The $DATA attribute of the pagefile.sys has to be non-resident. */
+ if (!NAttrNonResident(na)) {
+ ntfs_log_debug("pagefile.sys $DATA attribute is resident!?!\n");
+ errno = EIO;
+ goto io_error_exit;
+ }
+
+ /* Get length of pagefile.sys contents. */
+ len = na->data_size;
+ if (!len) {
+ ntfs_log_debug("pagefile.sys has zero length, no disk write "
+ "needed.\n");
+ return 0;
+ }
+
+ memset(buf, byte, NTFS_BUF_SIZE2);
+
+ /* Set the $DATA attribute. */
+ pos = 0;
+ while ((count = len - pos) > 0) {
+ if (count > NTFS_BUF_SIZE2)
+ count = NTFS_BUF_SIZE2;
+
+ if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) {
+ ntfs_log_debug("Failed to set the pagefile.sys "
+ "attribute value.\n");
+ if (count != -1)
+ errno = EIO;
+ goto io_error_exit;
+ }
+
+ pos += count;
+ }
+
+ ntfs_attr_close(na);
+ ntfs_inode_close(ni);
+ ntfs_log_quiet("wipe_pagefile 0x%02x, %lld bytes\n", byte,
+ (long long)pos);
+ return pos;
+
+io_error_exit:
+ eo = errno;
+ ntfs_attr_close(na);
+ errno = eo;
+error_exit:
+ eo = errno;
+ ntfs_inode_close(ni);
+ errno = eo;
+ return -1;
+}
+
+/**
+ * Part of ntfsprogs.
+ * Modified: removed logging, signal handling, removed data.
+ *
+ * free_file - Release the resources used by a file object
+ * \param file The unwanted file object
+ *
+ * This will free up the memory used by a file object and iterate through the
+ * object's children, freeing their resources too.
+ *
+ * \return none
+ */
+static void free_file (struct ufile *file)
+{
+ struct ntfs_list_head *item = NULL, *tmp = NULL;
+ struct filename *f = NULL;
+ struct data *d = NULL;
+
+ if (file == NULL)
+ return;
+
+ ntfs_list_for_each_safe(item, tmp, &(file->name)) {
+ /* List of filenames */
+
+ f = ntfs_list_entry(item, struct filename, list);
+ if (f->name != NULL)
+ free(f->name);
+ if (f->parent_name != NULL) {
+ free(f->parent_name);
+ }
+ free(f);
+ }
+
+ ntfs_list_for_each_safe(item, tmp, &(file->data)) {
+ /* List of data streams */
+
+ d = ntfs_list_entry(item, struct data, list);
+ if (d->name != NULL)
+ free(d->name);
+ if (d->runlist != NULL)
+ free(d->runlist);
+ free(d);
+ }
+
+
+ free(file->mft);
+ free(file);
+}
+
+/**
+ * Fills the given buffer with one of predefined patterns.
+ * \param pat_no Pass number.
+ * \param buffer Buffer to be filled.
+ * \param buflen Length of the buffer.
+ */
+static void fill_buffer (
+ unsigned long int pat_no,
+ unsigned char * const buffer,
+ const size_t buflen,
+ int * const selected )
+ /*@requires notnull buffer @*/ /*@sets *buffer @*/
+{
+
+ size_t i;
+#if (!defined HAVE_MEMCPY) && (!defined HAVE_STRING_H)
+ size_t j;
+#endif
+ unsigned int bits;
+
+ if ((buffer == NULL) || (buflen == 0))
+ return;
+
+ /* De-select all patterns once every npasses calls. */
+ if (pat_no % npasses == 0) {
+ for (i = 0; i < NPAT; i++) {
+ selected[i] = 0;
+ }
+ }
+ pat_no %= npasses;
+ /* double check for npasses >= NPAT + 3: */
+ for (i = 0; i < NPAT; i++) {
+ if (selected[i] == 0)
+ break;
+ }
+ if (i >= NPAT) {
+ for (i = 0; i < NPAT; i++) {
+ selected[i] = 0;
+ }
+ }
+
+ /* The first, last and middle passess will be using a random pattern */
+ if ((pat_no == 0) || (pat_no == npasses-1) || (pat_no == npasses/2)) {
+#if (!defined __STRICT_ANSI__) && (defined HAVE_RANDOM)
+ bits = (unsigned int)(random() & 0xFFF);
+#else
+ bits = (unsigned int)(rand() & 0xFFF);
+#endif
+ } else {
+ /* For other passes, one of the fixed patterns is selected. */
+ do {
+#if (!defined __STRICT_ANSI__) && (defined HAVE_RANDOM)
+ i = (size_t)(random() % NPAT);
+#else
+ i = (size_t)(rand() % NPAT);
+#endif
+ } while (selected[i] == 1);
+ bits = opts.bytes[i];
+ selected[i] = 1;
+ }
+
+ buffer[0] = (unsigned char) bits;
+ buffer[1] = (unsigned char) bits;
+ buffer[2] = (unsigned char) bits;
+ for (i = 3; i < buflen / 2; i *= 2) {
+#ifdef HAVE_MEMCPY
+ memcpy(buffer + i, buffer, i);
+#elif defined HAVE_STRING_H
+ strncpy((char *)(buffer + i), (char *)buffer, i);
+#else
+ for (j = 0; j < i; j++) {
+ buffer[i+j] = buffer[j];
+ }
+#endif
+ }
+ if (i < buflen) {
+#ifdef HAVE_MEMCPY
+ memcpy(buffer + i, buffer, buflen - i);
+#elif defined HAVE_STRING_H
+ strncpy((char *)(buffer + i), (char *)buffer, buflen - i);
+#else
+ for (j=0; j<buflen - i; j++) {
+ buffer[i+j] = buffer[j];
+ }
+#endif
+ }
+}
+
+/**
+ * Destroys the specified record's filenames and data.
+ *
+ * \param nv The filesystem.
+ * \param record The record (i-node number), which filenames & data
+ * to destroy.
+ * \return 0 in case of no errors, other values otherwise.
+ */
+static int destroy_record(ntfs_volume *nv, const s64 record,
+ unsigned char * const buf)
+{
+ struct ufile *file = NULL;
+ runlist_element *rl = NULL;
+ ntfs_attr *mft = NULL;
+
+ ntfs_attr_search_ctx *ctx = NULL;
+ int ret_wfs = 0;
+ unsigned long int pass, i;
+ s64 j;
+ unsigned char * a_offset;
+ int selected[NPAT];
+
+ file = (struct ufile *) malloc(sizeof(struct ufile));
+ if (file == NULL) {
+ return -1;
+ }
+
+ NTFS_INIT_LIST_HEAD(&(file->name));
+ NTFS_INIT_LIST_HEAD(&(file->data));
+ file->inode = record;
+
+ file->mft = (MFT_RECORD*)malloc(nv->mft_record_size);
+ if (file->mft == NULL) {
+ free_file (file);
+ return -1;
+ }
+
+ mft = ntfs_attr_open(nv->mft_ni, AT_DATA, AT_UNNAMED, 0);
+ if (mft == NULL) {
+ free_file(file);
+ return -2;
+ }
+
+ /* Read the MFT reocrd of the i-node */
+ if (ntfs_attr_mst_pread(mft, nv->mft_record_size * record, 1LL,
+ nv->mft_record_size, file->mft) < 1) {
+
+ ntfs_attr_close(mft);
+ free_file(file);
+ return -3;
+ }
+ ntfs_attr_close(mft);
+ mft = NULL;
+
+ ctx = ntfs_attr_get_search_ctx(NULL, file->mft);
+ if (ctx == NULL) {
+ free_file(file);
+ return -4;
+ }
+
+ /* Wiping file names */
+ while (1 == 1) {
+
+ if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE,
+ 0LL, NULL, 0, ctx) != 0) {
+ break; /* None / no more of that type */
+ }
+ if (ctx->attr == NULL)
+ break;
+
+ /* We know this will always be resident.
+ Find the offset of the data, including the MFT record. */
+ a_offset = ((unsigned char *) ctx->attr
+ + le16_to_cpu(ctx->attr->value_offset));
+
+ for (pass = 0; pass < npasses; pass++) {
+ fill_buffer(pass, a_offset,
+ le32_to_cpu(ctx->attr->value_length),
+ selected);
+
+ if ( !opts.noaction ) {
+ if (ntfs_mft_records_write(nv,
+ MK_MREF(record, 0), 1LL,
+ ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+ /* Flush after each writing, if more than
+ 1 overwriting needs to be done. Allow I/O
+ bufferring (efficiency), if just one
+ pass is needed. */
+ if (npasses > 1) {
+ nv->dev->d_ops->sync(nv->dev);
+ }
+ }
+
+ }
+
+ /* Wiping file name length */
+ for (pass = 0; pass < npasses; pass++) {
+
+ fill_buffer (pass, (unsigned char *)
+ &(ctx->attr->value_length), sizeof(u32),
+ selected);
+
+ if (!opts.noaction) {
+ if (ntfs_mft_records_write(nv,
+ MK_MREF(record, 0),
+ 1LL, ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+
+ if (npasses > 1) {
+ nv->dev->d_ops->sync(nv->dev);
+ }
+ }
+ }
+ ctx->attr->value_length = cpu_to_le32(0);
+ if (!opts.noaction) {
+ if (ntfs_mft_records_write(nv, MK_MREF(record, 0),
+ 1LL, ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+ }
+ }
+
+ ntfs_attr_reinit_search_ctx(ctx);
+
+ /* Wiping file data */
+ while (1 == 1) {
+ if (ntfs_attr_lookup(AT_DATA, NULL, 0, CASE_SENSITIVE, 0LL,
+ NULL, 0, ctx) != 0) {
+ break; /* None / no more of that type */
+ }
+ if (ctx->attr == NULL)
+ break;
+
+ if (ctx->attr->non_resident == 0) {
+ /* attribute is resident (part of MFT record) */
+ /* find the offset of the data, including the MFT record */
+ a_offset = ((unsigned char *) ctx->attr
+ + le16_to_cpu(ctx->attr->value_offset));
+
+ /* Wiping the data itself */
+ for (pass = 0; pass < npasses; pass++) {
+
+ fill_buffer (pass, a_offset,
+ le32_to_cpu(ctx->attr->value_length),
+ selected);
+
+ if (!opts.noaction) {
+ if (ntfs_mft_records_write(nv,
+ MK_MREF(record, 0),
+ 1LL, ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+
+ if (npasses > 1) {
+ nv->dev->d_ops->sync(nv->dev);
+ }
+ }
+ }
+
+ /* Wiping data length */
+ for (pass = 0; pass < npasses; pass++) {
+
+ fill_buffer(pass, (unsigned char *)
+ &(ctx->attr->value_length),
+ sizeof(u32), selected);
+
+ if (!opts.noaction) {
+ if (ntfs_mft_records_write(nv,
+ MK_MREF(record, 0),
+ 1LL, ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+
+ if (npasses > 1) {
+ nv->dev->d_ops->sync(nv->dev);
+ }
+ }
+ }
+ ctx->attr->value_length = cpu_to_le32(0);
+ if ( !opts.noaction ) {
+ if (ntfs_mft_records_write(nv,
+ MK_MREF(record, 0),
+ 1LL, ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+ }
+ } else {
+ /* Non-resident here */
+
+ rl = ntfs_mapping_pairs_decompress(nv,
+ ctx->attr, NULL);
+ if (rl == NULL) {
+ continue;
+ }
+
+ if (rl[0].length <= 0) {
+ continue;
+ }
+
+ for (i = 0; (rl[i].length > 0) && (ret_wfs == 0); i++) {
+ if (rl[i].lcn == -1) {
+ continue;
+ }
+ for (j = rl[i].lcn;
+ (j < rl[i].lcn + rl[i].length)
+ && (ret_wfs == 0); j++) {
+
+ if (utils_cluster_in_use(nv, j) != 0)
+ continue;
+ for (pass = 0;
+ pass < npasses;
+ pass++) {
+
+ fill_buffer(pass, buf,
+ (size_t) nv->cluster_size,
+ selected);
+ if (!opts.noaction) {
+ if (ntfs_cluster_write(
+ nv, j, 1LL,
+ buf) < 1) {
+ ret_wfs = -5;
+ break;
+ }
+
+ if (npasses > 1) {
+ nv->dev->d_ops->sync
+ (nv->dev);
+ }
+ }
+ }
+ }
+ }
+
+ /* Wipe the data length here */
+ for (pass = 0; pass < npasses; pass++) {
+ fill_buffer(pass, (unsigned char *)
+ &(ctx->attr->lowest_vcn),
+ sizeof(VCN), selected);
+ fill_buffer(pass, (unsigned char *)
+ &(ctx->attr->highest_vcn),
+ sizeof(VCN), selected);
+ fill_buffer(pass, (unsigned char *)
+ &(ctx->attr->allocated_size),
+ sizeof(s64), selected);
+ fill_buffer(pass, (unsigned char *)
+ &(ctx->attr->data_size),
+ sizeof(s64), selected);
+ fill_buffer(pass, (unsigned char *)
+ &(ctx->attr->initialized_size),
+ sizeof(s64), selected);
+ fill_buffer(pass, (unsigned char *)
+ &(ctx->attr->compressed_size),
+ sizeof(s64), selected);
+
+ if ( !opts.noaction ) {
+ if (ntfs_mft_records_write(nv,
+ MK_MREF (record, 0),
+ 1LL, ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+
+ if (npasses > 1) {
+ nv->dev->d_ops->sync(nv->dev);
+ }
+ }
+ }
+ ctx->attr->lowest_vcn = cpu_to_le64(0);
+ ctx->attr->highest_vcn = cpu_to_le64(0);
+ ctx->attr->allocated_size = cpu_to_le64(0);
+ ctx->attr->data_size = cpu_to_le64(0);
+ ctx->attr->initialized_size = cpu_to_le64(0);
+ ctx->attr->compressed_size = cpu_to_le64(0);
+ if (!opts.noaction) {
+ if (ntfs_mft_records_write(nv,
+ MK_MREF (record, 0),
+ 1LL, ctx->mrec) != 0) {
+ ret_wfs = -5;
+ break;
+ }
+ }
+ } /* end of resident check */
+ } /* end of 'wiping file data' loop */
+
+ ntfs_attr_put_search_ctx(ctx);
+ free_file(file);
+
+ return ret_wfs;
+}
+
+/**
+ * Starts search for deleted inodes and undelete data on the given
+ * NTFS filesystem.
+ * \param FS The filesystem.
+ * \return 0 in case of no errors, other values otherwise.
+ */
+static int wipe_unrm(ntfs_volume *nv)
+{
+ int ret_wfs = 0, ret;
+ ntfs_attr *bitmapattr = NULL;
+ s64 bmpsize, size, nr_mft_records, i, j, k;
+ unsigned char b;
+ unsigned char * buf = NULL;
+
+#define MYBUF_SIZE 8192
+ unsigned char *mybuf;
+#define MINIM(x, y) ( ((x)<(y))?(x):(y) )
+
+ mybuf = (unsigned char *) malloc(MYBUF_SIZE);
+ if (mybuf == NULL) {
+ return -1;
+ }
+
+ buf = (unsigned char *) malloc(nv->cluster_size);
+ if (buf == NULL) {
+ free (mybuf);
+ return -1;
+ }
+
+ bitmapattr = ntfs_attr_open(nv->mft_ni, AT_BITMAP, AT_UNNAMED, 0);
+ if (bitmapattr == NULL) {
+ free (buf);
+ free (mybuf);
+ return -2;
+ }
+ bmpsize = bitmapattr->initialized_size;
+
+ nr_mft_records = nv->mft_na->initialized_size
+ >> nv->mft_record_size_bits;
+
+ /* just like ntfsundelete; detects i-node numbers fine */
+ for (i = 0; (i < bmpsize) && (ret_wfs==0); i += MYBUF_SIZE) {
+
+ /* read a part of the file bitmap */
+ size = ntfs_attr_pread(bitmapattr, i,
+ MINIM((bmpsize - i), MYBUF_SIZE), mybuf);
+ if (size < 0)
+ break;
+
+ /* parse each byte of the just-read part of the bitmap */
+ for (j = 0; (j < size) && (ret_wfs==0); j++) {
+ b = mybuf[j];
+ /* parse each bit of the byte Bit 1 means 'in use'. */
+ for (k = 0; (k < CHAR_BIT) && (ret_wfs==0);
+ k++, b>>=1) {
+ /* (i+j)*8+k is the i-node bit number */
+ if (((i+j)*CHAR_BIT+k) >= nr_mft_records) {
+ goto done;
+ }
+ if ((b & 1) != 0) {
+ /* i-node is in use, skip it */
+ continue;
+ }
+ /* wiping the i-node here: */
+ ret = destroy_record (nv,
+ (i+j)*CHAR_BIT+k, buf);
+ if (ret != 0) {
+ ret_wfs = ret;
+ }
+ }
+ }
+ }
+done:
+ ntfs_attr_close(bitmapattr);
+ free(buf);
+ free(mybuf);
+
+ ntfs_log_quiet("wipe_undelete\n");
+ return ret_wfs;
+}
+
+
+
+/**
+ * print_summary - Tell the user what we are about to do
+ *
+ * List the operations about to be performed. The output will be silenced by
+ * the --quiet option.
+ *
+ * Return: none
+ */
+static void print_summary(void)
+{
+ int i;
+
+ if (opts.noaction)
+ ntfs_log_quiet("%s is in 'no-action' mode, it will NOT write to disk."
+ "\n\n", EXEC_NAME);
+
+ ntfs_log_quiet("%s is about to wipe:\n", EXEC_NAME);
+ if (opts.unused)
+ ntfs_log_quiet("\tunused disk space\n");
+ if (opts.tails)
+ ntfs_log_quiet("\tfile tails\n");
+ if (opts.mft)
+ ntfs_log_quiet("\tunused mft areas\n");
+ if (opts.directory)
+ ntfs_log_quiet("\tunused directory index space\n");
+ if (opts.logfile)
+ ntfs_log_quiet("\tthe logfile (journal)\n");
+ if (opts.pagefile)
+ ntfs_log_quiet("\tthe pagefile (swap space)\n");
+ if (opts.undel)
+ ntfs_log_quiet("\tundelete data\n");
+
+ ntfs_log_quiet("\n%s will overwrite these areas with: ", EXEC_NAME);
+ if (opts.bytes) {
+ for (i = 0; opts.bytes[i] >= 0; i++)
+ ntfs_log_quiet("0x%02x ", opts.bytes[i]);
+ }
+ ntfs_log_quiet("\n");
+
+ if (opts.count > 1)
+ ntfs_log_quiet("%s will repeat these operations %d times.\n", EXEC_NAME, opts.count);
+ ntfs_log_quiet("\n");
+}
+
+/**
+ * main - Begin here
+ *
+ * Start from here.
+ *
+ * Return: 0 Success, the program worked
+ * 1 Error, something went wrong
+ */
+int main(int argc, char *argv[])
+{
+ ntfs_volume *vol;
+ int result = 1;
+ int flags = 0;
+ int i, j;
+ enum action act = act_info;
+
+ ntfs_log_set_handler(ntfs_log_handler_outerr);
+
+ if (!parse_options(argc, argv))
+ return 1;
+
+ utils_set_locale();
+
+ if (!opts.info)
+ print_summary();
+
+ if (opts.info || opts.noaction)
+ flags = NTFS_MNT_RDONLY;
+ if (opts.force)
+ flags |= NTFS_MNT_RECOVER;
+
+ vol = utils_mount_volume(opts.device, flags);
+ if (!vol)
+ goto free;
+
+ if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force)
+ goto umount;
+
+ if (opts.info) {
+ act = act_info;
+ opts.count = 1;
+ } else if (opts.noaction) {
+ act = act_test;
+ } else {
+ act = act_wipe;
+ }
+
+ /* Even if the output it quieted, you still get 5 seconds to abort. */
+ if ((act == act_wipe) && !opts.force) {
+ ntfs_log_quiet("\n%s will begin in 5 seconds, press CTRL-C to abort.\n", EXEC_NAME);
+ sleep(5);
+ }
+
+ for (i = 0; opts.bytes[i] >= 0; i++) {
+ npasses = i+1;
+ }
+ if (npasses == 0) {
+ npasses = opts.count;
+ }
+#ifdef HAVE_TIME_H
+ srandom(time(NULL));
+#else
+ /* use a pointer as a pseudorandom value */
+ srandom((int)vol + npasses);
+#endif
+ ntfs_log_info("\n");
+ for (i = 0; i < opts.count; i++) {
+ int byte;
+ s64 total = 0;
+ s64 wiped = 0;
+
+ for (j = 0; byte = opts.bytes[j], byte >= 0; j++) {
+
+ if (opts.directory) {
+ wiped = wipe_directory(vol, byte, act);
+ if (wiped < 0)
+ goto umount;
+ else
+ total += wiped;
+ }
+
+ if (opts.tails) {
+ wiped = wipe_tails(vol, byte, act);
+ if (wiped < 0)
+ goto umount;
+ else
+ total += wiped;
+ }
+
+ if (opts.logfile) {
+ wiped = wipe_logfile(vol, byte, act);
+ if (wiped < 0)
+ goto umount;
+ else
+ total += wiped;
+ }
+
+ if (opts.mft) {
+ wiped = wipe_mft(vol, byte, act);
+ if (wiped < 0)
+ goto umount;
+ else
+ total += wiped;
+ }
+
+ if (opts.pagefile) {
+ wiped = wipe_pagefile(vol, byte, act);
+ if (wiped < 0)
+ goto umount;
+ else
+ total += wiped;
+ }
+
+ if (opts.unused) {
+ wiped = wipe_unused(vol, byte, act);
+ if (wiped < 0)
+ goto umount;
+ else
+ total += wiped;
+ }
+
+ if (opts.undel) {
+ wiped = wipe_unrm(vol);
+ if (wiped != 0)
+ goto umount;
+ /*
+ else
+ total += wiped;
+ */
+ }
+
+ if (act == act_info)
+ break;
+ }
+
+ ntfs_log_info(
+ "%lld bytes were wiped (excluding undelete data)\n",
+ (long long)total);
+ }
+ result = 0;
+umount:
+ ntfs_umount(vol, FALSE);
+free:
+ if (opts.bytes)
+ free(opts.bytes);
+ return result;
+}