diff options
Diffstat (limited to 'usr/src/cmd/ntfsprogs/utils.c')
-rw-r--r-- | usr/src/cmd/ntfsprogs/utils.c | 1057 |
1 files changed, 0 insertions, 1057 deletions
diff --git a/usr/src/cmd/ntfsprogs/utils.c b/usr/src/cmd/ntfsprogs/utils.c deleted file mode 100644 index d35533de22..0000000000 --- a/usr/src/cmd/ntfsprogs/utils.c +++ /dev/null @@ -1,1057 +0,0 @@ -/** - * utils.c - Part of the Linux-NTFS project. - * - * Copyright (c) 2002-2005 Richard Russon - * Copyright (c) 2003-2006 Anton Altaparmakov - * Copyright (c) 2003 Lode Leroy - * Copyright (c) 2005-2007 Yura Pakhuchiy - * - * A set of shared functions for ntfs utilities - * - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_STDARG_H -#include <stdarg.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_LOCALE_H -#include <locale.h> -#endif -#ifdef HAVE_LIBINTL_H -#include <libintl.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_LIMITS_H -#include <limits.h> -#endif -#ifdef HAVE_CTYPE_H -#include <ctype.h> -#endif - -#include "compat.h" -#include "utils.h" -#include "types.h" -#include "volume.h" -#include "debug.h" -#include "dir.h" -#include "version.h" -#include "logging.h" - -const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n"; -const char *ntfs_home = "Linux NTFS homepage: http://www.linux-ntfs.org\n"; -const char *ntfs_gpl = "This program is free software, released under the GNU " - "General Public License\nand you are welcome to redistribute it under " - "certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for " - "details read the GNU General Public License to be\nfound in the file " - "\"COPYING\" distributed with this program, or online at:\n" - "http://www.gnu.org/copyleft/gpl.html\n"; - -static const char *invalid_ntfs_msg = -"The device '%s' doesn't have a valid NTFS.\n" -"Maybe you selected the wrong device? Or the whole disk instead of a\n" -"partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n"; - -static const char *corrupt_volume_msg = -"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" -"The usage of the /f parameter is very IMPORTANT! No modification was\n" -"made to NTFS by this software.\n"; - -static const char *hibernated_volume_msg = -"The NTFS partition is hibernated. Please resume Windows and turned it \n" -"off properly, so mounting could be done safely.\n"; - -static const char *unclean_journal_msg = -"Access is denied because the NTFS journal file is unclean. Choices are:\n" -" A) Shutdown Windows properly.\n" -" B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n" -" notification area before disconnecting the device.\n" -" C) Use 'Eject' from Windows Explorer to safely remove the device.\n" -" D) If you ran chkdsk previously then boot Windows again which will\n" -" automatically initialize the journal.\n" -" E) Submit 'force' option (WARNING: This solution it not recommended).\n" -" F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n"; - -static const char *opened_volume_msg = -"Access is denied because the NTFS volume is already exclusively opened.\n" -"The volume may be already mounted, or another software may use it which\n" -"could be identified for example by the help of the 'fuser' command.\n"; - -static const char *dirty_volume_msg = -"Volume is scheduled for check.\n" -"Please boot into Windows TWICE, or use the 'force' option.\n" -"NOTE: If you had not scheduled check and last time accessed this volume\n" -"using ntfsmount and shutdown system properly, then init scripts in your\n" -"distribution are broken. Please report to your distribution developers\n" -"(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n" -"shutdown instead of proper umount.\n"; - -static const char *fakeraid_msg = -"You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n" -"different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n" -"to mount NTFS. Please see the 'dmraid' documentation for help.\n"; - -/** - * utils_set_locale - */ -int utils_set_locale(void) -{ - const char *locale; - - locale = setlocale(LC_ALL, ""); - if (!locale) { - locale = setlocale(LC_ALL, NULL); - ntfs_log_error("Failed to set locale, using default '%s'.\n", - locale); - return 1; - } else { - return 0; - } -} - -/** - * utils_valid_device - Perform some safety checks on the device, before start - * @name: Full pathname of the device/file to work with - * @force: Continue regardless of problems - * - * Check that the name refers to a device and that is isn't already mounted. - * These checks can be overridden by using the force option. - * - * Return: 1 Success, we can continue - * 0 Error, we cannot use this device - */ -int utils_valid_device(const char *name, int force) -{ - unsigned long mnt_flags = 0; - struct stat st; - -#ifdef __CYGWIN32__ - /* FIXME: This doesn't work for Cygwin, so just return success. */ - return 1; -#endif - if (!name) { - errno = EINVAL; - return 0; - } - - if (stat(name, &st) == -1) { - if (errno == ENOENT) - ntfs_log_error("The device %s doesn't exist\n", name); - else - ntfs_log_perror("Error getting information about %s", - name); - return 0; - } - - /* Make sure the file system is not mounted. */ - if (ntfs_check_if_mounted(name, &mnt_flags)) { - ntfs_log_perror("Failed to determine whether %s is mounted", - name); - if (!force) { - ntfs_log_error("Use the force option to ignore this " - "error.\n"); - return 0; - } - ntfs_log_warning("Forced to continue.\n"); - } else if (mnt_flags & NTFS_MF_MOUNTED) { - if (!force) { - ntfs_log_error("%s", opened_volume_msg); - ntfs_log_error("You can use force option to avoid this " - "check, but this is not recommended\n" - "and may lead to data corruption.\n"); - return 0; - } - ntfs_log_warning("Forced to continue.\n"); - } - - return 1; -} - -/** - * utils_mount_volume - Mount an NTFS volume - */ -ntfs_volume * utils_mount_volume(const char *device, ntfs_mount_flags flags) -{ - ntfs_volume *vol; - - if (!device) { - errno = EINVAL; - return NULL; - } - - if (!utils_valid_device(device, flags & NTFS_MNT_FORCE)) - return NULL; - - vol = ntfs_mount(device, flags); - if (!vol) { - ntfs_log_perror("Failed to mount '%s'", device); - if (errno == EINVAL) - ntfs_log_error(invalid_ntfs_msg, device); - else if (errno == EIO) - ntfs_log_error("%s", corrupt_volume_msg); - else if (errno == EPERM) - ntfs_log_error("%s", hibernated_volume_msg); - else if (errno == EOPNOTSUPP) - ntfs_log_error("%s", unclean_journal_msg); - else if (errno == EBUSY) - ntfs_log_error("%s", opened_volume_msg); - else if (errno == ENXIO) - ntfs_log_error("%s", fakeraid_msg); - return NULL; - } - - if (NVolWasDirty(vol)) { - if (!(flags & NTFS_MNT_FORCE)) { - ntfs_log_error("%s", dirty_volume_msg); - ntfs_umount(vol, FALSE); - return NULL; - } - ntfs_log_error("WARNING: Dirty volume mount was forced by the " - "'force' mount option.\n"); - } - return vol; -} - -/** - * utils_parse_size - Convert a string representing a size - * @value: String to be parsed - * @size: Parsed size - * @scale: Whether or not to allow a suffix to scale the value - * - * Read a string and convert it to a number. Strings may be suffixed to scale - * them. Any number without a suffix is assumed to be in bytes. - * - * Suffix Description Multiple - * [tT] Terabytes 10^12 - * [gG] Gigabytes 10^9 - * [mM] Megabytes 10^6 - * [kK] Kilobytes 10^3 - * - * Notes: - * Only the first character of the suffix is read. - * The multipliers are decimal thousands, not binary: 1000, not 1024. - * If parse_size fails, @size will not be changed - * - * Return: 1 Success - * 0 Error, the string was malformed - */ -int utils_parse_size(const char *value, s64 *size, BOOL scale) -{ - long long result; - char *suffix = NULL; - - if (!value || !size) { - errno = EINVAL; - return 0; - } - - ntfs_log_debug("Parsing size '%s'.\n", value); - - result = strtoll(value, &suffix, 0); - if (result < 0 || errno == ERANGE) { - ntfs_log_error("Invalid size '%s'.\n", value); - return 0; - } - - if (!suffix) { - ntfs_log_error("Internal error, strtoll didn't return a suffix.\n"); - return 0; - } - - if (scale) { - switch (suffix[0]) { - case 't': case 'T': result *= 1000; - case 'g': case 'G': result *= 1000; - case 'm': case 'M': result *= 1000; - case 'k': case 'K': result *= 1000; - case '-': case 0: - break; - default: - ntfs_log_error("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix); - return 0; - } - } else { - if ((suffix[0] != '-') && (suffix[0] != 0)) { - ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value); - return 0; - } - } - - ntfs_log_debug("Parsed size = %lld.\n", result); - *size = result; - return 1; -} - -/** - * utils_parse_range - Convert a string representing a range of numbers - * @string: The string to be parsed - * @start: The beginning of the range will be stored here - * @finish: The end of the range will be stored here - * - * Read a string of the form n-m. If the lower end is missing, zero will be - * substituted. If the upper end is missing LONG_MAX will be used. If the - * string cannot be parsed correctly, @start and @finish will not be changed. - * - * Return: 1 Success, a valid string was found - * 0 Error, the string was not a valid range - */ -int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale) -{ - s64 a, b; - char *middle; - - if (!string || !start || !finish) { - errno = EINVAL; - return 0; - } - - middle = strchr(string, '-'); - if (string == middle) { - ntfs_log_debug("Range has no beginning, defaulting to 0.\n"); - a = 0; - } else { - if (!utils_parse_size(string, &a, scale)) - return 0; - } - - if (middle) { - if (middle[1] == 0) { - b = LONG_MAX; // XXX ULLONG_MAX - ntfs_log_debug("Range has no end, defaulting to %lld.\n", b); - } else { - if (!utils_parse_size(middle+1, &b, scale)) - return 0; - } - } else { - b = a; - } - - ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b); - - *start = a; - *finish = b; - return 1; -} - -/** - * find_attribute - Find an attribute of the given type - * @type: An attribute type, e.g. AT_FILE_NAME - * @ctx: A search context, created using ntfs_get_attr_search_ctx - * - * Using the search context to keep track, find the first/next occurrence of a - * given attribute type. - * - * N.B. This will return a pointer into @mft. As long as the search context - * has been created without an inode, it won't overflow the buffer. - * - * Return: Pointer Success, an attribute was found - * NULL Error, no matching attributes were found - */ -ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) -{ - if (!ctx) { - errno = EINVAL; - return NULL; - } - - if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) { - ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type); - return NULL; /* None / no more of that type */ - } - - ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type); - return ctx->attr; -} - -/** - * find_first_attribute - Find the first attribute of a given type - * @type: An attribute type, e.g. AT_FILE_NAME - * @mft: A buffer containing a raw MFT record - * - * Search through a raw MFT record for an attribute of a given type. - * The return value is a pointer into the MFT record that was supplied. - * - * N.B. This will return a pointer into @mft. The pointer won't stray outside - * the buffer, since we created the search context without an inode. - * - * Return: Pointer Success, an attribute was found - * NULL Error, no matching attributes were found - */ -ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *rec; - - if (!mft) { - errno = EINVAL; - return NULL; - } - - ctx = ntfs_attr_get_search_ctx(NULL, mft); - if (!ctx) { - ntfs_log_error("Couldn't create a search context.\n"); - return NULL; - } - - rec = find_attribute(type, ctx); - ntfs_attr_put_search_ctx(ctx); - if (rec) - ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type); - else - ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type); - return rec; -} - -/** - * utils_inode_get_name - * - * using inode - * get filename - * add name to list - * get parent - * if parent is 5 (/) stop - * get inode of parent - */ -#define max_path 20 -int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) -{ - // XXX option: names = posix/win32 or dos - // flags: path, filename, or both - - - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *rec; - FILE_NAME_ATTR *attr; - int name_space; - MFT_REF parent = FILE_root; - char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger? - int i, len, offset = 0; - - if (!inode || !buffer) { - errno = EINVAL; - return 0; - } - - vol = inode->vol; - - //ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names)); - memset(names, 0, sizeof(names)); - - for (i = 0; i < max_path; i++) { - - ctx = ntfs_attr_get_search_ctx(inode, NULL); - if (!ctx) { - ntfs_log_error("Couldn't create a search context.\n"); - return 0; - } - - //ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no); - - name_space = 4; - while ((rec = find_attribute(AT_FILE_NAME, ctx))) { - /* We know this will always be resident. */ - attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->u.res.value_offset)); - - if (attr->file_name_type > name_space) { //XXX find the ... - continue; - } - - name_space = attr->file_name_type; - parent = le64_to_cpu(attr->parent_directory); - - if (names[i]) { - free(names[i]); - names[i] = NULL; - } - - if (ntfs_ucstombs(attr->file_name, attr->file_name_length, - &names[i], 0) < 0) { - char *temp; - ntfs_log_error("Couldn't translate filename to current locale.\n"); - temp = ntfs_malloc(30); - if (!temp) - return 0; - snprintf(temp, 30, "<MFT%llu>", (unsigned - long long)inode->mft_no); - names[i] = temp; - } - - //ntfs_log_debug("names[%d] %s\n", i, names[i]); - //ntfs_log_debug("parent = %lld\n", MREF(parent)); - } - - ntfs_attr_put_search_ctx(ctx); - - if (i > 0) /* Don't close the original inode */ - ntfs_inode_close(inode); - - if (MREF(parent) == FILE_root) { /* The root directory, stop. */ - //ntfs_log_debug("inode 5\n"); - break; - } - - inode = ntfs_inode_open(vol, parent); - if (!inode) { - ntfs_log_error("Couldn't open inode %llu.\n", - (unsigned long long)MREF(parent)); - break; - } - } - - if (i >= max_path) { - /* If we get into an infinite loop, we'll end up here. */ - ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path); - return 0; - } - - /* Assemble the names in the correct order. */ - for (i = max_path; i >= 0; i--) { - if (!names[i]) - continue; - - len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]); - if (len >= (bufsize - offset)) { - ntfs_log_error("Pathname was truncated.\n"); - break; - } - - offset += len; - } - - /* Free all the allocated memory */ - for (i = 0; i < max_path; i++) - free(names[i]); - - ntfs_log_debug("Pathname: %s\n", buffer); - - return 1; -} -#undef max_path - -/** - * utils_attr_get_name - */ -int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize) -{ - int len, namelen; - char *name; - ATTR_DEF *attrdef; - - // flags: attr, name, or both - if (!attr || !buffer) { - errno = EINVAL; - return 0; - } - - attrdef = ntfs_attr_find_in_attrdef(vol, attr->type); - if (attrdef) { - name = NULL; - namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name)); - if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) { - ntfs_log_error("Couldn't translate attribute type to " - "current locale.\n"); - // <UNKNOWN>? - return 0; - } - len = snprintf(buffer, bufsize, "%s", name); - } else { - ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type); - len = snprintf(buffer, bufsize, "<UNKNOWN>"); - } - - if (len >= bufsize) { - ntfs_log_error("Attribute type was truncated.\n"); - return 0; - } - - if (!attr->name_length) { - return 0; - } - - buffer += len; - bufsize -= len; - - name = NULL; - namelen = attr->name_length; - if (ntfs_ucstombs((ntfschar *)((char *)attr + le16_to_cpu( - attr->name_offset)), namelen, &name, 0) < 0) { - ntfs_log_error("Couldn't translate attribute name to current " - "locale.\n"); - // <UNKNOWN>? - len = snprintf(buffer, bufsize, "<UNKNOWN>"); - return 0; - } - - len = snprintf(buffer, bufsize, "(%s)", name); - free(name); - - if (len >= bufsize) { - ntfs_log_error("Attribute name was truncated.\n"); - return 0; - } - - return 0; -} - -/** - * utils_cluster_in_use - Determine if a cluster is in use - * @vol: An ntfs volume obtained from ntfs_mount - * @lcn: The Logical Cluster Number to test - * - * The metadata file $Bitmap has one binary bit representing each cluster on - * disk. The bit will be set for each cluster that is in use. The function - * reads the relevant part of $Bitmap into a buffer and tests the bit. - * - * This function has a static buffer in which it caches a section of $Bitmap. - * If the lcn, being tested, lies outside the range, the buffer will be - * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the - * buffer. - * - * NOTE: Be very carefull with shifts by 3 everywhere in this function. - * - * Return: 1 Cluster is in use - * 0 Cluster is free space - * -1 Error occurred - */ -int utils_cluster_in_use(ntfs_volume *vol, long long lcn) -{ - static unsigned char buffer[512]; - static long long bmplcn = -(sizeof(buffer) << 3); - int byte, bit; - ntfs_attr *attr; - - if (!vol) { - errno = EINVAL; - return -1; - } - - /* Does lcn lie in the section of $Bitmap we already have cached? */ - if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof(buffer) << 3)))) { - ntfs_log_debug("Bit lies outside cache.\n"); - attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); - if (!attr) { - ntfs_log_perror("Couldn't open $Bitmap"); - return -1; - } - - /* Mark the buffer as in use, in case the read is shorter. */ - memset(buffer, 0xFF, sizeof(buffer)); - bmplcn = lcn & (~((sizeof(buffer) << 3) - 1)); - - if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer), - buffer) < 0) { - ntfs_log_perror("Couldn't read $Bitmap"); - ntfs_attr_close(attr); - return -1; - } - - ntfs_log_debug("Reloaded bitmap buffer.\n"); - ntfs_attr_close(attr); - } - - bit = 1 << (lcn & 7); - byte = (lcn >> 3) & (sizeof(buffer) - 1); - ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, " - "in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & - bit); - - return (buffer[byte] & bit); -} - -/** - * utils_mftrec_in_use - Determine if a MFT Record is in use - * @vol: An ntfs volume obtained from ntfs_mount - * @mref: MFT Reference (inode number) - * - * The metadata file $BITMAP has one binary bit representing each record in the - * MFT. The bit will be set for each record that is in use. The function - * reads the relevant part of $BITMAP into a buffer and tests the bit. - * - * This function has a static buffer in which it caches a section of $BITMAP. - * If the mref, being tested, lies outside the range, the buffer will be - * refreshed. - * - * Return: 1 MFT Record is in use - * 0 MFT Record is unused - * -1 Error occurred - */ -int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref) -{ - static u8 buffer[512]; - static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */ - int byte, bit; - - ntfs_log_trace("Entering.\n"); - - if (!vol) { - errno = EINVAL; - return -1; - } - - /* Does mref lie in the section of $Bitmap we already have cached? */ - if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref + - (sizeof(buffer) << 3)))) { - ntfs_log_debug("Bit lies outside cache.\n"); - - /* Mark the buffer as not in use, in case the read is shorter. */ - memset(buffer, 0, sizeof(buffer)); - bmpmref = mref & (~((sizeof(buffer) << 3) - 1)); - - if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) { - ntfs_log_perror("Couldn't read $MFT/$BITMAP"); - return -1; - } - - ntfs_log_debug("Reloaded bitmap buffer.\n"); - } - - bit = 1 << (mref & 7); - byte = (mref >> 3) & (sizeof(buffer) - 1); - ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit); - - return (buffer[byte] & bit); -} - -/** - * __metadata - */ -static int __metadata(ntfs_volume *vol, u64 num) -{ - if (num <= FILE_UpCase) - return 1; - if (!vol) - return -1; - if ((vol->major_ver == 3) && (num == FILE_Extend)) - return 1; - - return 0; -} - -/** - * utils_is_metadata - Determine if an inode represents a metadata file - * @inode: An ntfs inode to be tested - * - * A handful of files in the volume contain filesystem data - metadata. - * They can be identified by their inode number (offset in MFT/$DATA) or by - * their parent. - * - * Return: 1 inode is a metadata file - * 0 inode is not a metadata file - * -1 Error occurred - */ -int utils_is_metadata(ntfs_inode *inode) -{ - ntfs_volume *vol; - ATTR_RECORD *rec; - FILE_NAME_ATTR *attr; - MFT_RECORD *file; - u64 num; - - if (!inode) { - errno = EINVAL; - return -1; - } - - vol = inode->vol; - if (!vol) - return -1; - - num = inode->mft_no; - if (__metadata(vol, num) == 1) - return 1; - - file = inode->mrec; - if (file && (file->base_mft_record != 0)) { - num = MREF_LE(file->base_mft_record); - if (__metadata(vol, num) == 1) - return 1; - } - file = inode->mrec; - - rec = find_first_attribute(AT_FILE_NAME, inode->mrec); - if (!rec) - return -1; - - /* We know this will always be resident. */ - attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->u.res.value_offset)); - - num = MREF_LE(attr->parent_directory); - if ((num != FILE_root) && (__metadata(vol, num) == 1)) - return 1; - - return 0; -} - -/** - * utils_dump_mem - Display a block of memory in hex and ascii - * @buf: Buffer to be displayed - * @start: Offset into @buf to start from - * @length: Number of bytes to display - * @flags: Options to change the style of the output - * - * Display a block of memory in a tradition hex-dump manner. - * Optionally the ascii part can be turned off. - * - * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS). - * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the - * output); DM_NO_ASCII (only print the hex values). - */ -void utils_dump_mem(void *buf, int start, int length, int flags) -{ - int off, i, s, e, col; - u8 *mem = buf; - - s = start & ~15; // round down - e = (start + length + 15) & ~15; // round up - - for (off = s; off < e; off += 16) { - col = 30; - if (flags & DM_RED) - col += 1; - if (flags & DM_GREEN) - col += 2; - if (flags & DM_BLUE) - col += 4; - if (flags & DM_INDENT) - ntfs_log_debug("\t"); - if (flags & DM_BOLD) - ntfs_log_debug("\e[01m"); - if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) - ntfs_log_debug("\e[%dm", col); - if (off == s) - ntfs_log_debug("%6.6x ", start); - else - ntfs_log_debug("%6.6x ", off); - - for (i = 0; i < 16; i++) { - if ((i == 8) && (!(flags & DM_NO_DIVIDER))) - ntfs_log_debug(" -"); - if (((off+i) >= start) && ((off+i) < (start+length))) - ntfs_log_debug(" %02X", mem[off+i]); - else - ntfs_log_debug(" "); - } - if (!(flags & DM_NO_ASCII)) { - ntfs_log_debug(" "); - for (i = 0; i < 16; i++) { - if (((off+i) < start) || ((off+i) >= (start+length))) - ntfs_log_debug(" "); - else if (isprint(mem[off + i])) - ntfs_log_debug("%c", mem[off + i]); - else - ntfs_log_debug("."); - } - } - if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) - ntfs_log_debug("\e[0m"); - ntfs_log_debug("\n"); - } -} - - -/** - * mft_get_search_ctx - */ -struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol) -{ - struct mft_search_ctx *ctx; - - if (!vol) { - errno = EINVAL; - return NULL; - } - - ctx = calloc(1, sizeof *ctx); - - ctx->mft_num = -1; - ctx->vol = vol; - - return ctx; -} - -/** - * mft_put_search_ctx - */ -void mft_put_search_ctx(struct mft_search_ctx *ctx) -{ - if (!ctx) - return; - if (ctx->inode) - ntfs_inode_close(ctx->inode); - free(ctx); -} - -/** - * mft_next_record - */ -int mft_next_record(struct mft_search_ctx *ctx) -{ - s64 nr_mft_records; - ATTR_RECORD *attr10 = NULL; - ATTR_RECORD *attr20 = NULL; - ATTR_RECORD *attr80 = NULL; - ntfs_attr_search_ctx *attr_ctx; - - if (!ctx) { - errno = EINVAL; - return -1; - } - - if (ctx->inode) { - ntfs_inode_close(ctx->inode); - ctx->inode = NULL; - } - - nr_mft_records = ctx->vol->mft_na->initialized_size >> - ctx->vol->mft_record_size_bits; - - for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) { - int in_use; - - ctx->flags_match = 0; - in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num); - if (in_use == -1) { - ntfs_log_error("Error reading inode %llu. Aborting.\n", - (unsigned long long)ctx->mft_num); - return -1; - } - - if (in_use) { - ctx->flags_match |= FEMR_IN_USE; - - ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num); - if (ctx->inode == NULL) { - ntfs_log_error("Error reading inode %llu.\n", (unsigned - long long) ctx->mft_num); - continue; - } - - attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec); - attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, ctx->inode->mrec); - attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec); - - if (attr10) - ctx->flags_match |= FEMR_BASE_RECORD; - else - ctx->flags_match |= FEMR_NOT_BASE_RECORD; - - if (attr20) - ctx->flags_match |= FEMR_BASE_RECORD; - - if (attr80) - ctx->flags_match |= FEMR_FILE; - - if (ctx->flags_search & FEMR_DIR) { - attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL); - if (attr_ctx) { - if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0) - ctx->flags_match |= FEMR_DIR; - - ntfs_attr_put_search_ctx(attr_ctx); - } else { - ntfs_log_error("Couldn't create a search context.\n"); - return -1; - } - } - - switch (utils_is_metadata(ctx->inode)) { - case 1: ctx->flags_match |= FEMR_METADATA; break; - case 0: ctx->flags_match |= FEMR_NOT_METADATA; break; - default: - ctx->flags_match |= FEMR_NOT_METADATA; break; - //ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num); - //return -1; - } - - } else { // !in_use - ntfs_attr *mft; - - ctx->flags_match |= FEMR_NOT_IN_USE; - - ctx->inode = calloc(1, sizeof(*ctx->inode)); - if (!ctx->inode) { - ntfs_log_error("Out of memory. Aborting.\n"); - return -1; - } - - ctx->inode->mft_no = ctx->mft_num; - ctx->inode->vol = ctx->vol; - ctx->inode->mrec = ntfs_malloc(ctx->vol->mft_record_size); - if (!ctx->inode->mrec) { - free(ctx->inode); // == ntfs_inode_close - return -1; - } - - mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA, - AT_UNNAMED, 0); - if (!mft) { - ntfs_log_perror("Couldn't open $MFT/$DATA"); - // free / close - return -1; - } - - if (ntfs_attr_pread(mft, ctx->vol->mft_record_size * ctx->mft_num, ctx->vol->mft_record_size, ctx->inode->mrec) < ctx->vol->mft_record_size) { - ntfs_log_perror("Couldn't read MFT Record %llu", - (unsigned long long) ctx->mft_num); - // free / close - ntfs_attr_close(mft); - return -1; - } - - ntfs_attr_close(mft); - } - - if (ctx->flags_match & ctx->flags_search) { - break; - } - - if (ntfs_inode_close(ctx->inode)) { - ntfs_log_error("Error closing inode %llu.\n", - (unsigned long long)ctx->mft_num); - return -errno; - } - - ctx->inode = NULL; - } - - return (ctx->inode == NULL); -} - - |