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; +} |