parent
							
								
									60c6241ac8
								
							
						
					
					
						commit
						02745a21f1
					
				|  | @ -55,9 +55,7 @@ | |||
| 
 | ||||
| static const char *EXEC_NAME = "ntfsundelete"; | ||||
| static const char *MFTFILE   = "mft"; | ||||
| #ifdef DEBUG | ||||
| static const char *UNNAMED   = "<unnamed>"; | ||||
| #endif | ||||
| static const char *NONE      = "<none>"; | ||||
| static const char *UNKNOWN   = "unknown"; | ||||
| static struct options opts; | ||||
|  | @ -227,6 +225,7 @@ static void usage (void) | |||
| 		"    -d dir      --destination dir  Destination directory\n" | ||||
| 		"    -b num      --byte num         Fill missing parts with this byte\n" | ||||
| 		"    -T          --truncate         Truncate 100%% recoverable file to exact size.\n" | ||||
| 		"    -P          --parent           Show parent directory\n" | ||||
| 		"\n" | ||||
| 		"    -c range    --copy range       Write a range of MFT records to a file\n" | ||||
| 		"\n" | ||||
|  | @ -394,7 +393,7 @@ static int parse_time (const char *value, time_t *since) | |||
|  */ | ||||
| static int parse_options (int argc, char *argv[]) | ||||
| { | ||||
| 	static const char *sopt = "-b:Cc:d:fh?m:o:Op:sS:t:Tu:qvV"; | ||||
| 	static const char *sopt = "-b:Cc:d:fh?m:o:OPp:sS:t:Tu:qvV"; | ||||
| 	static const struct option lopt[] = { | ||||
| 		{ "byte",	 required_argument,	NULL, 'b' }, | ||||
| 		{ "case",	 no_argument,		NULL, 'C' }, | ||||
|  | @ -408,6 +407,7 @@ static int parse_options (int argc, char *argv[]) | |||
| 		{ "percentage",  required_argument,	NULL, 'p' }, | ||||
| 		{ "scan",	 no_argument,		NULL, 's' }, | ||||
| 		{ "size",	 required_argument,	NULL, 'S' }, | ||||
| 		{ "parent",	 no_argument,		NULL, 'P' }, | ||||
| 		{ "time",	 required_argument,	NULL, 't' }, | ||||
| 		{ "truncate",	 no_argument,		NULL, 'T' }, | ||||
| 		{ "undelete",	 required_argument,	NULL, 'u' }, | ||||
|  | @ -498,6 +498,13 @@ static int parse_options (int argc, char *argv[]) | |||
| 			        err++; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 'P': | ||||
| 			if (!opts.parent) { | ||||
| 				opts.parent++; | ||||
| 			} else { | ||||
| 				err++; | ||||
| 			} | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			if (opts.percent == -1) { | ||||
| 				end = NULL; | ||||
|  | @ -633,6 +640,11 @@ static int parse_options (int argc, char *argv[]) | |||
| 				err++; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (opts.parent && !opts.verbose) { | ||||
| 			Eprintf ("To use --parent, you must also use --verbose.\n"); | ||||
| 			err++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (opts.fillbyte == (char)-1) | ||||
|  | @ -668,6 +680,10 @@ static void free_file (struct ufile *file) | |||
| 		Dprintf ("freeing filename '%s'\n", f->name ? f->name : NONE); | ||||
| 		if (f->name) | ||||
| 			free (f->name); | ||||
| 		if (f->parent_name) { | ||||
| 			Dprintf ("\tand parent filename '%s'\n", f->parent_name ? f->parent_name : NONE); | ||||
| 			free (f->parent_name); | ||||
| 		} | ||||
| 		free (f); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -685,6 +701,122 @@ static void free_file (struct ufile *file) | |||
| 	free (file); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * verify_parent - confirm a record is parent of a file | ||||
|  * @name:	a filename of the file | ||||
|  * @rec:	the mft record of the possible parent | ||||
|  * | ||||
|  * Check that @rec is the parent of the file represented by @name. | ||||
|  * If @rec is a directory, but it is created after @name, then we | ||||
|  * can't determine wheter @rec is really @name's parent. | ||||
|  * | ||||
|  * Return:	@rec's filename, either same name space as @name or lowest space. | ||||
|  *		NULL if can't determine parenthood or on error. | ||||
|  */ | ||||
| static FILE_NAME_ATTR* verify_parent(struct filename* name, MFT_RECORD* rec) { | ||||
| 	ATTR_RECORD *attr30; | ||||
| 	FILE_NAME_ATTR *filename_attr = NULL, *lowest_space_name = NULL; | ||||
| 	ntfs_attr_search_ctx *ctx; | ||||
| 	int found_same_space = 1; | ||||
| 
 | ||||
| 	if (!name || !rec) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (!(rec->flags & MFT_RECORD_IS_DIRECTORY)) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	ctx = ntfs_attr_get_search_ctx(NULL, rec); | ||||
| 	if (!ctx) { | ||||
| 		Eprintf ("Couldn't create a search context.\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	attr30 = find_attribute(AT_FILE_NAME, ctx); | ||||
| 	if (!attr30) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->value_offset)); | ||||
| 	/* if name is older than this dir -> can't determine */ | ||||
| 	if (ntfs2utc(filename_attr->creation_time) > name->date_c) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (filename_attr->file_name_type != name->name_space) { | ||||
| 		found_same_space = 0; | ||||
| 		lowest_space_name = filename_attr; | ||||
| 
 | ||||
| 		while (!found_same_space && (attr30 = find_attribute(AT_FILE_NAME, ctx))) { | ||||
| 			filename_attr = (FILE_NAME_ATTR*)((char*)attr30 + le16_to_cpu(attr30->value_offset)); | ||||
| 
 | ||||
| 			if (filename_attr->file_name_type == name->name_space) { | ||||
| 				found_same_space = 1; | ||||
| 			} | ||||
| 			else { | ||||
| 				if (filename_attr->file_name_type < lowest_space_name->file_name_type) { | ||||
| 					lowest_space_name = filename_attr; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	ntfs_attr_put_search_ctx(ctx); | ||||
| 
 | ||||
| 	return (found_same_space ? filename_attr : lowest_space_name); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * get_parent_name - Find the name of a file's parent. | ||||
|  * @name:	the filename whose parent's name to find | ||||
|  * | ||||
|  */ | ||||
| static void get_parent_name(struct filename* name, ntfs_volume* vol) { | ||||
| 	ntfs_attr* mft_data; | ||||
| 	MFT_RECORD* rec; | ||||
| 	FILE_NAME_ATTR* filename_attr; | ||||
| 	long long inode_num; | ||||
| 
 | ||||
| 	if (!name || !vol) | ||||
| 		return; | ||||
| 	 | ||||
| 	rec = calloc(1, vol->mft_record_size); | ||||
| 	if (!rec) { | ||||
| 		Eprintf ("Couldn't allocate memory in get_parent_name()\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	mft_data = ntfs_attr_open(vol->mft_ni, AT_DATA, NULL, 0); | ||||
| 	if (!mft_data) { | ||||
| 		Eprintf ("Couldn't open $MFT/$DATA: %s\n", strerror (errno)); | ||||
| 	} | ||||
| 	else { | ||||
| 		inode_num = MREF(name->parent_mref); | ||||
| 
 | ||||
| 		if (ntfs_attr_pread(mft_data, vol->mft_record_size * inode_num, vol->mft_record_size, rec) < 1) { | ||||
| 			Eprintf ("Couldn't read MFT Record %lld.\n", inode_num); | ||||
| 		} | ||||
| 		else { | ||||
| 			if ((filename_attr = verify_parent(name, rec))) { | ||||
| 				if (ntfs_ucstombs(filename_attr->file_name, filename_attr->file_name_length, &name->parent_name, 0) < 0) { | ||||
| 					Dprintf ("Couldn't translate filename to current locale.\n"); | ||||
| 					name->parent_name = NULL; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (mft_data) { | ||||
| 		ntfs_attr_close(mft_data); | ||||
| 	} | ||||
| 
 | ||||
| 	if (rec) { | ||||
| 		free(rec); | ||||
| 	} | ||||
| 
 | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * get_filenames - Read an MFT Record's $FILENAME attributes | ||||
|  * @file:  The file object to work with | ||||
|  | @ -704,7 +836,7 @@ static void free_file (struct ufile *file) | |||
|  * Return:  n  The number of $FILENAME attributes found | ||||
|  *	   -1  Error | ||||
|  */ | ||||
| static int get_filenames (struct ufile *file) | ||||
| static int get_filenames (struct ufile *file, ntfs_volume* vol) | ||||
| { | ||||
| 	ATTR_RECORD *rec; | ||||
| 	FILE_NAME_ATTR *attr; | ||||
|  | @ -748,8 +880,16 @@ static int get_filenames (struct ufile *file) | |||
| 			Dprintf ("Couldn't translate filename to current locale.\n"); | ||||
| 		} | ||||
| 
 | ||||
| 		name->parent_name = NULL; | ||||
| 
 | ||||
| 		if (opts.parent) { | ||||
| 			name->parent_mref = attr->parent_directory; | ||||
| 			get_parent_name(name, vol); | ||||
| 		} | ||||
| 
 | ||||
| 		if (name->name_space < space) { | ||||
| 			file->pref_name = name->name; | ||||
| 			file->pref_pname = name->parent_name; | ||||
| 			space = name->name_space; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -915,7 +1055,7 @@ static struct ufile * read_record (ntfs_volume *vol, long long record) | |||
| 	if (attr90) | ||||
| 		file->directory = 1; | ||||
| 
 | ||||
| 	if (get_filenames (file) < 0) { | ||||
| 	if (get_filenames (file, vol) < 0) { | ||||
| 		Eprintf ("Couldn't get filenames.\n"); | ||||
| 	} | ||||
| 	if (get_data (file, vol) < 0) { | ||||
|  | @ -1105,7 +1245,13 @@ static void dump_record (struct ufile *file) | |||
| 		    FILE_ATTR_COMPRESSED | FILE_ATTR_ENCRYPTED))) { | ||||
| 			Qprintf (NONE); | ||||
| 		} | ||||
| 
 | ||||
| 		Qprintf ("\n"); | ||||
| 
 | ||||
| 		if (opts.parent) { | ||||
| 			Qprintf ("Parent: %s\n", f->parent_name ? f->parent_name : "<non-determined>"); | ||||
| 		} | ||||
| 
 | ||||
| 		Qprintf ("Size alloc: %lld\n", f->size_alloc); | ||||
| 		Qprintf ("Size data: %lld\n", f->size_data); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue