| /** |
| * ntfsinfo - Part of the Linux-NTFS project. |
| * |
| * Copyright (c) 2002-2004 Matthew J. Fanto |
| * Copyright (c) 2002-2006 Anton Altaparmakov |
| * Copyright (c) 2002-2005 Richard Russon |
| * Copyright (c) 2003-2006 Szabolcs Szakacsits |
| * Copyright (c) 2004-2005 Yuval Fledel |
| * Copyright (c) 2004-2007 Yura Pakhuchiy |
| * Copyright (c) 2005 Cristian Klein |
| * Copyright (c) 2011-2015 Jean-Pierre Andre |
| * |
| * This utility will dump a file's attributes. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program (in the main directory of the Linux-NTFS |
| * distribution in the file COPYING); if not, write to the Free Software |
| * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| /* |
| * TODO LIST: |
| * - Better error checking. (focus on ntfs_dump_volume) |
| * - Comment things better. |
| * - More things at verbose mode. |
| * - Dump ACLs when security_id exists (NTFS 3+ only). |
| * - Clean ups. |
| * - Internationalization. |
| * - Add more Indexed Attr Types. |
| * - Make formatting look more like www.flatcap.org/ntfs/info |
| * |
| * Still not dumping certain attributes. Need to find the best |
| * way to output some of these attributes. |
| * |
| * Still need to do: |
| * $REPARSE_POINT/$SYMBOLIC_LINK |
| * $LOGGED_UTILITY_STREAM |
| */ |
| |
| #include "config.h" |
| |
| #ifdef HAVE_STDIO_H |
| #include <stdio.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #ifdef HAVE_STRING_H |
| #include <string.h> |
| #endif |
| #ifdef HAVE_TIME_H |
| #include <time.h> |
| #endif |
| #ifdef HAVE_GETOPT_H |
| #include <getopt.h> |
| #endif |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| |
| #include "types.h" |
| #include "mft.h" |
| #include "attrib.h" |
| #include "layout.h" |
| #include "inode.h" |
| #include "index.h" |
| #include "utils.h" |
| #include "security.h" |
| #include "mst.h" |
| #include "dir.h" |
| #include "ntfstime.h" |
| /* #include "version.h" */ |
| #include "support.h" |
| #include "misc.h" |
| |
| static const char *EXEC_NAME = "ntfsinfo"; |
| |
| static struct options { |
| const char *device; /* Device/File to work with */ |
| const char *filename; /* Resolve this filename to mft number */ |
| s64 inode; /* Info for this inode */ |
| int quiet; /* Less output */ |
| int verbose; /* Extra output */ |
| int force; /* Override common sense */ |
| int notime; /* Don't report timestamps at all */ |
| int mft; /* Dump information about the volume as well */ |
| } opts; |
| |
| struct RUNCOUNT { |
| unsigned long runs; |
| unsigned long fragments; |
| } ; |
| |
| /** |
| * version - Print version information about the program |
| * |
| * Print a copyright statement and a brief description of the program. |
| * |
| * Return: none |
| */ |
| static void version(void) |
| { |
| printf("\n%s v%s (libntfs-3g) - Display information about an NTFS " |
| "Volume.\n\n", EXEC_NAME, VERSION); |
| printf("Copyright (c)\n"); |
| printf(" 2002-2004 Matthew J. Fanto\n"); |
| printf(" 2002-2006 Anton Altaparmakov\n"); |
| printf(" 2002-2005 Richard Russon\n"); |
| printf(" 2003-2006 Szabolcs Szakacsits\n"); |
| printf(" 2003 Leonard NorrgÄrd\n"); |
| printf(" 2004-2005 Yuval Fledel\n"); |
| printf(" 2004-2007 Yura Pakhuchiy\n"); |
| printf(" 2011-2014 Jean-Pierre Andre\n"); |
| printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); |
| } |
| |
| /** |
| * usage - Print a list of the parameters to the program |
| * |
| * Print a list of the parameters and options for the program. |
| * |
| * Return: none |
| */ |
| static void usage(void) |
| { |
| printf("\nUsage: %s [options] device\n" |
| " -i, --inode NUM Display information about this inode\n" |
| " -F, --file FILE Display information about this file (absolute path)\n" |
| " -m, --mft Dump information about the volume\n" |
| " -t, --notime Don't report timestamps\n" |
| "\n" |
| " -f, --force Use less caution\n" |
| " -q, --quiet Less output\n" |
| " -v, --verbose More output\n" |
| " -V, --version Display version information\n" |
| " -h, --help Display this help\n" |
| "\n", |
| EXEC_NAME); |
| printf("%s%s\n", ntfs_bugs, ntfs_home); |
| } |
| |
| /** |
| * parse_options - Read and validate the programs command line |
| * |
| * Read the command line, verify the syntax and parse the options. |
| * This function is very long, but quite simple. |
| * |
| * Return: 1 Success |
| * 0 Error, one or more problems |
| */ |
| static int parse_options(int argc, char *argv[]) |
| { |
| static const char *sopt = "-:dfhi:F:mqtTvV"; |
| static const struct option lopt[] = { |
| { "force", no_argument, NULL, 'f' }, |
| { "help", no_argument, NULL, 'h' }, |
| { "inode", required_argument, NULL, 'i' }, |
| { "file", required_argument, NULL, 'F' }, |
| { "quiet", no_argument, NULL, 'q' }, |
| { "verbose", no_argument, NULL, 'v' }, |
| { "version", no_argument, NULL, 'V' }, |
| { "notime", no_argument, NULL, 'T' }, |
| { "mft", no_argument, NULL, 'm' }, |
| { NULL, 0, NULL, 0 } |
| }; |
| |
| int c = -1; |
| int err = 0; |
| int ver = 0; |
| int help = 0; |
| int levels = 0; |
| |
| opterr = 0; /* We'll handle the errors, thank you. */ |
| |
| opts.inode = -1; |
| opts.filename = NULL; |
| |
| while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { |
| switch (c) { |
| case 1: |
| if (!opts.device) |
| opts.device = optarg; |
| else |
| err++; |
| break; |
| case 'i': |
| if ((opts.inode != -1) || |
| (!utils_parse_size(optarg, &opts.inode, FALSE))) { |
| err++; |
| } |
| break; |
| case 'F': |
| if (opts.filename == NULL) { |
| /* The inode can not be resolved here, |
| store the filename */ |
| opts.filename = argv[optind-1]; |
| } else { |
| /* "-F" can't appear more than once */ |
| err++; |
| } |
| break; |
| case 'f': |
| opts.force++; |
| break; |
| case 'h': |
| help++; |
| break; |
| case 'q': |
| opts.quiet++; |
| ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); |
| break; |
| case 't': |
| opts.notime++; |
| break; |
| case 'T': |
| /* 'T' is deprecated, notify */ |
| ntfs_log_error("Option 'T' is deprecated, it was " |
| "replaced by 't'.\n"); |
| err++; |
| break; |
| case 'v': |
| opts.verbose++; |
| ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); |
| break; |
| case 'V': |
| ver++; |
| break; |
| case 'm': |
| opts.mft++; |
| break; |
| case '?': |
| if (optopt=='?') { |
| help++; |
| continue; |
| } |
| if (ntfs_log_parse_option(argv[optind-1])) |
| continue; |
| ntfs_log_error("Unknown option '%s'.\n", |
| argv[optind-1]); |
| err++; |
| break; |
| case ':': |
| ntfs_log_error("Option '%s' requires an " |
| "argument.\n", argv[optind-1]); |
| err++; |
| break; |
| default: |
| ntfs_log_error("Unhandled option case: %d.\n", c); |
| err++; |
| break; |
| } |
| } |
| |
| /* Make sure we're in sync with the log levels */ |
| levels = ntfs_log_get_levels(); |
| if (levels & NTFS_LOG_LEVEL_VERBOSE) |
| opts.verbose++; |
| if (!(levels & NTFS_LOG_LEVEL_QUIET)) |
| opts.quiet++; |
| |
| if (help || ver) { |
| opts.quiet = 0; |
| } else { |
| if (opts.device == NULL) { |
| if (argc > 1) |
| ntfs_log_error("You must specify exactly one " |
| "device.\n"); |
| err++; |
| } |
| |
| if (opts.inode == -1 && !opts.filename && !opts.mft) { |
| if (argc > 1) |
| ntfs_log_error("You must specify an inode to " |
| "learn about.\n"); |
| err++; |
| } |
| |
| if (opts.quiet && opts.verbose) { |
| ntfs_log_error("You may not use --quiet and --verbose " |
| "at the same time.\n"); |
| err++; |
| } |
| |
| if ((opts.inode != -1) && (opts.filename != NULL)) { |
| if (argc > 1) |
| ntfs_log_error("You may not specify --inode " |
| "and --file together.\n"); |
| err++; |
| } |
| |
| } |
| |
| if (ver) |
| version(); |
| if (help || err) |
| usage(); |
| |
| /* tri-state 0 : done, 1 : error, -1 : proceed */ |
| return (err ? 1 : (help || ver ? 0 : -1)); |
| } |
| |
| |
| /* *************** utility functions ******************** */ |
| /** |
| * ntfsinfo_time_to_str() - |
| * @sle_ntfs_clock: on disk time format in 100ns units since 1st jan 1601 |
| * in little-endian format |
| * |
| * Return char* in a format 'Thu Jan 1 00:00:00 1970'. |
| * No need to free the returned memory. |
| * |
| * Example of usage: |
| * char *time_str = ntfsinfo_time_to_str( |
| * sle64_to_cpu(standard_attr->creation_time)); |
| * printf("\tFile Creation Time:\t %s", time_str); |
| */ |
| static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock) |
| { |
| /* JPA display timestamps in UTC */ |
| static const char *months[] |
| = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" } ; |
| static const char *wdays[] |
| = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ; |
| static char str[30]; |
| long long stamp; |
| u32 days; |
| u32 seconds; |
| unsigned int year; |
| unsigned int wday; |
| int mon; |
| int cnt; |
| |
| stamp = sle64_to_cpu(sle_ntfs_clock); |
| days = (stamp/(86400*10000000LL)) & 0x7ffff; |
| seconds = ((stamp/10000000LL)%86400) & 0x1ffff; |
| wday = (days + 1)%7; |
| year = 1601; |
| /* periods of 400 years */ |
| cnt = days/146097; |
| days -= 146097*cnt; |
| year += 400*cnt; |
| /* periods of 100 years */ |
| cnt = (3*days + 3)/109573; |
| days -= 36524*cnt; |
| year += 100*cnt; |
| /* periods of 4 years */ |
| cnt = days/1461; |
| days -= 1461*cnt; |
| year += 4*cnt; |
| /* periods of a single year */ |
| cnt = (3*days + 3)/1096; |
| days -= 365*cnt; |
| year += cnt; |
| |
| if ((!(year % 100) ? (year % 400) : (year % 4)) |
| && (days > 58)) days++; |
| if (days > 59) { |
| mon = (5*days + 161)/153; |
| days -= (153*mon - 162)/5; |
| } else { |
| mon = days/31 + 1; |
| days -= 31*(mon - 1) - 1; |
| } |
| sprintf(str,"%3s %3s %2u %02u:%02u:%02u %4u UTC\n", |
| wdays[wday], |
| months[mon-1],(unsigned int)days, |
| (unsigned int)(seconds/3600), |
| (unsigned int)(seconds/60%60), |
| (unsigned int)(seconds%60), |
| (unsigned int)year); |
| return (str); |
| } |
| |
| /** |
| * ntfs_attr_get_name() |
| * @attr: a valid attribute record |
| * |
| * return multi-byte string containing the attribute name if exist. the user |
| * is then responsible of freeing that memory. |
| * null if no name exists (attr->name_length==0). no memory allocated. |
| * null if cannot convert to multi-byte string. errno would contain the |
| * error id. no memory allocated in that case |
| */ |
| static char *ntfs_attr_get_name_mbs(ATTR_RECORD *attr) |
| { |
| ntfschar *ucs_attr_name; |
| char *mbs_attr_name = NULL; |
| int mbs_attr_name_size; |
| |
| /* Get name in unicode. */ |
| ucs_attr_name = ntfs_attr_get_name(attr); |
| /* Convert unicode to printable format. */ |
| mbs_attr_name_size = ntfs_ucstombs(ucs_attr_name, attr->name_length, |
| &mbs_attr_name, 0); |
| if (mbs_attr_name_size > 0) |
| return mbs_attr_name; |
| else |
| return NULL; |
| } |
| |
| static const char *reparse_type_name(le32 tag) |
| { |
| const char *name; |
| |
| if (tag == IO_REPARSE_TAG_MOUNT_POINT) |
| name = " (mount point)"; |
| else |
| if (tag == IO_REPARSE_TAG_SYMLINK) |
| name = " (symlink)"; |
| else |
| if (tag == IO_REPARSE_TAG_WOF) |
| name = " (Wof compressed)"; |
| else |
| name = ""; |
| return (name); |
| } |
| |
| /* *************** functions for dumping global info ******************** */ |
| /** |
| * ntfs_dump_volume - dump information about the volume |
| */ |
| static void ntfs_dump_volume(ntfs_volume *vol) |
| { |
| printf("Volume Information \n"); |
| printf("\tName of device: %s\n", vol->dev->d_name); |
| printf("\tDevice state: %lu\n", vol->dev->d_state); |
| printf("\tVolume Name: %s\n", vol->vol_name); |
| printf("\tVolume State: %lu\n", vol->state); |
| printf("\tVolume Flags: 0x%04x", (int)le16_to_cpu(vol->flags)); |
| if (vol->flags & VOLUME_IS_DIRTY) |
| printf(" DIRTY"); |
| if (vol->flags & VOLUME_MODIFIED_BY_CHKDSK) |
| printf(" MODIFIED_BY_CHKDSK"); |
| printf("\n"); |
| printf("\tVolume Version: %u.%u\n", vol->major_ver, vol->minor_ver); |
| printf("\tSector Size: %hu\n", vol->sector_size); |
| printf("\tCluster Size: %u\n", (unsigned int)vol->cluster_size); |
| printf("\tIndex Block Size: %u\n", (unsigned int)vol->indx_record_size); |
| printf("\tVolume Size in Clusters: %lld\n", |
| (long long)vol->nr_clusters); |
| |
| printf("MFT Information \n"); |
| printf("\tMFT Record Size: %u\n", (unsigned int)vol->mft_record_size); |
| printf("\tMFT Zone Multiplier: %u\n", vol->mft_zone_multiplier); |
| printf("\tMFT Data Position: %lld\n", (long long)vol->mft_data_pos); |
| printf("\tMFT Zone Start: %lld\n", (long long)vol->mft_zone_start); |
| printf("\tMFT Zone End: %lld\n", (long long)vol->mft_zone_end); |
| printf("\tMFT Zone Position: %lld\n", (long long)vol->mft_zone_pos); |
| printf("\tCurrent Position in First Data Zone: %lld\n", |
| (long long)vol->data1_zone_pos); |
| printf("\tCurrent Position in Second Data Zone: %lld\n", |
| (long long)vol->data2_zone_pos); |
| printf("\tAllocated clusters %lld (%2.1lf%%)\n", |
| (long long)vol->mft_na->allocated_size |
| >> vol->cluster_size_bits, |
| 100.0*(vol->mft_na->allocated_size |
| >> vol->cluster_size_bits) |
| / vol->nr_clusters); |
| printf("\tLCN of Data Attribute for FILE_MFT: %lld\n", |
| (long long)vol->mft_lcn); |
| printf("\tFILE_MFTMirr Size: %d\n", vol->mftmirr_size); |
| printf("\tLCN of Data Attribute for File_MFTMirr: %lld\n", |
| (long long)vol->mftmirr_lcn); |
| printf("\tSize of Attribute Definition Table: %d\n", |
| (int)vol->attrdef_len); |
| printf("\tNumber of Attached Extent Inodes: %d\n", |
| (int)vol->mft_ni->nr_extents); |
| |
| printf("FILE_Bitmap Information \n"); |
| printf("\tFILE_Bitmap MFT Record Number: %llu\n", |
| (unsigned long long)vol->lcnbmp_ni->mft_no); |
| printf("\tState of FILE_Bitmap Inode: %lu\n", vol->lcnbmp_ni->state); |
| printf("\tLength of Attribute List: %u\n", |
| (unsigned int)vol->lcnbmp_ni->attr_list_size); |
| /* JPA printf("\tAttribute List: %s\n", vol->lcnbmp_ni->attr_list); */ |
| printf("\tNumber of Attached Extent Inodes: %d\n", |
| (int)vol->lcnbmp_ni->nr_extents); |
| /* FIXME: need to add code for the union if nr_extens != 0, but |
| i dont know if it will ever != 0 with FILE_Bitmap */ |
| |
| printf("FILE_Bitmap Data Attribute Information\n"); |
| printf("\tDecompressed Runlist: not done yet\n"); |
| printf("\tBase Inode: %llu\n", |
| (unsigned long long)vol->lcnbmp_na->ni->mft_no); |
| printf("\tAttribute Types: not done yet\n"); |
| //printf("\tAttribute Name: %s\n", vol->lcnbmp_na->name); |
| printf("\tAttribute Name Length: %u\n", |
| (unsigned int)vol->lcnbmp_na->name_len); |
| printf("\tAttribute State: %lu\n", vol->lcnbmp_na->state); |
| printf("\tAttribute Allocated Size: %lld\n", |
| (long long)vol->lcnbmp_na->allocated_size); |
| printf("\tAttribute Data Size: %lld\n", |
| (long long)vol->lcnbmp_na->data_size); |
| printf("\tAttribute Initialized Size: %lld\n", |
| (long long)vol->lcnbmp_na->initialized_size); |
| printf("\tAttribute Compressed Size: %lld\n", |
| (long long)vol->lcnbmp_na->compressed_size); |
| printf("\tCompression Block Size: %u\n", |
| (unsigned int)vol->lcnbmp_na->compression_block_size); |
| printf("\tCompression Block Size Bits: %u\n", |
| vol->lcnbmp_na->compression_block_size_bits); |
| printf("\tCompression Block Clusters: %u\n", |
| vol->lcnbmp_na->compression_block_clusters); |
| if (!ntfs_volume_get_free_space(vol)) |
| printf("\tFree Clusters: %lld (%2.1lf%%)\n", |
| (long long)vol->free_clusters, |
| 100.0*vol->free_clusters |
| /(double)vol->nr_clusters); |
| |
| //TODO: Still need to add a few more attributes |
| } |
| |
| /** |
| * ntfs_dump_flags - Dump flags for STANDARD_INFORMATION and FILE_NAME. |
| * @type: dump flags for this attribute type |
| * @flags: flags for dumping |
| */ |
| static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, le32 flags) |
| { |
| const le32 original_flags = flags; |
| |
| printf("%sFile attributes:\t", indent); |
| if (flags & FILE_ATTR_READONLY) { |
| printf(" READONLY"); |
| flags &= ~FILE_ATTR_READONLY; |
| } |
| if (flags & FILE_ATTR_HIDDEN) { |
| printf(" HIDDEN"); |
| flags &= ~FILE_ATTR_HIDDEN; |
| } |
| if (flags & FILE_ATTR_SYSTEM) { |
| printf(" SYSTEM"); |
| flags &= ~FILE_ATTR_SYSTEM; |
| } |
| if (flags & FILE_ATTR_DIRECTORY) { |
| printf(" DIRECTORY"); |
| flags &= ~FILE_ATTR_DIRECTORY; |
| } |
| if (flags & FILE_ATTR_ARCHIVE) { |
| printf(" ARCHIVE"); |
| flags &= ~FILE_ATTR_ARCHIVE; |
| } |
| if (flags & FILE_ATTR_DEVICE) { |
| printf(" DEVICE"); |
| flags &= ~FILE_ATTR_DEVICE; |
| } |
| if (flags & FILE_ATTR_NORMAL) { |
| printf(" NORMAL"); |
| flags &= ~FILE_ATTR_NORMAL; |
| } |
| if (flags & FILE_ATTR_TEMPORARY) { |
| printf(" TEMPORARY"); |
| flags &= ~FILE_ATTR_TEMPORARY; |
| } |
| if (flags & FILE_ATTR_SPARSE_FILE) { |
| printf(" SPARSE_FILE"); |
| flags &= ~FILE_ATTR_SPARSE_FILE; |
| } |
| if (flags & FILE_ATTR_REPARSE_POINT) { |
| printf(" REPARSE_POINT"); |
| flags &= ~FILE_ATTR_REPARSE_POINT; |
| } |
| if (flags & FILE_ATTR_COMPRESSED) { |
| printf(" COMPRESSED"); |
| flags &= ~FILE_ATTR_COMPRESSED; |
| } |
| if (flags & FILE_ATTR_OFFLINE) { |
| printf(" OFFLINE"); |
| flags &= ~FILE_ATTR_OFFLINE; |
| } |
| if (flags & FILE_ATTR_NOT_CONTENT_INDEXED) { |
| printf(" NOT_CONTENT_INDEXED"); |
| flags &= ~FILE_ATTR_NOT_CONTENT_INDEXED; |
| } |
| if (flags & FILE_ATTR_ENCRYPTED) { |
| printf(" ENCRYPTED"); |
| flags &= ~FILE_ATTR_ENCRYPTED; |
| } |
| /* We know that FILE_ATTR_I30_INDEX_PRESENT only exists on $FILE_NAME, |
| and in case we are wrong, let it appear as UNKNOWN */ |
| if (type == AT_FILE_NAME) { |
| if (flags & FILE_ATTR_I30_INDEX_PRESENT) { |
| printf(" I30_INDEX"); |
| flags &= ~FILE_ATTR_I30_INDEX_PRESENT; |
| } |
| } |
| if (flags & FILE_ATTR_VIEW_INDEX_PRESENT) { |
| printf(" VIEW_INDEX"); |
| flags &= ~FILE_ATTR_VIEW_INDEX_PRESENT; |
| } |
| if (flags) |
| printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags)); |
| /* Print all the flags in hex. */ |
| printf(" (0x%08x)\n", (unsigned)le32_to_cpu(original_flags)); |
| } |
| |
| /** |
| * ntfs_dump_namespace |
| */ |
| static void ntfs_dump_namespace(const char *indent, u8 file_name_type) |
| { |
| const char *mbs_file_type; |
| |
| /* name space */ |
| switch (file_name_type) { |
| case FILE_NAME_POSIX: |
| mbs_file_type = "POSIX"; |
| break; |
| case FILE_NAME_WIN32: |
| mbs_file_type = "Win32"; |
| break; |
| case FILE_NAME_DOS: |
| mbs_file_type = "DOS"; |
| break; |
| case FILE_NAME_WIN32_AND_DOS: |
| mbs_file_type = "Win32 & DOS"; |
| break; |
| default: |
| mbs_file_type = "(unknown)"; |
| } |
| printf("%sNamespace:\t\t %s\n", indent, mbs_file_type); |
| } |
| |
| /* *************** functions for dumping attributes ******************** */ |
| /** |
| * ntfs_dump_standard_information |
| */ |
| static void ntfs_dump_attr_standard_information(ATTR_RECORD *attr) |
| { |
| STANDARD_INFORMATION *standard_attr = NULL; |
| u32 value_length; |
| |
| standard_attr = (STANDARD_INFORMATION*)((char *)attr + |
| le16_to_cpu(attr->value_offset)); |
| |
| /* time conversion stuff */ |
| if (!opts.notime) { |
| char *ntfs_time_str = NULL; |
| |
| ntfs_time_str = ntfsinfo_time_to_str(standard_attr->creation_time); |
| printf("\tFile Creation Time:\t %s",ntfs_time_str); |
| |
| ntfs_time_str = ntfsinfo_time_to_str( |
| standard_attr->last_data_change_time); |
| printf("\tFile Altered Time:\t %s",ntfs_time_str); |
| |
| ntfs_time_str = ntfsinfo_time_to_str( |
| standard_attr->last_mft_change_time); |
| printf("\tMFT Changed Time:\t %s",ntfs_time_str); |
| |
| ntfs_time_str = ntfsinfo_time_to_str(standard_attr->last_access_time); |
| printf("\tLast Accessed Time:\t %s",ntfs_time_str); |
| } |
| ntfs_dump_flags("\t", attr->type, standard_attr->file_attributes); |
| |
| value_length = le32_to_cpu(attr->value_length); |
| if (value_length == 48) { |
| /* Only 12 reserved bytes here */ |
| } else if (value_length == 72) { |
| printf("\tMaximum versions:\t %u \n", (unsigned int) |
| le32_to_cpu(standard_attr->maximum_versions)); |
| printf("\tVersion number:\t\t %u \n", (unsigned int) |
| le32_to_cpu(standard_attr->version_number)); |
| printf("\tClass ID:\t\t %u \n", |
| (unsigned int)le32_to_cpu(standard_attr->class_id)); |
| printf("\tUser ID:\t\t %u (0x%x)\n", |
| (unsigned int)le32_to_cpu(standard_attr->owner_id), |
| (unsigned int)le32_to_cpu(standard_attr->owner_id)); |
| printf("\tSecurity ID:\t\t %u (0x%x)\n", |
| (unsigned int)le32_to_cpu(standard_attr->security_id), |
| (unsigned int)le32_to_cpu(standard_attr->security_id)); |
| printf("\tQuota charged:\t\t %llu (0x%llx)\n", |
| (unsigned long long) |
| le64_to_cpu(standard_attr->quota_charged), |
| (unsigned long long) |
| le64_to_cpu(standard_attr->quota_charged)); |
| printf("\tUpdate Sequence Number:\t %llu (0x%llx)\n", |
| (unsigned long long) |
| le64_to_cpu(standard_attr->usn), |
| (unsigned long long) |
| le64_to_cpu(standard_attr->usn)); |
| } else { |
| printf("\tSize of STANDARD_INFORMATION is %u (0x%x). It " |
| "should be either 72 or 48, something is " |
| "wrong...\n", (unsigned int)value_length, |
| (unsigned)value_length); |
| } |
| } |
| |
| static void ntfs_dump_bytes(u8 *buf, int start, int stop) |
| { |
| int i; |
| |
| for (i = start; i < stop; i++) { |
| printf("%02x ", buf[i]); |
| } |
| } |
| |
| /** |
| * ntfs_dump_attr_list() |
| */ |
| static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol) |
| { |
| ATTR_LIST_ENTRY *entry; |
| u8 *value; |
| s64 l; |
| |
| if (!opts.verbose) |
| return; |
| |
| l = ntfs_get_attribute_value_length(attr); |
| if (!l) { |
| ntfs_log_perror("ntfs_get_attribute_value_length failed"); |
| return; |
| } |
| value = ntfs_malloc(l); |
| if (!value) |
| return; |
| |
| l = ntfs_get_attribute_value(vol, attr, value); |
| if (!l) { |
| ntfs_log_perror("ntfs_get_attribute_value failed"); |
| free(value); |
| return; |
| } |
| printf("\tDumping attribute list:"); |
| entry = (ATTR_LIST_ENTRY *) value; |
| for (;(u8 *)entry < (u8 *) value + l; entry = (ATTR_LIST_ENTRY *) |
| ((u8 *) entry + le16_to_cpu(entry->length))) { |
| printf("\n"); |
| printf("\t\tAttribute type:\t0x%x\n", |
| (unsigned int)le32_to_cpu(entry->type)); |
| printf("\t\tRecord length:\t%u (0x%x)\n", |
| (unsigned)le16_to_cpu(entry->length), |
| (unsigned)le16_to_cpu(entry->length)); |
| printf("\t\tName length:\t%u (0x%x)\n", |
| (unsigned)entry->name_length, |
| (unsigned)entry->name_length); |
| printf("\t\tName offset:\t%u (0x%x)\n", |
| (unsigned)entry->name_offset, |
| (unsigned)entry->name_offset); |
| printf("\t\tStarting VCN:\t%lld (0x%llx)\n", |
| (long long)sle64_to_cpu(entry->lowest_vcn), |
| (unsigned long long) |
| sle64_to_cpu(entry->lowest_vcn)); |
| printf("\t\tMFT reference:\t%lld (0x%llx)\n", |
| (unsigned long long) |
| MREF_LE(entry->mft_reference), |
| (unsigned long long) |
| MREF_LE(entry->mft_reference)); |
| printf("\t\tInstance:\t%u (0x%x)\n", |
| (unsigned)le16_to_cpu(entry->instance), |
| (unsigned)le16_to_cpu(entry->instance)); |
| printf("\t\tName:\t\t"); |
| if (entry->name_length) { |
| char *name = NULL; |
| int name_size; |
| |
| name_size = ntfs_ucstombs(entry->name, |
| entry->name_length, &name, 0); |
| |
| if (name_size > 0) { |
| printf("%s\n", name); |
| free(name); |
| } else |
| ntfs_log_perror("ntfs_ucstombs failed"); |
| } else |
| printf("unnamed\n"); |
| printf("\t\tPadding:\t"); |
| ntfs_dump_bytes((u8 *)entry, entry->name_offset + |
| sizeof(ntfschar) * entry->name_length, |
| le16_to_cpu(entry->length)); |
| printf("\n"); |
| } |
| free(value); |
| printf("\tEnd of attribute list reached.\n"); |
| } |
| |
| /** |
| * ntfs_dump_filename() |
| */ |
| static void ntfs_dump_filename(const char *indent, |
| FILE_NAME_ATTR *file_name_attr) |
| { |
| le32 tag; |
| |
| printf("%sParent directory:\t %lld (0x%llx)\n", indent, |
| (long long)MREF_LE(file_name_attr->parent_directory), |
| (long long)MREF_LE(file_name_attr->parent_directory)); |
| /* time stuff */ |
| if (!opts.notime) { |
| char *ntfs_time_str; |
| |
| ntfs_time_str = ntfsinfo_time_to_str( |
| file_name_attr->creation_time); |
| printf("%sFile Creation Time:\t %s", indent, ntfs_time_str); |
| |
| ntfs_time_str = ntfsinfo_time_to_str( |
| file_name_attr->last_data_change_time); |
| printf("%sFile Altered Time:\t %s", indent, ntfs_time_str); |
| |
| ntfs_time_str = ntfsinfo_time_to_str( |
| file_name_attr->last_mft_change_time); |
| printf("%sMFT Changed Time:\t %s", indent, ntfs_time_str); |
| |
| ntfs_time_str = ntfsinfo_time_to_str( |
| file_name_attr->last_access_time); |
| printf("%sLast Accessed Time:\t %s", indent, ntfs_time_str); |
| } |
| /* other basic stuff about the file */ |
| printf("%sAllocated Size:\t\t %lld (0x%llx)\n", indent, (long long) |
| sle64_to_cpu(file_name_attr->allocated_size), |
| (unsigned long long) |
| sle64_to_cpu(file_name_attr->allocated_size)); |
| printf("%sData Size:\t\t %lld (0x%llx)\n", indent, |
| (long long)sle64_to_cpu(file_name_attr->data_size), |
| (unsigned long long) |
| sle64_to_cpu(file_name_attr->data_size)); |
| printf("%sFilename Length:\t %d (0x%x)\n", indent, |
| (unsigned)file_name_attr->file_name_length, |
| (unsigned)file_name_attr->file_name_length); |
| ntfs_dump_flags(indent, AT_FILE_NAME, file_name_attr->file_attributes); |
| if (file_name_attr->file_attributes & FILE_ATTR_REPARSE_POINT && |
| file_name_attr->reparse_point_tag) { |
| tag = file_name_attr->reparse_point_tag; |
| printf("%sReparse point tag:\t 0x%08lx%s\n", indent, |
| (long)le32_to_cpu(tag), |
| reparse_type_name(tag)); |
| } else if (file_name_attr->reparse_point_tag) { |
| printf("%sEA Length:\t\t %d (0x%x)\n", indent, (unsigned) |
| le16_to_cpu(file_name_attr->packed_ea_size), |
| (unsigned) |
| le16_to_cpu(file_name_attr->packed_ea_size)); |
| if (file_name_attr->reserved) |
| printf("%sReserved:\t\t %d (0x%x)\n", indent, |
| (unsigned) |
| le16_to_cpu(file_name_attr->reserved), |
| (unsigned) |
| le16_to_cpu(file_name_attr->reserved)); |
| } |
| /* The filename. */ |
| ntfs_dump_namespace(indent, file_name_attr->file_name_type); |
| if (file_name_attr->file_name_length > 0) { |
| /* but first we need to convert the little endian unicode string |
| into a printable format */ |
| char *mbs_file_name = NULL; |
| int mbs_file_name_size; |
| |
| mbs_file_name_size = ntfs_ucstombs(file_name_attr->file_name, |
| file_name_attr->file_name_length,&mbs_file_name,0); |
| |
| if (mbs_file_name_size>0) { |
| printf("%sFilename:\t\t '%s'\n", indent, mbs_file_name); |
| free(mbs_file_name); |
| } else { |
| /* an error occurred, errno holds the reason - notify the user */ |
| ntfs_log_perror("ntfsinfo error: could not parse file name"); |
| } |
| } else { |
| printf("%sFile Name:\t\t unnamed?!?\n", indent); |
| } |
| } |
| |
| /** |
| * ntfs_dump_attr_file_name() |
| */ |
| static void ntfs_dump_attr_file_name(ATTR_RECORD *attr) |
| { |
| ntfs_dump_filename("\t", (FILE_NAME_ATTR*)((u8*)attr + |
| le16_to_cpu(attr->value_offset))); |
| } |
| |
| /** |
| * ntfs_dump_object_id |
| * |
| * dump the $OBJECT_ID attribute - not present on all systems |
| */ |
| static void ntfs_dump_attr_object_id(ATTR_RECORD *attr,ntfs_volume *vol) |
| { |
| OBJECT_ID_ATTR *obj_id_attr = NULL; |
| |
| obj_id_attr = (OBJECT_ID_ATTR *)((u8*)attr + |
| le16_to_cpu(attr->value_offset)); |
| |
| if (vol->major_ver >= 3.0) { |
| u32 value_length; |
| char printable_GUID[37]; |
| |
| value_length = le32_to_cpu(attr->value_length); |
| |
| /* Object ID is mandatory. */ |
| ntfs_guid_to_mbs(&obj_id_attr->object_id, printable_GUID); |
| printf("\tObject ID:\t\t %s\n", printable_GUID); |
| |
| /* Dump Birth Volume ID. */ |
| if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( |
| &obj_id_attr->birth_volume_id)) { |
| ntfs_guid_to_mbs(&obj_id_attr->birth_volume_id, |
| printable_GUID); |
| printf("\tBirth Volume ID:\t\t %s\n", printable_GUID); |
| } else |
| printf("\tBirth Volume ID:\t missing\n"); |
| |
| /* Dumping Birth Object ID */ |
| if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( |
| &obj_id_attr->birth_object_id)) { |
| ntfs_guid_to_mbs(&obj_id_attr->birth_object_id, |
| printable_GUID); |
| printf("\tBirth Object ID:\t\t %s\n", printable_GUID); |
| } else |
| printf("\tBirth Object ID:\t missing\n"); |
| |
| /* Dumping Domain_id - reserved for now */ |
| if ((value_length > sizeof(GUID)) && !ntfs_guid_is_zero( |
| &obj_id_attr->domain_id)) { |
| ntfs_guid_to_mbs(&obj_id_attr->domain_id, |
| printable_GUID); |
| printf("\tDomain ID:\t\t\t %s\n", printable_GUID); |
| } else |
| printf("\tDomain ID:\t\t missing\n"); |
| } else |
| printf("\t$OBJECT_ID not present. Only NTFS versions > 3.0\n" |
| "\thave $OBJECT_ID. Your version of NTFS is %d.\n", |
| vol->major_ver); |
| } |
| |
| /** |
| * ntfs_dump_acl |
| * |
| * given an acl, print it in a beautiful & lovely way. |
| */ |
| static void ntfs_dump_acl(const char *prefix, ACL *acl) |
| { |
| unsigned int i; |
| u16 ace_count; |
| ACCESS_ALLOWED_ACE *ace; |
| |
| printf("%sRevision\t %u\n", prefix, acl->revision); |
| |
| /* |
| * Do not recalculate le16_to_cpu every iteration (minor speedup on |
| * big-endian machines. |
| */ |
| ace_count = le16_to_cpu(acl->ace_count); |
| |
| /* initialize 'ace' to the first ace (if any) */ |
| ace = (ACCESS_ALLOWED_ACE *)((char *)acl + 8); |
| |
| /* iterate through ACE's */ |
| for (i = 1; i <= ace_count; i++) { |
| const char *ace_type; |
| char *sid; |
| |
| /* set ace_type. */ |
| switch (ace->type) { |
| case ACCESS_ALLOWED_ACE_TYPE: |
| ace_type = "allow"; |
| break; |
| case ACCESS_DENIED_ACE_TYPE: |
| ace_type = "deny"; |
| break; |
| case SYSTEM_AUDIT_ACE_TYPE: |
| ace_type = "audit"; |
| break; |
| default: |
| ace_type = "unknown"; |
| break; |
| } |
| |
| printf("%sACE:\t\t type:%s flags:0x%x access:0x%x\n", prefix, |
| ace_type, (unsigned int)ace->flags, |
| (unsigned int)le32_to_cpu(ace->mask)); |
| /* get a SID string */ |
| sid = ntfs_sid_to_mbs(&ace->sid, NULL, 0); |
| printf("%s\t\t SID: %s\n", prefix, sid); |
| free(sid); |
| |
| /* proceed to next ACE */ |
| ace = (ACCESS_ALLOWED_ACE *)(((char *)ace) + |
| le16_to_cpu(ace->size)); |
| } |
| } |
| |
| |
| static void ntfs_dump_security_descriptor(SECURITY_DESCRIPTOR_ATTR *sec_desc, |
| const char *indent) |
| { |
| char *sid; |
| |
| printf("%s\tRevision:\t\t %u\n", indent, sec_desc->revision); |
| |
| /* TODO: parse the flags */ |
| printf("%s\tControl:\t\t 0x%04x\n", indent, |
| le16_to_cpu(sec_desc->control)); |
| |
| if (~sec_desc->control & SE_SELF_RELATIVE) { |
| SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)sec_desc; |
| |
| printf("%s\tOwner SID pointer:\t %p\n", indent, sd->owner); |
| printf("%s\tGroup SID pointer:\t %p\n", indent, sd->group); |
| printf("%s\tSACL pointer:\t\t %p\n", indent, sd->sacl); |
| printf("%s\tDACL pointer:\t\t %p\n", indent, sd->dacl); |
| |
| return; |
| } |
| |
| if (sec_desc->owner) { |
| sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + |
| le32_to_cpu(sec_desc->owner)), NULL, 0); |
| printf("%s\tOwner SID:\t\t %s\n", indent, sid); |
| free(sid); |
| } else |
| printf("%s\tOwner SID:\t\t missing\n", indent); |
| |
| if (sec_desc->group) { |
| sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + |
| le32_to_cpu(sec_desc->group)), NULL, 0); |
| printf("%s\tGroup SID:\t\t %s\n", indent, sid); |
| free(sid); |
| } else |
| printf("%s\tGroup SID:\t\t missing\n", indent); |
| |
| printf("%s\tSystem ACL:\t\t ", indent); |
| if (sec_desc->control & SE_SACL_PRESENT) { |
| if (sec_desc->control & SE_SACL_DEFAULTED) { |
| printf("defaulted"); |
| } |
| printf("\n"); |
| ntfs_dump_acl(indent ? "\t\t\t" : "\t\t", |
| (ACL *)((char *)sec_desc + |
| le32_to_cpu(sec_desc->sacl))); |
| } else { |
| printf("missing\n"); |
| } |
| |
| printf("%s\tDiscretionary ACL:\t ", indent); |
| if (sec_desc->control & SE_DACL_PRESENT) { |
| if (sec_desc->control & SE_SACL_DEFAULTED) { |
| printf("defaulted"); |
| } |
| printf("\n"); |
| ntfs_dump_acl(indent ? "\t\t\t" : "\t\t", |
| (ACL *)((char *)sec_desc + |
| le32_to_cpu(sec_desc->dacl))); |
| } else { |
| printf("missing\n"); |
| } |
| } |
| |
| /** |
| * ntfs_dump_security_descriptor() |
| * |
| * dump the security information about the file |
| */ |
| static void ntfs_dump_attr_security_descriptor(ATTR_RECORD *attr, ntfs_volume *vol) |
| { |
| SECURITY_DESCRIPTOR_ATTR *sec_desc_attr; |
| |
| if (attr->non_resident) { |
| /* FIXME: We don't handle fragmented mapping pairs case. */ |
| runlist *rl = ntfs_mapping_pairs_decompress(vol, attr, NULL); |
| if (rl) { |
| s64 data_size, bytes_read; |
| |
| data_size = sle64_to_cpu(attr->data_size); |
| sec_desc_attr = ntfs_malloc(data_size); |
| if (!sec_desc_attr) { |
| free(rl); |
| return; |
| } |
| bytes_read = ntfs_rl_pread(vol, rl, 0, |
| data_size, sec_desc_attr); |
| if (bytes_read != data_size) { |
| ntfs_log_error("ntfsinfo error: could not " |
| "read security descriptor\n"); |
| free(rl); |
| free(sec_desc_attr); |
| return; |
| } |
| free(rl); |
| } else { |
| ntfs_log_error("ntfsinfo error: could not " |
| "decompress runlist\n"); |
| return; |
| } |
| } else { |
| sec_desc_attr = (SECURITY_DESCRIPTOR_ATTR *)((u8*)attr + |
| le16_to_cpu(attr->value_offset)); |
| } |
| |
| ntfs_dump_security_descriptor(sec_desc_attr, ""); |
| |
| if (attr->non_resident) |
| free(sec_desc_attr); |
| } |
| |
| /** |
| * ntfs_dump_volume_name() |
| * |
| * dump the name of the volume the inode belongs to |
| */ |
| static void ntfs_dump_attr_volume_name(ATTR_RECORD *attr) |
| { |
| ntfschar *ucs_vol_name = NULL; |
| |
| if (le32_to_cpu(attr->value_length) > 0) { |
| char *mbs_vol_name = NULL; |
| int mbs_vol_name_size; |
| /* calculate volume name position */ |
| ucs_vol_name = (ntfschar*)((u8*)attr + |
| le16_to_cpu(attr->value_offset)); |
| /* convert the name to current locale multibyte sequence */ |
| mbs_vol_name_size = ntfs_ucstombs(ucs_vol_name, |
| le32_to_cpu(attr->value_length) / |
| sizeof(ntfschar), &mbs_vol_name, 0); |
| |
| if (mbs_vol_name_size>0) { |
| /* output the converted name. */ |
| printf("\tVolume Name:\t\t '%s'\n", mbs_vol_name); |
| free(mbs_vol_name); |
| } else |
| ntfs_log_perror("ntfsinfo error: could not parse " |
| "volume name"); |
| } else |
| printf("\tVolume Name:\t\t unnamed\n"); |
| } |
| |
| /** |
| * ntfs_dump_volume_information() |
| * |
| * dump the information for the volume the inode belongs to |
| * |
| */ |
| static void ntfs_dump_attr_volume_information(ATTR_RECORD *attr) |
| { |
| VOLUME_INFORMATION *vol_information = NULL; |
| |
| vol_information = (VOLUME_INFORMATION*)((char *)attr+ |
| le16_to_cpu(attr->value_offset)); |
| |
| printf("\tVolume Version:\t\t %d.%d\n", vol_information->major_ver, |
| vol_information->minor_ver); |
| printf("\tVolume Flags:\t\t "); |
| if (vol_information->flags & VOLUME_IS_DIRTY) |
| printf("DIRTY "); |
| if (vol_information->flags & VOLUME_RESIZE_LOG_FILE) |
| printf("RESIZE_LOG "); |
| if (vol_information->flags & VOLUME_UPGRADE_ON_MOUNT) |
| printf("UPG_ON_MOUNT "); |
| if (vol_information->flags & VOLUME_MOUNTED_ON_NT4) |
| printf("MOUNTED_NT4 "); |
| if (vol_information->flags & VOLUME_DELETE_USN_UNDERWAY) |
| printf("DEL_USN "); |
| if (vol_information->flags & VOLUME_REPAIR_OBJECT_ID) |
| printf("REPAIR_OBJID "); |
| if (vol_information->flags & VOLUME_CHKDSK_UNDERWAY) |
| printf("CHKDSK_UNDERWAY "); |
| if (vol_information->flags & VOLUME_MODIFIED_BY_CHKDSK) |
| printf("MOD_BY_CHKDSK "); |
| if (vol_information->flags & VOLUME_FLAGS_MASK) { |
| printf("(0x%04x)\n", |
| (unsigned)le16_to_cpu(vol_information->flags)); |
| } else |
| printf("none set (0x0000)\n"); |
| if (vol_information->flags & (~VOLUME_FLAGS_MASK)) |
| printf("\t\t\t\t Unknown Flags: 0x%04x\n", |
| le16_to_cpu(vol_information->flags & |
| (~VOLUME_FLAGS_MASK))); |
| } |
| |
| static ntfschar NTFS_DATA_SDS[5] = { const_cpu_to_le16('$'), |
| const_cpu_to_le16('S'), const_cpu_to_le16('D'), |
| const_cpu_to_le16('S'), const_cpu_to_le16('\0') }; |
| |
| static void ntfs_dump_sds_entry(SECURITY_DESCRIPTOR_HEADER *sds) |
| { |
| SECURITY_DESCRIPTOR_RELATIVE *sd; |
| |
| ntfs_log_verbose("\n"); |
| ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", |
| (unsigned)le32_to_cpu(sds->hash)); |
| ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(sds->security_id), |
| (unsigned)le32_to_cpu(sds->security_id)); |
| ntfs_log_verbose("\t\tOffset:\t\t\t %llu (0x%llx)\n", |
| (unsigned long long)le64_to_cpu(sds->offset), |
| (unsigned long long)le64_to_cpu(sds->offset)); |
| ntfs_log_verbose("\t\tLength:\t\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(sds->length), |
| (unsigned)le32_to_cpu(sds->length)); |
| |
| sd = (SECURITY_DESCRIPTOR_RELATIVE *)((char *)sds + |
| sizeof(SECURITY_DESCRIPTOR_HEADER)); |
| |
| ntfs_dump_security_descriptor(sd, "\t"); |
| } |
| |
| static void ntfs_dump_sds(ATTR_RECORD *attr, ntfs_inode *ni) |
| { |
| SECURITY_DESCRIPTOR_HEADER *sds, *sd; |
| ntfschar *name; |
| int name_len; |
| s64 data_size; |
| u64 inode; |
| |
| inode = ni->mft_no; |
| if (ni->nr_extents < 0) |
| inode = ni->base_ni->mft_no; |
| if (FILE_Secure != inode) |
| return; |
| |
| name_len = attr->name_length; |
| if (!name_len) |
| return; |
| |
| name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); |
| if (!ntfs_names_are_equal(NTFS_DATA_SDS, sizeof(NTFS_DATA_SDS) / 2 - 1, |
| name, name_len, CASE_SENSITIVE, NULL, 0)) |
| return; |
| |
| sd = sds = ntfs_attr_readall(ni, AT_DATA, name, name_len, &data_size); |
| if (!sd) { |
| ntfs_log_perror("Failed to read $SDS attribute"); |
| return; |
| } |
| /* |
| * FIXME: The right way is based on the indexes, so we couldn't |
| * miss real entries. For now, dump until it makes sense. |
| */ |
| while (sd->length && sd->hash && |
| le64_to_cpu(sd->offset) < (u64)data_size && |
| le32_to_cpu(sd->length) < (u64)data_size && |
| le64_to_cpu(sd->offset) + |
| le32_to_cpu(sd->length) < (u64)data_size) { |
| ntfs_dump_sds_entry(sd); |
| sd = (SECURITY_DESCRIPTOR_HEADER *)((char*)sd + |
| ((le32_to_cpu(sd->length) + 15) & ~15)); |
| } |
| free(sds); |
| } |
| |
| static const char *get_attribute_type_name(le32 type) |
| { |
| switch (type) { |
| case AT_UNUSED: return "$UNUSED"; |
| case AT_STANDARD_INFORMATION: return "$STANDARD_INFORMATION"; |
| case AT_ATTRIBUTE_LIST: return "$ATTRIBUTE_LIST"; |
| case AT_FILE_NAME: return "$FILE_NAME"; |
| case AT_OBJECT_ID: return "$OBJECT_ID"; |
| case AT_SECURITY_DESCRIPTOR: return "$SECURITY_DESCRIPTOR"; |
| case AT_VOLUME_NAME: return "$VOLUME_NAME"; |
| case AT_VOLUME_INFORMATION: return "$VOLUME_INFORMATION"; |
| case AT_DATA: return "$DATA"; |
| case AT_INDEX_ROOT: return "$INDEX_ROOT"; |
| case AT_INDEX_ALLOCATION: return "$INDEX_ALLOCATION"; |
| case AT_BITMAP: return "$BITMAP"; |
| case AT_REPARSE_POINT: return "$REPARSE_POINT"; |
| case AT_EA_INFORMATION: return "$EA_INFORMATION"; |
| case AT_EA: return "$EA"; |
| case AT_PROPERTY_SET: return "$PROPERTY_SET"; |
| case AT_LOGGED_UTILITY_STREAM: return "$LOGGED_UTILITY_STREAM"; |
| case AT_END: return "$END"; |
| } |
| |
| return "$UNKNOWN"; |
| } |
| |
| static const char * ntfs_dump_lcn(LCN lcn) |
| { |
| switch (lcn) { |
| case LCN_HOLE: |
| return "<HOLE>\t"; |
| case LCN_RL_NOT_MAPPED: |
| return "<RL_NOT_MAPPED>"; |
| case LCN_ENOENT: |
| return "<ENOENT>\t"; |
| case LCN_EINVAL: |
| return "<EINVAL>\t"; |
| case LCN_EIO: |
| return "<EIO>\t"; |
| default: |
| ntfs_log_error("Invalid LCN value %llx passed to " |
| "ntfs_dump_lcn().\n", (long long)lcn); |
| return "???\t"; |
| } |
| } |
| |
| static void ntfs_dump_attribute_header(ntfs_attr_search_ctx *ctx, |
| ntfs_volume *vol, struct RUNCOUNT *runcount) |
| { |
| ATTR_RECORD *a = ctx->attr; |
| |
| printf("Dumping attribute %s (0x%x) from mft record %lld (0x%llx)\n", |
| get_attribute_type_name(a->type), |
| (unsigned)le32_to_cpu(a->type), |
| (unsigned long long)ctx->ntfs_ino->mft_no, |
| (unsigned long long)ctx->ntfs_ino->mft_no); |
| |
| ntfs_log_verbose("\tAttribute length:\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(a->length), |
| (unsigned)le32_to_cpu(a->length)); |
| printf("\tResident: \t\t %s\n", a->non_resident ? "No" : "Yes"); |
| ntfs_log_verbose("\tName length:\t\t %u (0x%x)\n", |
| (unsigned)a->name_length, (unsigned)a->name_length); |
| ntfs_log_verbose("\tName offset:\t\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(a->name_offset), |
| (unsigned)le16_to_cpu(a->name_offset)); |
| |
| /* Dump the attribute (stream) name */ |
| if (a->name_length) { |
| char *attribute_name = NULL; |
| |
| attribute_name = ntfs_attr_get_name_mbs(a); |
| if (attribute_name) { |
| printf("\tAttribute name:\t\t '%s'\n", attribute_name); |
| free(attribute_name); |
| } else |
| ntfs_log_perror("Error: couldn't parse attribute name"); |
| } |
| |
| /* TODO: parse the flags */ |
| printf("\tAttribute flags:\t 0x%04x\n", |
| (unsigned)le16_to_cpu(a->flags)); |
| printf("\tAttribute instance:\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(a->instance), |
| (unsigned)le16_to_cpu(a->instance)); |
| |
| /* Resident attribute */ |
| if (!a->non_resident) { |
| printf("\tData size:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(a->value_length), |
| (unsigned)le32_to_cpu(a->value_length)); |
| ntfs_log_verbose("\tData offset:\t\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(a->value_offset), |
| (unsigned)le16_to_cpu(a->value_offset)); |
| /* TODO: parse the flags */ |
| printf("\tResident flags:\t\t 0x%02x\n", |
| (unsigned)a->resident_flags); |
| ntfs_log_verbose("\tReservedR:\t\t %d (0x%x)\n", |
| (unsigned)a->reservedR, (unsigned)a->reservedR); |
| return; |
| } |
| |
| /* Non-resident attribute */ |
| ntfs_log_verbose("\tLowest VCN\t\t %lld (0x%llx)\n", |
| (long long)sle64_to_cpu(a->lowest_vcn), |
| (unsigned long long)sle64_to_cpu(a->lowest_vcn)); |
| ntfs_log_verbose("\tHighest VCN:\t\t %lld (0x%llx)\n", |
| (long long)sle64_to_cpu(a->highest_vcn), |
| (unsigned long long)sle64_to_cpu(a->highest_vcn)); |
| ntfs_log_verbose("\tMapping pairs offset:\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(a->mapping_pairs_offset), |
| (unsigned)le16_to_cpu(a->mapping_pairs_offset)); |
| printf("\tCompression unit:\t %u (0x%x)\n", |
| (unsigned)a->compression_unit, |
| (unsigned)a->compression_unit); |
| /* TODO: dump the 5 reserved bytes here in verbose mode */ |
| |
| if (!a->lowest_vcn) { |
| printf("\tData size:\t\t %llu (0x%llx)\n", |
| (long long)sle64_to_cpu(a->data_size), |
| (unsigned long long)sle64_to_cpu(a->data_size)); |
| printf("\tAllocated size:\t\t %llu (0x%llx)\n", |
| (long long)sle64_to_cpu(a->allocated_size), |
| (unsigned long long) |
| sle64_to_cpu(a->allocated_size)); |
| printf("\tInitialized size:\t %llu (0x%llx)\n", |
| (long long)sle64_to_cpu(a->initialized_size), |
| (unsigned long long) |
| sle64_to_cpu(a->initialized_size)); |
| if (a->compression_unit || a->flags & ATTR_IS_COMPRESSED || |
| a->flags & ATTR_IS_SPARSE) |
| printf("\tCompressed size:\t %llu (0x%llx)\n", |
| (signed long long) |
| sle64_to_cpu(a->compressed_size), |
| (signed long long) |
| sle64_to_cpu(a->compressed_size)); |
| } |
| |
| if (opts.verbose) { |
| runlist *rl; |
| |
| rl = ntfs_mapping_pairs_decompress(vol, a, NULL); |
| if (rl) { |
| runlist *rlc = rl; |
| LCN next_lcn; |
| |
| next_lcn = LCN_HOLE; |
| // TODO: Switch this to properly aligned hex... |
| printf("\tRunlist:\tVCN\t\tLCN\t\tLength\n"); |
| runcount->fragments++; |
| while (rlc->length) { |
| runcount->runs++; |
| if (rlc->lcn >= 0) { |
| printf("\t\t\t0x%llx\t\t0x%llx\t\t" |
| "0x%llx\n", |
| (long long)rlc->vcn, |
| (long long)rlc->lcn, |
| (long long)rlc->length); |
| if ((next_lcn >= 0) |
| && (rlc->lcn != next_lcn)) |
| runcount->fragments++; |
| next_lcn = rlc->lcn + rlc->length; |
| } else |
| printf("\t\t\t0x%llx\t\t%s\t" |
| "0x%llx\n", |
| (long long)rlc->vcn, |
| ntfs_dump_lcn(rlc->lcn), |
| (long long)rlc->length); |
| rlc++; |
| } |
| free(rl); |
| } else |
| ntfs_log_error("Error: couldn't decompress runlist\n"); |
| } |
| } |
| |
| /** |
| * ntfs_dump_data_attr() |
| * |
| * dump some info about the data attribute if it's metadata |
| */ |
| static void ntfs_dump_attr_data(ATTR_RECORD *attr, ntfs_inode *ni) |
| { |
| if (opts.verbose) |
| ntfs_dump_sds(attr, ni); |
| } |
| |
| typedef enum { |
| INDEX_ATTR_UNKNOWN, |
| INDEX_ATTR_DIRECTORY_I30, |
| INDEX_ATTR_SECURE_SII, |
| INDEX_ATTR_SECURE_SDH, |
| INDEX_ATTR_OBJID_O, |
| INDEX_ATTR_REPARSE_R, |
| INDEX_ATTR_QUOTA_O, |
| INDEX_ATTR_QUOTA_Q, |
| } INDEX_ATTR_TYPE; |
| |
| static void ntfs_dump_index_key(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) |
| { |
| char *sid; |
| char printable_GUID[37]; |
| le32 tag; |
| |
| switch (type) { |
| case INDEX_ATTR_SECURE_SII: |
| ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n", |
| (unsigned) |
| le32_to_cpu(entry->key.sii.security_id), |
| (unsigned) |
| le32_to_cpu(entry->key.sii.security_id)); |
| break; |
| case INDEX_ATTR_SECURE_SDH: |
| ntfs_log_verbose("\t\tKey hash:\t\t 0x%08x\n", |
| (unsigned)le32_to_cpu(entry->key.sdh.hash)); |
| ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n", |
| (unsigned) |
| le32_to_cpu(entry->key.sdh.security_id), |
| (unsigned) |
| le32_to_cpu(entry->key.sdh.security_id)); |
| break; |
| case INDEX_ATTR_OBJID_O: |
| ntfs_guid_to_mbs(&entry->key.object_id, printable_GUID); |
| ntfs_log_verbose("\t\tKey GUID:\t\t %s\n", printable_GUID); |
| break; |
| case INDEX_ATTR_REPARSE_R: |
| tag = entry->key.reparse.reparse_tag; |
| ntfs_log_verbose("\t\tKey reparse tag:\t 0x%08lx%s\n", |
| (long)le32_to_cpu(tag), |
| reparse_type_name(tag)); |
| ntfs_log_verbose("\t\tKey file id:\t\t %llu (0x%llx)\n", |
| (unsigned long long) |
| le64_to_cpu(entry->key.reparse.file_id), |
| (unsigned long long) |
| le64_to_cpu(entry->key.reparse.file_id)); |
| break; |
| case INDEX_ATTR_QUOTA_O: |
| sid = ntfs_sid_to_mbs(&entry->key.sid, NULL, 0); |
| ntfs_log_verbose("\t\tKey SID:\t\t %s\n", sid); |
| free(sid); |
| break; |
| case INDEX_ATTR_QUOTA_Q: |
| ntfs_log_verbose("\t\tKey owner id:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(entry->key.owner_id), |
| (unsigned)le32_to_cpu(entry->key.owner_id)); |
| break; |
| default: |
| ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", |
| (unsigned)type); |
| break; |
| } |
| } |
| |
| typedef union { |
| SII_INDEX_DATA sii; /* $SII index data in $Secure */ |
| SDH_INDEX_DATA sdh; /* $SDH index data in $Secure */ |
| QUOTA_O_INDEX_DATA quota_o; /* $O index data in $Quota */ |
| QUOTA_CONTROL_ENTRY quota_q; /* $Q index data in $Quota */ |
| } __attribute__((__packed__)) INDEX_ENTRY_DATA; |
| |
| static void ntfs_dump_index_data(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) |
| { |
| INDEX_ENTRY_DATA *data; |
| |
| data = (INDEX_ENTRY_DATA *)((u8 *)entry + |
| le16_to_cpu(entry->data_offset)); |
| |
| switch (type) { |
| case INDEX_ATTR_SECURE_SII: |
| ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", |
| (unsigned)le32_to_cpu(data->sii.hash)); |
| ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(data->sii.security_id), |
| (unsigned)le32_to_cpu(data->sii.security_id)); |
| ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n", |
| (unsigned long long) |
| le64_to_cpu(data->sii.offset), |
| (unsigned long long) |
| le64_to_cpu(data->sii.offset)); |
| ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(data->sii.length), |
| (unsigned)le32_to_cpu(data->sii.length)); |
| break; |
| case INDEX_ATTR_SECURE_SDH: |
| ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", |
| (unsigned)le32_to_cpu(data->sdh.hash)); |
| ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(data->sdh.security_id), |
| (unsigned)le32_to_cpu(data->sdh.security_id)); |
| ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n", |
| (unsigned long long) |
| le64_to_cpu(data->sdh.offset), |
| (unsigned long long) |
| le64_to_cpu(data->sdh.offset)); |
| ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(data->sdh.length), |
| (unsigned)le32_to_cpu(data->sdh.length)); |
| ntfs_log_verbose("\t\tUnknown (padding):\t 0x%08x\n", |
| (unsigned)le32_to_cpu(data->sdh.reserved_II)); |
| break; |
| case INDEX_ATTR_OBJID_O: { |
| OBJ_ID_INDEX_DATA *object_id_data; |
| char printable_GUID[37]; |
| |
| object_id_data = (OBJ_ID_INDEX_DATA*)((u8*)entry + |
| le16_to_cpu(entry->data_offset)); |
| ntfs_log_verbose("\t\tMFT Number:\t\t 0x%llx\n", |
| (unsigned long long) |
| MREF_LE(object_id_data->mft_reference)); |
| ntfs_log_verbose("\t\tMFT Sequence Number:\t 0x%x\n", |
| (unsigned) |
| MSEQNO_LE(object_id_data->mft_reference)); |
| ntfs_guid_to_mbs(&object_id_data->birth_volume_id, |
| printable_GUID); |
| ntfs_log_verbose("\t\tBirth volume id GUID:\t %s\n", |
| printable_GUID); |
| ntfs_guid_to_mbs(&object_id_data->birth_object_id, |
| printable_GUID); |
| ntfs_log_verbose("\t\tBirth object id GUID:\t %s\n", |
| printable_GUID); |
| ntfs_guid_to_mbs(&object_id_data->domain_id, printable_GUID); |
| ntfs_log_verbose("\t\tDomain id GUID:\t\t %s\n", |
| printable_GUID); |
| } |
| break; |
| case INDEX_ATTR_REPARSE_R: |
| /* TODO */ |
| break; |
| case INDEX_ATTR_QUOTA_O: |
| ntfs_log_verbose("\t\tOwner id:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(data->quota_o.owner_id), |
| (unsigned)le32_to_cpu(data->quota_o.owner_id)); |
| ntfs_log_verbose("\t\tUnknown:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(data->quota_o.unknown), |
| (unsigned)le32_to_cpu(data->quota_o.unknown)); |
| break; |
| case INDEX_ATTR_QUOTA_Q: |
| ntfs_log_verbose("\t\tVersion:\t\t %u\n", |
| (unsigned)le32_to_cpu(data->quota_q.version)); |
| ntfs_log_verbose("\t\tQuota flags:\t\t 0x%08x\n", |
| (unsigned)le32_to_cpu(data->quota_q.flags)); |
| ntfs_log_verbose("\t\tBytes used:\t\t %llu (0x%llx)\n", |
| (unsigned long long) |
| le64_to_cpu(data->quota_q.bytes_used), |
| (unsigned long long) |
| le64_to_cpu(data->quota_q.bytes_used)); |
| ntfs_log_verbose("\t\tLast changed:\t\t %s", |
| ntfsinfo_time_to_str( |
| data->quota_q.change_time)); |
| ntfs_log_verbose("\t\tThreshold:\t\t %lld (0x%llx)\n", |
| (unsigned long long) |
| sle64_to_cpu(data->quota_q.threshold), |
| (unsigned long long) |
| sle64_to_cpu(data->quota_q.threshold)); |
| ntfs_log_verbose("\t\tLimit:\t\t\t %lld (0x%llx)\n", |
| (unsigned long long) |
| sle64_to_cpu(data->quota_q.limit), |
| (unsigned long long) |
| sle64_to_cpu(data->quota_q.limit)); |
| ntfs_log_verbose("\t\tExceeded time:\t\t %lld (0x%llx)\n", |
| (unsigned long long) |
| sle64_to_cpu(data->quota_q.exceeded_time), |
| (unsigned long long) |
| sle64_to_cpu(data->quota_q.exceeded_time)); |
| if (le16_to_cpu(entry->data_length) > 48) { |
| char *sid; |
| sid = ntfs_sid_to_mbs(&data->quota_q.sid, NULL, 0); |
| ntfs_log_verbose("\t\tOwner SID:\t\t %s\n", sid); |
| free(sid); |
| } |
| break; |
| default: |
| ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", |
| (unsigned)type); |
| break; |
| } |
| } |
| |
| /** |
| * ntfs_dump_index_entries() |
| * |
| * dump sequence of index_entries and return number of entries dumped. |
| */ |
| static int ntfs_dump_index_entries(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) |
| { |
| int numb_entries = 1; |
| while (1) { |
| if (!opts.verbose) { |
| if (entry->ie_flags & INDEX_ENTRY_END) |
| break; |
| entry = (INDEX_ENTRY *)((u8 *)entry + |
| le16_to_cpu(entry->length)); |
| numb_entries++; |
| continue; |
| } |
| ntfs_log_verbose("\t\tEntry length:\t\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(entry->length), |
| (unsigned)le16_to_cpu(entry->length)); |
| ntfs_log_verbose("\t\tKey length:\t\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(entry->key_length), |
| (unsigned)le16_to_cpu(entry->key_length)); |
| ntfs_log_verbose("\t\tIndex entry flags:\t 0x%02x\n", |
| (unsigned)le16_to_cpu(entry->ie_flags)); |
| |
| if (entry->ie_flags & INDEX_ENTRY_NODE) |
| ntfs_log_verbose("\t\tSubnode VCN:\t\t %lld (0x%llx)\n", |
| (long long)ntfs_ie_get_vcn(entry), |
| (long long)ntfs_ie_get_vcn(entry)); |
| if (entry->ie_flags & INDEX_ENTRY_END) |
| break; |
| |
| switch (type) { |
| case INDEX_ATTR_DIRECTORY_I30: |
| ntfs_log_verbose("\t\tFILE record number:\t %llu " |
| "(0x%llx)\n", (unsigned long long) |
| MREF_LE(entry->indexed_file), |
| (unsigned long long) |
| MREF_LE(entry->indexed_file)); |
| ntfs_dump_filename("\t\t", &entry->key.file_name); |
| break; |
| default: |
| ntfs_log_verbose("\t\tData offset:\t\t %u (0x%x)\n", |
| (unsigned) |
| le16_to_cpu(entry->data_offset), |
| (unsigned) |
| le16_to_cpu(entry->data_offset)); |
| ntfs_log_verbose("\t\tData length:\t\t %u (0x%x)\n", |
| (unsigned) |
| le16_to_cpu(entry->data_length), |
| (unsigned) |
| le16_to_cpu(entry->data_length)); |
| ntfs_dump_index_key(entry, type); |
| ntfs_log_verbose("\t\tKey Data:\n"); |
| ntfs_dump_index_data(entry, type); |
| break; |
| } |
| if (!entry->length) { |
| ntfs_log_verbose("\tWARNING: Corrupt index entry, " |
| "skipping the remainder of this index " |
| "block.\n"); |
| break; |
| } |
| entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length)); |
| numb_entries++; |
| ntfs_log_verbose("\n"); |
| } |
| ntfs_log_verbose("\tEnd of index block reached\n"); |
| return numb_entries; |
| } |
| |
| #define COMPARE_INDEX_NAMES(attr, name) \ |
| ntfs_names_are_equal((name), sizeof(name) / 2 - 1, \ |
| (ntfschar*)((char*)(attr) + le16_to_cpu((attr)->name_offset)), \ |
| (attr)->name_length, CASE_SENSITIVE, NULL, 0) |
| |
| static INDEX_ATTR_TYPE get_index_attr_type(ntfs_inode *ni, ATTR_RECORD *attr, |
| INDEX_ROOT *index_root) |
| { |
| char file_name[64]; |
| |
| if (!attr->name_length) |
| return INDEX_ATTR_UNKNOWN; |
| |
| if (index_root->type) { |
| if (index_root->type == AT_FILE_NAME) |
| return INDEX_ATTR_DIRECTORY_I30; |
| else |
| /* weird, this should be illegal */ |
| ntfs_log_error("Unknown index attribute type: 0x%0X\n", |
| le32_to_cpu(index_root->type)); |
| return INDEX_ATTR_UNKNOWN; |
| } |
| |
| if (utils_is_metadata(ni) <= 0) |
| return INDEX_ATTR_UNKNOWN; |
| if (utils_inode_get_name(ni, file_name, sizeof(file_name)) <= 0) |
| return INDEX_ATTR_UNKNOWN; |
| |
| if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SDH)) |
| return INDEX_ATTR_SECURE_SDH; |
| else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII)) |
| return INDEX_ATTR_SECURE_SII; |
| else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII)) |
| return INDEX_ATTR_SECURE_SII; |
| else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_Q)) |
| return INDEX_ATTR_QUOTA_Q; |
| else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_R)) |
| return INDEX_ATTR_REPARSE_R; |
| else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_O)) { |
| if (!strcmp(file_name, "/$Extend/$Quota")) |
| return INDEX_ATTR_QUOTA_O; |
| else if (!strcmp(file_name, "/$Extend/$ObjId")) |
| return INDEX_ATTR_OBJID_O; |
| } |
| |
| return INDEX_ATTR_UNKNOWN; |
| } |
| |
| static void ntfs_dump_index_attr_type(INDEX_ATTR_TYPE type) |
| { |
| if (type == INDEX_ATTR_DIRECTORY_I30) |
| printf("DIRECTORY_I30"); |
| else if (type == INDEX_ATTR_SECURE_SDH) |
| printf("SECURE_SDH"); |
| else if (type == INDEX_ATTR_SECURE_SII) |
| printf("SECURE_SII"); |
| else if (type == INDEX_ATTR_OBJID_O) |
| printf("OBJID_O"); |
| else if (type == INDEX_ATTR_QUOTA_O) |
| printf("QUOTA_O"); |
| else if (type == INDEX_ATTR_QUOTA_Q) |
| printf("QUOTA_Q"); |
| else if (type == INDEX_ATTR_REPARSE_R) |
| printf("REPARSE_R"); |
| else |
| printf("UNKNOWN"); |
| printf("\n"); |
| } |
| |
| static void ntfs_dump_index_header(const char *indent, INDEX_HEADER *idx) |
| { |
| printf("%sEntries Offset:\t\t %u (0x%x)\n", indent, |
| (unsigned)le32_to_cpu(idx->entries_offset), |
| (unsigned)le32_to_cpu(idx->entries_offset)); |
| printf("%sIndex Size:\t\t %u (0x%x)\n", indent, |
| (unsigned)le32_to_cpu(idx->index_length), |
| (unsigned)le32_to_cpu(idx->index_length)); |
| printf("%sAllocated Size:\t\t %u (0x%x)\n", indent, |
| (unsigned)le32_to_cpu(idx->allocated_size), |
| (unsigned)le32_to_cpu(idx->allocated_size)); |
| printf("%sIndex header flags:\t 0x%02x\n", indent, idx->ih_flags); |
| |
| /* FIXME: there are 3 reserved bytes here */ |
| } |
| |
| /** |
| * ntfs_dump_attr_index_root() |
| * |
| * dump the index_root attribute |
| */ |
| static void ntfs_dump_attr_index_root(ATTR_RECORD *attr, ntfs_inode *ni) |
| { |
| INDEX_ATTR_TYPE type; |
| INDEX_ROOT *index_root = NULL; |
| INDEX_ENTRY *entry; |
| |
| index_root = (INDEX_ROOT*)((u8*)attr + le16_to_cpu(attr->value_offset)); |
| |
| /* attr_type dumping */ |
| type = get_index_attr_type(ni, attr, index_root); |
| printf("\tIndexed Attr Type:\t "); |
| ntfs_dump_index_attr_type(type); |
| |
| /* collation rule dumping */ |
| printf("\tCollation Rule:\t\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(index_root->collation_rule), |
| (unsigned)le32_to_cpu(index_root->collation_rule)); |
| /* COLLATION_BINARY, COLLATION_FILE_NAME, COLLATION_UNICODE_STRING, |
| COLLATION_NTOFS_ULONG, COLLATION_NTOFS_SID, |
| COLLATION_NTOFS_SECURITY_HASH, COLLATION_NTOFS_ULONGS */ |
| |
| printf("\tIndex Block Size:\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(index_root->index_block_size), |
| (unsigned)le32_to_cpu(index_root->index_block_size)); |
| if (le32_to_cpu(index_root->index_block_size) < ni->vol->cluster_size) |
| printf("\t512-byte Units Per Block:\t %u (0x%x)\n", |
| (unsigned)index_root->clusters_per_index_block, |
| (unsigned)index_root->clusters_per_index_block); |
| else |
| printf("\tClusters Per Block:\t %u (0x%x)\n", |
| (unsigned)index_root->clusters_per_index_block, |
| (unsigned)index_root->clusters_per_index_block); |
| |
| ntfs_dump_index_header("\t", &index_root->index); |
| |
| entry = (INDEX_ENTRY*)((u8*)index_root + |
| le32_to_cpu(index_root->index.entries_offset) + 0x10); |
| ntfs_log_verbose("\tDumping index root:\n"); |
| printf("\tIndex entries total:\t %d\n", |
| ntfs_dump_index_entries(entry, type)); |
| } |
| |
| static void ntfs_dump_usa_lsn(const char *indent, MFT_RECORD *mrec) |
| { |
| printf("%sUpd. Seq. Array Off.:\t %u (0x%x)\n", indent, |
| (unsigned)le16_to_cpu(mrec->usa_ofs), |
| (unsigned)le16_to_cpu(mrec->usa_ofs)); |
| printf("%sUpd. Seq. Array Count:\t %u (0x%x)\n", indent, |
| (unsigned)le16_to_cpu(mrec->usa_count), |
| (unsigned)le16_to_cpu(mrec->usa_count)); |
| printf("%sUpd. Seq. Number:\t %u (0x%x)\n", indent, |
| (unsigned)le16_to_cpup((le16*)((u8*)mrec + |
| le16_to_cpu(mrec->usa_ofs))), |
| (unsigned)le16_to_cpup((le16*)((u8*)mrec + |
| le16_to_cpu(mrec->usa_ofs)))); |
| printf("%sLogFile Seq. Number:\t 0x%llx\n", indent, |
| (unsigned long long)sle64_to_cpu(mrec->lsn)); |
| } |
| |
| |
| static s32 ntfs_dump_index_block(INDEX_BLOCK *ib, INDEX_ATTR_TYPE type, |
| u32 ib_size) |
| { |
| INDEX_ENTRY *entry; |
| |
| if (ntfs_mst_post_read_fixup((NTFS_RECORD*)ib, ib_size)) { |
| ntfs_log_perror("Damaged INDX record"); |
| return -1; |
| } |
| ntfs_log_verbose("\tDumping index block:\n"); |
| if (opts.verbose) |
| ntfs_dump_usa_lsn("\t\t", (MFT_RECORD*)ib); |
| |
| ntfs_log_verbose("\t\tNode VCN:\t\t %lld (0x%llx)\n", |
| (unsigned long long)sle64_to_cpu(ib->index_block_vcn), |
| (unsigned long long)sle64_to_cpu(ib->index_block_vcn)); |
| |
| entry = (INDEX_ENTRY*)((u8*)ib + |
| le32_to_cpu(ib->index.entries_offset) + 0x18); |
| |
| if (opts.verbose) { |
| ntfs_dump_index_header("\t\t", &ib->index); |
| printf("\n"); |
| } |
| |
| return ntfs_dump_index_entries(entry, type); |
| } |
| |
| /** |
| * ntfs_dump_attr_index_allocation() |
| * |
| * dump context of the index_allocation attribute |
| */ |
| static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni) |
| { |
| INDEX_ALLOCATION *allocation, *tmp_alloc; |
| INDEX_ROOT *ir; |
| INDEX_ATTR_TYPE type; |
| int total_entries = 0; |
| int total_indx_blocks = 0; |
| u8 *bitmap, *byte; |
| int bit; |
| ntfschar *name; |
| u32 name_len; |
| s64 data_size; |
| |
| ir = ntfs_index_root_get(ni, attr); |
| if (!ir) { |
| ntfs_log_perror("Failed to read $INDEX_ROOT attribute"); |
| return; |
| } |
| |
| type = get_index_attr_type(ni, attr, ir); |
| |
| name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); |
| name_len = attr->name_length; |
| |
| byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL); |
| if (!byte) { |
| ntfs_log_perror("Failed to read $BITMAP attribute"); |
| goto out_index_root; |
| } |
| |
| tmp_alloc = allocation = ntfs_attr_readall(ni, AT_INDEX_ALLOCATION, |
| name, name_len, &data_size); |
| if (!tmp_alloc) { |
| ntfs_log_perror("Failed to read $INDEX_ALLOCATION attribute"); |
| goto out_bitmap; |
| } |
| |
| bit = 0; |
| while ((u8 *)tmp_alloc < (u8 *)allocation + data_size) { |
| if (*byte & (1 << bit)) { |
| int entries; |
| |
| entries = ntfs_dump_index_block(tmp_alloc, type, |
| le32_to_cpu( |
| ir->index_block_size)); |
| if (entries != -1) { |
| total_entries += entries; |
| total_indx_blocks++; |
| ntfs_log_verbose("\tIndex entries:\t\t %d\n", |
| entries); |
| } |
| } |
| tmp_alloc = (INDEX_ALLOCATION *)((u8 *)tmp_alloc + |
| le32_to_cpu( |
| ir->index_block_size)); |
| bit++; |
| if (bit > 7) { |
| bit = 0; |
| byte++; |
| } |
| } |
| |
| printf("\tIndex entries total:\t %d\n", total_entries); |
| printf("\tINDX blocks total:\t %d\n", total_indx_blocks); |
| |
| free(allocation); |
| out_bitmap: |
| free(bitmap); |
| out_index_root: |
| free(ir); |
| } |
| |
| /** |
| * ntfs_dump_attr_bitmap() |
| * |
| * dump the bitmap attribute |
| */ |
| static void ntfs_dump_attr_bitmap(ATTR_RECORD *attr __attribute__((unused))) |
| { |
| /* TODO */ |
| } |
| |
| /** |
| * ntfs_dump_attr_reparse_point() |
| * |
| * of ntfs 3.x dumps the reparse_point attribute |
| */ |
| static void ntfs_dump_attr_reparse_point(ATTR_RECORD *attr |
| __attribute__((unused)), ntfs_inode *inode) |
| { |
| REPARSE_POINT *reparse; |
| le32 tag; |
| const char *name; |
| u8 *pvalue; |
| s64 size; |
| unsigned int length; |
| unsigned int cnt; |
| |
| if (attr->non_resident) { |
| reparse = ntfs_attr_readall(inode, AT_REPARSE_POINT, |
| (ntfschar*)NULL, 0, &size); |
| } else { |
| reparse = (REPARSE_POINT*)((u8*)attr + |
| le16_to_cpu(attr->value_offset)); |
| } |
| if (reparse) { |
| tag = reparse->reparse_tag; |
| name = reparse_type_name(tag); |
| printf("\tReparse tag:\t\t 0x%08lx%s\n", |
| (long)le32_to_cpu(tag),name); |
| length = le16_to_cpu(reparse->reparse_data_length); |
| printf("\tData length:\t\t %u (0x%x)\n", |
| (unsigned int)length,(unsigned int)length); |
| cnt = length; |
| pvalue = reparse->reparse_data; |
| printf("\tData:\t\t\t"); |
| printf(cnt ? " 0x" : "(NONE)"); |
| if (cnt > 32) |
| cnt = 32; |
| while (cnt-- > 0) |
| printf("%02x",*pvalue++); |
| if (length > 32) |
| printf("...\n"); |
| else |
| printf("\n"); |
| if (attr->non_resident) |
| free(reparse); |
| } else { |
| ntfs_log_perror("Failed to get the reparse data"); |
| } |
| } |
| |
| /** |
| * ntfs_dump_attr_ea_information() |
| * |
| * dump the ea_information attribute |
| */ |
| static void ntfs_dump_attr_ea_information(ATTR_RECORD *attr) |
| { |
| EA_INFORMATION *ea_info; |
| |
| ea_info = (EA_INFORMATION*)((u8*)attr + |
| le16_to_cpu(attr->value_offset)); |
| printf("\tPacked EA length:\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(ea_info->ea_length), |
| (unsigned)le16_to_cpu(ea_info->ea_length)); |
| printf("\tNEED_EA count:\t\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(ea_info->need_ea_count), |
| (unsigned)le16_to_cpu(ea_info->need_ea_count)); |
| printf("\tUnpacked EA length:\t %u (0x%x)\n", |
| (unsigned)le32_to_cpu(ea_info->ea_query_length), |
| (unsigned)le32_to_cpu(ea_info->ea_query_length)); |
| } |
| |
| /** |
| * ntfs_dump_attr_ea() |
| * |
| * dump the ea attribute |
| */ |
| static void ntfs_dump_attr_ea(ATTR_RECORD *attr, ntfs_volume *vol) |
| { |
| const EA_ATTR *ea; |
| const u8 *pvalue; |
| u8 *buf = NULL; |
| const le32 *pval; |
| int offset; |
| int cnt; |
| s64 data_size; |
| |
| if (attr->non_resident) { |
| runlist *rl; |
| |
| data_size = sle64_to_cpu(attr->data_size); |
| if (!opts.verbose) |
| return; |
| /* FIXME: We don't handle fragmented mapping pairs case. */ |
| rl = ntfs_mapping_pairs_decompress(vol, attr, NULL); |
| if (rl) { |
| s64 bytes_read; |
| |
| buf = ntfs_malloc(data_size); |
| if (!buf) { |
| free(rl); |
| return; |
| } |
| bytes_read = ntfs_rl_pread(vol, rl, 0, data_size, buf); |
| if (bytes_read != data_size) { |
| ntfs_log_perror("ntfs_rl_pread failed"); |
| free(buf); |
| free(rl); |
| return; |
| } |
| free(rl); |
| ea = (EA_ATTR*)buf; |
| } else { |
| ntfs_log_perror("ntfs_mapping_pairs_decompress failed"); |
| return; |
| } |
| } else { |
| data_size = le32_to_cpu(attr->value_length); |
| if (!opts.verbose) |
| return; |
| ea = (EA_ATTR*)((u8*)attr + le16_to_cpu(attr->value_offset)); |
| } |
| offset = 0; |
| while (1) { |
| printf("\n\tEA flags:\t\t "); |
| if (ea->flags) { |
| if (ea->flags == NEED_EA) |
| printf("NEED_EA\n"); |
| else |
| printf("Unknown (0x%02x)\n", |
| (unsigned)ea->flags); |
| } else |
| printf("NONE\n"); |
| printf("\tName length:\t %d (0x%x)\n", |
| (unsigned)ea->name_length, |
| (unsigned)ea->name_length); |
| printf("\tValue length:\t %d (0x%x)\n", |
| (unsigned)le16_to_cpu(ea->value_length), |
| (unsigned)le16_to_cpu(ea->value_length)); |
| /* Name expected to be null terminated ? */ |
| printf("\tName:\t\t '%s'\n", ea->name); |
| printf("\tValue:\t\t "); |
| if (ea->name_length == 11 && |
| !strncmp((const char*)"SETFILEBITS", |
| (const char*)ea->name, 11)) { |
| pval = (const le32*)(ea->value + ea->name_length + 1); |
| printf("0%lo\n", (unsigned long)le32_to_cpu(*pval)); |
| } else { |
| /* No alignment for value */ |
| pvalue = ea->value + ea->name_length + 1; |
| /* Hex show a maximum of 32 bytes */ |
| cnt = le16_to_cpu(ea->value_length); |
| printf(cnt ? "0x" : "(NONE)"); |
| if (cnt > 32) |
| cnt = 32; |
| while (cnt-- > 0) |
| printf("%02x",*pvalue++); |
| if (le16_to_cpu(ea->value_length) > 32) |
| printf("...\n"); |
| else |
| printf("\n"); |
| } |
| if (ea->next_entry_offset) { |
| offset += le32_to_cpu(ea->next_entry_offset); |
| ea = (const EA_ATTR*)((const u8*)ea |
| + le32_to_cpu(ea->next_entry_offset)); |
| } else |
| break; |
| if (offset >= data_size) |
| break; |
| } |
| free(buf); |
| } |
| |
| /** |
| * ntfs_dump_attr_property_set() |
| * |
| * dump the property_set attribute |
| */ |
| static void ntfs_dump_attr_property_set(ATTR_RECORD *attr __attribute__((unused))) |
| { |
| /* TODO */ |
| } |
| |
| static void ntfs_hex_dump(void *buf,unsigned int length); |
| |
| /** |
| * ntfs_dump_attr_logged_utility_stream() |
| * |
| * dump the property_set attribute |
| */ |
| static void ntfs_dump_attr_logged_utility_stream(ATTR_RECORD *attr, |
| ntfs_inode *ni) |
| { |
| char *buf; |
| s64 size; |
| |
| if (!opts.verbose) |
| return; |
| buf = ntfs_attr_readall(ni, AT_LOGGED_UTILITY_STREAM, |
| ntfs_attr_get_name(attr), attr->name_length, &size); |
| if (buf) |
| ntfs_hex_dump(buf, size); |
| free(buf); |
| /* TODO */ |
| } |
| |
| /** |
| * ntfs_hex_dump |
| */ |
| static void ntfs_hex_dump(void *buf,unsigned int length) |
| { |
| unsigned int i=0; |
| while (i<length) { |
| unsigned int j; |
| |
| /* line start */ |
| printf("\t%04X ",i); |
| |
| /* hex content */ |
| for (j=i;(j<length) && (j<i+16);j++) { |
| unsigned char c = *((char *)buf + j); |
| printf("%02hhX ",c); |
| } |
| |
| /* realign */ |
| for (;j<i+16;j++) { |
| printf(" "); |
| } |
| |
| /* char content */ |
| for (j=i;(j<length) && (j<i+16);j++) { |
| unsigned char c = *((char *)buf + j); |
| /* display unprintable chars as '.' */ |
| if ((c<32) || (c>126)) { |
| c = '.'; |
| } |
| printf("%c",c); |
| } |
| |
| /* end line */ |
| printf("\n"); |
| i=j; |
| } |
| } |
| |
| /** |
| * ntfs_dump_attr_unknown |
| */ |
| static void ntfs_dump_attr_unknown(ATTR_RECORD *attr) |
| { |
| printf("===== Please report this unknown attribute type to %s =====\n", |
| NTFS_DEV_LIST); |
| |
| if (!attr->non_resident) { |
| /* hex dump */ |
| printf("\tDumping some of the attribute data:\n"); |
| ntfs_hex_dump((u8*)attr + le16_to_cpu(attr->value_offset), |
| (le32_to_cpu(attr->value_length) > 128) ? |
| 128 : le32_to_cpu(attr->value_length)); |
| } |
| } |
| |
| /** |
| * ntfs_dump_inode_general_info |
| */ |
| static void ntfs_dump_inode_general_info(ntfs_inode *inode) |
| { |
| MFT_RECORD *mrec = inode->mrec; |
| le16 inode_flags = mrec->flags; |
| |
| printf("Dumping Inode %llu (0x%llx)\n", |
| (long long)inode->mft_no, |
| (unsigned long long)inode->mft_no); |
| |
| ntfs_dump_usa_lsn("", mrec); |
| printf("MFT Record Seq. Numb.:\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(mrec->sequence_number), |
| (unsigned)le16_to_cpu(mrec->sequence_number)); |
| printf("Number of Hard Links:\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(mrec->link_count), |
| (unsigned)le16_to_cpu(mrec->link_count)); |
| printf("Attribute Offset:\t %u (0x%x)\n", |
| (unsigned)le16_to_cpu(mrec->attrs_offset), |
| (unsigned)le16_to_cpu(mrec->attrs_offset)); |
| |
| printf("MFT Record Flags:\t "); |
| if (inode_flags) { |
| if (MFT_RECORD_IN_USE & inode_flags) { |
| printf("IN_USE "); |
| inode_flags &= ~MFT_RECORD_IN_USE; |
| } |
| if (MFT_RECORD_IS_DIRECTORY & inode_flags) { |
| printf("DIRECTORY "); |
| inode_flags &= ~MFT_RECORD_IS_DIRECTORY; |
| } |
| /* The meaning of IS_4 is illusive but not its existence. */ |
| if (MFT_RECORD_IS_4 & inode_flags) { |
| printf("IS_4 "); |
| inode_flags &= ~MFT_RECORD_IS_4; |
| } |
| if (MFT_RECORD_IS_VIEW_INDEX & inode_flags) { |
| printf("VIEW_INDEX "); |
| inode_flags &= ~MFT_RECORD_IS_VIEW_INDEX; |
| } |
| if (inode_flags) |
| printf("UNKNOWN: 0x%04x", (unsigned)le16_to_cpu( |
| inode_flags)); |
| } else { |
| printf("none"); |
| } |
| printf("\n"); |
| |
| printf("Bytes Used:\t\t %u (0x%x) bytes\n", |
| (unsigned)le32_to_cpu(mrec->bytes_in_use), |
| (unsigned)le32_to_cpu(mrec->bytes_in_use)); |
| printf("Bytes Allocated:\t %u (0x%x) bytes\n", |
| (unsigned)le32_to_cpu(mrec->bytes_allocated), |
| (unsigned)le32_to_cpu(mrec->bytes_allocated)); |
| |
| if (mrec->base_mft_record) { |
| printf("Base MFT Record:\t %llu (0x%llx)\n", |
| (unsigned long long) |
| MREF_LE(mrec->base_mft_record), |
| (unsigned long long) |
| MREF_LE(mrec->base_mft_record)); |
| } |
| printf("Next Attribute Instance: %u (0x%x)\n", |
| (unsigned)le16_to_cpu(mrec->next_attr_instance), |
| (unsigned)le16_to_cpu(mrec->next_attr_instance)); |
| |
| printf("MFT Padding:\t"); |
| ntfs_dump_bytes((u8 *)mrec, le16_to_cpu(mrec->usa_ofs) + |
| 2 * le16_to_cpu(mrec->usa_count), |
| le16_to_cpu(mrec->attrs_offset)); |
| printf("\n"); |
| } |
| |
| /** |
| * ntfs_get_file_attributes |
| */ |
| static void ntfs_dump_file_attributes(ntfs_inode *inode) |
| { |
| struct RUNCOUNT runcount; |
| ntfs_attr_search_ctx *ctx = NULL; |
| |
| runcount.runs = 0; |
| runcount.fragments = 0; |
| /* then start enumerating attributes |
| see ntfs_attr_lookup documentation for detailed explanation */ |
| ctx = ntfs_attr_get_search_ctx(inode, NULL); |
| while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, |
| 0, NULL, 0, ctx)) { |
| if (ctx->attr->type == AT_END || ctx->attr->type == AT_UNUSED) { |
| printf("Weird: %s attribute type was found, please " |
| "report this.\n", |
| get_attribute_type_name( |
| ctx->attr->type)); |
| continue; |
| } |
| |
| ntfs_dump_attribute_header(ctx, inode->vol, &runcount); |
| |
| switch (ctx->attr->type) { |
| case AT_STANDARD_INFORMATION: |
| ntfs_dump_attr_standard_information(ctx->attr); |
| break; |
| case AT_ATTRIBUTE_LIST: |
| ntfs_dump_attr_list(ctx->attr, inode->vol); |
| break; |
| case AT_FILE_NAME: |
| ntfs_dump_attr_file_name(ctx->attr); |
| break; |
| case AT_OBJECT_ID: |
| ntfs_dump_attr_object_id(ctx->attr, inode->vol); |
| break; |
| case AT_SECURITY_DESCRIPTOR: |
| ntfs_dump_attr_security_descriptor(ctx->attr, |
| inode->vol); |
| break; |
| case AT_VOLUME_NAME: |
| ntfs_dump_attr_volume_name(ctx->attr); |
| break; |
| case AT_VOLUME_INFORMATION: |
| ntfs_dump_attr_volume_information(ctx->attr); |
| break; |
| case AT_DATA: |
| ntfs_dump_attr_data(ctx->attr, inode); |
| break; |
| case AT_INDEX_ROOT: |
| ntfs_dump_attr_index_root(ctx->attr, inode); |
| break; |
| case AT_INDEX_ALLOCATION: |
| ntfs_dump_attr_index_allocation(ctx->attr, inode); |
| break; |
| case AT_BITMAP: |
| ntfs_dump_attr_bitmap(ctx->attr); |
| break; |
| case AT_REPARSE_POINT: |
| ntfs_dump_attr_reparse_point(ctx->attr, inode); |
| break; |
| case AT_EA_INFORMATION: |
| ntfs_dump_attr_ea_information(ctx->attr); |
| break; |
| case AT_EA: |
| ntfs_dump_attr_ea(ctx->attr, inode->vol); |
| break; |
| case AT_PROPERTY_SET: |
| ntfs_dump_attr_property_set(ctx->attr); |
| break; |
| case AT_LOGGED_UTILITY_STREAM: |
| ntfs_dump_attr_logged_utility_stream(ctx->attr, inode); |
| break; |
| default: |
| ntfs_dump_attr_unknown(ctx->attr); |
| } |
| } |
| |
| /* if we exited the loop before we're done - notify the user */ |
| if (errno != ENOENT) { |
| ntfs_log_perror("ntfsinfo error: stopped before finished " |
| "enumerating attributes"); |
| } else { |
| printf("End of inode reached\n"); |
| if (opts.verbose) { |
| printf("Total runs: %lu (fragments: %lu)\n", |
| runcount.runs, runcount.fragments); |
| } |
| } |
| |
| /* close all data-structures we used */ |
| ntfs_attr_put_search_ctx(ctx); |
| ntfs_inode_close(inode); |
| } |
| |
| /** |
| * main() - Begin here |
| * |
| * Start from here. |
| * |
| * Return: 0 Success, the program worked |
| * 1 Error, something went wrong |
| */ |
| int main(int argc, char **argv) |
| { |
| ntfs_volume *vol; |
| int res; |
| |
| setlinebuf(stdout); |
| |
| ntfs_log_set_handler(ntfs_log_handler_outerr); |
| |
| res = parse_options(argc, argv); |
| if (res > 0) |
| printf("Failed to parse command line options\n"); |
| if (res >= 0) |
| exit(res); |
| |
| utils_set_locale(); |
| |
| vol = utils_mount_volume(opts.device, NTFS_MNT_RDONLY | |
| (opts.force ? NTFS_MNT_RECOVER : 0)); |
| if (!vol) { |
| printf("Failed to open '%s'.\n", opts.device); |
| exit(1); |
| } |
| |
| /* |
| * if opts.mft is not 0, then we will print out information about |
| * the volume, such as the sector size and whatnot. |
| */ |
| if (opts.mft) |
| ntfs_dump_volume(vol); |
| |
| if ((opts.inode != -1) || opts.filename) { |
| ntfs_inode *inode; |
| /* obtain the inode */ |
| if (opts.filename) { |
| #ifdef HAVE_WINDOWS_H |
| char *unix_name; |
| |
| unix_name = ntfs_utils_unix_path(opts.filename); |
| if (unix_name) { |
| inode = ntfs_pathname_to_inode(vol, NULL, |
| unix_name); |
| free(unix_name); |
| } else |
| inode = (ntfs_inode*)NULL; |
| #else |
| inode = ntfs_pathname_to_inode(vol, NULL, |
| opts.filename); |
| #endif |
| } else { |
| inode = ntfs_inode_open(vol, MK_MREF(opts.inode, 0)); |
| } |
| |
| /* dump the inode information */ |
| if (inode) { |
| /* general info about the inode's mft record */ |
| ntfs_dump_inode_general_info(inode); |
| /* dump attributes */ |
| ntfs_dump_file_attributes(inode); |
| } else { |
| /* can't open inode */ |
| /* |
| * note: when the specified inode does not exist, either |
| * EIO or or ESPIPE is returned, we should notify better |
| * in those cases |
| */ |
| ntfs_log_perror("Error loading node"); |
| } |
| } |
| |
| ntfs_umount(vol, FALSE); |
| return 0; |
| } |
| |