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