/** * ntfsrm - Part of the Linux-NTFS project. * * Copyright (c) 2004 Richard Russon * * This utility will delete files from 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" #include #include #include #include #include #include "utils.h" #include "ntfsrm.h" #include "debug.h" #include "dir.h" #include "lcnalloc.h" #include "mft.h" static const char *EXEC_NAME = "ntfsrm"; static struct options opts; static const char *space = " "; GEN_PRINTF (Eprintf, stderr, NULL, FALSE) GEN_PRINTF (Vprintf, stdout, &opts.verbose, TRUE) GEN_PRINTF (Qprintf, stdout, &opts.quiet, FALSE) /** * version - Print version information about the program * * Print a copyright statement and a brief description of the program. * * Return: none */ static void version (void) { printf ("\n%s v%s - Delete files from an NTFS volume.\n\n", EXEC_NAME, VERSION); printf ("Copyright (c) 2004 Richard Russon\n"); printf ("\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) { printf ("\nUsage: %s [options] device file\n" "\n" " -r --recursive Delete files in subdirectories\n" " -i --interactive Ask before deleting files\n" //" -I num --inode num Delete the file with this inode number\n" //" -U --unlink Unlink the file, deleting all references \n" "\n" " -D --no-dirty Do not mark volume dirty (require chkdsk)\n" " -n --no-action Do not write to disk\n" " -f --force Use less caution\n" " -h --help Print this help\n" " -q --quiet Less output\n" " -V --version Version information\n" " -v --verbose More output\n\n", EXEC_NAME); printf ("%s%s\n", ntfs_bugs, ntfs_home); } /** * 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 = "-Dfh?inqRrVv"; //"-Dfh?I:inqRrUVv"; static const struct option lopt[] = { { "force", no_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, //{ "inode", required_argument, NULL, 'I' }, { "interactive", no_argument, NULL, 'i' }, { "no-action", no_argument, NULL, 'n' }, { "no-dirty", no_argument, NULL, 'D' }, { "quiet", no_argument, NULL, 'q' }, { "recursive", no_argument, NULL, 'r' }, //{ "unlink", no_argument, NULL, 'U' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; char c = -1; int err = 0; int ver = 0; int help = 0; opterr = 0; /* We'll handle the errors, thank you. */ 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 if (!opts.file) { opts.file = argv[optind-1]; } else { opts.device = NULL; opts.file = NULL; err++; } break; case 'D': opts.nodirty++; break; case 'f': opts.force++; break; case 'h': case '?': help++; break; case 'i': opts.interactive++; break; case 'n': opts.noaction++; break; case 'q': opts.quiet++; break; case 'R': case 'r': opts.recursive++; break; case 'V': ver++; break; case 'v': opts.verbose++; break; default: Eprintf ("Unknown option '%s'.\n", argv[optind-1]); err++; break; } } if (help || ver) { opts.quiet = 0; } else { if ((opts.device == NULL) || (opts.file == NULL)) { if (argc > 1) Eprintf ("You must specify one device and one file.\n"); err++; } if (opts.quiet && opts.verbose) { Eprintf("You may not use --quiet and --verbose at the " "same time.\n"); err++; } } if (ver) version(); if (help || err) usage(); return (!err && !help && !ver); } struct ntfs_dir; static void ntfs_ie_dump (INDEX_ENTRY *ie); static VCN ntfs_ie_get_vcn (INDEX_ENTRY *ie); static INDEX_ENTRY * ntfs_ie_set_name (INDEX_ENTRY *ie, ntfschar *name, int namelen, FILE_NAME_TYPE_FLAGS nametype); #define RED "" #define GREEN "" #define YELLOW "" #define BLUE "" #define MAGENTA "" #define CYAN "" #define BOLD "" #define END "" #define ROUND_UP(num,bound) (((num)+((bound)-1)) & ~((bound)-1)) #define ATTR_SIZE(s) ROUND_UP(s,8) /** * struct ntfs_bmp * a cache for either dir/$BITMAP, $MFT/$BITMAP or $Bitmap/$DATA */ struct ntfs_bmp { ntfs_attr *attr; u8 **data; VCN *data_vcn; int count; //int cluster_size; }; /** * struct ntfs_dt */ struct ntfs_dt { struct ntfs_dir *dir; struct ntfs_dt *parent; u8 *data; int data_len; int child_count; INDEX_ENTRY **children; struct ntfs_dt **sub_nodes; INDEX_HEADER *header; VCN vcn; }; /** * struct ntfs_dir */ struct ntfs_dir { ntfs_volume *vol; struct ntfs_dir *parent; ntfschar *name; int name_len; struct ntfs_dt *index; struct ntfs_dir **children; int child_count; MFT_REF mft_num; struct mft_bitmap *bitmap; ntfs_inode *inode; ntfs_attr *iroot; ntfs_attr *ialloc; ntfs_attr *ibmp; int index_size; }; /** * ntfs_name_print */ static void ntfs_name_print (ntfschar *name, int name_len) { char *buffer = NULL; if (name_len) { ntfs_ucstombs (name, name_len, (char**) &buffer, 0); printf ("%s", buffer); free (buffer); } else { printf ("!"); } } /** * ntfs_dir_print */ static void ntfs_dir_print (struct ntfs_dir *dir, int indent) { int i; if (!dir) return; printf ("%.*s%p ", indent, space, dir); ntfs_name_print (dir->name, dir->name_len); printf ("\n"); for (i = 0; i < dir->child_count; i++) { ntfs_dir_print (dir->children[i], indent + 4); } } /** * ntfs_dt_print */ static void ntfs_dt_print (struct ntfs_dt *dt, int indent) { int i; if (!dt) return; printf ("%.*s%p (%d)\n", indent, space, dt, dt->child_count); for (i = 0; i < dt->child_count; i++) { ntfs_dt_print (dt->sub_nodes[i], indent + 4); } } /** * ntfs_bmp_free */ static void ntfs_bmp_free (struct ntfs_bmp *bmp) { int i; if (!bmp) return; for (i = 0; i < bmp->count; i++) free (bmp->data[i]); ntfs_attr_close (bmp->attr); free (bmp->data); free (bmp->data_vcn); free (bmp); } /** * ntfs_bmp_alloc */ static struct ntfs_bmp * ntfs_bmp_alloc (ntfs_inode *inode, ATTR_TYPES type, ntfschar *name, int name_len) { struct ntfs_bmp *bmp; ntfs_attr *attr; if (!inode) return NULL; attr = ntfs_attr_open (inode, type, name, name_len); if (!attr) return NULL; bmp = calloc (1, sizeof (*bmp)); if (!bmp) return NULL; bmp->attr = attr; bmp->data = calloc (16, sizeof (*bmp->data)); bmp->data_vcn = calloc (16, sizeof (*bmp->data_vcn)); bmp->count = 0; if (!bmp->data || !bmp->data_vcn) { ntfs_bmp_free (bmp); return NULL; } return bmp; } /** * ntfs_bmp_add_data */ static int ntfs_bmp_add_data (struct ntfs_bmp *bmp, VCN vcn, u8 *data) { int i = 0; int old; int new; if (!bmp || !data) return -1; old = ((bmp->count + 15) & ~15); bmp->count++; new = ((bmp->count + 15) & ~15); if (old != new) { bmp->data = realloc (bmp->data, new * sizeof (*bmp->data)); bmp->data_vcn = realloc (bmp->data_vcn , new * sizeof (*bmp->data_vcn)); } for (i = 0; i < bmp->count-1; i++) if (bmp->data_vcn[i] > vcn) break; if ((bmp->count-i) > 0) { memmove (&bmp->data[i+1], &bmp->data[i], (bmp->count-i) * sizeof (*bmp->data)); memmove (&bmp->data_vcn[i+1], &bmp->data_vcn[i], (bmp->count-i) * sizeof (*bmp->data_vcn)); } bmp->data[i] = data; bmp->data_vcn[i] = vcn; return bmp->count; } /** * ntfs_bmp_get_data */ static u8 * ntfs_bmp_get_data (struct ntfs_bmp *bmp, VCN vcn) { u8 *buffer; int i; VCN begin; VCN end; if (!bmp) return NULL; for (i = 0; i < bmp->count; i++) { begin = (bmp->data_vcn[i] >> 3) & (~(512-1)); end = begin + (512 << 3); if ((vcn >= begin) && (vcn < end)) { //printf ("%lld, %lld, %lld\n", begin, vcn, end); return bmp->data[i]; } } buffer = malloc (512); if (!buffer) return NULL; begin = (vcn>>3) & (~(512-1)); //printf ("loading from offset %lld\n", begin); if (ntfs_attr_pread (bmp->attr, begin, 512, buffer) < 0) { free (buffer); return NULL; } ntfs_bmp_add_data (bmp, vcn, buffer); return buffer; } /** * ntfs_bmp_set_range */ static int ntfs_bmp_set_range (struct ntfs_bmp *bmp, VCN vcn, u64 length, int value) { u64 i; u8 *buffer; VCN begin; VCN end; int start; int finish; u8 sta_part; u8 fin_part; if (!bmp) return -1; //printf ("\n"); //printf ("set range: %lld - %lld\n", vcn, vcn+length-1); for (i = vcn; i < (vcn+length); i += 4096) { buffer = ntfs_bmp_get_data (bmp, i); if (!buffer) return -1; #if 0 memset (buffer, 0xFF, 512); value = 0; memset (buffer, 0x00, 512); value = 1; #endif //utils_dump_mem (buffer, 0, 32, DM_DEFAULTS); //printf ("\n"); begin = i & ~4095; end = begin + 4095; //printf ("begin = %lld, vcn = %lld,%lld end = %lld\n", begin, vcn, vcn+length-1, end); if ((vcn > begin) && (vcn < end)) { //printf ("1\n"); start = ((vcn+8) >> 3) & 511; sta_part = 0xff << (vcn&7); } else { //printf ("2\n"); start = 0; } if (((vcn+length-1) >= begin) && ((vcn+length-1) <= end)) { //printf ("3\n"); finish = ((vcn+length-1) >> 3) & 511; fin_part = 0xff >> (7-((vcn+length-1)&7)); } else { //printf ("4\n"); finish = 511; } #if 0 //printf ("\n"); printf ("%lld) ", i>>12); if (start > 0) { printf ("(%02x) ", sta_part); } else { printf (" "); } printf ("%d - %d", start, finish); if (finish < 511) { printf (" (%02x)\n", fin_part); } else { printf (" \n"); } #endif if (value) { if (start != 0) buffer[start-1] |= sta_part; if ((finish - start) > 0) memset (buffer+start, 0xff, finish-start); buffer[finish] |= fin_part; } else { if (start != 0) buffer[start-1] &= ~sta_part; if ((finish - start) > 0) memset (buffer+start, 0x00, finish-start); buffer[finish] &= ~fin_part; } //utils_dump_mem (buffer, 0, 16, DM_DEFAULTS); } printf (GREEN "Modified: inode %lld, " END, bmp->attr->ni->mft_no); switch (bmp->attr->type) { case AT_BITMAP: printf ("$BITMAP"); break; case AT_DATA: printf ("$DATA"); break; } printf (" vcn %lld-%lld\n", vcn>>12, (vcn+length)>>12); return 1; } /** * ntfs_bmp_commit */ static int ntfs_bmp_commit (struct ntfs_bmp *bmp) { if (!bmp) return -1; return 0; } /** * ntfs_bmp_rollback */ static int ntfs_bmp_rollback (struct ntfs_bmp *bmp) { if (!bmp) return -1; return 0; } /** * ntfs_dt_alloc_children */ static INDEX_ENTRY ** ntfs_dt_alloc_children (INDEX_ENTRY **children, int count) { // XXX calculate for 2K and 4K indexes max and min filenames (inc/exc VCN) int old = (count + 0x1e) & ~0x1f; int new = (count + 0x1f) & ~0x1f; if (old == new) return children; return realloc (children, new * sizeof (INDEX_ENTRY*)); } /** * ntfs_dt_alloc_children2 */ static BOOL ntfs_dt_alloc_children2 (struct ntfs_dt *dt, int count) { // XXX calculate for 2K and 4K indexes max and min filenames (inc/exc VCN) int old = (count + 0x1e) & ~0x1f; int new = (count + 0x1f) & ~0x1f; if (old == new) return TRUE; dt->children = realloc (dt->children, new * sizeof (*dt->children)); dt->sub_nodes = realloc (dt->sub_nodes, new * sizeof (*dt->sub_nodes)); return (dt->children && dt->sub_nodes); } /** * ntfs_dt_count_root */ static int ntfs_dt_count_root (struct ntfs_dt *dt) { u8 *buffer = NULL; u8 *ptr = NULL; VCN vcn; s64 size = 0; char *name = NULL; INDEX_ROOT *root; INDEX_HEADER *header; INDEX_ENTRY *entry; if (!dt) return -1; buffer = dt->data; size = dt->data_len; root = (INDEX_ROOT*) buffer; if (root->type != AT_FILE_NAME) return -1; header = (INDEX_HEADER*) (buffer + 0x10); if (header->index_length > size) return -1; dt->child_count = 0; ptr = buffer + header->entries_offset + 0x10; while (ptr < (buffer + size)) { entry = (INDEX_ENTRY*) ptr; dt->child_count++; dt->children = ntfs_dt_alloc_children (dt->children, dt->child_count); if (entry->flags & INDEX_ENTRY_NODE) { vcn = ntfs_ie_get_vcn ((INDEX_ENTRY*) ptr); //printf ("VCN %lld\n", vcn); } if (!(entry->flags & INDEX_ENTRY_END)) { ntfs_ucstombs (entry->key.file_name.file_name, entry->key.file_name.file_name_length, &name, entry->key.file_name.file_name_length); //printf ("\tinode %8lld %s\n", MREF (entry->indexed_file), name); free (name); name = NULL; } //printf ("CC[%d] = %p\n", dt->child_count-1, entry); dt->children[dt->child_count-1] = entry; ptr += entry->length; } //printf ("count = %d\n\n", dt->child_count); if (dt->child_count > 0) { //printf ("%d subnodes\n", dt->child_count); dt->sub_nodes = calloc (dt->child_count, sizeof (struct ntfs_dt *)); } return dt->child_count; } /** * ntfs_dt_count_alloc */ static int ntfs_dt_count_alloc (struct ntfs_dt *dt) { u8 *buffer = NULL; u8 *ptr = NULL; VCN vcn; s64 size = 0; char *name = NULL; INDEX_BLOCK *block; INDEX_ENTRY *entry; if (!dt) return -1; buffer = dt->data; size = dt->data_len; //utils_dump_mem (buffer, 0, 128, DM_DEFAULTS); block = (INDEX_BLOCK*) buffer; //printf ("INDX %lld\n", block->index_block_vcn); ptr = buffer + 0x18 + block->index.entries_offset; //printf ("block size %d\n", block->index.index_length); dt->child_count = 0; while (ptr < (buffer + 0x18 + block->index.index_length)) { entry = (INDEX_ENTRY*) ptr; dt->child_count++; dt->children = ntfs_dt_alloc_children (dt->children, dt->child_count); if (entry->flags & INDEX_ENTRY_NODE) { vcn = ntfs_ie_get_vcn ((INDEX_ENTRY*) ptr); //printf ("\tVCN %lld\n", vcn); } dt->children[dt->child_count-1] = entry; if (entry->flags & INDEX_ENTRY_END) { break; } else { ntfs_ucstombs (entry->key.file_name.file_name, entry->key.file_name.file_name_length, &name, entry->key.file_name.file_name_length); //printf ("\tinode %8lld %s\n", MREF (entry->indexed_file), name); free (name); name = NULL; } ptr += entry->length; } //printf ("count = %d\n", dt->child_count); if (dt->child_count > 0) { //printf ("%d subnodes\n", dt->child_count); dt->sub_nodes = calloc (dt->child_count, sizeof (struct ntfs_dt *)); } return dt->child_count; } /** * ntfs_dt_alloc */ static struct ntfs_dt * ntfs_dt_alloc (struct ntfs_dir *dir, struct ntfs_dt *parent, VCN vcn) { struct ntfs_dt *dt = NULL; //int i; if (!dir) return NULL; dt = calloc (1, sizeof (*dt)); if (!dt) return NULL; dt->dir = dir; dt->parent = parent; dt->children = NULL; dt->child_count = 0; dt->sub_nodes = NULL; dt->vcn = vcn; if (parent) { //printf ("alloc a = %lld\n", dir->ialloc->allocated_size); //printf ("alloc d = %lld\n", dir->ialloc->data_size); //printf ("alloc i = %lld\n", dir->ialloc->initialized_size); //printf ("vcn = %lld\n", vcn); dt->data_len = dt->dir->index_size; //printf ("parent size = %d\n", dt->data_len); dt->data = malloc (dt->data_len); //printf ("%lld\n", ntfs_attr_mst_pread (dir->ialloc, vcn*512, 1, dt->data_len, dt->data)); ntfs_attr_mst_pread (dir->ialloc, vcn*512, 1, dt->data_len, dt->data); //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); //printf ("\n"); ntfs_dt_count_alloc (dt); dt->header = &((INDEX_BLOCK*)dt->data)->index; //printf ("USA = %d\n", ((INDEX_BLOCK*)dt->data)->usa_count); #if 0 for (i = 0; i < dt->child_count; i++) { INDEX_ENTRY *ie = dt->children[i]; printf ("%d\n", ((u8*)ie) - dt->data); if (ie->flags & INDEX_ENTRY_END) printf ("IE (%d)\n", ie->length); else printf ("IE %lld (%d)\n", MREF (ie->key.file_name.parent_directory), ie->length); utils_dump_mem ((u8*)ie, 0, ie->length, DM_DEFAULTS); printf ("\n"); } #endif } else { //printf ("root a = %lld\n", dir->iroot->allocated_size); //printf ("root d = %lld\n", dir->iroot->data_size); //printf ("root i = %lld\n", dir->iroot->initialized_size); dt->data_len = dir->iroot->allocated_size; dt->data = malloc (dt->data_len); //printf ("%lld\n", ntfs_attr_pread (dir->iroot, 0, dt->data_len, dt->data)); ntfs_attr_pread (dir->iroot, 0, dt->data_len, dt->data); //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); ntfs_dt_count_root (dt); dt->header = &((INDEX_ROOT*)dt->data)->index; //dt->data_len = ((INDEX_ROOT*)dt->data)->index_block_size; //printf ("IBS = %d\n", ((INDEX_ROOT*)dt->data)->index_block_size); #if 0 for (i = 0; i < dt->child_count; i++) { INDEX_ENTRY *ie = dt->children[i]; printf ("%d\n", ((u8*)ie) - dt->data); if (ie->flags & INDEX_ENTRY_END) printf ("IE (%d)\n", ie->length); else printf ("IE %lld (%d)\n", MREF (ie->key.file_name.parent_directory), ie->length); utils_dump_mem ((u8*)ie, 0, ie->length, DM_DEFAULTS); printf ("\n"); } #endif } //printf ("index_header (%d,%d)\n", dt->header.index_length, dt->header.allocated_size); return dt; } /** * ntfs_dt_free */ static void ntfs_dt_free (struct ntfs_dt *dt) { int i; if (!dt) return; for (i = 0; i < dt->child_count; i++) ntfs_dt_free (dt->sub_nodes[i]); free (dt->sub_nodes); free (dt->children); free (dt->data); free (dt); } /** * ntfs_dt_find * find dt by name, return MFT_REF * maps dt's as necessary */ static MFT_REF ntfs_dt_find (struct ntfs_dt *dt, ntfschar *name, int name_len) { MFT_REF res = -1; INDEX_ENTRY *ie; struct ntfs_dt *sub; VCN vcn; int i; int r; if (!dt || !name) return -1; /* * State Children Action * ------------------------------------------- * collates after - keep searching * match name - return MREF * collates before no return -1 * collates before yes map & recurse * end marker no return -1 * end marker yes map & recurse */ //printf ("child_count = %d\n", dt->child_count); for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; if (ie->flags & INDEX_ENTRY_END) { r = -1; } else { //printf ("\t"); ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); printf ("\n"); r = ntfs_names_collate (name, name_len, ie->key.file_name.file_name, ie->key.file_name.file_name_length, 2, IGNORE_CASE, dt->dir->vol->upcase, dt->dir->vol->upcase_len); } //printf ("%d, %d\n", i, r); if (r == 1) { //printf ("keep searching\n"); continue; } else if (r == 0) { res = MREF (ie->indexed_file); //printf ("match %lld\n", res); } else if (r == -1) { if (ie->flags & INDEX_ENTRY_NODE) { //printf ("map & recurse\n"); //printf ("sub %p\n", dt->sub_nodes); if (!dt->sub_nodes[i]) { vcn = ntfs_ie_get_vcn (ie); //printf ("vcn = %lld\n", vcn); sub = ntfs_dt_alloc (dt->dir, dt, vcn); dt->sub_nodes[i] = sub; } res = ntfs_dt_find (dt->sub_nodes[i], name, name_len); } else { //printf ("ENOENT\n"); } } else { printf ("error collating name\n"); } break; } return res; } /** * ntfs_dt_find2 * find dt by name, returns dt and index * maps dt's as necessary */ static struct ntfs_dt * ntfs_dt_find2 (struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num) { struct ntfs_dt *res = NULL; INDEX_ENTRY *ie; int i; int r; if (!dt || !name) return NULL; /* * State Children Action * ------------------------------------------- * collates after - keep searching * match name - return MREF * collates before no return -1 * collates before yes map & recurse * end marker no return -1 * end marker yes map & recurse */ //printf ("child_count = %d\n", dt->child_count); for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; if (ie->flags & INDEX_ENTRY_END) { r = -1; } else { //printf ("\t"); ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); printf ("\n"); r = ntfs_names_collate (name, name_len, ie->key.file_name.file_name, ie->key.file_name.file_name_length, 2, IGNORE_CASE, dt->dir->vol->upcase, dt->dir->vol->upcase_len); } //printf ("%d, %d\n", i, r); if (r == 1) { //printf ("keep searching\n"); continue; } else if (r == 0) { res = dt; //printf ("match %p\n", res); if (index_num) *index_num = i; } else if (r == -1) { //printf ("recurse\n"); res = ntfs_dt_find2 (dt->sub_nodes[i], name, name_len, index_num); } else { printf ("error collating name\n"); } break; } return res; } /** * ntfs_dt_find3 * find dt by name, returns dt and index * does not map new dt's */ static struct ntfs_dt * ntfs_dt_find3 (struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num) { struct ntfs_dt *res = NULL; INDEX_ENTRY *ie; int i; int r; if (!dt || !name) return NULL; //printf ("child_count = %d\n", dt->child_count); for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; if (ie->flags & INDEX_ENTRY_END) { r = -1; } else { //printf ("\t"); ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); printf ("\n"); r = ntfs_names_collate (name, name_len, ie->key.file_name.file_name, ie->key.file_name.file_name_length, 2, IGNORE_CASE, dt->dir->vol->upcase, dt->dir->vol->upcase_len); } //printf ("%d, %d\n", i, r); if (r == 1) { //printf ("keep searching\n"); continue; } else if (r == 0) { res = dt; //printf ("match %p\n", res); if (index_num) *index_num = i; } else if (r == -1) { if (ie->flags & INDEX_ENTRY_NODE) { //printf ("recurse\n"); res = ntfs_dt_find3 (dt->sub_nodes[i], name, name_len, index_num); } else { //printf ("no match\n"); res = dt; if (index_num) *index_num = i; } } else { printf ("error collating name\n"); } break; } return res; } /** * ntfs_dt_find4 * find successor to specified name, returns dt and index * maps dt's as necessary */ static struct ntfs_dt * ntfs_dt_find4 (struct ntfs_dt *dt, ntfschar *name, int name_len, int *index_num) { struct ntfs_dt *res = NULL; struct ntfs_dt *sub = NULL; INDEX_ENTRY *ie; VCN vcn; int i; int r; if (!dt || !name) return NULL; //printf ("child_count = %d\n", dt->child_count); for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; if (ie->flags & INDEX_ENTRY_END) { r = -1; } else { //printf ("\t"); ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); printf ("\n"); r = ntfs_names_collate (name, name_len, ie->key.file_name.file_name, ie->key.file_name.file_name_length, 2, IGNORE_CASE, dt->dir->vol->upcase, dt->dir->vol->upcase_len); } //printf ("%d, %d\n", i, r); if (r == 1) { //printf ("keep searching\n"); } else if (r == 0) { //res = dt; //printf ("match\n"); // ignore } else if (r == -1) { if (ie->flags & INDEX_ENTRY_NODE) { //printf ("recurse\n"); if (!dt->sub_nodes[i]) { vcn = ntfs_ie_get_vcn (ie); //printf ("vcn = %lld\n", vcn); sub = ntfs_dt_alloc (dt->dir, dt, vcn); dt->sub_nodes[i] = sub; } res = ntfs_dt_find4 (dt->sub_nodes[i], name, name_len, index_num); } else { //printf ("no match\n"); res = dt; if (index_num) *index_num = i; } break; } else { printf ("error collating name\n"); } //break; } return res; } /** * ntfs_dt_commit */ static int ntfs_dt_commit (struct ntfs_dt *dt) { if (!dt) return -1; return 0; } /** * ntfs_dt_rollback */ static int ntfs_dt_rollback (struct ntfs_dt *dt) { if (!dt) return -1; return 0; } /** * ntfs_dt_find_parent */ static int ntfs_dt_find_parent (struct ntfs_dt *dt) { int i; struct ntfs_dt *parent; if (!dt) return -1; parent = dt->parent; if (!parent) return -1; for (i = 0; i < parent->child_count; i++) if (parent->sub_nodes[i] == dt) return i; return -1; } /** * ntfs_dt_root */ static BOOL ntfs_dt_root (struct ntfs_dt *dt) { if (!dt) return FALSE; return (dt->parent == NULL); } /** * ntfs_dir_alloc */ static struct ntfs_dir * ntfs_dir_alloc (ntfs_volume *vol, MFT_REF mft_num) { struct ntfs_dir *dir = NULL; ntfs_inode *inode = NULL; ATTR_RECORD *rec = NULL; INDEX_ROOT *ir = NULL; if (!vol) return NULL; inode = ntfs_inode_open (vol, mft_num); if (!inode) return NULL; dir = calloc (1, sizeof (*dir)); if (!dir) { ntfs_inode_close (inode); return NULL; } dir->inode = inode; dir->iroot = ntfs_attr_open (inode, AT_INDEX_ROOT, I30, 4); dir->ialloc = ntfs_attr_open (inode, AT_INDEX_ALLOCATION, I30, 4); dir->ibmp = ntfs_attr_open (inode, AT_BITMAP, I30, 4); dir->vol = vol; dir->parent = NULL; dir->name = NULL; dir->name_len = 0; dir->index = NULL; dir->children = NULL; dir->child_count = 0; dir->mft_num = mft_num; dir->bitmap = NULL; if (dir->ialloc) { rec = find_first_attribute (AT_INDEX_ROOT, inode->mrec); ir = (INDEX_ROOT*) ((u8*)rec + rec->value_offset); dir->index_size = ir->index_block_size; } else { dir->index_size = 0; } if (!dir->iroot) { free (dir); return NULL; } return dir; } /** * ntfs_dir_free */ static void ntfs_dir_free (struct ntfs_dir *dir) { struct ntfs_dir *parent; int i; if (!dir) return; parent = dir->parent; if (parent) { for (i = 0; i < parent->child_count; i++) { if (parent->children[i] == dir) { parent->children[i] = NULL; } } } ntfs_attr_close (dir->iroot); ntfs_attr_close (dir->ialloc); ntfs_attr_close (dir->ibmp); ntfs_inode_close (dir->inode); for (i = 0; i < dir->child_count; i++) ntfs_dir_free (dir->children[i]); free (dir->children); ntfs_dt_free (dir->index); free (dir); } /** * ntfs_dir_find */ static MFT_REF ntfs_dir_find (struct ntfs_dir *dir, char *name) { MFT_REF mft_num; ntfschar *uname = NULL; int len; if (!dir || !name) return -1; len = ntfs_mbstoucs (name, &uname, 0); if (len < 0) return -1; if (!dir->index) dir->index = ntfs_dt_alloc (dir, NULL, -1); //printf ("dir->index = %p\n", dir->index); //printf ("dir->child_count = %d\n", dir->child_count); //printf ("uname = %p\n", uname); mft_num = ntfs_dt_find (dir->index, uname, len); free (uname); return mft_num; } /** * ntfs_dir_add */ static void ntfs_dir_add (struct ntfs_dir *parent, struct ntfs_dir *child) { if (!parent || !child) return; parent->child_count++; //printf ("child count = %d\n", parent->child_count); parent->children = realloc (parent->children, parent->child_count * sizeof (struct ntfs_dir*)); child->parent = parent; parent->children[parent->child_count-1] = child; } /** * ntfs_dir_commit */ static int ntfs_dir_commit (struct ntfs_dir *dir) { if (!dir) return -1; if (0) ntfs_dt_commit (NULL); if (0) ntfs_bmp_commit (NULL); return 0; } /** * ntfs_dir_rollback */ static int ntfs_dir_rollback (struct ntfs_dir *dir) { if (!dir) return -1; if (0) ntfs_dt_rollback (NULL); if (0) ntfs_bmp_rollback (NULL); return 0; } /** * utils_pathname_to_mftref */ static MFT_REF utils_pathname_to_mftref (ntfs_volume *vol, struct ntfs_dir *parent, const char *pathname, struct ntfs_dir **finddir) { MFT_REF mft_num; MFT_REF result = -1; char *p, *q; char *ascii = NULL; struct ntfs_dir *dir = NULL; if (!vol || !parent || !pathname) { errno = EINVAL; return -1; } ascii = strdup (pathname); // Work with a r/w copy if (!ascii) { Eprintf ("Out of memory.\n"); goto close; } p = ascii; while (p && *p && *p == PATH_SEP) // Remove leading /'s p++; while (p && *p) { q = strchr (p, PATH_SEP); // Find the end of the first token if (q != NULL) { *q = '\0'; q++; } //printf ("looking for %s in %p\n", p, parent); mft_num = ntfs_dir_find (parent, p); if (mft_num == (u64)-1) { Eprintf ("Couldn't find name '%s' in pathname '%s'.\n", p, pathname); goto close; } if (q) { dir = ntfs_dir_alloc (vol, mft_num); if (!dir) { Eprintf ("Couldn't allocate a new directory (%lld).\n", mft_num); goto close; } ntfs_dir_add (parent, dir); parent = dir; } else { //printf ("file %s\n", p); result = mft_num; if (finddir) *finddir = dir ? dir : parent; break; } p = q; while (p && *p && *p == PATH_SEP) p++; } close: free (ascii); return result; } /** * utils_mftrec_mark_free */ static int utils_mftrec_mark_free (ntfs_volume *vol, MFT_REF mref) { static u8 buffer[512]; static s64 bmpmref = -sizeof (buffer) - 1; /* Which bit of $BITMAP is in the buffer */ int byte, bit; if (!vol) { errno = EINVAL; return -1; } mref = MREF (mref); //printf ("mref = %lld\n", mref); /* Does mref lie in the section of $Bitmap we already have cached? */ if (((s64)mref < bmpmref) || ((s64)mref >= (bmpmref + (sizeof (buffer) << 3)))) { Dprintf ("Bit lies outside cache.\n"); /* Mark the buffer as not in use, in case the read is shorter. */ memset (buffer, 0, sizeof (buffer)); bmpmref = mref & (~((sizeof (buffer) << 3) - 1)); if (ntfs_attr_pread (vol->mftbmp_na, (bmpmref>>3), sizeof (buffer), buffer) < 0) { Eprintf ("Couldn't read $MFT/$BITMAP: %s\n", strerror (errno)); return -1; } Dprintf ("Reloaded bitmap buffer.\n"); } bit = 1 << (mref & 7); byte = (mref >> 3) & (sizeof (buffer) - 1); Dprintf ("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit); if ((buffer[byte] & bit) == 0) { Eprintf ("MFT record isn't in use (1).\n"); return -1; } //utils_dump_mem (buffer, byte, 1, DM_NO_ASCII); buffer[byte] &= ~bit; //utils_dump_mem (buffer, byte, 1, DM_NO_ASCII); if (ntfs_attr_pwrite (vol->mftbmp_na, (bmpmref>>3), sizeof (buffer), buffer) < 0) { Eprintf ("Couldn't write $MFT/$BITMAP: %s\n", strerror (errno)); return -1; } return (buffer[byte] & bit); } /** * utils_mftrec_mark_free2 */ static int utils_mftrec_mark_free2 (ntfs_volume *vol, MFT_REF mref) { u8 buffer[1024]; s64 res; MFT_RECORD *rec; if (!vol) return -1; mref = MREF (mref); rec = (MFT_RECORD*) buffer; res = ntfs_mft_record_read (vol, mref, rec); printf ("res = %lld\n", res); if ((rec->flags & MFT_RECORD_IN_USE) == 0) { Eprintf ("MFT record isn't in use (2).\n"); return -1; } rec->flags &= ~MFT_RECORD_IN_USE; //printf ("\n"); //utils_dump_mem (buffer, 0, 1024, DM_DEFAULTS); res = ntfs_mft_record_write (vol, mref, rec); printf ("res = %lld\n", res); return 0; } /** * utils_mftrec_mark_free3 */ static int utils_mftrec_mark_free3 (struct ntfs_bmp *bmp, MFT_REF mref) { return ntfs_bmp_set_range (bmp, (VCN) MREF (mref), 1, 0); } /** * utils_mftrec_mark_free4 */ static int utils_mftrec_mark_free4 (ntfs_inode *inode) { MFT_RECORD *rec; if (!inode) return -1; rec = (MFT_RECORD*) inode->mrec; if ((rec->flags & MFT_RECORD_IN_USE) == 0) { Eprintf ("MFT record isn't in use (2).\n"); return -1; } rec->flags &= ~MFT_RECORD_IN_USE; //printf ("\n"); //utils_dump_mem (buffer, 0, 1024, DM_DEFAULTS); printf (GREEN "Modified: inode %lld MFT_RECORD header\n" END, inode->mft_no); return 0; } /** * utils_free_non_residents */ static int utils_free_non_residents (ntfs_inode *inode) { // XXX need to do this in memory ntfs_attr_search_ctx *ctx; ntfs_attr *na; ATTR_RECORD *arec; if (!inode) return -1; ctx = ntfs_attr_get_search_ctx (NULL, inode->mrec); if (!ctx) { printf ("can't create a search context\n"); return -1; } while (ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx) == 0) { arec = ctx->attr; if (arec->non_resident) { na = ntfs_attr_open (inode, arec->type, NULL, 0); if (na) { runlist_element *rl; LCN size; LCN count; ntfs_attr_map_whole_runlist (na); rl = na->rl; size = na->allocated_size >> inode->vol->cluster_size_bits; for (count = 0; count < size; count += rl->length, rl++) { //printf ("rl(%llu,%llu,%lld)\n", rl->vcn, rl->lcn, rl->length); //printf ("freed %d\n", ntfs_cluster_free (inode->vol, na, rl->vcn, rl->length)); ntfs_cluster_free (inode->vol, na, rl->vcn, rl->length); } ntfs_attr_close (na); } } } ntfs_attr_put_search_ctx (ctx); return 0; } /** * utils_free_non_residents2 */ static int utils_free_non_residents2 (ntfs_inode *inode, struct ntfs_bmp *bmp) { ntfs_attr_search_ctx *ctx; ntfs_attr *na; ATTR_RECORD *arec; if (!inode) return -1; ctx = ntfs_attr_get_search_ctx (NULL, inode->mrec); if (!ctx) { printf ("can't create a search context\n"); return -1; } while (ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx) == 0) { arec = ctx->attr; if (arec->non_resident) { na = ntfs_attr_open (inode, arec->type, NULL, 0); if (na) { runlist_element *rl; LCN size; LCN count; ntfs_attr_map_whole_runlist (na); rl = na->rl; size = na->allocated_size >> inode->vol->cluster_size_bits; for (count = 0; count < size; count += rl->length, rl++) { ntfs_bmp_set_range (bmp, rl->lcn, rl->length, 0); } ntfs_attr_close (na); } } } ntfs_attr_put_search_ctx (ctx); return 0; } /** * ntfs_mft_resize_resident */ static int ntfs_mft_resize_resident (ntfs_inode *inode, ATTR_TYPES type, ntfschar *name, int name_len, u8 *data, int data_len) { int mft_size; int mft_usage; int mft_free; int attr_orig; int attr_new; u8 *src; u8 *dst; u8 *end; int len; ntfs_attr_search_ctx *ctx = NULL; ATTR_RECORD *arec = NULL; MFT_RECORD *mrec = NULL; int res = -1; if ((!inode) || (!inode->mrec)) return -1; if ((!data) || (data_len < 0)) return -1; mrec = inode->mrec; mft_size = mrec->bytes_allocated; mft_usage = mrec->bytes_in_use; mft_free = mft_size - mft_usage; //printf ("mft_size = %d\n", mft_size); //printf ("mft_usage = %d\n", mft_usage); //printf ("mft_free = %d\n", mft_free); //printf ("\n"); ctx = ntfs_attr_get_search_ctx (NULL, mrec); if (!ctx) goto done; if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, ctx) != 0) goto done; arec = ctx->attr; if (arec->non_resident) { printf ("attribute isn't resident\n"); goto done; } attr_orig = arec->value_length; attr_new = data_len; //printf ("attr orig = %d\n", attr_orig); //printf ("attr new = %d\n", attr_new); //printf ("\n"); if ((attr_new - attr_orig + mft_usage) > mft_size) { printf ("attribute won't fit into mft record\n"); goto done; } //printf ("new free space = %d\n", mft_size - (attr_new - attr_orig + mft_usage)); src = (u8*)arec + arec->length; dst = src + (attr_new - attr_orig); end = (u8*)mrec + mft_usage; len = end - src; //printf ("src = %d\n", src - (u8*)mrec); //printf ("dst = %d\n", dst - (u8*)mrec); //printf ("end = %d\n", end - (u8*)mrec); //printf ("len = %d\n", len); if (src != dst) memmove (dst, src, len); memcpy ((u8*)arec + arec->value_offset, data, data_len); mrec->bytes_in_use += (attr_new - attr_orig); arec->length += (attr_new - attr_orig); arec->value_length += (attr_new - attr_orig); memset ((u8*)mrec + mrec->bytes_in_use, 0, mft_size - mrec->bytes_in_use); mft_usage += (attr_new - attr_orig); //utils_dump_mem ((u8*) mrec, 0, mft_size, DM_DEFAULTS); res = 0; done: ntfs_attr_put_search_ctx (ctx); return res; } /** * ntfs_mft_free_space */ static int ntfs_mft_free_space (struct ntfs_dir *dir) { int res = 0; MFT_RECORD *mft; if ((!dir) || (!dir->inode)) return -1; mft = (MFT_RECORD*) dir->inode->mrec; res = mft->bytes_allocated - mft->bytes_in_use; return res; } /** * ntfs_dt_add_alloc */ static int ntfs_dt_add_alloc (struct ntfs_dt *parent, int index_num, INDEX_ENTRY *ie) { INDEX_BLOCK *block; int need; int space; u8 *src; u8 *dst; int len; if (!parent || !ie) return 0; block = (INDEX_BLOCK*) parent->data; need = ie->length; space = parent->data_len - block->index.index_length - 24; printf ("need %d, have %d\n", need, space); if (need > space) { printf ("no room"); return 0; } //utils_dump_mem (parent->data, 0, parent->data_len, DM_DEFAULTS); //printf ("\n"); src = (u8*) parent->children[index_num]; dst = src + need; len = parent->data + parent->data_len - src - space; //printf ("src = %d\n", src - parent->data); //printf ("dst = %d\n", dst - parent->data); //printf ("len = %d\n", len); memmove (dst, src, len); dst = src; src = (u8*) ie; len = need; memcpy (dst, src, len); block->index.index_length += len; dst = parent->data + block->index.index_length + 24; len = parent->data_len - block->index.index_length - 24; memset (dst, 0, len); //utils_dump_mem (parent->data, 0, parent->data_len, DM_DEFAULTS); //printf ("\n"); return 0; } /** * ntfs_dt_add_root */ static int ntfs_dt_add_root (struct ntfs_dt *parent, int index_num, INDEX_ENTRY *ie) { INDEX_ROOT *root; int need; int space; u8 *attr; u8 *src; u8 *dst; int len; if (!parent || !ie) return 0; root = (INDEX_ROOT*) parent->data; utils_dump_mem (parent->data, 0, parent->data_len, DM_DEFAULTS); printf ("\n"); need = ie->length; space = ntfs_mft_free_space (parent->dir); printf ("need %d, have %d\n", need, space); if (need > space) { printf ("no room"); return 0; } attr = malloc (parent->data_len + need); src = parent->data; dst = attr; len = root->index.entries_offset + 16; memcpy (dst, src, len); dst += len; src = (u8*) ie; len = ie->length; memcpy (dst, src, len); dst += len; src = (u8*) parent->children[index_num]; len = parent->data + parent->data_len - src; memcpy (dst, src, len); free (parent->data); parent->data = attr; parent->data_len += need; root = (INDEX_ROOT*) parent->data; root->index.index_length = parent->data_len - 16; root->index.allocated_size = parent->data_len - 16; utils_dump_mem (parent->data, 0, parent->data_len, DM_DEFAULTS); printf ("\n"); ntfs_mft_resize_resident (parent->dir->inode, AT_INDEX_ROOT, I30, 4, parent->data, parent->data_len); return 0; } /** * ntfs_dt_add */ static int ntfs_dt_add (struct ntfs_dt *parent, INDEX_ENTRY *ie) { FILE_NAME_ATTR *file; struct ntfs_dt *dt; int index_num = -1; if (!ie) return 0; file = &ie->key.file_name; dt = ntfs_dt_find3 (parent, file->file_name, file->file_name_length, &index_num); if (!dt) return 0; //printf ("dt = %p, index = %d\n", dt, index_num); //ntfs_ie_dump (dt->children[index_num]); //utils_dump_mem ((u8*)dt->children[index_num], 0, dt->children[index_num]->length, DM_DEFAULTS); //printf ("\n"); if (0) ntfs_dt_add_alloc (dt, index_num, ie); if (0) ntfs_dt_add_root (dt->dir->index, 0, ie); return 0; } /** * ntfs_dt_add2 */ static int ntfs_dt_add2 (INDEX_ENTRY *ie, struct ntfs_dt *suc, int suc_num, struct ntfs_dt *ded) { if (!ie || !suc) return -1; if (ntfs_dt_root (suc)) ntfs_dt_add_root (suc, suc_num, ie); else ntfs_dt_add_alloc (suc, suc_num, ie); return 0; } /** * ntfs_dt_remove_alloc */ static int ntfs_dt_remove_alloc (struct ntfs_dt *dt, int index_num) { INDEX_ENTRY *ie = NULL; int i; u8 *dst; u8 *src; u8 *end; int off; int len; s64 res; //printf ("removing entry %d of %d\n", index_num+1, dt->child_count); //printf ("index size = %d\n", dt->data_len); //printf ("index use = %d\n", dt->header->index_length); //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); off = (u8*)dt->children[0] - dt->data; for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; //printf ("%2d %4d ", i, off); off += ie->length; if (ie->flags & INDEX_ENTRY_END) { //printf ("END (%d)\n", ie->length); break; } //ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); //printf (" (%d)\n", ie->length); } //printf ("total = %d\n", off); ie = dt->children[index_num]; dst = (u8*)ie; src = dst + ie->length; ie = dt->children[dt->child_count-1]; end = (u8*)ie + ie->length; len = end - src; //printf ("move %d bytes\n", len); //printf ("%d, %d, %d\n", dst - dt->data, src - dt->data, len); memmove (dst, src, len); //printf ("clear %d bytes\n", dt->data_len - (dst - dt->data) - len); //printf ("%d, %d, %d\n", dst - dt->data + len, 0, dt->data_len - (dst - dt->data) - len); //ntfs_dt_print (dt->dir->index, 0); memset (dst + len, 0, dt->data_len - (dst - dt->data) - len); for (i = 0; i < dt->child_count; i++) { if (dt->sub_nodes[i]) { printf ("this shouldn't happen %p\n", dt->sub_nodes[i]); ntfs_dt_free (dt->sub_nodes[i]); // shouldn't be any, yet } } free (dt->sub_nodes); dt->sub_nodes = NULL; free (dt->children); dt->children = NULL; dt->child_count = 0; //printf ("before = %d\n", dt->header->index_length + 24); dt->header->index_length -= src - dst; //printf ("after = %d\n", dt->header->index_length + 24); ntfs_dt_count_alloc (dt); //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); #if 0 //printf ("\n"); //printf ("index size = %d\n", dt->data_len); //printf ("index use = %d\n", dt->header.index_length); off = (u8*)dt->children[0] - dt->data; for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; printf ("%2d %4d ", i, off); off += ie->length; if (ie->flags & INDEX_ENTRY_END) { printf ("END (%d)\n", ie->length); break; } ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); printf (" (%d)\n", ie->length); } #endif //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); res = ntfs_attr_mst_pwrite (dt->dir->ialloc, dt->vcn*512, 1, dt->data_len, dt->data); printf ("res = %lld\n", res); return 0; } /** * ntfs_dt_remove_root */ static int ntfs_dt_remove_root (struct ntfs_dt *dt, int index_num) { INDEX_ENTRY *ie = NULL; INDEX_ROOT *ir = NULL; int i; u8 *dst; u8 *src; u8 *end; int off; int len; s64 res; //printf ("removing entry %d of %d\n", index_num+1, dt->child_count); //printf ("index size = %d\n", dt->data_len); //printf ("index use = %d\n", dt->header->index_length); //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); off = (u8*)dt->children[0] - dt->data; for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; //printf ("%2d %4d ", i+1, off); off += ie->length; if (ie->flags & INDEX_ENTRY_END) { //printf ("END (%d)\n", ie->length); break; } //ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); //printf (" (%d)\n", ie->length); } //printf ("total = %d\n", off); ie = dt->children[index_num]; dst = (u8*)ie; src = dst + ie->length; ie = dt->children[dt->child_count-1]; end = (u8*)ie + ie->length; len = end - src; //printf ("move %d bytes\n", len); //printf ("%d, %d, %d\n", dst - dt->data, src - dt->data, len); memmove (dst, src, len); dt->data_len -= (src - dt->data - sizeof (INDEX_ROOT)); dt->child_count--; ir = (INDEX_ROOT*) dt->data; ir->index.index_length = dt->data_len - 16; ir->index.allocated_size = dt->data_len - 16; ntfs_mft_resize_resident (dt->dir->inode, AT_INDEX_ROOT, I30, 4, dt->data, dt->data_len); dt->data = realloc (dt->data, dt->data_len); //printf ("ih->index_length = %d\n", ir->index.index_length); //printf ("ih->allocated_size = %d\n", ir->index.allocated_size); //printf ("dt->data_len = %d\n", dt->data_len); //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); //ntfs_dt_print (dt->dir->index, 0); #if 1 for (i = 0; i < dt->child_count; i++) { if (dt->sub_nodes[i]) { printf ("this shouldn't happen %p\n", dt->sub_nodes[i]); ntfs_dt_free (dt->sub_nodes[i]); // shouldn't be any, yet } } free (dt->sub_nodes); dt->sub_nodes = NULL; free (dt->children); dt->children = NULL; dt->child_count = 0; //printf ("before = %d\n", dt->header->index_length + 24); dt->header->index_length -= src - dst; //printf ("after = %d\n", dt->header->index_length + 24); ntfs_dt_count_root (dt); #endif //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); #if 0 //printf ("\n"); //printf ("index size = %d\n", dt->data_len); //printf ("index use = %d\n", dt->header.index_length); off = (u8*)dt->children[0] - dt->data; for (i = 0; i < dt->child_count; i++) { ie = dt->children[i]; printf ("%2d %4d ", i, off); off += ie->length; if (ie->flags & INDEX_ENTRY_END) { printf ("END (%d)\n", ie->length); break; } ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); printf (" (%d)\n", ie->length); } #endif //utils_dump_mem (dt->data, 0, dt->data_len, DM_DEFAULTS); res = ntfs_mft_record_write (dt->dir->inode->vol, dt->dir->inode->mft_no, dt->dir->inode->mrec); printf ("res = %lld\n", res); return 0; } /** * ntfs_dt_remove */ static int ntfs_dt_remove (struct ntfs_dt *dt, int index_num) { if (!dt) return 1; if ((index_num < 0) || (index_num >= dt->child_count)) return 1; if (ntfs_dt_root (dt)) return ntfs_dt_remove_root (dt, index_num); else return ntfs_dt_remove_alloc (dt, index_num); } /** * ntfs_dt_del_child */ static int ntfs_dt_del_child (struct ntfs_dt *dt, ntfschar *uname, int len) { struct ntfs_dt *del; INDEX_ENTRY *ie; ntfs_inode *ichild = NULL; ntfs_inode *iparent = NULL; ntfs_attr *attr = NULL; ntfs_attr_search_ctx *ctx = NULL; int index_num = 0; int res = 1; ATTR_RECORD *arec = NULL; MFT_REF mft_num = -1; FILE_NAME_ATTR *file; int filenames = 0; // compressed & encrypted files? del = ntfs_dt_find2 (dt, uname, len, &index_num); if (!del) { printf ("can't find item to delete\n"); goto close; } if ((index_num < 0) || (index_num >= del->child_count)) { printf ("error in dt_find\n"); goto close; } if (del->header->flags & INDEX_NODE) { printf ("can only delete leaf nodes\n"); goto close; } /* if (!del->parent) { printf ("has 0xA0, but isn't in use\n"); goto close; } */ ie = del->children[index_num]; if (ie->key.file_name.file_attributes & FILE_ATTR_DIRECTORY) { printf ("can't delete directories\n"); goto close; } if (ie->key.file_name.file_attributes & FILE_ATTR_SYSTEM) { printf ("can't delete system files\n"); goto close; } ichild = ntfs_inode_open (dt->dir->vol, MREF (ie->indexed_file)); if (!ichild) { printf ("can't open inode\n"); goto close; } ctx = ntfs_attr_get_search_ctx (NULL, ichild->mrec); if (!ctx) { printf ("can't create a search context\n"); goto close; } while (ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx) == 0) { arec = ctx->attr; if (arec->type == AT_ATTRIBUTE_LIST) { printf ("can't delete files with an attribute list\n"); goto close; } if (arec->type == AT_INDEX_ROOT) { printf ("can't delete directories\n"); goto close; } if (arec->type == AT_FILE_NAME) { filenames++; file = (FILE_NAME_ATTR*) ((u8*) arec + arec->value_offset); mft_num = MREF (file->parent_directory); } } if (filenames != 1) { printf ("file has more than one name\n"); goto close; } iparent = ntfs_inode_open (dt->dir->vol, mft_num); if (!iparent) { printf ("can't open parent directory\n"); goto close; } /* attr = ntfs_attr_open (iparent, AT_INDEX_ALLOCATION, I30, 4); if (!attr) { printf ("parent doesn't have 0xA0\n"); goto close; } */ //printf ("deleting file\n"); //ntfs_dt_print (del->dir->index, 0); if (1) res = utils_free_non_residents (ichild); if (1) res = utils_mftrec_mark_free (dt->dir->vol, del->children[index_num]->indexed_file); if (1) res = utils_mftrec_mark_free2 (dt->dir->vol, del->children[index_num]->indexed_file); if (1) res = ntfs_dt_remove (del, index_num); close: ntfs_attr_put_search_ctx (ctx); ntfs_attr_close (attr); ntfs_inode_close (iparent); ntfs_inode_close (ichild); return res; } /** * ntfsrm */ static int ntfsrm (ntfs_volume *vol, char *name) { struct ntfs_dir *dir = NULL; struct ntfs_dir *finddir = NULL; MFT_REF mft_num; ntfschar *uname = NULL; int len; dir = ntfs_dir_alloc (vol, FILE_root); if (!dir) return 1; //mft_num = ntfs_dir_find (dir, name); //printf ("%s = %lld\n", name, mft_num); mft_num = utils_pathname_to_mftref (vol, dir, name, &finddir); //printf ("mft_num = %lld\n", mft_num); //ntfs_dir_print (finddir, 0); if (!finddir) { printf ("Couldn't find the index entry for %s\n", name); return 1; } if (rindex (name, PATH_SEP)) name = rindex (name, PATH_SEP) + 1; len = ntfs_mbstoucs (name, &uname, 0); if (len < 0) return 1; ntfs_dt_del_child (finddir->index, uname, len); ntfs_dir_free (dir); free (uname); return 0; } /** * ntfsinfo_time_to_str() - * @sle_ntfs_clock: on disk time format in 100ns units since 1st jan 1601 * in little-endian format * * Return char* in a format 'Thu Jan 1 00:00:00 1970'. * No need to free the returned memory. * * Example of usage: * char *time_str = ntfsinfo_time_to_str( * sle64_to_cpu(standard_attr->creation_time)); * printf("\tFile Creation Time:\t %s", time_str); */ static const char *ntfsinfo_time_to_str(const s64 sle_ntfs_clock) { time_t unix_clock = ntfs2utc(sle64_to_cpu(sle_ntfs_clock)); if (sle_ntfs_clock == 0) return "none\n"; else return ctime(&unix_clock); } /** * ntfs_ie_dump */ static void ntfs_ie_dump (INDEX_ENTRY *ie) { if (!ie) return; printf ("________________________________________________"); printf ("\n"); utils_dump_mem ((u8*)ie, 0, ie->length, DM_DEFAULTS); printf ("MFT Ref: 0x%llx\n", ie->indexed_file); printf ("length: %d\n", ie->length); printf ("keylen: %d\n", ie->key_length); printf ("flags: "); if (ie->flags & INDEX_ENTRY_NODE) printf ("NODE "); if (ie->flags & INDEX_ENTRY_END) printf ("END"); if (!(ie->flags & (INDEX_ENTRY_NODE | INDEX_ENTRY_END))) printf ("none"); printf ("\n"); printf ("reserved 0x%04x\n", ie->reserved); if (ie->key_length > 0) { printf ("mft parent: 0x%llx\n", ie->key.file_name.parent_directory); printf ("ctime: %s", ntfsinfo_time_to_str(ie->key.file_name.creation_time)); printf ("dtime: %s", ntfsinfo_time_to_str(ie->key.file_name.last_data_change_time)); printf ("mtime: %s", ntfsinfo_time_to_str(ie->key.file_name.last_mft_change_time)); printf ("atime: %s", ntfsinfo_time_to_str(ie->key.file_name.last_access_time)); printf ("alloc size: %lld\n", ie->key.file_name.allocated_size); printf ("data size: %lld\n", ie->key.file_name.data_size); printf ("file flags: 0x%04x\n", ie->key.file_name.file_attributes); printf ("reserved: 0x%04x\n", ie->key.file_name.reserved); printf ("name len: %d\n", ie->key.file_name.file_name_length); if (ie->key.file_name.file_name_length > 0) { int i, r; printf ("name type: %d\n", ie->key.file_name.file_name_type); printf ("name: "); ntfs_name_print (ie->key.file_name.file_name, ie->key.file_name.file_name_length); printf ("\n"); r = ATTR_SIZE (2 * (ie->key.file_name.file_name_length+1)) - (2 * (ie->key.file_name.file_name_length+1)); if (r > 0) { u8 *ptr; printf ("padding: "); ptr = (u8*) (ie->key.file_name.file_name + ie->key.file_name.file_name_length); for (i = 0; i < r; i++, ptr++) printf ("0x%02x ", *ptr); printf ("\n"); } } } if (ie->flags == INDEX_ENTRY_NODE) { printf ("child vcn = %lld\n", ntfs_ie_get_vcn (ie)); } } /** * ntfs_ie_create */ static INDEX_ENTRY * ntfs_ie_create (void) { int length; INDEX_ENTRY *ie; length = 16; ie = malloc (length); if (!ie) return NULL; ie->indexed_file = 0; ie->length = length; ie->key_length = 0; ie->flags = INDEX_ENTRY_END; ie->reserved = 0; return ie; } /** * ntfs_ie_copy */ static INDEX_ENTRY * ntfs_ie_copy (INDEX_ENTRY *ie) { INDEX_ENTRY *copy = NULL; if (!ie) return NULL; copy = malloc (ie->length); if (!copy) return NULL; memcpy (copy, ie, ie->length); return copy; } /** * ntfs_ie_set_vcn */ static INDEX_ENTRY * ntfs_ie_set_vcn (INDEX_ENTRY *ie, VCN vcn) { if (!ie) return 0; if (!(ie->flags & INDEX_ENTRY_NODE)) { ie->length += 8; ie = realloc (ie, ie->length); if (!ie) return NULL; ie->flags |= INDEX_ENTRY_NODE; } *((VCN*) ((u8*) ie + ie->length - 8)) = vcn; return ie; } /** * ntfs_ie_remove_vcn */ static INDEX_ENTRY * ntfs_ie_remove_vcn (INDEX_ENTRY *ie) { if (!ie) return NULL; if (!(ie->flags & INDEX_ENTRY_NODE)) return ie; ie->length -= 8; ie->flags &= ~INDEX_ENTRY_NODE; ie = realloc (ie, ie->length); return ie; } /** * ntfs_ie_set_name */ static INDEX_ENTRY * ntfs_ie_set_name (INDEX_ENTRY *ie, ntfschar *name, int namelen, FILE_NAME_TYPE_FLAGS nametype) { FILE_NAME_ATTR *file; int klen; int need; VCN vcn = 0; if (!ie || !name) return NULL; /* * INDEX_ENTRY * MFT_REF indexed_file; * u16 length; * u16 key_length; * INDEX_ENTRY_FLAGS flags; * u16 reserved; * * FILENAME * MFT_REF parent_directory; * s64 creation_time; * s64 last_data_change_time; * s64 last_mft_change_time; * s64 last_access_time; * s64 allocated_size; * s64 data_size; * FILE_ATTR_FLAGS file_attributes; * u32 reserved; * u8 file_name_length; * FILE_NAME_TYPE_FLAGS file_name_type; * ntfschar file_name[l]; * u8 reserved[n] * * VCN vcn; */ file = &ie->key.file_name; klen = ATTR_SIZE (ie->key_length); need = ATTR_SIZE (sizeof (FILE_NAME_ATTR) + (namelen * sizeof (ntfschar))); //printf ("ilen = %d\n", ie->length); //printf ("klen = %d\n", klen); //printf ("need = %d\n", need); if (ie->flags & INDEX_ENTRY_NODE) vcn = ntfs_ie_get_vcn (ie); ie->length = 16 + need; ie->key_length = sizeof (FILE_NAME_ATTR) + (namelen * sizeof (ntfschar)); ie = realloc (ie, ie->length + ie->key_length); if (!ie) return NULL; memcpy (ie->key.file_name.file_name, name, namelen * 2); if (ie->flags & INDEX_ENTRY_NODE) ie = ntfs_ie_set_vcn (ie, vcn); ie->key.file_name.file_name_length = namelen; ie->key.file_name.file_name_type = nametype; ie->flags &= ~INDEX_ENTRY_END; return ie; } /** * ntfs_ie_remove_name */ static INDEX_ENTRY * ntfs_ie_remove_name (INDEX_ENTRY *ie) { VCN vcn; if (!ie) return NULL; if (ie->key_length == 0) return ie; if (ie->flags & INDEX_ENTRY_NODE) vcn = ntfs_ie_get_vcn (ie); ie->length -= ATTR_SIZE (ie->key_length); ie->key_length = 0; ie->flags |= INDEX_ENTRY_END; ie = realloc (ie, ie->length); if (!ie) return NULL; if (ie->flags & INDEX_ENTRY_NODE) ie = ntfs_ie_set_vcn (ie, vcn); return ie; } /** * ntfs_ie_test */ static int ntfs_ie_test (void) { INDEX_ENTRY *ie1 = NULL; INDEX_ENTRY *ie2 = NULL; int namelen = 0; ntfschar *name = NULL; if (1) { ie1 = ntfs_ie_create(); //ntfs_ie_dump (ie1); } if (0) { ie2 = ntfs_ie_copy (ie1); ntfs_ie_dump (ie2); } if (1) { namelen = ntfs_mbstoucs("richard", &name, 0); ie1 = ntfs_ie_set_name (ie1, name, namelen, FILE_NAME_WIN32); free (name); name = NULL; ntfs_ie_dump (ie1); } if (1) { namelen = ntfs_mbstoucs("richard2", &name, 0); ie1 = ntfs_ie_set_name (ie1, name, namelen, FILE_NAME_WIN32); free (name); name = NULL; ntfs_ie_dump (ie1); } if (1) { ie1 = ntfs_ie_remove_name (ie1); ntfs_ie_dump (ie1); } if (1) { ie1 = ntfs_ie_set_vcn (ie1, 1234); ntfs_ie_dump (ie1); } if (1) { ie1 = ntfs_ie_remove_vcn (ie1); ntfs_ie_dump (ie1); } ie1->indexed_file = 1234; ie1->key.file_name.parent_directory = 5; ie1->key.file_name.creation_time = utc2ntfs (time(NULL)); ie1->key.file_name.last_data_change_time = utc2ntfs (time(NULL)); ie1->key.file_name.last_mft_change_time = utc2ntfs (time(NULL)); ie1->key.file_name.last_access_time = utc2ntfs (time(NULL)); ie1->key.file_name.allocated_size = 4096; ie1->key.file_name.data_size = 3973; //ntfs_ie_dump (ie1); free (name); free (ie1); free (ie2); return 0; } /** * ntfs_ie_get_vcn */ static VCN ntfs_ie_get_vcn (INDEX_ENTRY *ie) { if (!ie) return -1; return *((VCN*) ((u8*) ie + ie->length - 8)); } /** * ntfs_index_dump_alloc */ static int ntfs_index_dump_alloc (ntfs_attr *attr, VCN vcn, int indent) { u8 buffer[4096]; INDEX_BLOCK *block; INDEX_ENTRY *entry; u8 *ptr; int size; VCN *newvcn; ntfs_attr_mst_pread (attr, vcn*512, 1, sizeof (buffer), buffer); block = (INDEX_BLOCK*) buffer; size = block->index.allocated_size; for (ptr = buffer + 64; ptr < (buffer + size); ptr += entry->length) { entry = (INDEX_ENTRY*) ptr; if (entry->flags & INDEX_ENTRY_NODE) { newvcn = (VCN*) (ptr + ((entry->key_length + 0x17) & ~7)); ntfs_index_dump_alloc (attr, *newvcn, indent+4); } printf ("%.*s", indent, space); if (entry->flags & INDEX_ENTRY_END) { printf ("[END]"); } else { ntfs_name_print (entry->key.file_name.file_name, entry->key.file_name.file_name_length); } if (entry->flags & INDEX_ENTRY_NODE) { printf (" (%lld)\n", *newvcn); } else { printf ("\n"); } if (entry->flags & INDEX_ENTRY_END) break; } printf ("%.*s", indent, space); printf ("fill = %d/%d\n", block->index.index_length, block->index.allocated_size); return 0; } /** * ntfs_index_dump */ static int ntfs_index_dump (ntfs_inode *inode) { u8 buffer[1024]; ntfs_attr *iroot; ntfs_attr *ialloc; INDEX_ROOT *root; INDEX_ENTRY *entry; u8 *ptr; int size; VCN *vcn; if (!inode) return 0; iroot = ntfs_attr_open (inode, AT_INDEX_ROOT, I30, 4); ialloc = ntfs_attr_open (inode, AT_INDEX_ALLOCATION, I30, 4); size = (int) ntfs_attr_pread (iroot, 0, sizeof (buffer), buffer); root = (INDEX_ROOT*) buffer; ptr = buffer + root->index.entries_offset + 0x10; while (ptr < (buffer + size)) { entry = (INDEX_ENTRY*) ptr; if (entry->flags & INDEX_ENTRY_NODE) { vcn = (VCN*) (ptr + ((entry->key_length + 0x17) & ~7)); ntfs_index_dump_alloc (ialloc, *vcn, 4); } if (entry->flags & INDEX_ENTRY_END) { printf ("[END]"); } else { ntfs_name_print (entry->key.file_name.file_name, entry->key.file_name.file_name_length); } if (entry->flags & INDEX_ENTRY_NODE) { printf (" (%lld)", *vcn); } printf ("\n"); ptr += entry->length; } printf ("fill = %d\n", ptr - buffer); return 0; } /** * ntfs_dt_root_replace */ static int ntfs_dt_root_replace (struct ntfs_dt *del, int del_num, INDEX_ENTRY *del_ie, INDEX_ENTRY *suc_ie) { u8 *src; u8 *dst; u8 *attr; int len; int i; if (!del || !del_ie || !suc_ie) return FALSE; //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); //printf ("\n"); attr = malloc (del->data_len + suc_ie->length - del_ie->length); dst = attr; src = del->data; len = (u8*) del_ie - del->data; memcpy (dst, src, len); dst += len; src = (u8*) suc_ie; len = suc_ie->length; memcpy (dst, src, len); dst += len; src = (u8*) del_ie + del_ie->length; len = del->data_len + (del->data - (u8*) del_ie) - del_ie->length; memcpy (dst, src, len); src = (u8*) del->data; dst = attr; len = del->data_len + suc_ie->length - del_ie->length; free (del->data); del->data = attr; del->data_len = len; ntfs_mft_resize_resident (del->dir->inode, AT_INDEX_ROOT, I30, 4, del->data, del->data_len); //utils_dump_mem (attr, 0, del->data_len, DM_DEFAULTS); //printf ("\n"); //printf (BOLD YELLOW "Adjust children\n" END); //for (i = 0; i < del->child_count; i++) // printf ("\tChild %d %p %d\n", i, del->children[i], del->children[i]->flags); //printf ("\n"); len = suc_ie->length - del_ie->length; //printf ("src = %p, dst = %p, len = %d\n", src, dst, len); fflush (stdout); for (i = 0; i < del->child_count; i++) del->children[i] = (INDEX_ENTRY*) (dst + ((u8*) del->children[i] - src)); for (i = del_num+1; i < del->child_count; i++) del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] + len); //for (i = 0; i < del->child_count; i++) // printf ("\tChild %d %p %d\n", i, del->children[i], del->children[i]->flags); //printf ("\n"); //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); //printf ("\n"); printf (GREEN "Modified: inode %lld, $INDEX_ROOT\n" END, del->dir->inode->mft_no); return TRUE; } /** * ntfs_dt_alloc_replace */ static BOOL ntfs_dt_alloc_replace (struct ntfs_dt *del, int del_num, INDEX_ENTRY *del_ie, INDEX_ENTRY *suc_ie) { u8 *src; u8 *dst; int len; int i; if (!del || !del_ie || !suc_ie) return FALSE; //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); src = (u8*) del_ie + del_ie->length; dst = (u8*) del_ie + suc_ie->length; len = del->header->index_length + 24 + (del->data - src); //printf ("src = %d\n", src - del->data); //printf ("dst = %d\n", dst - del->data); //printf ("len = %d\n", len); if (src != dst) memmove (dst, src, len); src = (u8*) suc_ie; dst = (u8*) del_ie; len = suc_ie->length; memcpy (dst, src, len); //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); del->header->index_length += suc_ie->length - del_ie->length; dst = del->data + del->header->index_length + 24; len = del->data_len - del->header->index_length - 24; memset (dst, 0, len); //for (i = 0; i < del->child_count; i++) // printf ("Child %d %p\n", i, del->children[i]); //printf ("\n"); len = suc_ie->length - del_ie->length; //printf ("len = %d\n", len); for (i = del_num+1; i < del->child_count; i++) del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] + len); //for (i = 0; i < del->child_count; i++) // printf ("Child %d %p\n", i, del->children[i]); //printf ("\n"); //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); printf (GREEN "Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n" END, del->dir->inode->mft_no, del->vcn, del->vcn + 4); return TRUE; } /** * ntfs_dt_root_remove */ static BOOL ntfs_dt_root_remove (struct ntfs_dt *del, int del_num) { INDEX_ENTRY *del_ie = NULL; u8 *src; u8 *dst; int len; int i; //int off; if (!del) return FALSE; //utils_dump_mem (del->data, 0, del->data_len, DM_RED); //printf ("\n"); #if 0 off = (u8*) del->children[0] - del->data; for (i = 0; i < del->child_count; i++) { del_ie = del->children[i]; printf ("%2d %4d ", i+1, off); off += del_ie->length; if (del_ie->flags & INDEX_ENTRY_END) { printf ("END (%d)\n", del_ie->length); break; } ntfs_name_print (del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); printf (" (%d)\n", del_ie->length); } printf ("total = %d\n", off); #endif del_ie = del->children[del_num]; src = (u8*) del_ie + del_ie->length; dst = (u8*) del_ie; len = del->header->index_length + 16 - (src - del->data); //printf ("src = %d\n", src - del->data); //printf ("dst = %d\n", dst - del->data); //printf ("len = %d\n", len); memmove (dst, src, len); del->data_len -= del_ie->length; del->child_count--; del->header->index_length = del->data_len - 16; del->header->allocated_size = del->data_len - 16; ntfs_mft_resize_resident (del->dir->inode, AT_INDEX_ROOT, I30, 4, del->data, del->data_len); del->data = realloc (del->data, del->data_len); //utils_dump_mem (del->data, 0, del->data_len, DM_GREEN | DM_RED); src = (u8*) (&del->children[del_num+1]); dst = (u8*) (&del->children[del_num]); len = (del->child_count - del_num) * sizeof (INDEX_ENTRY*); //printf ("src = %d\n", src - (u8*) del->children); //printf ("dst = %d\n", dst - (u8*) del->children); //printf ("len = %d\n", len); memmove (dst, src, len); src = (u8*) (&del->sub_nodes[del_num+1]); dst = (u8*) (&del->sub_nodes[del_num]); len = (del->child_count - del_num) * sizeof (struct ntfs_dt*); //printf ("src = %d\n", src - (u8*) del->children); //printf ("dst = %d\n", dst - (u8*) del->children); //printf ("len = %d\n", len); memmove (dst, src, len); //printf ("del_num = %d\n", del_num); for (i = del_num; i < del->child_count; i++) del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] - del_ie->length); if (!ntfs_dt_alloc_children2 (del, del->child_count)) return FALSE; #if 0 off = (u8*) del->children[0] - del->data; for (i = 0; i < del->child_count; i++) { del_ie = del->children[i]; printf ("%2d %4d ", i+1, off); off += del_ie->length; if (del_ie->flags & INDEX_ENTRY_END) { printf ("END (%d)\n", del_ie->length); break; } ntfs_name_print (del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); printf (" (%d)\n", del_ie->length); } printf ("total = %d\n", off); #endif //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); printf (GREEN "Modified: inode %lld, $INDEX_ROOT\n" END, del->dir->inode->mft_no); return TRUE; } /** * ntfs_dt_alloc_remove */ static BOOL ntfs_dt_alloc_remove (struct ntfs_dt *del, int del_num) { INDEX_ENTRY *del_ie = NULL; u8 *dst; u8 *src; int len; int i; //int off; if (!del) return FALSE; #if 0 off = (u8*)del->children[0] - del->data; for (i = 0; i < del->child_count; i++) { del_ie = del->children[i]; printf ("%2d %4d ", i, off); off += del_ie->length; if (del_ie->flags & INDEX_ENTRY_END) { printf ("END (%d)\n", del_ie->length); break; } ntfs_name_print (del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); printf (" (%d)\n", del_ie->length); } printf ("total = %d\n", off); printf ("\n"); #endif //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); //printf ("\n"); del_ie = del->children[del_num]; src = (u8*) del_ie + del_ie->length; dst = (u8*) del_ie; len = del->header->index_length + 24 - (src - del->data); //printf ("src = %d\n", src - del->data); //printf ("dst = %d\n", dst - del->data); //printf ("len = %d\n", len); memmove (dst, src, len); del->header->index_length -= src - dst; del->child_count--; dst += len; len = del->data_len - del->header->index_length - 24; //printf ("dst = %d\n", dst - del->data); //printf ("len = %d\n", len); memset (dst, 0, len); src = (u8*) (&del->children[del_num+1]); dst = (u8*) (&del->children[del_num]); len = (del->child_count - del_num) * sizeof (INDEX_ENTRY*); //printf ("src = %d\n", src - (u8*) del->children); //printf ("dst = %d\n", dst - (u8*) del->children); //printf ("len = %d\n", len); memmove (dst, src, len); src = (u8*) (&del->sub_nodes[del_num+1]); dst = (u8*) (&del->sub_nodes[del_num]); len = (del->child_count - del_num) * sizeof (struct ntfs_dt*); //printf ("src = %d\n", src - (u8*) del->children); //printf ("dst = %d\n", dst - (u8*) del->children); //printf ("len = %d\n", len); memmove (dst, src, len); //printf ("del_num = %d\n", del_num); for (i = del_num; i < del->child_count; i++) del->children[i] = (INDEX_ENTRY*) ((u8*) del->children[i] - del_ie->length); if (!ntfs_dt_alloc_children2 (del, del->child_count)) return FALSE; //utils_dump_mem (del->data, 0, del->data_len, DM_DEFAULTS); #if 0 off = (u8*)del->children[0] - del->data; for (i = 0; i < del->child_count; i++) { del_ie = del->children[i]; printf ("%2d %4d ", i, off); off += del_ie->length; if (del_ie->flags & INDEX_ENTRY_END) { printf ("END (%d)\n", del_ie->length); break; } ntfs_name_print (del_ie->key.file_name.file_name, del_ie->key.file_name.file_name_length); printf (" (%d)\n", del_ie->length); } printf ("total = %d\n", off); printf ("\n"); #endif printf (GREEN "Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n" END, del->dir->inode->mft_no, del->vcn, del->vcn + 4); return TRUE; } /** * ntfs_dt_root_add */ static int ntfs_dt_root_add (struct ntfs_dt *add, INDEX_ENTRY *add_ie) { FILE_NAME_ATTR *file; struct ntfs_dt *suc; int suc_num; int need; int space; u8 *attr; u8 *src; u8 *dst; int len; if (!add || !add_ie) return 0; //utils_dump_mem (add->data, 0, add->data_len, DM_DEFAULTS); //printf ("\n"); need = add_ie->length; space = ntfs_mft_free_space (add->dir); file = &add_ie->key.file_name; suc = ntfs_dt_find3 (add, file->file_name, file->file_name_length, &suc_num); if (!suc) return 0; // hmm, suc == add printf ("need %d, have %d\n", need, space); if (need > space) { printf ("no room"); return 0; } attr = malloc (add->data_len + need); src = add->data; dst = attr; len = add->header->entries_offset + 16; memcpy (dst, src, len); dst += len; src = (u8*) add_ie; len = add_ie->length; memcpy (dst, src, len); dst += len; src = (u8*) suc->children[suc_num]; len = add->data + add->data_len - src; memcpy (dst, src, len); free (add->data); add->data = attr; add->data_len += need; add->header->index_length = add->data_len - 16; add->header->allocated_size = add->data_len - 16; ntfs_mft_resize_resident (add->dir->inode, AT_INDEX_ROOT, I30, 4, add->data, add->data_len); //utils_dump_mem (add->data, 0, add->data_len, DM_DEFAULTS); //printf ("\n"); printf (GREEN "Modified: inode %lld, $INDEX_ROOT\n" END, add->dir->inode->mft_no); return 0; } /** * ntfs_dt_alloc_add */ static int ntfs_dt_alloc_add (struct ntfs_dt *add, INDEX_ENTRY *add_ie) { FILE_NAME_ATTR *file; struct ntfs_dt *suc_dt; int suc_num; int need; int space; u8 *src; u8 *dst; int len; if (!add || !add_ie) return 0; need = add_ie->length; space = add->data_len - add->header->index_length - 24; file = &add_ie->key.file_name; suc_dt = ntfs_dt_find3 (add, file->file_name, file->file_name_length, &suc_num); if (!suc_dt) return 0; // hmm, suc_dt == add printf ("need %d, have %d\n", need, space); if (need > space) { printf ("no room"); return 0; } //utils_dump_mem (add->data, 0, add->data_len, DM_DEFAULTS); //printf ("\n"); src = (u8*) suc_dt->children[suc_num]; dst = src + need; len = add->data + add->data_len - src - space; //printf ("src = %d\n", src - add->data); //printf ("dst = %d\n", dst - add->data); //printf ("len = %d\n", len); memmove (dst, src, len); dst = src; src = (u8*) add_ie; len = need; memcpy (dst, src, len); add->header->index_length += len; dst = add->data + add->header->index_length + 24; len = add->data_len - add->header->index_length - 24; memset (dst, 0, len); //utils_dump_mem (add->data, 0, add->data_len, DM_DEFAULTS); //printf ("\n"); printf (GREEN "Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n" END, add->dir->inode->mft_no, add->vcn, add->vcn + 4); return 0; } /** * ntfs_file_add */ static int ntfs_file_add (ntfs_volume *vol, char *name) { struct ntfs_dir *dir = NULL; struct ntfs_dir *finddir = NULL; struct ntfs_dt *del = NULL; INDEX_ENTRY *ie = NULL; MFT_REF mft_num; ntfschar *uname = NULL; int len; int index_num = 0; dir = ntfs_dir_alloc (vol, FILE_root); if (!dir) return 1; mft_num = utils_pathname_to_mftref (vol, dir, name, &finddir); //printf ("mft_num = %lld\n", mft_num); //ntfs_dir_print (finddir, 0); if (!finddir) { printf ("Couldn't find the index entry for %s\n", name); return 1; } if (rindex (name, PATH_SEP)) name = rindex (name, PATH_SEP) + 1; len = ntfs_mbstoucs (name, &uname, 0); if (len < 0) return 1; del = ntfs_dt_find2 (finddir->index, uname, len, &index_num); if (!del) { printf ("can't find item to delete\n"); goto done; } ie = ntfs_ie_copy (del->children[index_num]); if (!ie) goto done; free (uname); uname = NULL; len = ntfs_mbstoucs ("file26a", &uname, 0); if (len < 0) goto done; ie = ntfs_ie_set_name (ie, uname, len, FILE_NAME_WIN32); if (!ie) goto done; //utils_dump_mem ((u8*)ie, 0, ie->length, DM_DEFAULTS); //printf ("\n"); //printf ("ie = %lld\n", MREF (ie->indexed_file)); //ntfs_dt_del_child (finddir->index, uname, len); //ntfs_dt_print (finddir->index, 0); ntfs_dt_add (finddir->index, ie); // test if (0) ntfs_dt_alloc_add (del, ie); if (0) ntfs_dt_root_add (del, ie); // test done: ntfs_dir_free (dir); free (uname); free (ie); return 0; } /** * ntfs_file_remove */ static int ntfs_file_remove (ntfs_volume *vol, char *name) { // XXX work with inode - lookup name outside? // how do I do the inode -> dt lookup? struct ntfs_dir *root_dir = NULL; struct ntfs_dir *find_dir = NULL; struct ntfs_dt *del = NULL; struct ntfs_dt *suc = NULL; struct ntfs_dt *old = NULL; struct ntfs_dt *par = NULL; struct ntfs_dt *ded = NULL; MFT_REF mft_num; ntfschar *uname = NULL; int name_len; int del_num = 0; int suc_num = 0; int par_num = -1; INDEX_ENTRY *del_ie = NULL; INDEX_ENTRY *suc_ie = NULL; INDEX_ENTRY *par_ie = NULL; INDEX_ENTRY *add_ie = NULL; int res; VCN vcn; FILE_NAME_ATTR *file = NULL; //int i; root_dir = ntfs_dir_alloc (vol, FILE_root); if (!root_dir) return 1; mft_num = utils_pathname_to_mftref (vol, root_dir, name, &find_dir); if (!find_dir) { printf ("Couldn't find the index entry for %s\n", name); goto done; } if (rindex (name, PATH_SEP)) name = rindex (name, PATH_SEP) + 1; name_len = ntfs_mbstoucs (name, &uname, 0); if (name_len < 0) goto done; del = ntfs_dt_find2 (find_dir->index, uname, name_len, &del_num); if (!del) { printf ("can't find item to delete\n"); goto done; } del_ie = del->children[del_num]; //utils_dump_mem ((u8*)del_ie, 0, del_ie->length, DM_DEFAULTS); //printf ("\n"); /* * If the key is not in a leaf node, then replace it with its successor. * Continue the delete as if the successor had been deleted. */ if (del->header->flags & INDEX_NODE) { printf (BOLD YELLOW "Replace key with its successor:\n" END); vcn = ntfs_ie_get_vcn (del_ie); //printf ("vcn = %lld\n", vcn); suc = ntfs_dt_find4 (find_dir->index, uname, name_len, &suc_num); //printf ("succ = %p, index = %d\n", suc, suc_num); //printf ("\n"); suc_ie = ntfs_ie_copy (suc->children[suc_num]); //utils_dump_mem ((u8*)suc_ie, 0, suc_ie->length, DM_BLUE|DM_GREEN|DM_INDENT); //printf ("\n"); suc_ie = ntfs_ie_set_vcn (suc_ie, vcn); //utils_dump_mem ((u8*)suc_ie, 0, suc_ie->length, DM_BLUE|DM_GREEN|DM_INDENT); //printf ("\n"); file = &del_ie->key.file_name; printf ("\trep name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); file = &suc_ie->key.file_name; printf ("\tsuc name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); //utils_dump_mem (del->data, 0, del->data_len, DM_BLUE|DM_GREEN|DM_INDENT); if (ntfs_dt_root (del)) res = ntfs_dt_root_replace (del, del_num, del_ie, suc_ie); else res = ntfs_dt_alloc_replace (del, del_num, del_ie, suc_ie); //printf ("\n"); //utils_dump_mem (del->data, 0, del->data_len, DM_BLUE|DM_GREEN|DM_INDENT); free (suc_ie); if (res == FALSE) goto done; del = suc; // Continue delete with the successor del_num = suc_num; del_ie = suc->children[suc_num]; } /* * Now we have the simpler case of deleting from a leaf node. * If this step creates an empty node, we have more to do. */ printf ("\n"); printf (BOLD YELLOW "Delete key:\n" END); file = &del->children[del_num]->key.file_name; printf ("\tdel name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); //utils_dump_mem (del->data, 0, del->header->index_length+24, DM_BLUE|DM_GREEN|DM_INDENT); // XXX if del->child_count == 2, we could skip this step // no, if we combine with another node, we'll have to remember if (ntfs_dt_root (del)) ntfs_dt_root_remove (del, del_num); else ntfs_dt_alloc_remove (del, del_num); //printf ("\n"); //utils_dump_mem (del->data, 0, del->header->index_length+24, DM_BLUE|DM_GREEN|DM_INDENT); if (del->child_count > 1) // XXX ntfs_dt_empty (dt), ntfs_dt_full (dt, new) goto commit; /* * Ascend the tree until we find a node that is not empty. Take the * ancestor key and unhook it. This will free up some space in the * index allocation. Finally add the ancestor to the node of its * successor. */ // find the key nearest the root which has no descendents printf ("\n"); printf (BOLD YELLOW "Find childless parent:\n" END); for (par = del->parent, old = par; par; old = par, par = par->parent) { if (par->child_count > 1) break; par_num = ntfs_dt_find_parent (par); } //utils_dump_mem (par->data, 0, par->data_len, DM_BLUE|DM_GREEN|DM_INDENT); file = &par->children[par_num]->key.file_name; printf ("\tpar name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); if (par == NULL) { // unhook everything printf ("whole dir is empty\n"); goto freedts; } printf ("\n"); //utils_dump_mem (par->data, 0, par->data_len, DM_BLUE|DM_GREEN|DM_INDENT); //printf ("\n"); // find if parent has left siblings if (par->children[par_num]->flags & INDEX_ENTRY_END) { printf (BOLD YELLOW "Swap the children of the parent and its left sibling\n" END); par_ie = par->children[par_num]; vcn = ntfs_ie_get_vcn (par_ie); //printf ("\toffset = %d\n", (u8*)par_ie - par->data); printf ("\tflags = %d\n", par_ie->flags); printf ("\tvcn = %lld\n", vcn); printf ("\tlength = %d\n", par_ie->length); //utils_dump_mem ((u8*)par_ie, 0, par_ie->length, DM_DEFAULTS); //printf ("\n"); //printf ("\toffset = %d\n", (u8*)par_ie - par->data); printf ("\tflags = %d\n", par_ie->flags); printf ("\tvcn = %lld\n", vcn); printf ("\tlength = %d\n", par_ie->length); //utils_dump_mem ((u8*)par_ie, 0, par_ie->length, DM_DEFAULTS); //printf ("\n"); file = &par->children[par_num] ->key.file_name; printf ("\tpar name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); file = &par->children[par_num-1]->key.file_name; printf ("\tsib name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); old = par->sub_nodes[par_num]; par->sub_nodes[par_num] = par->sub_nodes[par_num-1]; par->sub_nodes[par_num-1] = old; par_ie = par->children[par_num-1]; vcn = ntfs_ie_get_vcn (par_ie); par_ie = par->children[par_num]; ntfs_ie_set_vcn (par_ie, vcn); par_num--; if (ntfs_dt_root (par)) printf (GREEN "Modified: inode %lld, $INDEX_ROOT\n" END, par->dir->inode->mft_no); else printf (GREEN "Modified: inode %lld, $INDEX_ALLOCATION vcn %lld-%lld\n" END, par->dir->inode->mft_no, par->vcn, par->vcn + 4); } //printf ("\n"); //utils_dump_mem (par->data, 0, par->data_len, DM_DEFAULTS); // unhook and hold onto the ded dt's printf ("\n"); printf (BOLD YELLOW "Remove parent\n" END); file = &par->children[par_num]->key.file_name; printf ("\tpar name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); add_ie = ntfs_ie_copy (par->children[par_num]); add_ie = ntfs_ie_remove_vcn (add_ie); if (!add_ie) goto done; //printf ("\n"); //utils_dump_mem ((u8*)add_ie, 0, add_ie->length, DM_BLUE|DM_GREEN|DM_INDENT); ded = par->sub_nodes[par_num]; par->sub_nodes[par_num] = NULL; //ntfs_dt_print (ded, 8); if (ntfs_dt_root (par)) ntfs_dt_root_remove (par, par_num); else ntfs_dt_alloc_remove (par, par_num); printf ("\n"); file = &add_ie->key.file_name; suc = NULL; suc_num = -1; suc = ntfs_dt_find4 (find_dir->index, file->file_name, file->file_name_length, &suc_num); printf ("%p, %d\n", suc, suc_num); if (!suc) goto done; file = &suc->children[suc_num]->key.file_name; printf ("\tsuc name: "); ntfs_name_print (file->file_name, file->file_name_length); printf ("\n"); // insert key into successor // if any new nodes are needed, reuse the preserved nodes if (!ntfs_dt_add2 (del_ie, suc, suc_num, ded)) goto done; // remove any unused nodes // XXX mark dts, dirs and inodes dirty // XXX attach bmp to dir and volume // XXX attach root dir to volume // XXX add freed dts to a list for immediate reuse (attach to dir?) // XXX commit will free list of spare dts // XXX reduce size of alloc if (0) ntfs_dt_add2 (del_ie, suc, suc_num, ded); if (0) ntfs_dir_commit (find_dir); if (0) ntfs_dir_rollback (find_dir); printf ("empty\n"); goto done; freedts: commit: printf ("commit\n"); done: ntfs_dir_free (root_dir); free (uname); return 0; } /** * ntfs_test_bmp */ static int ntfs_test_bmp (ntfs_volume *vol, ntfs_inode *inode) { ntfs_inode *volbmp; struct ntfs_bmp *bmp; struct ntfs_bmp *bmp2; //u8 *buffer; //int i; volbmp = ntfs_inode_open (vol, FILE_Bitmap); if (!volbmp) return 1; bmp = ntfs_bmp_alloc (volbmp, AT_DATA, NULL, 0); if (!bmp) return 1; bmp2 = ntfs_bmp_alloc (vol->mft_ni, AT_BITMAP, NULL, 0); if (!bmp2) return 1; if (0) ntfs_bmp_set_range (bmp, 0, 9, 1); if (0) utils_free_non_residents2 (inode, bmp); if (0) utils_mftrec_mark_free3 (bmp2, inode->mft_no); if (0) utils_mftrec_mark_free4 (inode); ntfs_bmp_free (bmp); return 0; } /** * 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 = NULL; ntfs_inode *inode = NULL; int flags = 0; int result = 1; if (!parse_options (argc, argv)) goto done; utils_set_locale(); #if 0 printf ("sizeof (ntfs_bmp) = %d\n", sizeof (struct ntfs_bmp)); printf ("sizeof (ntfs_dt) = %d\n", sizeof (struct ntfs_dt)); printf ("sizeof (ntfs_dir) = %d\n", sizeof (struct ntfs_dir)); printf ("\n"); #endif if (opts.noaction) flags |= MS_RDONLY; vol = utils_mount_volume (opts.device, flags, opts.force); if (!vol) { printf ("!vol\n"); goto done; } inode = utils_pathname_to_inode (vol, NULL, opts.file); if (!inode) { printf ("!inode\n"); goto done; } if (0) result = ntfs_index_dump (inode); if (0) result = ntfsrm (vol, opts.file); if (0) result = ntfs_ie_test(); if (0) result = ntfs_file_add (vol, opts.file); if (0) result = ntfs_file_remove (vol, opts.file); if (0) result = ntfs_test_bmp (vol, inode); done: ntfs_inode_close (inode); ntfs_umount (vol, FALSE); return result; }