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