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