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