ntfsinfo.c revision 9663:ace9a2ac3683
1/**
2 * ntfsinfo - Part of the Linux-NTFS project.
3 *
4 * Copyright (c) 2002-2004 Matthew J. Fanto
5 * Copyright (c) 2002-2006 Anton Altaparmakov
6 * Copyright (c) 2002-2005 Richard Russon
7 * Copyright (c) 2003-2006 Szabolcs Szakacsits
8 * Copyright (c) 2004-2005 Yuval Fledel
9 * Copyright (c) 2004-2007 Yura Pakhuchiy
10 * Copyright (c)      2005 Cristian Klein
11 *
12 * This utility will dump a file's attributes.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program (in the main directory of the Linux-NTFS
26 * distribution in the file COPYING); if not, write to the Free Software
27 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 */
29/*
30 * TODO LIST:
31 *	- Better error checking. (focus on ntfs_dump_volume)
32 *	- Comment things better.
33 *	- More things at verbose mode.
34 *	- Dump ACLs when security_id exists (NTFS 3+ only).
35 *	- Clean ups.
36 *	- Internationalization.
37 *	- Add more Indexed Attr Types.
38 *	- Make formatting look more like www.flatcap.org/ntfs/info
39 *
40 *	Still not dumping certain attributes. Need to find the best
41 *	way to output some of these attributes.
42 *
43 *	Still need to do:
44 *	    $REPARSE_POINT/$SYMBOLIC_LINK
45 *	    $LOGGED_UTILITY_STREAM
46 */
47
48#include "config.h"
49
50#ifdef HAVE_STDIO_H
51#include <stdio.h>
52#endif
53#ifdef HAVE_STDLIB_H
54#include <stdlib.h>
55#endif
56#ifdef HAVE_STRING_H
57#include <string.h>
58#endif
59#ifdef HAVE_TIME_H
60#include <time.h>
61#endif
62#ifdef HAVE_GETOPT_H
63#include <getopt.h>
64#endif
65#ifdef HAVE_ERRNO_H
66#include <errno.h>
67#endif
68
69#include "types.h"
70#include "mft.h"
71#include "attrib.h"
72#include "layout.h"
73#include "inode.h"
74#include "index.h"
75#include "utils.h"
76#include "security.h"
77#include "mst.h"
78#include "dir.h"
79#include "ntfstime.h"
80#include "version.h"
81#include "support.h"
82
83static const char *EXEC_NAME = "ntfsinfo";
84
85static struct options {
86	const char *device;	/* Device/File to work with */
87	const char *filename;	/* Resolve this filename to mft number */
88	s64	 inode;		/* Info for this inode */
89	int	 debug;		/* Debug output */
90	int	 quiet;		/* Less output */
91	int	 verbose;	/* Extra output */
92	int	 force;		/* Override common sense */
93	int	 notime;	/* Don't report timestamps at all */
94	int	 mft;		/* Dump information about the volume as well */
95} opts;
96
97/**
98 * version - Print version information about the program
99 *
100 * Print a copyright statement and a brief description of the program.
101 *
102 * Return:  none
103 */
104static void version(void)
105{
106	printf("\n%s v%s (libntfs %s) - Display information about an NTFS "
107			"Volume.\n\n", EXEC_NAME, VERSION,
108			ntfs_libntfs_version());
109	printf("Copyright (c)\n");
110	printf("    2002-2004 Matthew J. Fanto\n");
111	printf("    2002-2006 Anton Altaparmakov\n");
112	printf("    2002-2005 Richard Russon\n");
113	printf("    2003-2006 Szabolcs Szakacsits\n");
114	printf("    2003      Leonard Norrg��rd\n");
115	printf("    2004-2005 Yuval Fledel\n");
116	printf("    2004-2007 Yura Pakhuchiy\n");
117	printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
118}
119
120/**
121 * usage - Print a list of the parameters to the program
122 *
123 * Print a list of the parameters and options for the program.
124 *
125 * Return:  none
126 */
127static void usage(void)
128{
129	printf("\nUsage: %s [options] device\n"
130		"    -i, --inode NUM  Display information about this inode\n"
131		"    -F, --file FILE  Display information about this file (absolute path)\n"
132		"    -m, --mft        Dump information about the volume\n"
133		"    -t, --notime     Don't report timestamps\n"
134		"\n"
135		"    -f, --force      Use less caution\n"
136		"    -q, --quiet      Less output\n"
137		"    -v, --verbose    More output\n"
138		"    -V, --version    Display version information\n"
139		"    -h, --help       Display this help\n"
140#ifdef DEBUG
141                "    -d, --debug            Show debug information\n"
142#endif
143	        "\n",
144		EXEC_NAME);
145	printf("%s%s\n", ntfs_bugs, ntfs_home);
146}
147
148/**
149 * parse_options - Read and validate the programs command line
150 *
151 * Read the command line, verify the syntax and parse the options.
152 * This function is very long, but quite simple.
153 *
154 * Return:  1 Success
155 *	    0 Error, one or more problems
156 */
157static int parse_options(int argc, char *argv[])
158{
159	static const char *sopt = "-:dfhi:F:mqtTvV";
160	static const struct option lopt[] = {
161#ifdef DEBUG
162                { "debug",      no_argument,            NULL, 'd' },
163#endif
164		{ "force",	 no_argument,		NULL, 'f' },
165		{ "help",	 no_argument,		NULL, 'h' },
166		{ "inode",	 required_argument,	NULL, 'i' },
167		{ "file",	 required_argument,	NULL, 'F' },
168		{ "quiet",	 no_argument,		NULL, 'q' },
169		{ "verbose",	 no_argument,		NULL, 'v' },
170		{ "version",	 no_argument,		NULL, 'V' },
171		{ "notime",	 no_argument,		NULL, 'T' },
172		{ "mft",	 no_argument,		NULL, 'm' },
173		{ NULL,		 0,			NULL,  0  }
174	};
175
176	int c = -1;
177	int err  = 0;
178	int ver  = 0;
179	int help = 0;
180	int levels = 0;
181
182	opterr = 0; /* We'll handle the errors, thank you. */
183
184	opts.inode = -1;
185	opts.filename = NULL;
186
187	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
188		switch (c) {
189		case 1:
190			if (!opts.device)
191				opts.device = optarg;
192			else
193				err++;
194			break;
195		case 'd':
196			opts.debug++;
197			break;
198		case 'i':
199			if ((opts.inode != -1) ||
200			    (!utils_parse_size(optarg, &opts.inode, FALSE))) {
201				err++;
202			}
203			break;
204		case 'F':
205			if (opts.filename == NULL) {
206				/* The inode can not be resolved here,
207				   store the filename */
208				opts.filename = argv[optind-1];
209			} else {
210				/* "-F" can't appear more than once */
211				err++;
212			}
213			break;
214		case 'f':
215			opts.force++;
216			break;
217		case 'h':
218			help++;
219			break;
220		case 'q':
221			opts.quiet++;
222			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
223			break;
224		case 't':
225			opts.notime++;
226			break;
227		case 'T':
228			/* 'T' is deprecated, notify */
229			ntfs_log_error("Option 'T' is deprecated, it was "
230				"replaced by 't'.\n");
231			err++;
232			break;
233		case 'v':
234			opts.verbose++;
235			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
236			break;
237		case 'V':
238			ver++;
239			break;
240		case 'm':
241			opts.mft++;
242			break;
243		case '?':
244			if (optopt=='?') {
245				help++;
246				continue;
247			}
248			if (ntfs_log_parse_option(argv[optind-1]))
249				continue;
250			ntfs_log_error("Unknown option '%s'.\n",
251					argv[optind-1]);
252			err++;
253			break;
254		case ':':
255			ntfs_log_error("Option '%s' requires an "
256					"argument.\n", argv[optind-1]);
257			err++;
258			break;
259		default:
260			ntfs_log_error("Unhandled option case: %d.\n", c);
261			err++;
262			break;
263		}
264	}
265
266	/* Make sure we're in sync with the log levels */
267	levels = ntfs_log_get_levels();
268	if (levels & NTFS_LOG_LEVEL_VERBOSE)
269		opts.verbose++;
270	if (!(levels & NTFS_LOG_LEVEL_QUIET))
271		opts.quiet++;
272
273	if (help || ver) {
274		opts.quiet = 0;
275	} else {
276		if (opts.device == NULL) {
277			if (argc > 1)
278				ntfs_log_error("You must specify exactly one "
279					"device.\n");
280			err++;
281		}
282
283		if ((opts.inode == -1) && (opts.filename == NULL) && !opts.mft) {
284			if (argc > 1)
285				ntfs_log_error("You must specify an inode to "
286					"learn about.\n");
287			err++;
288		}
289
290		if (opts.quiet && opts.verbose) {
291			ntfs_log_error("You may not use --quiet and --verbose "
292				"at the same time.\n");
293			err++;
294		}
295
296		if ((opts.inode != -1) && (opts.filename != NULL)) {
297			if (argc > 1)
298				ntfs_log_error("You may not specify --inode "
299					"and --file together.\n");
300			err++;
301		}
302
303	}
304
305#ifdef DEBUG
306	if (!opts.debug)
307		if (!freopen("/dev/null", "w", stderr)) {
308			ntfs_log_perror("Failed to freopen stderr to /dev/null");
309			exit(1);
310		}
311#endif
312
313	if (ver)
314		version();
315	if (help || err)
316		usage();
317
318	return (!err && !help && !ver);
319}
320
321
322/* *************** utility functions ******************** */
323/**
324 * ntfsinfo_time_to_str() -
325 * @sle_ntfs_clock:	on disk time format in 100ns units since 1st jan 1601
326 *			in little-endian format
327 *
328 * Return char* in a format 'Thu Jan  1 00:00:00 1970'.
329 * No need to free the returned memory.
330 *
331 * Example of usage:
332 *	char *time_str = ntfsinfo_time_to_str(
333 *			sle64_to_cpu(standard_attr->creation_time));
334 *	printf("\tFile Creation Time:\t %s", time_str);
335 */
336static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock)
337{
338	time_t unix_clock = ntfs2utc(sle_ntfs_clock);
339	return ctime(&unix_clock);
340}
341
342/**
343 * ntfs_attr_get_name()
344 * @attr:	a valid attribute record
345 *
346 * return multi-byte string containing the attribute name if exist. the user
347 *             is then responsible of freeing that memory.
348 *        null if no name exists (attr->name_length==0). no memory allocated.
349 *        null if cannot convert to multi-byte string. errno would contain the
350 *             error id. no memory allocated in that case
351 */
352static char *ntfs_attr_get_name_mbs(ATTR_RECORD *attr)
353{
354	ntfschar *ucs_attr_name;
355	char *mbs_attr_name = NULL;
356	int mbs_attr_name_size;
357
358	/* Get name in unicode. */
359	ucs_attr_name = ntfs_attr_get_name(attr);
360	/* Convert unicode to printable format. */
361	mbs_attr_name_size = ntfs_ucstombs(ucs_attr_name, attr->name_length,
362			&mbs_attr_name, 0);
363	if (mbs_attr_name_size > 0)
364		return mbs_attr_name;
365	else
366		return NULL;
367}
368
369
370/* *************** functions for dumping global info ******************** */
371/**
372 * ntfs_dump_volume - dump information about the volume
373 */
374static void ntfs_dump_volume(ntfs_volume *vol)
375{
376	printf("Volume Information \n");
377	printf("\tName of device: %s\n", vol->u.dev->d_name);
378	printf("\tDevice state: %lu\n", vol->u.dev->d_state);
379	printf("\tVolume Name: %s\n", vol->vol_name);
380	printf("\tVolume State: %lu\n", vol->state);
381	printf("\tVolume Version: %u.%u\n", vol->major_ver, vol->minor_ver);
382	printf("\tSector Size: %hu\n", vol->sector_size);
383	printf("\tCluster Size: %u\n", (unsigned int)vol->cluster_size);
384	printf("\tVolume Size in Clusters: %lld\n",
385			(long long)vol->nr_clusters);
386
387	printf("MFT Information \n");
388	printf("\tMFT Record Size: %u\n", (unsigned int)vol->mft_record_size);
389	printf("\tMFT Zone Multiplier: %u\n", vol->mft_zone_multiplier);
390	printf("\tMFT Data Position: %lld\n", (long long)vol->mft_data_pos);
391	printf("\tMFT Zone Start: %lld\n", (long long)vol->mft_zone_start);
392	printf("\tMFT Zone End: %lld\n", (long long)vol->mft_zone_end);
393	printf("\tMFT Zone Position: %lld\n", (long long)vol->mft_zone_pos);
394	printf("\tCurrent Position in First Data Zone: %lld\n",
395			(long long)vol->data1_zone_pos);
396	printf("\tCurrent Position in Second Data Zone: %lld\n",
397			(long long)vol->data2_zone_pos);
398	printf("\tLCN of Data Attribute for FILE_MFT: %lld\n",
399			(long long)vol->mft_lcn);
400	printf("\tFILE_MFTMirr Size: %d\n", vol->mftmirr_size);
401	printf("\tLCN of Data Attribute for File_MFTMirr: %lld\n",
402			(long long)vol->mftmirr_lcn);
403	printf("\tSize of Attribute Definition Table: %d\n",
404			(int)vol->attrdef_len);
405
406	printf("FILE_Bitmap Information \n");
407	printf("\tFILE_Bitmap MFT Record Number: %llu\n",
408			(unsigned long long)vol->lcnbmp_ni->mft_no);
409	printf("\tState of FILE_Bitmap Inode: %lu\n", vol->lcnbmp_ni->state);
410	printf("\tLength of Attribute List: %u\n",
411			(unsigned int)vol->lcnbmp_ni->attr_list_size);
412	printf("\tAttribute List: %s\n", vol->lcnbmp_ni->attr_list);
413	printf("\tNumber of Attached Extent Inodes: %d\n",
414			(int)vol->lcnbmp_ni->nr_extents);
415	/* FIXME: need to add code for the union if nr_extens != 0, but
416	   i dont know if it will ever != 0 with FILE_Bitmap */
417
418	printf("FILE_Bitmap Data Attribute Information\n");
419	printf("\tDecompressed Runlist: not done yet\n");
420	printf("\tBase Inode: %llu\n",
421			(unsigned long long)vol->lcnbmp_na->ni->mft_no);
422	printf("\tAttribute Types: not done yet\n");
423	//printf("\tAttribute Name: %s\n", vol->lcnbmp_na->name);
424	printf("\tAttribute Name Length: %u\n",
425			(unsigned int)vol->lcnbmp_na->name_len);
426	printf("\tAttribute State: %lu\n", vol->lcnbmp_na->state);
427	printf("\tAttribute Allocated Size: %lld\n",
428			(long long)vol->lcnbmp_na->allocated_size);
429	printf("\tAttribute Data Size: %lld\n",
430			(long long)vol->lcnbmp_na->data_size);
431	printf("\tAttribute Initialized Size: %lld\n",
432			(long long)vol->lcnbmp_na->initialized_size);
433	printf("\tAttribute Compressed Size: %lld\n",
434			(long long)vol->lcnbmp_na->compressed_size);
435	printf("\tCompression Block Size: %u\n",
436			(unsigned int)vol->lcnbmp_na->compression_block_size);
437	printf("\tCompression Block Size Bits: %u\n",
438			vol->lcnbmp_na->compression_block_size_bits);
439	printf("\tCompression Block Clusters: %u\n",
440			vol->lcnbmp_na->compression_block_clusters);
441
442	//TODO: Still need to add a few more attributes
443}
444
445/**
446 * ntfs_dump_flags - Dump flags for STANDARD_INFORMATION and FILE_NAME.
447 * @type:	dump flags for this attribute type
448 * @flags:	flags for dumping
449 */
450static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, le32 flags)
451{
452	printf("%sFile attributes:\t", indent);
453	if (flags & FILE_ATTR_READONLY) {
454		printf(" READONLY");
455		flags &= ~FILE_ATTR_READONLY;
456	}
457	if (flags & FILE_ATTR_HIDDEN) {
458		printf(" HIDDEN");
459		flags &= ~FILE_ATTR_HIDDEN;
460	}
461	if (flags & FILE_ATTR_SYSTEM) {
462		printf(" SYSTEM");
463		flags &= ~FILE_ATTR_SYSTEM;
464	}
465	if (flags & FILE_ATTR_DIRECTORY) {
466		printf(" DIRECTORY");
467		flags &= ~FILE_ATTR_DIRECTORY;
468	}
469	if (flags & FILE_ATTR_ARCHIVE) {
470		printf(" ARCHIVE");
471		flags &= ~FILE_ATTR_ARCHIVE;
472	}
473	if (flags & FILE_ATTR_DEVICE) {
474		printf(" DEVICE");
475		flags &= ~FILE_ATTR_DEVICE;
476	}
477	if (flags & FILE_ATTR_NORMAL) {
478		printf(" NORMAL");
479		flags &= ~FILE_ATTR_NORMAL;
480	}
481	if (flags & FILE_ATTR_TEMPORARY) {
482		printf(" TEMPORARY");
483		flags &= ~FILE_ATTR_TEMPORARY;
484	}
485	if (flags & FILE_ATTR_SPARSE_FILE) {
486		printf(" SPARSE_FILE");
487		flags &= ~FILE_ATTR_SPARSE_FILE;
488	}
489	if (flags & FILE_ATTR_REPARSE_POINT) {
490		printf(" REPARSE_POINT");
491		flags &= ~FILE_ATTR_REPARSE_POINT;
492	}
493	if (flags & FILE_ATTR_COMPRESSED) {
494		printf(" COMPRESSED");
495		flags &= ~FILE_ATTR_COMPRESSED;
496	}
497	if (flags & FILE_ATTR_OFFLINE) {
498		printf(" OFFLINE");
499		flags &= ~FILE_ATTR_OFFLINE;
500	}
501	if (flags & FILE_ATTR_NOT_CONTENT_INDEXED) {
502		printf(" NOT_CONTENT_INDEXED");
503		flags &= ~FILE_ATTR_NOT_CONTENT_INDEXED;
504	}
505	if (flags & FILE_ATTR_ENCRYPTED) {
506		printf(" ENCRYPTED");
507		flags &= ~FILE_ATTR_ENCRYPTED;
508	}
509	/* We know that FILE_ATTR_I30_INDEX_PRESENT only exists on $FILE_NAME,
510	   and in case we are wrong, let it appear as UNKNOWN */
511	if (type == AT_FILE_NAME) {
512		if (flags & FILE_ATTR_I30_INDEX_PRESENT) {
513			printf(" I30_INDEX");
514			flags &= ~FILE_ATTR_I30_INDEX_PRESENT;
515		}
516	}
517	if (flags & FILE_ATTR_VIEW_INDEX_PRESENT) {
518		printf(" VIEW_INDEX");
519		flags &= ~FILE_ATTR_VIEW_INDEX_PRESENT;
520	}
521	if (flags)
522		printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags));
523	/* Print all the flags in hex. */
524	printf(" (0x%08x)\n", (unsigned)le32_to_cpu(flags));
525}
526
527/**
528 * ntfs_dump_namespace
529 */
530static void ntfs_dump_namespace(const char *indent, u8 file_name_type)
531{
532	const char *mbs_file_type;
533
534	/* name space */
535	switch (file_name_type) {
536	case FILE_NAME_POSIX:
537		mbs_file_type = "POSIX";
538		break;
539	case FILE_NAME_WIN32:
540		mbs_file_type = "Win32";
541		break;
542	case FILE_NAME_DOS:
543		mbs_file_type = "DOS";
544		break;
545	case FILE_NAME_WIN32_AND_DOS:
546		mbs_file_type = "Win32 & DOS";
547		break;
548	default:
549		mbs_file_type = "(unknown)";
550	}
551	printf("%sNamespace:\t\t %s\n", indent, mbs_file_type);
552}
553
554/* *************** functions for dumping attributes ******************** */
555/**
556 * ntfs_dump_standard_information
557 */
558static void ntfs_dump_attr_standard_information(ATTR_RECORD *attr)
559{
560	STANDARD_INFORMATION *standard_attr = NULL;
561	u32 value_length;
562
563	standard_attr = (STANDARD_INFORMATION*)((char *)attr +
564		le16_to_cpu(attr->u.res.value_offset));
565
566	/* time conversion stuff */
567	if (!opts.notime) {
568		char *ntfs_time_str = NULL;
569
570		ntfs_time_str = ntfsinfo_time_to_str(standard_attr->creation_time);
571		printf("\tFile Creation Time:\t %s",ntfs_time_str);
572
573		ntfs_time_str = ntfsinfo_time_to_str(
574			standard_attr->last_data_change_time);
575		printf("\tFile Altered Time:\t %s",ntfs_time_str);
576
577		ntfs_time_str = ntfsinfo_time_to_str(
578			standard_attr->last_mft_change_time);
579		printf("\tMFT Changed Time:\t %s",ntfs_time_str);
580
581		ntfs_time_str = ntfsinfo_time_to_str(standard_attr->last_access_time);
582		printf("\tLast Accessed Time:\t %s",ntfs_time_str);
583	}
584	ntfs_dump_flags("\t", attr->type, standard_attr->file_attributes);
585
586	value_length = le32_to_cpu(attr->u.res.value_length);
587	if (value_length == 48) {
588		/* Only 12 reserved bytes here */
589	} else if (value_length == 72) {
590		printf("\tMaximum versions:\t %u \n", (unsigned int)
591				le32_to_cpu(standard_attr->u.v30.maximum_versions));
592		printf("\tVersion number:\t\t %u \n", (unsigned int)
593				le32_to_cpu(standard_attr->u.v30.version_number));
594		printf("\tClass ID:\t\t %u \n",
595			(unsigned int)le32_to_cpu(standard_attr->u.v30.class_id));
596		printf("\tUser ID:\t\t %u (0x%x)\n",
597			(unsigned int)le32_to_cpu(standard_attr->u.v30.owner_id),
598			(unsigned int)le32_to_cpu(standard_attr->u.v30.owner_id));
599		printf("\tSecurity ID:\t\t %u (0x%x)\n",
600			(unsigned int)le32_to_cpu(standard_attr->u.v30.security_id),
601			(unsigned int)le32_to_cpu(standard_attr->u.v30.security_id));
602		printf("\tQuota charged:\t\t %llu (0x%llx)\n",
603				(unsigned long long)
604				le64_to_cpu(standard_attr->u.v30.quota_charged),
605				(unsigned long long)
606				le64_to_cpu(standard_attr->u.v30.quota_charged));
607		printf("\tUpdate Sequence Number:\t %llu (0x%llx)\n",
608				(unsigned long long)
609				le64_to_cpu(standard_attr->u.v30.usn),
610				(unsigned long long)
611				le64_to_cpu(standard_attr->u.v30.usn));
612	} else {
613		printf("\tSize of STANDARD_INFORMATION is %u (0x%x).  It "
614				"should be either 72 or 48, something is "
615				"wrong...\n", (unsigned int)value_length,
616				(unsigned)value_length);
617	}
618}
619
620static void ntfs_dump_bytes(u8 *buf, int start, int stop)
621{
622	int i;
623
624	for (i = start; i < stop; i++) {
625		printf("%02x ", buf[i]);
626	}
627}
628
629/**
630 * ntfs_dump_attr_list()
631 */
632static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol)
633{
634	ATTR_LIST_ENTRY *entry;
635	u8 *value;
636	s64 l;
637
638	if (!opts.verbose)
639		return;
640
641	l = ntfs_get_attribute_value_length(attr);
642	if (!l) {
643		ntfs_log_perror("ntfs_get_attribute_value_length failed");
644		return;
645	}
646	value = ntfs_malloc(l);
647	if (!value)
648		return;
649
650	l = ntfs_get_attribute_value(vol, attr, value);
651	if (!l) {
652		ntfs_log_perror("ntfs_get_attribute_value failed");
653		free(value);
654		return;
655	}
656	printf("\tDumping attribute list:");
657	entry = (ATTR_LIST_ENTRY *) value;
658	for (;(u8 *)entry < (u8 *) value + l; entry = (ATTR_LIST_ENTRY *)
659				((u8 *) entry + le16_to_cpu(entry->length))) {
660		printf("\n");
661		printf("\t\tAttribute type:\t0x%x\n",
662				(unsigned int)le32_to_cpu(entry->type));
663		printf("\t\tRecord length:\t%u (0x%x)\n",
664				(unsigned)le16_to_cpu(entry->length),
665				(unsigned)le16_to_cpu(entry->length));
666		printf("\t\tName length:\t%u (0x%x)\n",
667				(unsigned)entry->name_length,
668				(unsigned)entry->name_length);
669		printf("\t\tName offset:\t%u (0x%x)\n",
670				(unsigned)entry->name_offset,
671				(unsigned)entry->name_offset);
672		printf("\t\tStarting VCN:\t%lld (0x%llx)\n",
673				(long long)sle64_to_cpu(entry->lowest_vcn),
674				(unsigned long long)
675				sle64_to_cpu(entry->lowest_vcn));
676		printf("\t\tMFT reference:\t%lld (0x%llx)\n",
677				(unsigned long long)
678				MREF_LE(entry->mft_reference),
679				(unsigned long long)
680				MREF_LE(entry->mft_reference));
681		printf("\t\tInstance:\t%u (0x%x)\n",
682				(unsigned)le16_to_cpu(entry->instance),
683				(unsigned)le16_to_cpu(entry->instance));
684		printf("\t\tName:\t\t");
685		if (entry->name_length) {
686			char *name = NULL;
687			int name_size;
688
689			name_size = ntfs_ucstombs(entry->name,
690					entry->name_length, &name, 0);
691
692			if (name_size > 0) {
693				printf("%s\n", name);
694				free(name);
695			} else
696				ntfs_log_perror("ntfs_ucstombs failed");
697		} else
698			printf("unnamed\n");
699		printf("\t\tPadding:\t");
700		ntfs_dump_bytes((u8 *)entry, entry->name_offset +
701				sizeof(ntfschar) * entry->name_length,
702				le16_to_cpu(entry->length));
703		printf("\n");
704	}
705	free(value);
706	printf("\tEnd of attribute list reached.\n");
707}
708
709/**
710 * ntfs_dump_filename()
711 */
712static void ntfs_dump_filename(const char *indent,
713		FILE_NAME_ATTR *file_name_attr)
714{
715	printf("%sParent directory:\t %lld (0x%llx)\n", indent,
716			(long long)MREF_LE(file_name_attr->parent_directory),
717			(long long)MREF_LE(file_name_attr->parent_directory));
718	/* time stuff */
719	if (!opts.notime) {
720		char *ntfs_time_str;
721
722		ntfs_time_str = ntfsinfo_time_to_str(
723				file_name_attr->creation_time);
724		printf("%sFile Creation Time:\t %s", indent, ntfs_time_str);
725
726		ntfs_time_str = ntfsinfo_time_to_str(
727				file_name_attr->last_data_change_time);
728		printf("%sFile Altered Time:\t %s", indent, ntfs_time_str);
729
730		ntfs_time_str = ntfsinfo_time_to_str(
731				file_name_attr->last_mft_change_time);
732		printf("%sMFT Changed Time:\t %s", indent, ntfs_time_str);
733
734		ntfs_time_str = ntfsinfo_time_to_str(
735				file_name_attr->last_access_time);
736		printf("%sLast Accessed Time:\t %s", indent, ntfs_time_str);
737	}
738	/* other basic stuff about the file */
739	printf("%sAllocated Size:\t\t %lld (0x%llx)\n", indent, (long long)
740			sle64_to_cpu(file_name_attr->allocated_size),
741			(unsigned long long)
742			sle64_to_cpu(file_name_attr->allocated_size));
743	printf("%sData Size:\t\t %lld (0x%llx)\n", indent,
744			(long long)sle64_to_cpu(file_name_attr->data_size),
745			(unsigned long long)
746			sle64_to_cpu(file_name_attr->data_size));
747	printf("%sFilename Length:\t %d (0x%x)\n", indent,
748			(unsigned)file_name_attr->file_name_length,
749			(unsigned)file_name_attr->file_name_length);
750	ntfs_dump_flags(indent, AT_FILE_NAME, file_name_attr->file_attributes);
751	if (file_name_attr->file_attributes & FILE_ATTR_REPARSE_POINT &&
752			file_name_attr->u.reparse_point_tag)
753		printf("%sReparse point tag:\t 0x%x\n", indent, (unsigned)
754				le32_to_cpu(file_name_attr->u.reparse_point_tag));
755	else if (file_name_attr->u.reparse_point_tag) {
756		printf("%sEA Length:\t\t %d (0x%x)\n", indent, (unsigned)
757				le16_to_cpu(file_name_attr->u.s.packed_ea_size),
758				(unsigned)
759				le16_to_cpu(file_name_attr->u.s.packed_ea_size));
760		if (file_name_attr->u.s.reserved)
761			printf("%sReserved:\t\t %d (0x%x)\n", indent,
762					(unsigned)
763					le16_to_cpu(file_name_attr->u.s.reserved),
764					(unsigned)
765					le16_to_cpu(file_name_attr->u.s.reserved));
766	}
767	/* The filename. */
768	ntfs_dump_namespace(indent, file_name_attr->file_name_type);
769	if (file_name_attr->file_name_length > 0) {
770		/* but first we need to convert the little endian unicode string
771		   into a printable format */
772		char *mbs_file_name = NULL;
773		int mbs_file_name_size;
774
775		mbs_file_name_size = ntfs_ucstombs(file_name_attr->file_name,
776			file_name_attr->file_name_length,&mbs_file_name,0);
777
778		if (mbs_file_name_size>0) {
779			printf("%sFilename:\t\t '%s'\n", indent, mbs_file_name);
780			free(mbs_file_name);
781		} else {
782			/* an error occurred, errno holds the reason - notify the user */
783			ntfs_log_perror("ntfsinfo error: could not parse file name");
784		}
785	} else {
786		printf("%sFile Name:\t\t unnamed?!?\n", indent);
787	}
788}
789
790/**
791 * ntfs_dump_attr_file_name()
792 */
793static void ntfs_dump_attr_file_name(ATTR_RECORD *attr)
794{
795	ntfs_dump_filename("\t", (FILE_NAME_ATTR*)((u8*)attr +
796			le16_to_cpu(attr->u.res.value_offset)));
797}
798
799/**
800 * ntfs_dump_object_id
801 *
802 * dump the $OBJECT_ID attribute - not present on all systems
803 */
804static void ntfs_dump_attr_object_id(ATTR_RECORD *attr,ntfs_volume *vol)
805{
806	OBJECT_ID_ATTR *obj_id_attr = NULL;
807
808	obj_id_attr = (OBJECT_ID_ATTR *)((u8*)attr +
809			le16_to_cpu(attr->u.res.value_offset));
810
811	if (vol->major_ver >= 3.0) {
812		u32 value_length;
813		char printable_GUID[37];
814
815		value_length = le32_to_cpu(attr->u.res.value_length);
816
817		/* Object ID is mandatory. */
818		ntfs_guid_to_mbs(&obj_id_attr->object_id, printable_GUID);
819		printf("\tObject ID:\t\t %s\n", printable_GUID);
820
821		/* Dump Birth Volume ID. */
822		if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero(
823				&obj_id_attr->u.s.birth_volume_id)) {
824			ntfs_guid_to_mbs(&obj_id_attr->u.s.birth_volume_id,
825					printable_GUID);
826			printf("\tBirth Volume ID:\t\t %s\n", printable_GUID);
827		} else
828			printf("\tBirth Volume ID:\t missing\n");
829
830		/* Dumping Birth Object ID */
831		if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero(
832				&obj_id_attr->u.s.birth_object_id)) {
833			ntfs_guid_to_mbs(&obj_id_attr->u.s.birth_object_id,
834					printable_GUID);
835			printf("\tBirth Object ID:\t\t %s\n", printable_GUID);
836		} else
837			printf("\tBirth Object ID:\t missing\n");
838
839		/* Dumping Domain_id - reserved for now */
840		if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero(
841				&obj_id_attr->u.s.domain_id)) {
842			ntfs_guid_to_mbs(&obj_id_attr->u.s.domain_id,
843					printable_GUID);
844			printf("\tDomain ID:\t\t\t %s\n", printable_GUID);
845		} else
846			printf("\tDomain ID:\t\t missing\n");
847	} else
848		printf("\t$OBJECT_ID not present. Only NTFS versions > 3.0\n"
849			"\thave $OBJECT_ID. Your version of NTFS is %d.\n",
850				vol->major_ver);
851}
852
853/**
854 * ntfs_dump_acl
855 *
856 * given an acl, print it in a beautiful & lovely way.
857 */
858static void ntfs_dump_acl(const char *prefix, ACL *acl)
859{
860	unsigned int i;
861	u16 ace_count;
862	ACCESS_ALLOWED_ACE *ace;
863
864	printf("%sRevision\t %u\n", prefix, acl->revision);
865
866	/*
867	 * Do not recalculate le16_to_cpu every iteration (minor speedup on
868	 * big-endian machines.
869	 */
870	ace_count = le16_to_cpu(acl->ace_count);
871
872	/* initialize 'ace' to the first ace (if any) */
873	ace = (ACCESS_ALLOWED_ACE *)((char *)acl + 8);
874
875	/* iterate through ACE's */
876	for (i = 1; i <= ace_count; i++) {
877		const char *ace_type;
878		char *sid;
879
880		/* set ace_type. */
881		switch (ace->type) {
882		case ACCESS_ALLOWED_ACE_TYPE:
883			ace_type = "allow";
884			break;
885		case ACCESS_DENIED_ACE_TYPE:
886			ace_type = "deny";
887			break;
888		case SYSTEM_AUDIT_ACE_TYPE:
889			ace_type = "audit";
890			break;
891		default:
892			ace_type = "unknown";
893			break;
894		}
895
896		printf("%sACE:\t\t type:%s  flags:0x%x  access:0x%x\n", prefix,
897			ace_type, (unsigned int)ace->flags,
898			(unsigned int)le32_to_cpu(ace->mask));
899		/* get a SID string */
900		sid = ntfs_sid_to_mbs(&ace->sid, NULL, 0);
901		printf("%s\t\t SID: %s\n", prefix, sid);
902		free(sid);
903
904		/* proceed to next ACE */
905		ace = (ACCESS_ALLOWED_ACE *)(((char *)ace) +
906				le16_to_cpu(ace->size));
907	}
908}
909
910
911static void ntfs_dump_security_descriptor(SECURITY_DESCRIPTOR_ATTR *sec_desc,
912					  const char *indent)
913{
914	char *sid;
915
916	printf("%s\tRevision:\t\t %u\n", indent, sec_desc->revision);
917
918	/* TODO: parse the flags */
919	printf("%s\tControl:\t\t 0x%04x\n", indent,
920			le16_to_cpu(sec_desc->control));
921
922	if (~sec_desc->control & SE_SELF_RELATIVE) {
923		SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)sec_desc;
924
925		printf("%s\tOwner SID pointer:\t %p\n", indent, sd->owner);
926		printf("%s\tGroup SID pointer:\t %p\n", indent, sd->group);
927		printf("%s\tSACL pointer:\t\t %p\n", indent, sd->sacl);
928		printf("%s\tDACL pointer:\t\t %p\n", indent, sd->dacl);
929
930		return;
931	}
932
933	if (sec_desc->owner) {
934		sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc +
935			le32_to_cpu(sec_desc->owner)), NULL, 0);
936		printf("%s\tOwner SID:\t\t %s\n", indent, sid);
937		free(sid);
938	} else
939		printf("%s\tOwner SID:\t\t missing\n", indent);
940
941	if (sec_desc->group) {
942		sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc +
943			le32_to_cpu(sec_desc->group)), NULL, 0);
944		printf("%s\tGroup SID:\t\t %s\n", indent, sid);
945		free(sid);
946	} else
947		printf("%s\tGroup SID:\t\t missing\n", indent);
948
949	printf("%s\tSystem ACL:\t\t ", indent);
950	if (sec_desc->control & SE_SACL_PRESENT) {
951		if (sec_desc->control & SE_SACL_DEFAULTED) {
952			printf("defaulted");
953		}
954		printf("\n");
955		ntfs_dump_acl(indent ? "\t\t\t" : "\t\t",
956			      (ACL *)((char *)sec_desc +
957				      le32_to_cpu(sec_desc->sacl)));
958	} else {
959		printf("missing\n");
960	}
961
962	printf("%s\tDiscretionary ACL:\t ", indent);
963	if (sec_desc->control & SE_DACL_PRESENT) {
964		if (sec_desc->control & SE_SACL_DEFAULTED) {
965			printf("defaulted");
966		}
967		printf("\n");
968		ntfs_dump_acl(indent ? "\t\t\t" : "\t\t",
969			      (ACL *)((char *)sec_desc +
970				      le32_to_cpu(sec_desc->dacl)));
971	} else {
972		printf("missing\n");
973	}
974}
975
976/**
977 * ntfs_dump_security_descriptor()
978 *
979 * dump the security information about the file
980 */
981static void ntfs_dump_attr_security_descriptor(ATTR_RECORD *attr, ntfs_volume *vol)
982{
983	SECURITY_DESCRIPTOR_ATTR *sec_desc_attr;
984
985	if (attr->non_resident) {
986		/* FIXME: We don't handle fragmented mapping pairs case. */
987		runlist *rl = ntfs_mapping_pairs_decompress(vol, attr, NULL);
988		if (rl) {
989			s64 data_size, bytes_read;
990
991			data_size = sle64_to_cpu(attr->u.nonres.data_size);
992			sec_desc_attr = ntfs_malloc(data_size);
993			if (!sec_desc_attr) {
994				free(rl);
995				return;
996			}
997			bytes_read = ntfs_rl_pread(vol, rl, 0,
998						data_size, sec_desc_attr);
999			if (bytes_read != data_size) {
1000				ntfs_log_error("ntfsinfo error: could not "
1001						"read security descriptor\n");
1002				free(rl);
1003				free(sec_desc_attr);
1004				return;
1005			}
1006			free(rl);
1007		} else {
1008			ntfs_log_error("ntfsinfo error: could not "
1009						"decompress runlist\n");
1010			return;
1011		}
1012	} else {
1013		sec_desc_attr = (SECURITY_DESCRIPTOR_ATTR *)((u8*)attr +
1014				le16_to_cpu(attr->u.res.value_offset));
1015	}
1016
1017	ntfs_dump_security_descriptor(sec_desc_attr, "");
1018
1019	if (attr->non_resident)
1020		free(sec_desc_attr);
1021}
1022
1023/**
1024 * ntfs_dump_volume_name()
1025 *
1026 * dump the name of the volume the inode belongs to
1027 */
1028static void ntfs_dump_attr_volume_name(ATTR_RECORD *attr)
1029{
1030	ntfschar *ucs_vol_name = NULL;
1031
1032	if (le32_to_cpu(attr->u.res.value_length) > 0) {
1033		char *mbs_vol_name = NULL;
1034		int mbs_vol_name_size;
1035		/* calculate volume name position */
1036		ucs_vol_name = (ntfschar*)((u8*)attr +
1037				le16_to_cpu(attr->u.res.value_offset));
1038		/* convert the name to current locale multibyte sequence */
1039		mbs_vol_name_size = ntfs_ucstombs(ucs_vol_name,
1040				le32_to_cpu(attr->u.res.value_length) /
1041				sizeof(ntfschar), &mbs_vol_name, 0);
1042
1043		if (mbs_vol_name_size>0) {
1044			/* output the converted name. */
1045			printf("\tVolume Name:\t\t '%s'\n", mbs_vol_name);
1046			free(mbs_vol_name);
1047		} else
1048			ntfs_log_perror("ntfsinfo error: could not parse "
1049					"volume name");
1050	} else
1051		printf("\tVolume Name:\t\t unnamed\n");
1052}
1053
1054/**
1055 * ntfs_dump_volume_information()
1056 *
1057 * dump the information for the volume the inode belongs to
1058 *
1059 */
1060static void ntfs_dump_attr_volume_information(ATTR_RECORD *attr)
1061{
1062	VOLUME_INFORMATION *vol_information = NULL;
1063
1064	vol_information = (VOLUME_INFORMATION*)((char *)attr+
1065		le16_to_cpu(attr->u.res.value_offset));
1066
1067	printf("\tVolume Version:\t\t %d.%d\n", vol_information->major_ver,
1068		vol_information->minor_ver);
1069	printf("\tVolume Flags:\t\t ");
1070	if (vol_information->flags & VOLUME_IS_DIRTY)
1071		printf("DIRTY ");
1072	if (vol_information->flags & VOLUME_RESIZE_LOG_FILE)
1073		printf("RESIZE_LOG ");
1074	if (vol_information->flags & VOLUME_UPGRADE_ON_MOUNT)
1075		printf("UPG_ON_MOUNT ");
1076	if (vol_information->flags & VOLUME_MOUNTED_ON_NT4)
1077		printf("MOUNTED_NT4 ");
1078	if (vol_information->flags & VOLUME_DELETE_USN_UNDERWAY)
1079		printf("DEL_USN ");
1080	if (vol_information->flags & VOLUME_REPAIR_OBJECT_ID)
1081		printf("REPAIR_OBJID ");
1082	if (vol_information->flags & VOLUME_CHKDSK_UNDERWAY)
1083		printf("CHKDSK_UNDERWAY ");
1084	if (vol_information->flags & VOLUME_MODIFIED_BY_CHKDSK)
1085		printf("MOD_BY_CHKDSK ");
1086	if (vol_information->flags & VOLUME_FLAGS_MASK) {
1087		printf("(0x%04x)\n",
1088				(unsigned)le16_to_cpu(vol_information->flags));
1089	} else
1090		printf("none set (0x0000)\n");
1091	if (vol_information->flags & (~VOLUME_FLAGS_MASK))
1092		printf("\t\t\t\t Unknown Flags: 0x%04x\n",
1093				le16_to_cpu(vol_information->flags &
1094					(~VOLUME_FLAGS_MASK)));
1095}
1096
1097static ntfschar NTFS_DATA_SDS[5] = { const_cpu_to_le16('$'),
1098	const_cpu_to_le16('S'), const_cpu_to_le16('D'),
1099	const_cpu_to_le16('S'), const_cpu_to_le16('\0') };
1100
1101static void ntfs_dump_sds_entry(SECURITY_DESCRIPTOR_HEADER *sds)
1102{
1103	SECURITY_DESCRIPTOR_RELATIVE *sd;
1104
1105	ntfs_log_verbose("\n");
1106	ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n",
1107			(unsigned)le32_to_cpu(sds->hash));
1108	ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n",
1109			(unsigned)le32_to_cpu(sds->security_id),
1110			(unsigned)le32_to_cpu(sds->security_id));
1111	ntfs_log_verbose("\t\tOffset:\t\t\t %llu (0x%llx)\n",
1112			(unsigned long long)le64_to_cpu(sds->offset),
1113			(unsigned long long)le64_to_cpu(sds->offset));
1114	ntfs_log_verbose("\t\tLength:\t\t\t %u (0x%x)\n",
1115			(unsigned)le32_to_cpu(sds->length),
1116			(unsigned)le32_to_cpu(sds->length));
1117
1118	sd = (SECURITY_DESCRIPTOR_RELATIVE *)((char *)sds +
1119		sizeof(SECURITY_DESCRIPTOR_HEADER));
1120
1121	ntfs_dump_security_descriptor(sd, "\t");
1122}
1123
1124static void ntfs_dump_sds(ATTR_RECORD *attr, ntfs_inode *ni)
1125{
1126	SECURITY_DESCRIPTOR_HEADER *sds, *sd;
1127	ntfschar *name;
1128	int name_len;
1129	s64 data_size;
1130	u64 inode;
1131
1132	inode = ni->mft_no;
1133	if (ni->nr_extents < 0)
1134		inode = ni->u.base_ni->mft_no;
1135	if (FILE_Secure != inode)
1136		return;
1137
1138	name_len = attr->name_length;
1139	if (!name_len)
1140		return;
1141
1142	name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset));
1143	if (!ntfs_names_are_equal(NTFS_DATA_SDS, sizeof(NTFS_DATA_SDS) / 2 - 1,
1144				  name, name_len, 0, NULL, 0))
1145		return;
1146
1147	sd = sds = ntfs_attr_readall(ni, AT_DATA, name, name_len, &data_size);
1148	if (!sd) {
1149		ntfs_log_perror("Failed to read $SDS attribute");
1150		return;
1151	}
1152	/*
1153	 * FIXME: The right way is based on the indexes, so we couldn't
1154	 * miss real entries. For now, dump until it makes sense.
1155	 */
1156	while (sd->length && sd->hash &&
1157	       le64_to_cpu(sd->offset) < (u64)data_size &&
1158	       le32_to_cpu(sd->length) < (u64)data_size &&
1159	       le64_to_cpu(sd->offset) +
1160			le32_to_cpu(sd->length) < (u64)data_size) {
1161		ntfs_dump_sds_entry(sd);
1162		sd = (SECURITY_DESCRIPTOR_HEADER *)((char*)sd +
1163				((le32_to_cpu(sd->length) + 15) & ~15));
1164	}
1165	free(sds);
1166}
1167
1168static const char *get_attribute_type_name(le32 type)
1169{
1170	switch (type) {
1171	case AT_UNUSED:			return "$UNUSED";
1172	case AT_STANDARD_INFORMATION:   return "$STANDARD_INFORMATION";
1173	case AT_ATTRIBUTE_LIST:         return "$ATTRIBUTE_LIST";
1174	case AT_FILE_NAME:              return "$FILE_NAME";
1175	case AT_OBJECT_ID:              return "$OBJECT_ID";
1176	case AT_SECURITY_DESCRIPTOR:    return "$SECURITY_DESCRIPTOR";
1177	case AT_VOLUME_NAME:            return "$VOLUME_NAME";
1178	case AT_VOLUME_INFORMATION:     return "$VOLUME_INFORMATION";
1179	case AT_DATA:                   return "$DATA";
1180	case AT_INDEX_ROOT:             return "$INDEX_ROOT";
1181	case AT_INDEX_ALLOCATION:       return "$INDEX_ALLOCATION";
1182	case AT_BITMAP:                 return "$BITMAP";
1183	case AT_REPARSE_POINT:          return "$REPARSE_POINT";
1184	case AT_EA_INFORMATION:         return "$EA_INFORMATION";
1185	case AT_EA:                     return "$EA";
1186	case AT_PROPERTY_SET:           return "$PROPERTY_SET";
1187	case AT_LOGGED_UTILITY_STREAM:  return "$LOGGED_UTILITY_STREAM";
1188	case AT_END:                    return "$END";
1189	}
1190
1191	return "$UNKNOWN";
1192}
1193
1194static const char * ntfs_dump_lcn(LCN lcn)
1195{
1196	switch (lcn) {
1197		case LCN_HOLE:
1198			return "<HOLE>\t";
1199		case LCN_RL_NOT_MAPPED:
1200			return "<RL_NOT_MAPPED>";
1201		case LCN_ENOENT:
1202			return "<ENOENT>\t";
1203		case LCN_EINVAL:
1204			return "<EINVAL>\t";
1205		case LCN_EIO:
1206			return "<EIO>\t";
1207		default:
1208			ntfs_log_error("Invalid LCN value %llx passed to "
1209					"ntfs_dump_lcn().\n", lcn);
1210			return "???\t";
1211	}
1212}
1213
1214static void ntfs_dump_attribute_header(ntfs_attr_search_ctx *ctx,
1215		ntfs_volume *vol)
1216{
1217	ATTR_RECORD *a = ctx->attr;
1218
1219	printf("Dumping attribute %s (0x%x) from mft record %lld (0x%llx)\n",
1220			get_attribute_type_name(a->type),
1221			(unsigned)le32_to_cpu(a->type),
1222			(unsigned long long)ctx->ntfs_ino->mft_no,
1223			(unsigned long long)ctx->ntfs_ino->mft_no);
1224
1225	ntfs_log_verbose("\tAttribute length:\t %u (0x%x)\n",
1226			(unsigned)le32_to_cpu(a->length),
1227			(unsigned)le32_to_cpu(a->length));
1228	printf("\tResident: \t\t %s\n", a->non_resident ? "No" : "Yes");
1229	ntfs_log_verbose("\tName length:\t\t %u (0x%x)\n",
1230			(unsigned)a->name_length, (unsigned)a->name_length);
1231	ntfs_log_verbose("\tName offset:\t\t %u (0x%x)\n",
1232			(unsigned)le16_to_cpu(a->name_offset),
1233			(unsigned)le16_to_cpu(a->name_offset));
1234
1235	/* Dump the attribute (stream) name */
1236	if (a->name_length) {
1237		char *attribute_name = NULL;
1238
1239		attribute_name = ntfs_attr_get_name_mbs(a);
1240		if (attribute_name) {
1241			printf("\tAttribute name:\t\t '%s'\n", attribute_name);
1242			free(attribute_name);
1243		} else
1244			ntfs_log_perror("Error: couldn't parse attribute name");
1245	}
1246
1247	/* TODO: parse the flags */
1248	printf("\tAttribute flags:\t 0x%04x\n",
1249			(unsigned)le16_to_cpu(a->flags));
1250	printf("\tAttribute instance:\t %u (0x%x)\n",
1251			(unsigned)le16_to_cpu(a->instance),
1252			(unsigned)le16_to_cpu(a->instance));
1253
1254	/* Resident attribute */
1255	if (!a->non_resident) {
1256		printf("\tData size:\t\t %u (0x%x)\n",
1257				(unsigned)le32_to_cpu(a->u.res.value_length),
1258				(unsigned)le32_to_cpu(a->u.res.value_length));
1259		ntfs_log_verbose("\tData offset:\t\t %u (0x%x)\n",
1260				(unsigned)le16_to_cpu(a->u.res.value_offset),
1261				(unsigned)le16_to_cpu(a->u.res.value_offset));
1262		/* TODO: parse the flags */
1263		printf("\tResident flags:\t\t 0x%02x\n",
1264				(unsigned)a->u.res.resident_flags);
1265		ntfs_log_verbose("\tReservedR:\t\t %d (0x%x)\n",
1266				(unsigned)a->u.res.reservedR, (unsigned)a->u.res.reservedR);
1267		return;
1268	}
1269
1270	/* Non-resident attribute */
1271	ntfs_log_verbose("\tLowest VCN\t\t %lld (0x%llx)\n",
1272			(long long)sle64_to_cpu(a->u.nonres.lowest_vcn),
1273			(unsigned long long)sle64_to_cpu(a->u.nonres.lowest_vcn));
1274	ntfs_log_verbose("\tHighest VCN:\t\t %lld (0x%llx)\n",
1275			(long long)sle64_to_cpu(a->u.nonres.highest_vcn),
1276			(unsigned long long)sle64_to_cpu(a->u.nonres.highest_vcn));
1277	ntfs_log_verbose("\tMapping pairs offset:\t %u (0x%x)\n",
1278			(unsigned)le16_to_cpu(a->u.nonres.mapping_pairs_offset),
1279			(unsigned)le16_to_cpu(a->u.nonres.mapping_pairs_offset));
1280	printf("\tCompression unit:\t %u (0x%x)\n",
1281			(unsigned)a->u.nonres.compression_unit,
1282			(unsigned)a->u.nonres.compression_unit);
1283	/* TODO: dump the 5 reserved bytes here in verbose mode */
1284
1285	if (!a->u.nonres.lowest_vcn) {
1286		printf("\tData size:\t\t %llu (0x%llx)\n",
1287				(long long)sle64_to_cpu(a->u.nonres.data_size),
1288				(unsigned long long)sle64_to_cpu(a->u.nonres.data_size));
1289		printf("\tAllocated size:\t\t %llu (0x%llx)\n",
1290				(long long)sle64_to_cpu(a->u.nonres.allocated_size),
1291				(unsigned long long)
1292				sle64_to_cpu(a->u.nonres.allocated_size));
1293		printf("\tInitialized size:\t %llu (0x%llx)\n",
1294				(long long)sle64_to_cpu(a->u.nonres.initialized_size),
1295				(unsigned long long)
1296				sle64_to_cpu(a->u.nonres.initialized_size));
1297		if (a->u.nonres.compression_unit || a->flags & ATTR_IS_COMPRESSED ||
1298				a->flags & ATTR_IS_SPARSE)
1299			printf("\tCompressed size:\t %llu (0x%llx)\n",
1300					(signed long long)
1301					sle64_to_cpu(a->u.nonres.compressed_size),
1302					(signed long long)
1303					sle64_to_cpu(a->u.nonres.compressed_size));
1304	}
1305
1306	if (opts.verbose) {
1307		runlist *rl;
1308
1309		rl = ntfs_mapping_pairs_decompress(vol, a, NULL);
1310		if (rl) {
1311			runlist *rlc = rl;
1312
1313			// TODO: Switch this to properly aligned hex...
1314			printf("\tRunlist:\tVCN\t\tLCN\t\tLength\n");
1315			while (rlc->length) {
1316				if (rlc->lcn >= 0)
1317					printf("\t\t\t0x%llx\t\t0x%llx\t\t"
1318							"0x%llx\n", rlc->vcn,
1319							rlc->lcn, rlc->length);
1320				else
1321					printf("\t\t\t0x%llx\t\t%s\t"
1322							"0x%llx\n", rlc->vcn,
1323							ntfs_dump_lcn(rlc->lcn),
1324							rlc->length);
1325				rlc++;
1326			}
1327			free(rl);
1328		} else
1329			ntfs_log_error("Error: couldn't decompress runlist\n");
1330	}
1331}
1332
1333/**
1334 * ntfs_dump_data_attr()
1335 *
1336 * dump some info about the data attribute if it's metadata
1337 */
1338static void ntfs_dump_attr_data(ATTR_RECORD *attr, ntfs_inode *ni)
1339{
1340	if (opts.verbose)
1341		ntfs_dump_sds(attr, ni);
1342}
1343
1344typedef enum {
1345	INDEX_ATTR_UNKNOWN,
1346	INDEX_ATTR_DIRECTORY_I30,
1347	INDEX_ATTR_SECURE_SII,
1348	INDEX_ATTR_SECURE_SDH,
1349	INDEX_ATTR_OBJID_O,
1350	INDEX_ATTR_REPARSE_R,
1351	INDEX_ATTR_QUOTA_O,
1352	INDEX_ATTR_QUOTA_Q,
1353} INDEX_ATTR_TYPE;
1354
1355static void ntfs_dump_index_key(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type)
1356{
1357	char *sid;
1358	char printable_GUID[37];
1359
1360	switch (type) {
1361	case INDEX_ATTR_SECURE_SII:
1362		ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n",
1363				(unsigned)
1364				le32_to_cpu(entry->key.sii.security_id),
1365				(unsigned)
1366				le32_to_cpu(entry->key.sii.security_id));
1367		break;
1368	case INDEX_ATTR_SECURE_SDH:
1369		ntfs_log_verbose("\t\tKey hash:\t\t 0x%08x\n",
1370				(unsigned)le32_to_cpu(entry->key.sdh.hash));
1371		ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n",
1372				(unsigned)
1373				le32_to_cpu(entry->key.sdh.security_id),
1374				(unsigned)
1375				le32_to_cpu(entry->key.sdh.security_id));
1376		break;
1377	case INDEX_ATTR_OBJID_O:
1378		ntfs_guid_to_mbs(&entry->key.object_id, printable_GUID);
1379		ntfs_log_verbose("\t\tKey GUID:\t\t %s\n", printable_GUID);
1380		break;
1381	case INDEX_ATTR_REPARSE_R:
1382		ntfs_log_verbose("\t\tKey reparse tag:\t 0x%08x\n", (unsigned)
1383				le32_to_cpu(entry->key.reparse.reparse_tag));
1384		ntfs_log_verbose("\t\tKey file id:\t\t %llu (0x%llx)\n",
1385				(unsigned long long)
1386				le64_to_cpu(entry->key.reparse.file_id),
1387				(unsigned long long)
1388				le64_to_cpu(entry->key.reparse.file_id));
1389		break;
1390	case INDEX_ATTR_QUOTA_O:
1391		sid = ntfs_sid_to_mbs(&entry->key.sid, NULL, 0);
1392		ntfs_log_verbose("\t\tKey SID:\t\t %s\n", sid);
1393		free(sid);
1394		break;
1395	case INDEX_ATTR_QUOTA_Q:
1396		ntfs_log_verbose("\t\tKey owner id:\t\t %u (0x%x)\n",
1397				(unsigned)le32_to_cpu(entry->key.owner_id),
1398				(unsigned)le32_to_cpu(entry->key.owner_id));
1399		break;
1400	default:
1401		ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n",
1402				(unsigned)type);
1403		break;
1404	}
1405}
1406
1407#ifdef __sun
1408#pragma pack(1)
1409#endif
1410typedef union {
1411	SII_INDEX_DATA sii;		/* $SII index data in $Secure */
1412	SDH_INDEX_DATA sdh;		/* $SDH index data in $Secure */
1413	QUOTA_O_INDEX_DATA quota_o;	/* $O index data in $Quota    */
1414	QUOTA_CONTROL_ENTRY quota_q;	/* $Q index data in $Quota    */
1415} __attribute__((__packed__)) INDEX_ENTRY_DATA;
1416#ifdef __sun
1417#pragma pack()
1418#endif
1419
1420static void ntfs_dump_index_data(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type)
1421{
1422	INDEX_ENTRY_DATA *data;
1423
1424	data = (INDEX_ENTRY_DATA *)((u8 *)entry +
1425			le16_to_cpu(entry->u.s.data_offset));
1426
1427	switch (type) {
1428	case INDEX_ATTR_SECURE_SII:
1429		ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n",
1430				(unsigned)le32_to_cpu(data->sii.hash));
1431		ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n",
1432				(unsigned)le32_to_cpu(data->sii.security_id),
1433				(unsigned)le32_to_cpu(data->sii.security_id));
1434		ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n",
1435				(unsigned long long)
1436				le64_to_cpu(data->sii.offset),
1437				(unsigned long long)
1438				le64_to_cpu(data->sii.offset));
1439		ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n",
1440				(unsigned)le32_to_cpu(data->sii.length),
1441				(unsigned)le32_to_cpu(data->sii.length));
1442		break;
1443	case INDEX_ATTR_SECURE_SDH:
1444		ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n",
1445				(unsigned)le32_to_cpu(data->sdh.hash));
1446		ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n",
1447				(unsigned)le32_to_cpu(data->sdh.security_id),
1448				(unsigned)le32_to_cpu(data->sdh.security_id));
1449		ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n",
1450				(unsigned long long)
1451				le64_to_cpu(data->sdh.offset),
1452				(unsigned long long)
1453				le64_to_cpu(data->sdh.offset));
1454		ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n",
1455				(unsigned)le32_to_cpu(data->sdh.length),
1456				(unsigned)le32_to_cpu(data->sdh.length));
1457		ntfs_log_verbose("\t\tUnknown (padding):\t 0x%08x\n",
1458				(unsigned)le32_to_cpu(data->sdh.reserved_II));
1459		break;
1460	case INDEX_ATTR_OBJID_O: {
1461		OBJ_ID_INDEX_DATA *object_id_data;
1462		char printable_GUID[37];
1463
1464		object_id_data = (OBJ_ID_INDEX_DATA*)((u8*)entry +
1465				le16_to_cpu(entry->u.s.data_offset));
1466		ntfs_log_verbose("\t\tMFT Number:\t\t 0x%llx\n",
1467				(unsigned long long)
1468				MREF_LE(object_id_data->mft_reference));
1469		ntfs_log_verbose("\t\tMFT Sequence Number:\t 0x%x\n",
1470				(unsigned)
1471				MSEQNO_LE(object_id_data->mft_reference));
1472		ntfs_guid_to_mbs(&object_id_data->u.s.birth_volume_id,
1473				printable_GUID);
1474		ntfs_log_verbose("\t\tBirth volume id GUID:\t %s\n",
1475				printable_GUID);
1476		ntfs_guid_to_mbs(&object_id_data->u.s.birth_object_id,
1477				printable_GUID);
1478		ntfs_log_verbose("\t\tBirth object id GUID:\t %s\n",
1479				printable_GUID);
1480		ntfs_guid_to_mbs(&object_id_data->u.s.domain_id, printable_GUID);
1481		ntfs_log_verbose("\t\tDomain id GUID:\t\t %s\n",
1482				printable_GUID);
1483		}
1484		break;
1485	case INDEX_ATTR_REPARSE_R:
1486		/* TODO */
1487		break;
1488	case INDEX_ATTR_QUOTA_O:
1489		ntfs_log_verbose("\t\tOwner id:\t\t %u (0x%x)\n",
1490				(unsigned)le32_to_cpu(data->quota_o.owner_id),
1491				(unsigned)le32_to_cpu(data->quota_o.owner_id));
1492		ntfs_log_verbose("\t\tUnknown:\t\t %u (0x%x)\n",
1493				(unsigned)le32_to_cpu(data->quota_o.unknown),
1494				(unsigned)le32_to_cpu(data->quota_o.unknown));
1495		break;
1496	case INDEX_ATTR_QUOTA_Q:
1497		ntfs_log_verbose("\t\tVersion:\t\t %u\n",
1498				(unsigned)le32_to_cpu(data->quota_q.version));
1499		ntfs_log_verbose("\t\tQuota flags:\t\t 0x%08x\n",
1500				(unsigned)le32_to_cpu(data->quota_q.flags));
1501		ntfs_log_verbose("\t\tBytes used:\t\t %llu (0x%llx)\n",
1502				(unsigned long long)
1503				le64_to_cpu(data->quota_q.bytes_used),
1504				(unsigned long long)
1505				le64_to_cpu(data->quota_q.bytes_used));
1506		ntfs_log_verbose("\t\tLast changed:\t\t %s",
1507				ntfsinfo_time_to_str(
1508				data->quota_q.change_time));
1509		ntfs_log_verbose("\t\tThreshold:\t\t %lld (0x%llx)\n",
1510				(unsigned long long)
1511				sle64_to_cpu(data->quota_q.threshold),
1512				(unsigned long long)
1513				sle64_to_cpu(data->quota_q.threshold));
1514		ntfs_log_verbose("\t\tLimit:\t\t\t %lld (0x%llx)\n",
1515				(unsigned long long)
1516				sle64_to_cpu(data->quota_q.limit),
1517				(unsigned long long)
1518				sle64_to_cpu(data->quota_q.limit));
1519		ntfs_log_verbose("\t\tExceeded time:\t\t %lld (0x%llx)\n",
1520				(unsigned long long)
1521				sle64_to_cpu(data->quota_q.exceeded_time),
1522				(unsigned long long)
1523				sle64_to_cpu(data->quota_q.exceeded_time));
1524		if (le16_to_cpu(entry->u.s.data_length) > 48) {
1525			char *sid;
1526			sid = ntfs_sid_to_mbs(&data->quota_q.sid, NULL, 0);
1527			ntfs_log_verbose("\t\tOwner SID:\t\t %s\n", sid);
1528			free(sid);
1529		}
1530		break;
1531	default:
1532		ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n",
1533				(unsigned)type);
1534		break;
1535	}
1536}
1537
1538/**
1539 * ntfs_dump_index_entries()
1540 *
1541 * dump sequence of index_entries and return number of entries dumped.
1542 */
1543static int ntfs_dump_index_entries(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type)
1544{
1545	int numb_entries = 1;
1546	while (1) {
1547		if (!opts.verbose) {
1548			if (entry->flags & INDEX_ENTRY_END)
1549				break;
1550			entry = (INDEX_ENTRY *)((u8 *)entry +
1551						le16_to_cpu(entry->length));
1552			numb_entries++;
1553			continue;
1554		}
1555		ntfs_log_verbose("\t\tEntry length:\t\t %u (0x%x)\n",
1556				(unsigned)le16_to_cpu(entry->length),
1557				(unsigned)le16_to_cpu(entry->length));
1558		ntfs_log_verbose("\t\tKey length:\t\t %u (0x%x)\n",
1559				(unsigned)le16_to_cpu(entry->key_length),
1560				(unsigned)le16_to_cpu(entry->key_length));
1561		ntfs_log_verbose("\t\tIndex entry flags:\t 0x%02x\n",
1562				(unsigned)le16_to_cpu(entry->flags));
1563
1564		if (entry->flags & INDEX_ENTRY_NODE)
1565			ntfs_log_verbose("\t\tSubnode VCN:\t\t %lld (0x%llx)\n",
1566					 ntfs_ie_get_vcn(entry),
1567					 ntfs_ie_get_vcn(entry));
1568		if (entry->flags & INDEX_ENTRY_END)
1569			break;
1570
1571		switch (type) {
1572		case INDEX_ATTR_DIRECTORY_I30:
1573			ntfs_log_verbose("\t\tFILE record number:\t %llu "
1574					"(0x%llx)\n", (unsigned long long)
1575					MREF_LE(entry->u.indexed_file),
1576					(unsigned long long)
1577					MREF_LE(entry->u.indexed_file));
1578			ntfs_dump_filename("\t\t", &entry->key.file_name);
1579			break;
1580		default:
1581			ntfs_log_verbose("\t\tData offset:\t\t %u (0x%x)\n",
1582					(unsigned)
1583					le16_to_cpu(entry->u.s.data_offset),
1584					(unsigned)
1585					le16_to_cpu(entry->u.s.data_offset));
1586			ntfs_log_verbose("\t\tData length:\t\t %u (0x%x)\n",
1587					(unsigned)
1588					le16_to_cpu(entry->u.s.data_length),
1589					(unsigned)
1590					le16_to_cpu(entry->u.s.data_length));
1591			ntfs_dump_index_key(entry, type);
1592			ntfs_log_verbose("\t\tKey Data:\n");
1593			ntfs_dump_index_data(entry, type);
1594			break;
1595		}
1596		if (!entry->length) {
1597			ntfs_log_verbose("\tWARNING: Corrupt index entry, "
1598					"skipping the remainder of this index "
1599					"block.\n");
1600			break;
1601		}
1602		entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length));
1603		numb_entries++;
1604		ntfs_log_verbose("\n");
1605	}
1606	ntfs_log_verbose("\tEnd of index block reached\n");
1607	return numb_entries;
1608}
1609
1610#define	COMPARE_INDEX_NAMES(attr, name)					       \
1611	ntfs_names_are_equal((name), sizeof(name) / 2 - 1,		       \
1612		(ntfschar*)((char*)(attr) + le16_to_cpu((attr)->name_offset)), \
1613		(attr)->name_length, 0, NULL, 0)
1614
1615static INDEX_ATTR_TYPE get_index_attr_type(ntfs_inode *ni, ATTR_RECORD *attr,
1616					   INDEX_ROOT *index_root)
1617{
1618	char file_name[64];
1619
1620	if (!attr->name_length)
1621		return INDEX_ATTR_UNKNOWN;
1622
1623	if (index_root->type) {
1624		if (index_root->type == AT_FILE_NAME)
1625			return INDEX_ATTR_DIRECTORY_I30;
1626		else
1627			/* weird, this should be illegal */
1628			ntfs_log_error("Unknown index attribute type: 0x%0X\n",
1629				       index_root->type);
1630		return INDEX_ATTR_UNKNOWN;
1631	}
1632
1633	if (utils_is_metadata(ni) <= 0)
1634		return INDEX_ATTR_UNKNOWN;
1635	if (utils_inode_get_name(ni, file_name, sizeof(file_name)) <= 0)
1636		return INDEX_ATTR_UNKNOWN;
1637
1638	if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SDH))
1639		return INDEX_ATTR_SECURE_SDH;
1640	else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII))
1641		return INDEX_ATTR_SECURE_SII;
1642	else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII))
1643		return INDEX_ATTR_SECURE_SII;
1644	else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_Q))
1645		return INDEX_ATTR_QUOTA_Q;
1646	else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_R))
1647		return INDEX_ATTR_REPARSE_R;
1648	else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_O)) {
1649		if (!strcmp(file_name, "/$Extend/$Quota"))
1650			return INDEX_ATTR_QUOTA_O;
1651		else if (!strcmp(file_name, "/$Extend/$ObjId"))
1652			return INDEX_ATTR_OBJID_O;
1653	}
1654
1655	return INDEX_ATTR_UNKNOWN;
1656}
1657
1658static void ntfs_dump_index_attr_type(INDEX_ATTR_TYPE type)
1659{
1660	if (type == INDEX_ATTR_DIRECTORY_I30)
1661		printf("DIRECTORY_I30");
1662	else if (type == INDEX_ATTR_SECURE_SDH)
1663		printf("SECURE_SDH");
1664	else if (type == INDEX_ATTR_SECURE_SII)
1665		printf("SECURE_SII");
1666	else if (type == INDEX_ATTR_OBJID_O)
1667		printf("OBJID_O");
1668	else if (type == INDEX_ATTR_QUOTA_O)
1669		printf("QUOTA_O");
1670	else if (type == INDEX_ATTR_QUOTA_Q)
1671		printf("QUOTA_Q");
1672	else if (type == INDEX_ATTR_REPARSE_R)
1673		printf("REPARSE_R");
1674	else
1675		printf("UNKNOWN");
1676	printf("\n");
1677}
1678
1679static void ntfs_dump_index_header(const char *indent, INDEX_HEADER *idx)
1680{
1681	printf("%sEntries Offset:\t\t %u (0x%x)\n", indent,
1682			(unsigned)le32_to_cpu(idx->entries_offset),
1683			(unsigned)le32_to_cpu(idx->entries_offset));
1684	printf("%sIndex Size:\t\t %u (0x%x)\n", indent,
1685			(unsigned)le32_to_cpu(idx->index_length),
1686			(unsigned)le32_to_cpu(idx->index_length));
1687	printf("%sAllocated Size:\t\t %u (0x%x)\n", indent,
1688			(unsigned)le32_to_cpu(idx->allocated_size),
1689			(unsigned)le32_to_cpu(idx->allocated_size));
1690	printf("%sIndex header flags:\t 0x%02x\n", indent, idx->flags);
1691
1692	/* FIXME: there are 3 reserved bytes here */
1693}
1694
1695/**
1696 * ntfs_dump_attr_index_root()
1697 *
1698 * dump the index_root attribute
1699 */
1700static void ntfs_dump_attr_index_root(ATTR_RECORD *attr, ntfs_inode *ni)
1701{
1702	INDEX_ATTR_TYPE type;
1703	INDEX_ROOT *index_root = NULL;
1704	INDEX_ENTRY *entry;
1705
1706	index_root = (INDEX_ROOT*)((u8*)attr + le16_to_cpu(attr->u.res.value_offset));
1707
1708	/* attr_type dumping */
1709	type = get_index_attr_type(ni, attr, index_root);
1710	printf("\tIndexed Attr Type:\t ");
1711	ntfs_dump_index_attr_type(type);
1712
1713	/* collation rule dumping */
1714	printf("\tCollation Rule:\t\t %u (0x%x)\n",
1715			(unsigned)le32_to_cpu(index_root->collation_rule),
1716			(unsigned)le32_to_cpu(index_root->collation_rule));
1717/*	COLLATION_BINARY, COLLATION_FILE_NAME, COLLATION_UNICODE_STRING,
1718	COLLATION_NTOFS_ULONG, COLLATION_NTOFS_SID,
1719	COLLATION_NTOFS_SECURITY_HASH, COLLATION_NTOFS_ULONGS */
1720
1721	printf("\tIndex Block Size:\t %u (0x%x)\n",
1722			(unsigned)le32_to_cpu(index_root->index_block_size),
1723			(unsigned)le32_to_cpu(index_root->index_block_size));
1724	printf("\tClusters Per Block:\t %u (0x%x)\n",
1725			(unsigned)index_root->clusters_per_index_block,
1726			(unsigned)index_root->clusters_per_index_block);
1727
1728	ntfs_dump_index_header("\t", &index_root->index);
1729
1730	entry = (INDEX_ENTRY*)((u8*)index_root +
1731			le32_to_cpu(index_root->index.entries_offset) + 0x10);
1732	ntfs_log_verbose("\tDumping index root:\n");
1733	printf("\tIndex entries total:\t %d\n",
1734			ntfs_dump_index_entries(entry, type));
1735}
1736
1737static void ntfs_dump_usa_lsn(const char *indent, MFT_RECORD *mrec)
1738{
1739	printf("%sUpd. Seq. Array Off.:\t %u (0x%x)\n", indent,
1740			(unsigned)le16_to_cpu(mrec->usa_ofs),
1741			(unsigned)le16_to_cpu(mrec->usa_ofs));
1742	printf("%sUpd. Seq. Array Count:\t %u (0x%x)\n", indent,
1743			(unsigned)le16_to_cpu(mrec->usa_count),
1744			(unsigned)le16_to_cpu(mrec->usa_count));
1745	printf("%sUpd. Seq. Number:\t %u (0x%x)\n", indent,
1746			(unsigned)le16_to_cpup((u16 *)((u8 *)mrec +
1747			le16_to_cpu(mrec->usa_ofs))),
1748			(unsigned)le16_to_cpup((u16 *)((u8 *)mrec +
1749			le16_to_cpu(mrec->usa_ofs))));
1750	printf("%sLogFile Seq. Number:\t 0x%llx\n", indent,
1751			(unsigned long long)sle64_to_cpu(mrec->lsn));
1752}
1753
1754
1755static s32 ntfs_dump_index_block(INDEX_BLOCK *ib, INDEX_ATTR_TYPE type,
1756		u32 ib_size)
1757{
1758	INDEX_ENTRY *entry;
1759
1760	if (ntfs_mst_post_read_fixup((NTFS_RECORD*)ib, ib_size)) {
1761		ntfs_log_perror("Damaged INDX record");
1762		return -1;
1763	}
1764	ntfs_log_verbose("\tDumping index block:\n");
1765	if (opts.verbose)
1766		ntfs_dump_usa_lsn("\t\t", (MFT_RECORD*)ib);
1767
1768	ntfs_log_verbose("\t\tNode VCN:\t\t %lld (0x%llx)\n",
1769			(unsigned long long)sle64_to_cpu(ib->index_block_vcn),
1770			(unsigned long long)sle64_to_cpu(ib->index_block_vcn));
1771
1772	entry = (INDEX_ENTRY*)((u8*)ib +
1773				le32_to_cpu(ib->index.entries_offset) + 0x18);
1774
1775	if (opts.verbose) {
1776		ntfs_dump_index_header("\t\t", &ib->index);
1777		printf("\n");
1778	}
1779
1780	return ntfs_dump_index_entries(entry, type);
1781}
1782
1783/**
1784 * ntfs_dump_attr_index_allocation()
1785 *
1786 * dump context of the index_allocation attribute
1787 */
1788static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni)
1789{
1790	INDEX_ALLOCATION *allocation, *tmp_alloc;
1791	INDEX_ROOT *ir;
1792	INDEX_ATTR_TYPE type;
1793	int total_entries = 0;
1794	int total_indx_blocks = 0;
1795	u8 *bitmap, *byte;
1796	int bit;
1797	ntfschar *name;
1798	u32 name_len;
1799	s64 data_size;
1800
1801	ir = ntfs_index_root_get(ni, attr);
1802	if (!ir) {
1803		ntfs_log_perror("Failed to read $INDEX_ROOT attribute");
1804		return;
1805	}
1806
1807	type = get_index_attr_type(ni, attr, ir);
1808
1809	name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset));
1810	name_len = attr->name_length;
1811
1812	byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL);
1813	if (!byte) {
1814		ntfs_log_perror("Failed to read $BITMAP attribute");
1815		goto out_index_root;
1816	}
1817
1818	tmp_alloc = allocation = ntfs_attr_readall(ni, AT_INDEX_ALLOCATION,
1819						   name, name_len, &data_size);
1820	if (!tmp_alloc) {
1821		ntfs_log_perror("Failed to read $INDEX_ALLOCATION attribute");
1822		goto out_bitmap;
1823	}
1824
1825	bit = 0;
1826	while ((u8 *)tmp_alloc < (u8 *)allocation + data_size) {
1827		if (*byte & (1 << bit)) {
1828			int entries;
1829
1830			entries = ntfs_dump_index_block(tmp_alloc, type,
1831							le32_to_cpu(
1832							ir->index_block_size));
1833	       		if (entries != -1) {
1834				total_entries += entries;
1835				total_indx_blocks++;
1836				ntfs_log_verbose("\tIndex entries:\t\t %d\n",
1837						entries);
1838			}
1839		}
1840		tmp_alloc = (INDEX_ALLOCATION *)((u8 *)tmp_alloc +
1841						le32_to_cpu(
1842						ir->index_block_size));
1843		bit++;
1844		if (bit > 7) {
1845			bit = 0;
1846			byte++;
1847		}
1848	}
1849
1850	printf("\tIndex entries total:\t %d\n", total_entries);
1851	printf("\tINDX blocks total:\t %d\n", total_indx_blocks);
1852
1853	free(allocation);
1854out_bitmap:
1855	free(bitmap);
1856out_index_root:
1857	free(ir);
1858}
1859
1860/**
1861 * ntfs_dump_attr_bitmap()
1862 *
1863 * dump the bitmap attribute
1864 */
1865static void ntfs_dump_attr_bitmap(ATTR_RECORD *attr __attribute__((unused)))
1866{
1867	/* TODO */
1868}
1869
1870/**
1871 * ntfs_dump_attr_reparse_point()
1872 *
1873 * of ntfs 3.x dumps the reparse_point attribute
1874 */
1875static void ntfs_dump_attr_reparse_point(ATTR_RECORD *attr __attribute__((unused)))
1876{
1877	/* TODO */
1878}
1879
1880/**
1881 * ntfs_dump_attr_ea_information()
1882 *
1883 * dump the ea_information attribute
1884 */
1885static void ntfs_dump_attr_ea_information(ATTR_RECORD *attr)
1886{
1887	EA_INFORMATION *ea_info;
1888
1889	ea_info = (EA_INFORMATION*)((u8*)attr +
1890			le16_to_cpu(attr->u.res.value_offset));
1891	printf("\tPacked EA length:\t %u (0x%x)\n",
1892			(unsigned)le16_to_cpu(ea_info->ea_length),
1893			(unsigned)le16_to_cpu(ea_info->ea_length));
1894	printf("\tNEED_EA count:\t\t %u (0x%x)\n",
1895			(unsigned)le16_to_cpu(ea_info->need_ea_count),
1896			(unsigned)le16_to_cpu(ea_info->need_ea_count));
1897	printf("\tUnpacked EA length:\t %u (0x%x)\n",
1898			(unsigned)le32_to_cpu(ea_info->ea_query_length),
1899			(unsigned)le32_to_cpu(ea_info->ea_query_length));
1900}
1901
1902/**
1903 * ntfs_dump_attr_ea()
1904 *
1905 * dump the ea attribute
1906 */
1907static void ntfs_dump_attr_ea(ATTR_RECORD *attr, ntfs_volume *vol)
1908{
1909	EA_ATTR *ea;
1910	u8 *buf = NULL;
1911	s64 data_size;
1912
1913	if (attr->non_resident) {
1914		runlist *rl;
1915
1916		data_size = sle64_to_cpu(attr->u.nonres.data_size);
1917		if (!opts.verbose)
1918			return;
1919		/* FIXME: We don't handle fragmented mapping pairs case. */
1920		rl = ntfs_mapping_pairs_decompress(vol, attr, NULL);
1921		if (rl) {
1922			s64 bytes_read;
1923
1924			buf = ntfs_malloc(data_size);
1925			if (!buf) {
1926				free(rl);
1927				return;
1928			}
1929			bytes_read = ntfs_rl_pread(vol, rl, 0, data_size, buf);
1930			if (bytes_read != data_size) {
1931				ntfs_log_perror("ntfs_rl_pread failed");
1932				free(buf);
1933				free(rl);
1934				return;
1935			}
1936			free(rl);
1937			ea = (EA_ATTR*)buf;
1938		} else {
1939			ntfs_log_perror("ntfs_mapping_pairs_decompress failed");
1940			return;
1941		}
1942	} else {
1943		data_size = le32_to_cpu(attr->u.res.value_length);
1944		if (!opts.verbose)
1945			return;
1946		ea = (EA_ATTR*)((u8*)attr + le16_to_cpu(attr->u.res.value_offset));
1947	}
1948	while (1) {
1949		printf("\n\tEA flags:\t\t ");
1950		if (ea->flags) {
1951			if (ea->flags == NEED_EA)
1952				printf("NEED_EA\n");
1953			else
1954				printf("Unknown (0x%02x)\n",
1955						(unsigned)ea->flags);
1956		} else
1957			printf("NONE\n");
1958		printf("\tName length:\t %d (0x%x)\n",
1959				(unsigned)ea->name_length,
1960				(unsigned)ea->name_length);
1961		printf("\tValue length:\t %d (0x%x)\n",
1962				(unsigned)le16_to_cpu(ea->value_length),
1963				(unsigned)le16_to_cpu(ea->value_length));
1964		printf("\tName:\t\t '%s'\n", ea->name);
1965		printf("\tValue:\t\t ");
1966		if (ea->name_length == 11 &&
1967				!strncmp((const char*)"SETFILEBITS",
1968				(const char*)ea->name, 11))
1969			printf("0%o\n", (unsigned)le32_to_cpu(*(le32*)
1970					(ea->name + ea->name_length + 1)));
1971		else
1972			printf("'%s'\n", ea->name + ea->name_length + 1);
1973		if (ea->next_entry_offset)
1974			ea = (EA_ATTR*)((u8*)ea +
1975					le32_to_cpu(ea->next_entry_offset));
1976		else
1977			break;
1978		if ((u8*)ea - buf >= data_size)
1979			break;
1980	}
1981	free(buf);
1982}
1983
1984/**
1985 * ntfs_dump_attr_property_set()
1986 *
1987 * dump the property_set attribute
1988 */
1989static void ntfs_dump_attr_property_set(ATTR_RECORD *attr __attribute__((unused)))
1990{
1991	/* TODO */
1992}
1993
1994static void ntfs_hex_dump(void *buf,unsigned int length);
1995
1996/**
1997 * ntfs_dump_attr_logged_utility_stream()
1998 *
1999 * dump the property_set attribute
2000 */
2001static void ntfs_dump_attr_logged_utility_stream(ATTR_RECORD *attr,
2002		ntfs_inode *ni)
2003{
2004	char *buf;
2005	s64 size;
2006
2007	if (!opts.verbose)
2008		return;
2009	buf = ntfs_attr_readall(ni, AT_LOGGED_UTILITY_STREAM,
2010			ntfs_attr_get_name(attr), attr->name_length, &size);
2011	if (buf)
2012		ntfs_hex_dump(buf, size);
2013	free(buf);
2014	/* TODO */
2015}
2016
2017/**
2018 * ntfs_hex_dump
2019 */
2020static void ntfs_hex_dump(void *buf,unsigned int length)
2021{
2022	unsigned int i=0;
2023	while (i<length) {
2024		unsigned int j;
2025
2026		/* line start */
2027		printf("\t%04X  ",i);
2028
2029		/* hex content */
2030		for (j=i;(j<length) && (j<i+16);j++) {
2031			unsigned char c = *((char *)buf + j);
2032			printf("%02hhX ",c);
2033		}
2034
2035		/* realign */
2036		for (;j<i+16;j++) {
2037			printf("   ");
2038		}
2039
2040		/* char content */
2041		for (j=i;(j<length) && (j<i+16);j++) {
2042			unsigned char c = *((char *)buf + j);
2043			/* display unprintable chars as '.' */
2044			if ((c<32) || (c>126)) {
2045				c = '.';
2046			}
2047			printf("%c",c);
2048		}
2049
2050		/* end line */
2051		printf("\n");
2052		i=j;
2053	}
2054}
2055
2056/**
2057 * ntfs_dump_attr_unknown
2058 */
2059static void ntfs_dump_attr_unknown(ATTR_RECORD *attr)
2060{
2061	printf("=====  Please report this unknown attribute type to %s =====\n",
2062	       NTFS_DEV_LIST);
2063
2064	if (!attr->non_resident) {
2065		/* hex dump */
2066		printf("\tDumping some of the attribute data:\n");
2067		ntfs_hex_dump((u8*)attr + le16_to_cpu(attr->u.res.value_offset),
2068				(le32_to_cpu(attr->u.res.value_length) > 128) ?
2069				128 : le32_to_cpu(attr->u.res.value_length));
2070	}
2071}
2072
2073/**
2074 * ntfs_dump_inode_general_info
2075 */
2076static void ntfs_dump_inode_general_info(ntfs_inode *inode)
2077{
2078	MFT_RECORD *mrec = inode->mrec;
2079	le16 inode_flags  = mrec->flags;
2080
2081	printf("Dumping Inode %llu (0x%llx)\n",
2082			(long long)inode->mft_no,
2083			(unsigned long long)inode->mft_no);
2084
2085	ntfs_dump_usa_lsn("", mrec);
2086	printf("MFT Record Seq. Numb.:\t %u (0x%x)\n",
2087			(unsigned)le16_to_cpu(mrec->sequence_number),
2088			(unsigned)le16_to_cpu(mrec->sequence_number));
2089	printf("Number of Hard Links:\t %u (0x%x)\n",
2090			(unsigned)le16_to_cpu(mrec->link_count),
2091			(unsigned)le16_to_cpu(mrec->link_count));
2092	printf("Attribute Offset:\t %u (0x%x)\n",
2093			(unsigned)le16_to_cpu(mrec->attrs_offset),
2094			(unsigned)le16_to_cpu(mrec->attrs_offset));
2095
2096	printf("MFT Record Flags:\t ");
2097	if (inode_flags) {
2098		if (MFT_RECORD_IN_USE & inode_flags) {
2099			printf("IN_USE ");
2100			inode_flags &= ~MFT_RECORD_IN_USE;
2101		}
2102		if (MFT_RECORD_IS_DIRECTORY & inode_flags) {
2103			printf("DIRECTORY ");
2104			inode_flags &= ~MFT_RECORD_IS_DIRECTORY;
2105		}
2106		/* The meaning of IS_4 is illusive but not its existence. */
2107		if (MFT_RECORD_IS_4 & inode_flags) {
2108			printf("IS_4 ");
2109			inode_flags &= ~MFT_RECORD_IS_4;
2110		}
2111		if (MFT_RECORD_IS_VIEW_INDEX & inode_flags) {
2112			printf("VIEW_INDEX ");
2113			inode_flags &= ~MFT_RECORD_IS_VIEW_INDEX;
2114		}
2115		if (inode_flags)
2116			printf("UNKNOWN: 0x%04x", (unsigned)le16_to_cpu(
2117						inode_flags));
2118	} else {
2119		printf("none");
2120	}
2121	printf("\n");
2122
2123	printf("Bytes Used:\t\t %u (0x%x) bytes\n",
2124			(unsigned)le32_to_cpu(mrec->bytes_in_use),
2125			(unsigned)le32_to_cpu(mrec->bytes_in_use));
2126	printf("Bytes Allocated:\t %u (0x%x) bytes\n",
2127			(unsigned)le32_to_cpu(mrec->bytes_allocated),
2128			(unsigned)le32_to_cpu(mrec->bytes_allocated));
2129
2130	if (mrec->base_mft_record) {
2131		printf("Base MFT Record:\t %llu (0x%llx)\n",
2132				(unsigned long long)
2133				MREF_LE(mrec->base_mft_record),
2134				(unsigned long long)
2135				MREF_LE(mrec->base_mft_record));
2136	}
2137	printf("Next Attribute Instance: %u (0x%x)\n",
2138			(unsigned)le16_to_cpu(mrec->next_attr_instance),
2139			(unsigned)le16_to_cpu(mrec->next_attr_instance));
2140
2141	printf("MFT Padding:\t");
2142	ntfs_dump_bytes((u8 *)mrec, le16_to_cpu(mrec->usa_ofs) +
2143			2 * le16_to_cpu(mrec->usa_count),
2144			le16_to_cpu(mrec->attrs_offset));
2145	printf("\n");
2146}
2147
2148/**
2149 * ntfs_get_file_attributes
2150 */
2151static void ntfs_dump_file_attributes(ntfs_inode *inode)
2152{
2153	ntfs_attr_search_ctx *ctx = NULL;
2154
2155	/* then start enumerating attributes
2156	   see ntfs_attr_lookup documentation for detailed explanation */
2157	ctx = ntfs_attr_get_search_ctx(inode, NULL);
2158	while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) {
2159		if (ctx->attr->type == AT_END || ctx->attr->type == AT_UNUSED) {
2160			printf("Weird: %s attribute type was found, please "
2161					"report this.\n",
2162					get_attribute_type_name(
2163					ctx->attr->type));
2164			continue;
2165		}
2166
2167		ntfs_dump_attribute_header(ctx, inode->vol);
2168
2169		switch (ctx->attr->type) {
2170		case AT_STANDARD_INFORMATION:
2171			ntfs_dump_attr_standard_information(ctx->attr);
2172			break;
2173		case AT_ATTRIBUTE_LIST:
2174			ntfs_dump_attr_list(ctx->attr, inode->vol);
2175			break;
2176		case AT_FILE_NAME:
2177			ntfs_dump_attr_file_name(ctx->attr);
2178			break;
2179		case AT_OBJECT_ID:
2180			ntfs_dump_attr_object_id(ctx->attr, inode->vol);
2181			break;
2182		case AT_SECURITY_DESCRIPTOR:
2183			ntfs_dump_attr_security_descriptor(ctx->attr,
2184					inode->vol);
2185			break;
2186		case AT_VOLUME_NAME:
2187			ntfs_dump_attr_volume_name(ctx->attr);
2188			break;
2189		case AT_VOLUME_INFORMATION:
2190			ntfs_dump_attr_volume_information(ctx->attr);
2191			break;
2192		case AT_DATA:
2193			ntfs_dump_attr_data(ctx->attr, inode);
2194			break;
2195		case AT_INDEX_ROOT:
2196			ntfs_dump_attr_index_root(ctx->attr, inode);
2197			break;
2198		case AT_INDEX_ALLOCATION:
2199			ntfs_dump_attr_index_allocation(ctx->attr, inode);
2200			break;
2201		case AT_BITMAP:
2202			ntfs_dump_attr_bitmap(ctx->attr);
2203			break;
2204		case AT_REPARSE_POINT:
2205			ntfs_dump_attr_reparse_point(ctx->attr);
2206			break;
2207		case AT_EA_INFORMATION:
2208			ntfs_dump_attr_ea_information(ctx->attr);
2209			break;
2210		case AT_EA:
2211			ntfs_dump_attr_ea(ctx->attr, inode->vol);
2212			break;
2213		case AT_PROPERTY_SET:
2214			ntfs_dump_attr_property_set(ctx->attr);
2215			break;
2216		case AT_LOGGED_UTILITY_STREAM:
2217			ntfs_dump_attr_logged_utility_stream(ctx->attr, inode);
2218			break;
2219		default:
2220			ntfs_dump_attr_unknown(ctx->attr);
2221		}
2222	}
2223
2224	/* if we exited the loop before we're done - notify the user */
2225	if (errno != ENOENT) {
2226		ntfs_log_perror("ntfsinfo error: stopped before finished "
2227				"enumerating attributes");
2228	} else {
2229		printf("End of inode reached\n");
2230	}
2231
2232	/* close all data-structures we used */
2233	ntfs_attr_put_search_ctx(ctx);
2234	ntfs_inode_close(inode);
2235}
2236
2237/**
2238 * main() - Begin here
2239 *
2240 * Start from here.
2241 *
2242 * Return:  0  Success, the program worked
2243 *	    1  Error, something went wrong
2244 */
2245int main(int argc, char **argv)
2246{
2247	ntfs_volume *vol;
2248
2249	setlinebuf(stdout);
2250
2251	ntfs_log_set_handler(ntfs_log_handler_outerr);
2252
2253	if (!parse_options(argc, argv)) {
2254		printf("Failed to parse command line options\n");
2255		exit(1);
2256	}
2257
2258	utils_set_locale();
2259
2260	vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY |
2261			(opts.force ? NTFS_MNT_FORCE : 0));
2262	if (!vol) {
2263		printf("Failed to open '%s'.\n", opts.device);
2264		exit(1);
2265	}
2266
2267	/*
2268	 * if opts.mft is not 0, then we will print out information about
2269	 * the volume, such as the sector size and whatnot.
2270	 */
2271	if (opts.mft)
2272		ntfs_dump_volume(vol);
2273
2274	if ((opts.inode != -1) || opts.filename) {
2275		ntfs_inode *inode;
2276		/* obtain the inode */
2277		if (opts.filename) {
2278			inode = ntfs_pathname_to_inode(vol, NULL,
2279					opts.filename);
2280		} else {
2281			inode = ntfs_inode_open(vol, MK_MREF(opts.inode, 0));
2282		}
2283
2284		/* dump the inode information */
2285		if (inode) {
2286			/* general info about the inode's mft record */
2287			ntfs_dump_inode_general_info(inode);
2288			/* dump attributes */
2289			ntfs_dump_file_attributes(inode);
2290		} else {
2291			/* can't open inode */
2292			/*
2293			 * note: when the specified inode does not exist, either
2294			 * EIO or or ESPIPE is returned, we should notify better
2295			 * in those cases
2296			 */
2297			ntfs_log_perror("Error loading node");
2298		}
2299	}
2300
2301	ntfs_umount(vol, FALSE);
2302	return 0;
2303}
2304
2305