| /** |
| * 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 |
| #ifdef HAVE_LIMITS_H |
| #include <limits.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" |
| " -U --unused-fast Wipe unused clusters (fast)\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?ilmnpqtuUvVs"; |
| 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' }, |
| { "unused-fast",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': |
| 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 'U': |
| opts.unused_fast++; |
| break; |
| case 'v': |
| opts.verbose++; |
| ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); |
| break; |
| case 'V': |
| ver++; |
| break; |
| case '?': |
| if (strncmp (argv[optind-1], "--log-", 6) == 0) { |
| if (!ntfs_log_parse_option (argv[optind-1])) |
| err++; |
| break; |
| } |
| /* fall through */ |
| 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.unused_fast && !opts.undel) { |
| opts.info = 1; |
| } |
| } |
| |
| if (ver) |
| version(); |
| if (help || err) |
| usage(); |
| |
| /* tri-state 0 : done, 1 : error, -1 : proceed */ |
| return (err ? 1 : (help || ver ? 0 : -1)); |
| } |
| |
| /** |
| * 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_unused_fast - Faster 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. |
| * |
| * - read/write on a block basis (64 clusters, arbitrary) |
| * - skip of fully used block |
| * - skip non-used block already wiped |
| * |
| * Return: >0 Success, the attribute was wiped |
| * 0 Nothing to wipe |
| * -1 Error, something went wrong |
| */ |
| static s64 wipe_unused_fast(ntfs_volume *vol, int byte, enum action act) |
| { |
| s64 i; |
| s64 total = 0; |
| s64 unused = 0; |
| s64 result; |
| u8 *buffer; |
| u8 *big_buffer; |
| u32 *u32_buffer; |
| u32 u32_bytes; |
| unsigned int blksize; |
| unsigned int j,k; |
| BOOL wipe_needed; |
| |
| if (!vol || (byte < 0)) |
| return -1; |
| |
| big_buffer = (u8*)malloc(vol->cluster_size*64); |
| if (!big_buffer) { |
| ntfs_log_error("malloc failed\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < vol->nr_clusters; i+=64) { |
| blksize = vol->nr_clusters - i; |
| if (blksize > 64) |
| blksize = 64; |
| /* if all clusters in this block are used, ignore the block */ |
| result = 0; |
| for (j = 0; j < blksize; j++) { |
| if (utils_cluster_in_use(vol, i+j)) |
| result++; |
| } |
| unused += (blksize - result) * vol->cluster_size; |
| |
| if (result == blksize) { |
| continue; |
| } |
| /* |
| * if all unused clusters in this block are already wiped, |
| * ignore the block |
| */ |
| if (ntfs_pread(vol->dev, vol->cluster_size * i, |
| vol->cluster_size * blksize, big_buffer) |
| != vol->cluster_size * blksize) { |
| ntfs_log_error("Read failed at cluster %lld\n", |
| (long long)i); |
| goto free; |
| } |
| |
| result = 0; |
| wipe_needed = FALSE; |
| u32_bytes = (byte & 255)*0x01010101; |
| buffer = big_buffer; |
| for (j = 0; (j < blksize) && !wipe_needed; j++) { |
| u32_buffer = (u32*)buffer; |
| if (!utils_cluster_in_use(vol, i+j)) { |
| for (k = 0; (k < vol->cluster_size) |
| && (*u32_buffer++ == u32_bytes); k+=4) { |
| } |
| if (k < vol->cluster_size) |
| wipe_needed = TRUE; |
| } |
| buffer += vol->cluster_size; |
| } |
| |
| if (!wipe_needed) { |
| continue; |
| } |
| /* else wipe unused clusters in the block */ |
| buffer = big_buffer; |
| |
| for (j = 0; j < blksize; j++) { |
| if (!utils_cluster_in_use(vol, i+j)) { |
| memset(buffer, byte, vol->cluster_size); |
| total += vol->cluster_size; |
| } |
| buffer += vol->cluster_size; |
| } |
| |
| if ((act == act_wipe) |
| && (ntfs_pwrite(vol->dev, vol->cluster_size * i, |
| vol->cluster_size * blksize, big_buffer) |
| != vol->cluster_size * blksize)) { |
| ntfs_log_error("Write failed at cluster %lld\n", |
| (long long)i); |
| goto free; |
| } |
| } |
| |
| ntfs_log_quiet("wipe_unused_fast 0x%02x, %lld bytes" |
| " already wiped, %lld more bytes wiped\n", |
| byte, (long long)(unused - total), (long long)total); |
| free: |
| free(big_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; |
| |
| /* Avoid getting fixup warnings on unitialized inodes */ |
| NVolSetNoFixupWarn(vol); |
| |
| 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) { |
| if (opts.verbose) |
| ntfs_log_verbose("Could not open inode\n"); |
| else |
| ntfs_log_verbose("\r"); |
| 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 : |
| NVolClearNoFixupWarn(vol); |
| 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 ((size <= 0) || (size > (int)vol->mft_record_size)) { |
| ntfs_log_error("Bad mft record %lld\n", |
| (long long)i); |
| total = -1; |
| goto free; |
| } |
| 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 = const_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 = const_cpu_to_le16(0x0001); |
| |
| // End marker. |
| *((le32*) (((u8*) rec) + attrs_offset)) = const_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; |
| |
| /* Avoid getting fixup warnings on unitialized inodes */ |
| NVolSetNoFixupWarn(vol); |
| |
| 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); |
| } |
| |
| NVolClearNoFixupWarn(vol); |
| 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; |
| } |
| |
| /* Avoid getting fixup warnings on unitialized inodes */ |
| NVolSetNoFixupWarn(nv); |
| /* 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) { |
| |
| NVolClearNoFixupWarn(nv); |
| ntfs_attr_close(mft); |
| free_file(file); |
| return -3; |
| } |
| NVolClearNoFixupWarn(nv); |
| 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 = const_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 = const_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 = const_cpu_to_sle64(0); |
| ctx->attr->highest_vcn = const_cpu_to_sle64(0); |
| ctx->attr->allocated_size = const_cpu_to_sle64(0); |
| ctx->attr->data_size = const_cpu_to_sle64(0); |
| ctx->attr->initialized_size = const_cpu_to_sle64(0); |
| ctx->attr->compressed_size = const_cpu_to_sle64(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.unused_fast) |
| ntfs_log_quiet("\tunused disk space (fast)\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 res; |
| int i, j; |
| enum action act = act_info; |
| |
| ntfs_log_set_handler(ntfs_log_handler_outerr); |
| |
| res = parse_options(argc, argv); |
| if (res >= 0) |
| return (res); |
| |
| 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 || opts.unused_fast) { |
| if (opts.unused_fast) |
| wiped = wipe_unused_fast(vol, byte, |
| act); |
| else |
| 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; |
| } |
| |
| if (opts.noaction || opts.info) |
| ntfs_log_info("%lld bytes would be wiped" |
| " (excluding undelete data)\n", |
| (long long)total); |
| else |
| 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; |
| } |