xref: /onnv-gate/usr/src/cmd/ntfsprogs/utils.c (revision 10465:f9789e1a1626)
19663SMark.Logan@Sun.COM /**
29663SMark.Logan@Sun.COM  * utils.c - Part of the Linux-NTFS project.
39663SMark.Logan@Sun.COM  *
49663SMark.Logan@Sun.COM  * Copyright (c) 2002-2005 Richard Russon
59663SMark.Logan@Sun.COM  * Copyright (c) 2003-2006 Anton Altaparmakov
69663SMark.Logan@Sun.COM  * Copyright (c) 2003 Lode Leroy
79663SMark.Logan@Sun.COM  * Copyright (c) 2005-2007 Yura Pakhuchiy
89663SMark.Logan@Sun.COM  *
99663SMark.Logan@Sun.COM  * A set of shared functions for ntfs utilities
109663SMark.Logan@Sun.COM  *
119663SMark.Logan@Sun.COM  * This program is free software; you can redistribute it and/or modify
129663SMark.Logan@Sun.COM  * it under the terms of the GNU General Public License as published by
139663SMark.Logan@Sun.COM  * the Free Software Foundation; either version 2 of the License, or
149663SMark.Logan@Sun.COM  * (at your option) any later version.
159663SMark.Logan@Sun.COM  *
169663SMark.Logan@Sun.COM  * This program is distributed in the hope that it will be useful,
179663SMark.Logan@Sun.COM  * but WITHOUT ANY WARRANTY; without even the implied warranty of
189663SMark.Logan@Sun.COM  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
199663SMark.Logan@Sun.COM  * GNU General Public License for more details.
209663SMark.Logan@Sun.COM  *
219663SMark.Logan@Sun.COM  * You should have received a copy of the GNU General Public License
229663SMark.Logan@Sun.COM  * along with this program (in the main directory of the Linux-NTFS
239663SMark.Logan@Sun.COM  * distribution in the file COPYING); if not, write to the Free Software
249663SMark.Logan@Sun.COM  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
259663SMark.Logan@Sun.COM  */
269663SMark.Logan@Sun.COM 
279663SMark.Logan@Sun.COM #ifdef HAVE_CONFIG_H
289663SMark.Logan@Sun.COM #include "config.h"
299663SMark.Logan@Sun.COM #endif
309663SMark.Logan@Sun.COM 
319663SMark.Logan@Sun.COM #ifdef HAVE_STDIO_H
329663SMark.Logan@Sun.COM #include <stdio.h>
339663SMark.Logan@Sun.COM #endif
349663SMark.Logan@Sun.COM #ifdef HAVE_STDARG_H
359663SMark.Logan@Sun.COM #include <stdarg.h>
369663SMark.Logan@Sun.COM #endif
379663SMark.Logan@Sun.COM #ifdef HAVE_ERRNO_H
389663SMark.Logan@Sun.COM #include <errno.h>
399663SMark.Logan@Sun.COM #endif
409663SMark.Logan@Sun.COM #ifdef HAVE_SYS_TYPES_H
419663SMark.Logan@Sun.COM #include <sys/types.h>
429663SMark.Logan@Sun.COM #endif
439663SMark.Logan@Sun.COM #ifdef HAVE_SYS_STAT_H
449663SMark.Logan@Sun.COM #include <sys/stat.h>
459663SMark.Logan@Sun.COM #endif
469663SMark.Logan@Sun.COM #ifdef HAVE_UNISTD_H
479663SMark.Logan@Sun.COM #include <unistd.h>
489663SMark.Logan@Sun.COM #endif
499663SMark.Logan@Sun.COM #ifdef HAVE_STRING_H
509663SMark.Logan@Sun.COM #include <string.h>
519663SMark.Logan@Sun.COM #endif
529663SMark.Logan@Sun.COM #ifdef HAVE_LOCALE_H
539663SMark.Logan@Sun.COM #include <locale.h>
549663SMark.Logan@Sun.COM #endif
559663SMark.Logan@Sun.COM #ifdef HAVE_LIBINTL_H
569663SMark.Logan@Sun.COM #include <libintl.h>
579663SMark.Logan@Sun.COM #endif
589663SMark.Logan@Sun.COM #ifdef HAVE_STDLIB_H
599663SMark.Logan@Sun.COM #include <stdlib.h>
609663SMark.Logan@Sun.COM #endif
619663SMark.Logan@Sun.COM #ifdef HAVE_LIMITS_H
629663SMark.Logan@Sun.COM #include <limits.h>
639663SMark.Logan@Sun.COM #endif
649663SMark.Logan@Sun.COM #ifdef HAVE_CTYPE_H
659663SMark.Logan@Sun.COM #include <ctype.h>
669663SMark.Logan@Sun.COM #endif
679663SMark.Logan@Sun.COM 
68*10465SMark.Logan@Sun.COM #include "compat.h"
699663SMark.Logan@Sun.COM #include "utils.h"
709663SMark.Logan@Sun.COM #include "types.h"
719663SMark.Logan@Sun.COM #include "volume.h"
729663SMark.Logan@Sun.COM #include "debug.h"
739663SMark.Logan@Sun.COM #include "dir.h"
749663SMark.Logan@Sun.COM #include "version.h"
759663SMark.Logan@Sun.COM #include "logging.h"
769663SMark.Logan@Sun.COM 
779663SMark.Logan@Sun.COM const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n";
789663SMark.Logan@Sun.COM const char *ntfs_home = "Linux NTFS homepage: http://www.linux-ntfs.org\n";
799663SMark.Logan@Sun.COM const char *ntfs_gpl = "This program is free software, released under the GNU "
809663SMark.Logan@Sun.COM 	"General Public License\nand you are welcome to redistribute it under "
819663SMark.Logan@Sun.COM 	"certain conditions.  It comes with\nABSOLUTELY NO WARRANTY; for "
829663SMark.Logan@Sun.COM 	"details read the GNU General Public License to be\nfound in the file "
839663SMark.Logan@Sun.COM 	"\"COPYING\" distributed with this program, or online at:\n"
849663SMark.Logan@Sun.COM 	"http://www.gnu.org/copyleft/gpl.html\n";
859663SMark.Logan@Sun.COM 
869663SMark.Logan@Sun.COM static const char *invalid_ntfs_msg =
879663SMark.Logan@Sun.COM "The device '%s' doesn't have a valid NTFS.\n"
889663SMark.Logan@Sun.COM "Maybe you selected the wrong device? Or the whole disk instead of a\n"
899663SMark.Logan@Sun.COM "partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n";
909663SMark.Logan@Sun.COM 
919663SMark.Logan@Sun.COM static const char *corrupt_volume_msg =
929663SMark.Logan@Sun.COM "NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n"
939663SMark.Logan@Sun.COM "The usage of the /f parameter is very IMPORTANT! No modification was\n"
949663SMark.Logan@Sun.COM "made to NTFS by this software.\n";
959663SMark.Logan@Sun.COM 
969663SMark.Logan@Sun.COM static const char *hibernated_volume_msg =
979663SMark.Logan@Sun.COM "The NTFS partition is hibernated. Please resume Windows and turned it \n"
989663SMark.Logan@Sun.COM "off properly, so mounting could be done safely.\n";
999663SMark.Logan@Sun.COM 
1009663SMark.Logan@Sun.COM static const char *unclean_journal_msg =
1019663SMark.Logan@Sun.COM "Access is denied because the NTFS journal file is unclean. Choices are:\n"
1029663SMark.Logan@Sun.COM " A) Shutdown Windows properly.\n"
1039663SMark.Logan@Sun.COM " B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n"
1049663SMark.Logan@Sun.COM "    notification area before disconnecting the device.\n"
1059663SMark.Logan@Sun.COM " C) Use 'Eject' from Windows Explorer to safely remove the device.\n"
1069663SMark.Logan@Sun.COM " D) If you ran chkdsk previously then boot Windows again which will\n"
1079663SMark.Logan@Sun.COM "    automatically initialize the journal.\n"
1089663SMark.Logan@Sun.COM " E) Submit 'force' option (WARNING: This solution it not recommended).\n"
1099663SMark.Logan@Sun.COM " F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n";
1109663SMark.Logan@Sun.COM 
1119663SMark.Logan@Sun.COM static const char *opened_volume_msg =
1129663SMark.Logan@Sun.COM "Access is denied because the NTFS volume is already exclusively opened.\n"
1139663SMark.Logan@Sun.COM "The volume may be already mounted, or another software may use it which\n"
1149663SMark.Logan@Sun.COM "could be identified for example by the help of the 'fuser' command.\n";
1159663SMark.Logan@Sun.COM 
1169663SMark.Logan@Sun.COM static const char *dirty_volume_msg =
1179663SMark.Logan@Sun.COM "Volume is scheduled for check.\n"
1189663SMark.Logan@Sun.COM "Please boot into Windows TWICE, or use the 'force' option.\n"
1199663SMark.Logan@Sun.COM "NOTE: If you had not scheduled check and last time accessed this volume\n"
1209663SMark.Logan@Sun.COM "using ntfsmount and shutdown system properly, then init scripts in your\n"
1219663SMark.Logan@Sun.COM "distribution are broken. Please report to your distribution developers\n"
1229663SMark.Logan@Sun.COM "(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n"
1239663SMark.Logan@Sun.COM "shutdown instead of proper umount.\n";
1249663SMark.Logan@Sun.COM 
1259663SMark.Logan@Sun.COM static const char *fakeraid_msg =
1269663SMark.Logan@Sun.COM "You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n"
1279663SMark.Logan@Sun.COM "different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n"
1289663SMark.Logan@Sun.COM "to mount NTFS. Please see the 'dmraid' documentation for help.\n";
1299663SMark.Logan@Sun.COM 
1309663SMark.Logan@Sun.COM /**
1319663SMark.Logan@Sun.COM  * utils_set_locale
1329663SMark.Logan@Sun.COM  */
utils_set_locale(void)1339663SMark.Logan@Sun.COM int utils_set_locale(void)
1349663SMark.Logan@Sun.COM {
1359663SMark.Logan@Sun.COM 	const char *locale;
1369663SMark.Logan@Sun.COM 
1379663SMark.Logan@Sun.COM 	locale = setlocale(LC_ALL, "");
1389663SMark.Logan@Sun.COM 	if (!locale) {
1399663SMark.Logan@Sun.COM 		locale = setlocale(LC_ALL, NULL);
1409663SMark.Logan@Sun.COM 		ntfs_log_error("Failed to set locale, using default '%s'.\n",
1419663SMark.Logan@Sun.COM 				locale);
1429663SMark.Logan@Sun.COM 		return 1;
1439663SMark.Logan@Sun.COM 	} else {
1449663SMark.Logan@Sun.COM 		return 0;
1459663SMark.Logan@Sun.COM 	}
1469663SMark.Logan@Sun.COM }
1479663SMark.Logan@Sun.COM 
1489663SMark.Logan@Sun.COM /**
1499663SMark.Logan@Sun.COM  * utils_valid_device - Perform some safety checks on the device, before start
1509663SMark.Logan@Sun.COM  * @name:   Full pathname of the device/file to work with
1519663SMark.Logan@Sun.COM  * @force:  Continue regardless of problems
1529663SMark.Logan@Sun.COM  *
1539663SMark.Logan@Sun.COM  * Check that the name refers to a device and that is isn't already mounted.
1549663SMark.Logan@Sun.COM  * These checks can be overridden by using the force option.
1559663SMark.Logan@Sun.COM  *
1569663SMark.Logan@Sun.COM  * Return:  1  Success, we can continue
1579663SMark.Logan@Sun.COM  *	    0  Error, we cannot use this device
1589663SMark.Logan@Sun.COM  */
utils_valid_device(const char * name,int force)1599663SMark.Logan@Sun.COM int utils_valid_device(const char *name, int force)
1609663SMark.Logan@Sun.COM {
1619663SMark.Logan@Sun.COM 	unsigned long mnt_flags = 0;
1629663SMark.Logan@Sun.COM 	struct stat st;
1639663SMark.Logan@Sun.COM 
1649663SMark.Logan@Sun.COM #ifdef __CYGWIN32__
1659663SMark.Logan@Sun.COM 	/* FIXME: This doesn't work for Cygwin, so just return success. */
1669663SMark.Logan@Sun.COM 	return 1;
1679663SMark.Logan@Sun.COM #endif
1689663SMark.Logan@Sun.COM 	if (!name) {
1699663SMark.Logan@Sun.COM 		errno = EINVAL;
1709663SMark.Logan@Sun.COM 		return 0;
1719663SMark.Logan@Sun.COM 	}
1729663SMark.Logan@Sun.COM 
1739663SMark.Logan@Sun.COM 	if (stat(name, &st) == -1) {
1749663SMark.Logan@Sun.COM 		if (errno == ENOENT)
1759663SMark.Logan@Sun.COM 			ntfs_log_error("The device %s doesn't exist\n", name);
1769663SMark.Logan@Sun.COM 		else
1779663SMark.Logan@Sun.COM 			ntfs_log_perror("Error getting information about %s",
1789663SMark.Logan@Sun.COM 					name);
1799663SMark.Logan@Sun.COM 		return 0;
1809663SMark.Logan@Sun.COM 	}
1819663SMark.Logan@Sun.COM 
1829663SMark.Logan@Sun.COM 	/* Make sure the file system is not mounted. */
1839663SMark.Logan@Sun.COM 	if (ntfs_check_if_mounted(name, &mnt_flags)) {
1849663SMark.Logan@Sun.COM 		ntfs_log_perror("Failed to determine whether %s is mounted",
1859663SMark.Logan@Sun.COM 				name);
1869663SMark.Logan@Sun.COM 		if (!force) {
1879663SMark.Logan@Sun.COM 			ntfs_log_error("Use the force option to ignore this "
1889663SMark.Logan@Sun.COM 					"error.\n");
1899663SMark.Logan@Sun.COM 			return 0;
1909663SMark.Logan@Sun.COM 		}
1919663SMark.Logan@Sun.COM 		ntfs_log_warning("Forced to continue.\n");
1929663SMark.Logan@Sun.COM 	} else if (mnt_flags & NTFS_MF_MOUNTED) {
1939663SMark.Logan@Sun.COM 		if (!force) {
1949663SMark.Logan@Sun.COM 			ntfs_log_error("%s", opened_volume_msg);
1959663SMark.Logan@Sun.COM 			ntfs_log_error("You can use force option to avoid this "
1969663SMark.Logan@Sun.COM 					"check, but this is not recommended\n"
1979663SMark.Logan@Sun.COM 					"and may lead to data corruption.\n");
1989663SMark.Logan@Sun.COM 			return 0;
1999663SMark.Logan@Sun.COM 		}
2009663SMark.Logan@Sun.COM 		ntfs_log_warning("Forced to continue.\n");
2019663SMark.Logan@Sun.COM 	}
2029663SMark.Logan@Sun.COM 
2039663SMark.Logan@Sun.COM 	return 1;
2049663SMark.Logan@Sun.COM }
2059663SMark.Logan@Sun.COM 
2069663SMark.Logan@Sun.COM /**
2079663SMark.Logan@Sun.COM  * utils_mount_volume - Mount an NTFS volume
2089663SMark.Logan@Sun.COM  */
utils_mount_volume(const char * device,ntfs_mount_flags flags)2099663SMark.Logan@Sun.COM ntfs_volume * utils_mount_volume(const char *device, ntfs_mount_flags flags)
2109663SMark.Logan@Sun.COM {
2119663SMark.Logan@Sun.COM 	ntfs_volume *vol;
2129663SMark.Logan@Sun.COM 
2139663SMark.Logan@Sun.COM 	if (!device) {
2149663SMark.Logan@Sun.COM 		errno = EINVAL;
2159663SMark.Logan@Sun.COM 		return NULL;
2169663SMark.Logan@Sun.COM 	}
2179663SMark.Logan@Sun.COM 
2189663SMark.Logan@Sun.COM 	if (!utils_valid_device(device, flags & NTFS_MNT_FORCE))
2199663SMark.Logan@Sun.COM 		return NULL;
2209663SMark.Logan@Sun.COM 
2219663SMark.Logan@Sun.COM 	vol = ntfs_mount(device, flags);
2229663SMark.Logan@Sun.COM 	if (!vol) {
2239663SMark.Logan@Sun.COM 		ntfs_log_perror("Failed to mount '%s'", device);
2249663SMark.Logan@Sun.COM 		if (errno == EINVAL)
2259663SMark.Logan@Sun.COM 			ntfs_log_error(invalid_ntfs_msg, device);
2269663SMark.Logan@Sun.COM 		else if (errno == EIO)
2279663SMark.Logan@Sun.COM 			ntfs_log_error("%s", corrupt_volume_msg);
2289663SMark.Logan@Sun.COM 		else if (errno == EPERM)
2299663SMark.Logan@Sun.COM 			ntfs_log_error("%s", hibernated_volume_msg);
2309663SMark.Logan@Sun.COM 		else if (errno == EOPNOTSUPP)
2319663SMark.Logan@Sun.COM 			ntfs_log_error("%s", unclean_journal_msg);
2329663SMark.Logan@Sun.COM 		else if (errno == EBUSY)
2339663SMark.Logan@Sun.COM 			ntfs_log_error("%s", opened_volume_msg);
2349663SMark.Logan@Sun.COM 		else if (errno == ENXIO)
2359663SMark.Logan@Sun.COM 			ntfs_log_error("%s", fakeraid_msg);
2369663SMark.Logan@Sun.COM 		return NULL;
2379663SMark.Logan@Sun.COM 	}
2389663SMark.Logan@Sun.COM 
2399663SMark.Logan@Sun.COM 	if (NVolWasDirty(vol)) {
2409663SMark.Logan@Sun.COM 		if (!(flags & NTFS_MNT_FORCE)) {
2419663SMark.Logan@Sun.COM 			ntfs_log_error("%s", dirty_volume_msg);
2429663SMark.Logan@Sun.COM 			ntfs_umount(vol, FALSE);
2439663SMark.Logan@Sun.COM 			return NULL;
2449663SMark.Logan@Sun.COM 		}
2459663SMark.Logan@Sun.COM 		ntfs_log_error("WARNING: Dirty volume mount was forced by the "
2469663SMark.Logan@Sun.COM 				"'force' mount option.\n");
2479663SMark.Logan@Sun.COM 	}
2489663SMark.Logan@Sun.COM 	return vol;
2499663SMark.Logan@Sun.COM }
2509663SMark.Logan@Sun.COM 
2519663SMark.Logan@Sun.COM /**
2529663SMark.Logan@Sun.COM  * utils_parse_size - Convert a string representing a size
2539663SMark.Logan@Sun.COM  * @value:  String to be parsed
2549663SMark.Logan@Sun.COM  * @size:   Parsed size
2559663SMark.Logan@Sun.COM  * @scale:  Whether or not to allow a suffix to scale the value
2569663SMark.Logan@Sun.COM  *
2579663SMark.Logan@Sun.COM  * Read a string and convert it to a number.  Strings may be suffixed to scale
2589663SMark.Logan@Sun.COM  * them.  Any number without a suffix is assumed to be in bytes.
2599663SMark.Logan@Sun.COM  *
2609663SMark.Logan@Sun.COM  * Suffix  Description  Multiple
2619663SMark.Logan@Sun.COM  *  [tT]    Terabytes     10^12
2629663SMark.Logan@Sun.COM  *  [gG]    Gigabytes     10^9
2639663SMark.Logan@Sun.COM  *  [mM]    Megabytes     10^6
2649663SMark.Logan@Sun.COM  *  [kK]    Kilobytes     10^3
2659663SMark.Logan@Sun.COM  *
2669663SMark.Logan@Sun.COM  * Notes:
2679663SMark.Logan@Sun.COM  *     Only the first character of the suffix is read.
2689663SMark.Logan@Sun.COM  *     The multipliers are decimal thousands, not binary: 1000, not 1024.
2699663SMark.Logan@Sun.COM  *     If parse_size fails, @size will not be changed
2709663SMark.Logan@Sun.COM  *
2719663SMark.Logan@Sun.COM  * Return:  1  Success
2729663SMark.Logan@Sun.COM  *	    0  Error, the string was malformed
2739663SMark.Logan@Sun.COM  */
utils_parse_size(const char * value,s64 * size,BOOL scale)2749663SMark.Logan@Sun.COM int utils_parse_size(const char *value, s64 *size, BOOL scale)
2759663SMark.Logan@Sun.COM {
2769663SMark.Logan@Sun.COM 	long long result;
2779663SMark.Logan@Sun.COM 	char *suffix = NULL;
2789663SMark.Logan@Sun.COM 
2799663SMark.Logan@Sun.COM 	if (!value || !size) {
2809663SMark.Logan@Sun.COM 		errno = EINVAL;
2819663SMark.Logan@Sun.COM 		return 0;
2829663SMark.Logan@Sun.COM 	}
2839663SMark.Logan@Sun.COM 
2849663SMark.Logan@Sun.COM 	ntfs_log_debug("Parsing size '%s'.\n", value);
2859663SMark.Logan@Sun.COM 
2869663SMark.Logan@Sun.COM 	result = strtoll(value, &suffix, 0);
2879663SMark.Logan@Sun.COM 	if (result < 0 || errno == ERANGE) {
2889663SMark.Logan@Sun.COM 		ntfs_log_error("Invalid size '%s'.\n", value);
2899663SMark.Logan@Sun.COM 		return 0;
2909663SMark.Logan@Sun.COM 	}
2919663SMark.Logan@Sun.COM 
2929663SMark.Logan@Sun.COM 	if (!suffix) {
2939663SMark.Logan@Sun.COM 		ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
2949663SMark.Logan@Sun.COM 		return 0;
2959663SMark.Logan@Sun.COM 	}
2969663SMark.Logan@Sun.COM 
2979663SMark.Logan@Sun.COM 	if (scale) {
2989663SMark.Logan@Sun.COM 		switch (suffix[0]) {
2999663SMark.Logan@Sun.COM 			case 't': case 'T': result *= 1000;
3009663SMark.Logan@Sun.COM 			case 'g': case 'G': result *= 1000;
3019663SMark.Logan@Sun.COM 			case 'm': case 'M': result *= 1000;
3029663SMark.Logan@Sun.COM 			case 'k': case 'K': result *= 1000;
3039663SMark.Logan@Sun.COM 			case '-': case 0:
3049663SMark.Logan@Sun.COM 				break;
3059663SMark.Logan@Sun.COM 			default:
3069663SMark.Logan@Sun.COM 				ntfs_log_error("Invalid size suffix '%s'.  Use T, G, M, or K.\n", suffix);
3079663SMark.Logan@Sun.COM 				return 0;
3089663SMark.Logan@Sun.COM 		}
3099663SMark.Logan@Sun.COM 	} else {
3109663SMark.Logan@Sun.COM 		if ((suffix[0] != '-') && (suffix[0] != 0)) {
3119663SMark.Logan@Sun.COM 			ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value);
3129663SMark.Logan@Sun.COM 			return 0;
3139663SMark.Logan@Sun.COM 		}
3149663SMark.Logan@Sun.COM 	}
3159663SMark.Logan@Sun.COM 
3169663SMark.Logan@Sun.COM 	ntfs_log_debug("Parsed size = %lld.\n", result);
3179663SMark.Logan@Sun.COM 	*size = result;
3189663SMark.Logan@Sun.COM 	return 1;
3199663SMark.Logan@Sun.COM }
3209663SMark.Logan@Sun.COM 
3219663SMark.Logan@Sun.COM /**
3229663SMark.Logan@Sun.COM  * utils_parse_range - Convert a string representing a range of numbers
3239663SMark.Logan@Sun.COM  * @string:  The string to be parsed
3249663SMark.Logan@Sun.COM  * @start:   The beginning of the range will be stored here
3259663SMark.Logan@Sun.COM  * @finish:  The end of the range will be stored here
3269663SMark.Logan@Sun.COM  *
3279663SMark.Logan@Sun.COM  * Read a string of the form n-m.  If the lower end is missing, zero will be
3289663SMark.Logan@Sun.COM  * substituted.  If the upper end is missing LONG_MAX will be used.  If the
3299663SMark.Logan@Sun.COM  * string cannot be parsed correctly, @start and @finish will not be changed.
3309663SMark.Logan@Sun.COM  *
3319663SMark.Logan@Sun.COM  * Return:  1  Success, a valid string was found
3329663SMark.Logan@Sun.COM  *	    0  Error, the string was not a valid range
3339663SMark.Logan@Sun.COM  */
utils_parse_range(const char * string,s64 * start,s64 * finish,BOOL scale)3349663SMark.Logan@Sun.COM int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale)
3359663SMark.Logan@Sun.COM {
3369663SMark.Logan@Sun.COM 	s64 a, b;
3379663SMark.Logan@Sun.COM 	char *middle;
3389663SMark.Logan@Sun.COM 
3399663SMark.Logan@Sun.COM 	if (!string || !start || !finish) {
3409663SMark.Logan@Sun.COM 		errno = EINVAL;
3419663SMark.Logan@Sun.COM 		return 0;
3429663SMark.Logan@Sun.COM 	}
3439663SMark.Logan@Sun.COM 
3449663SMark.Logan@Sun.COM 	middle = strchr(string, '-');
3459663SMark.Logan@Sun.COM 	if (string == middle) {
3469663SMark.Logan@Sun.COM 		ntfs_log_debug("Range has no beginning, defaulting to 0.\n");
3479663SMark.Logan@Sun.COM 		a = 0;
3489663SMark.Logan@Sun.COM 	} else {
3499663SMark.Logan@Sun.COM 		if (!utils_parse_size(string, &a, scale))
3509663SMark.Logan@Sun.COM 			return 0;
3519663SMark.Logan@Sun.COM 	}
3529663SMark.Logan@Sun.COM 
3539663SMark.Logan@Sun.COM 	if (middle) {
3549663SMark.Logan@Sun.COM 		if (middle[1] == 0) {
3559663SMark.Logan@Sun.COM 			b = LONG_MAX;		// XXX ULLONG_MAX
3569663SMark.Logan@Sun.COM 			ntfs_log_debug("Range has no end, defaulting to %lld.\n", b);
3579663SMark.Logan@Sun.COM 		} else {
3589663SMark.Logan@Sun.COM 			if (!utils_parse_size(middle+1, &b, scale))
3599663SMark.Logan@Sun.COM 				return 0;
3609663SMark.Logan@Sun.COM 		}
3619663SMark.Logan@Sun.COM 	} else {
3629663SMark.Logan@Sun.COM 		b = a;
3639663SMark.Logan@Sun.COM 	}
3649663SMark.Logan@Sun.COM 
3659663SMark.Logan@Sun.COM 	ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b);
3669663SMark.Logan@Sun.COM 
3679663SMark.Logan@Sun.COM 	*start  = a;
3689663SMark.Logan@Sun.COM 	*finish = b;
3699663SMark.Logan@Sun.COM 	return 1;
3709663SMark.Logan@Sun.COM }
3719663SMark.Logan@Sun.COM 
3729663SMark.Logan@Sun.COM /**
3739663SMark.Logan@Sun.COM  * find_attribute - Find an attribute of the given type
3749663SMark.Logan@Sun.COM  * @type:  An attribute type, e.g. AT_FILE_NAME
3759663SMark.Logan@Sun.COM  * @ctx:   A search context, created using ntfs_get_attr_search_ctx
3769663SMark.Logan@Sun.COM  *
3779663SMark.Logan@Sun.COM  * Using the search context to keep track, find the first/next occurrence of a
3789663SMark.Logan@Sun.COM  * given attribute type.
3799663SMark.Logan@Sun.COM  *
3809663SMark.Logan@Sun.COM  * N.B.  This will return a pointer into @mft.  As long as the search context
3819663SMark.Logan@Sun.COM  *       has been created without an inode, it won't overflow the buffer.
3829663SMark.Logan@Sun.COM  *
3839663SMark.Logan@Sun.COM  * Return:  Pointer  Success, an attribute was found
3849663SMark.Logan@Sun.COM  *	    NULL     Error, no matching attributes were found
3859663SMark.Logan@Sun.COM  */
find_attribute(const ATTR_TYPES type,ntfs_attr_search_ctx * ctx)3869663SMark.Logan@Sun.COM ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
3879663SMark.Logan@Sun.COM {
3889663SMark.Logan@Sun.COM 	if (!ctx) {
3899663SMark.Logan@Sun.COM 		errno = EINVAL;
3909663SMark.Logan@Sun.COM 		return NULL;
3919663SMark.Logan@Sun.COM 	}
3929663SMark.Logan@Sun.COM 
3939663SMark.Logan@Sun.COM 	if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) {
3949663SMark.Logan@Sun.COM 		ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type);
3959663SMark.Logan@Sun.COM 		return NULL;	/* None / no more of that type */
3969663SMark.Logan@Sun.COM 	}
3979663SMark.Logan@Sun.COM 
3989663SMark.Logan@Sun.COM 	ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type);
3999663SMark.Logan@Sun.COM 	return ctx->attr;
4009663SMark.Logan@Sun.COM }
4019663SMark.Logan@Sun.COM 
4029663SMark.Logan@Sun.COM /**
4039663SMark.Logan@Sun.COM  * find_first_attribute - Find the first attribute of a given type
4049663SMark.Logan@Sun.COM  * @type:  An attribute type, e.g. AT_FILE_NAME
4059663SMark.Logan@Sun.COM  * @mft:   A buffer containing a raw MFT record
4069663SMark.Logan@Sun.COM  *
4079663SMark.Logan@Sun.COM  * Search through a raw MFT record for an attribute of a given type.
4089663SMark.Logan@Sun.COM  * The return value is a pointer into the MFT record that was supplied.
4099663SMark.Logan@Sun.COM  *
4109663SMark.Logan@Sun.COM  * N.B.  This will return a pointer into @mft.  The pointer won't stray outside
4119663SMark.Logan@Sun.COM  *       the buffer, since we created the search context without an inode.
4129663SMark.Logan@Sun.COM  *
4139663SMark.Logan@Sun.COM  * Return:  Pointer  Success, an attribute was found
4149663SMark.Logan@Sun.COM  *	    NULL     Error, no matching attributes were found
4159663SMark.Logan@Sun.COM  */
find_first_attribute(const ATTR_TYPES type,MFT_RECORD * mft)4169663SMark.Logan@Sun.COM ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft)
4179663SMark.Logan@Sun.COM {
4189663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ctx;
4199663SMark.Logan@Sun.COM 	ATTR_RECORD *rec;
4209663SMark.Logan@Sun.COM 
4219663SMark.Logan@Sun.COM 	if (!mft) {
4229663SMark.Logan@Sun.COM 		errno = EINVAL;
4239663SMark.Logan@Sun.COM 		return NULL;
4249663SMark.Logan@Sun.COM 	}
4259663SMark.Logan@Sun.COM 
4269663SMark.Logan@Sun.COM 	ctx = ntfs_attr_get_search_ctx(NULL, mft);
4279663SMark.Logan@Sun.COM 	if (!ctx) {
4289663SMark.Logan@Sun.COM 		ntfs_log_error("Couldn't create a search context.\n");
4299663SMark.Logan@Sun.COM 		return NULL;
4309663SMark.Logan@Sun.COM 	}
4319663SMark.Logan@Sun.COM 
4329663SMark.Logan@Sun.COM 	rec = find_attribute(type, ctx);
4339663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(ctx);
4349663SMark.Logan@Sun.COM 	if (rec)
4359663SMark.Logan@Sun.COM 		ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type);
4369663SMark.Logan@Sun.COM 	else
4379663SMark.Logan@Sun.COM 		ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type);
4389663SMark.Logan@Sun.COM 	return rec;
4399663SMark.Logan@Sun.COM }
4409663SMark.Logan@Sun.COM 
4419663SMark.Logan@Sun.COM /**
4429663SMark.Logan@Sun.COM  * utils_inode_get_name
4439663SMark.Logan@Sun.COM  *
4449663SMark.Logan@Sun.COM  * using inode
4459663SMark.Logan@Sun.COM  * get filename
4469663SMark.Logan@Sun.COM  * add name to list
4479663SMark.Logan@Sun.COM  * get parent
4489663SMark.Logan@Sun.COM  * if parent is 5 (/) stop
4499663SMark.Logan@Sun.COM  * get inode of parent
4509663SMark.Logan@Sun.COM  */
4519663SMark.Logan@Sun.COM #define max_path 20
utils_inode_get_name(ntfs_inode * inode,char * buffer,int bufsize)4529663SMark.Logan@Sun.COM int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
4539663SMark.Logan@Sun.COM {
4549663SMark.Logan@Sun.COM 	// XXX option: names = posix/win32 or dos
4559663SMark.Logan@Sun.COM 	// flags: path, filename, or both
4569663SMark.Logan@Sun.COM 
4579663SMark.Logan@Sun.COM 
4589663SMark.Logan@Sun.COM 	ntfs_volume *vol;
4599663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ctx;
4609663SMark.Logan@Sun.COM 	ATTR_RECORD *rec;
4619663SMark.Logan@Sun.COM 	FILE_NAME_ATTR *attr;
4629663SMark.Logan@Sun.COM 	int name_space;
4639663SMark.Logan@Sun.COM 	MFT_REF parent = FILE_root;
4649663SMark.Logan@Sun.COM 	char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger?
4659663SMark.Logan@Sun.COM 	int i, len, offset = 0;
4669663SMark.Logan@Sun.COM 
4679663SMark.Logan@Sun.COM 	if (!inode || !buffer) {
4689663SMark.Logan@Sun.COM 		errno = EINVAL;
4699663SMark.Logan@Sun.COM 		return 0;
4709663SMark.Logan@Sun.COM 	}
4719663SMark.Logan@Sun.COM 
4729663SMark.Logan@Sun.COM 	vol = inode->vol;
4739663SMark.Logan@Sun.COM 
4749663SMark.Logan@Sun.COM 	//ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names));
4759663SMark.Logan@Sun.COM 	memset(names, 0, sizeof(names));
4769663SMark.Logan@Sun.COM 
4779663SMark.Logan@Sun.COM 	for (i = 0; i < max_path; i++) {
4789663SMark.Logan@Sun.COM 
4799663SMark.Logan@Sun.COM 		ctx = ntfs_attr_get_search_ctx(inode, NULL);
4809663SMark.Logan@Sun.COM 		if (!ctx) {
4819663SMark.Logan@Sun.COM 			ntfs_log_error("Couldn't create a search context.\n");
4829663SMark.Logan@Sun.COM 			return 0;
4839663SMark.Logan@Sun.COM 		}
4849663SMark.Logan@Sun.COM 
4859663SMark.Logan@Sun.COM 		//ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no);
4869663SMark.Logan@Sun.COM 
4879663SMark.Logan@Sun.COM 		name_space = 4;
4889663SMark.Logan@Sun.COM 		while ((rec = find_attribute(AT_FILE_NAME, ctx))) {
4899663SMark.Logan@Sun.COM 			/* We know this will always be resident. */
4909663SMark.Logan@Sun.COM 			attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->u.res.value_offset));
4919663SMark.Logan@Sun.COM 
4929663SMark.Logan@Sun.COM 			if (attr->file_name_type > name_space) { //XXX find the ...
4939663SMark.Logan@Sun.COM 				continue;
4949663SMark.Logan@Sun.COM 			}
4959663SMark.Logan@Sun.COM 
4969663SMark.Logan@Sun.COM 			name_space = attr->file_name_type;
4979663SMark.Logan@Sun.COM 			parent     = le64_to_cpu(attr->parent_directory);
4989663SMark.Logan@Sun.COM 
4999663SMark.Logan@Sun.COM 			if (names[i]) {
5009663SMark.Logan@Sun.COM 				free(names[i]);
5019663SMark.Logan@Sun.COM 				names[i] = NULL;
5029663SMark.Logan@Sun.COM 			}
5039663SMark.Logan@Sun.COM 
5049663SMark.Logan@Sun.COM 			if (ntfs_ucstombs(attr->file_name, attr->file_name_length,
5059663SMark.Logan@Sun.COM 			    &names[i], 0) < 0) {
5069663SMark.Logan@Sun.COM 				char *temp;
5079663SMark.Logan@Sun.COM 				ntfs_log_error("Couldn't translate filename to current locale.\n");
5089663SMark.Logan@Sun.COM 				temp = ntfs_malloc(30);
5099663SMark.Logan@Sun.COM 				if (!temp)
5109663SMark.Logan@Sun.COM 					return 0;
5119663SMark.Logan@Sun.COM 				snprintf(temp, 30, "<MFT%llu>", (unsigned
5129663SMark.Logan@Sun.COM 						long long)inode->mft_no);
5139663SMark.Logan@Sun.COM 				names[i] = temp;
5149663SMark.Logan@Sun.COM 			}
5159663SMark.Logan@Sun.COM 
5169663SMark.Logan@Sun.COM 			//ntfs_log_debug("names[%d] %s\n", i, names[i]);
5179663SMark.Logan@Sun.COM 			//ntfs_log_debug("parent = %lld\n", MREF(parent));
5189663SMark.Logan@Sun.COM 		}
5199663SMark.Logan@Sun.COM 
5209663SMark.Logan@Sun.COM 		ntfs_attr_put_search_ctx(ctx);
5219663SMark.Logan@Sun.COM 
5229663SMark.Logan@Sun.COM 		if (i > 0)			/* Don't close the original inode */
5239663SMark.Logan@Sun.COM 			ntfs_inode_close(inode);
5249663SMark.Logan@Sun.COM 
5259663SMark.Logan@Sun.COM 		if (MREF(parent) == FILE_root) {	/* The root directory, stop. */
5269663SMark.Logan@Sun.COM 			//ntfs_log_debug("inode 5\n");
5279663SMark.Logan@Sun.COM 			break;
5289663SMark.Logan@Sun.COM 		}
5299663SMark.Logan@Sun.COM 
5309663SMark.Logan@Sun.COM 		inode = ntfs_inode_open(vol, parent);
5319663SMark.Logan@Sun.COM 		if (!inode) {
5329663SMark.Logan@Sun.COM 			ntfs_log_error("Couldn't open inode %llu.\n",
5339663SMark.Logan@Sun.COM 					(unsigned long long)MREF(parent));
5349663SMark.Logan@Sun.COM 			break;
5359663SMark.Logan@Sun.COM 		}
5369663SMark.Logan@Sun.COM 	}
5379663SMark.Logan@Sun.COM 
5389663SMark.Logan@Sun.COM 	if (i >= max_path) {
5399663SMark.Logan@Sun.COM 		/* If we get into an infinite loop, we'll end up here. */
5409663SMark.Logan@Sun.COM 		ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path);
5419663SMark.Logan@Sun.COM 		return 0;
5429663SMark.Logan@Sun.COM 	}
5439663SMark.Logan@Sun.COM 
5449663SMark.Logan@Sun.COM 	/* Assemble the names in the correct order. */
5459663SMark.Logan@Sun.COM 	for (i = max_path; i >= 0; i--) {
5469663SMark.Logan@Sun.COM 		if (!names[i])
5479663SMark.Logan@Sun.COM 			continue;
5489663SMark.Logan@Sun.COM 
5499663SMark.Logan@Sun.COM 		len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]);
5509663SMark.Logan@Sun.COM 		if (len >= (bufsize - offset)) {
5519663SMark.Logan@Sun.COM 			ntfs_log_error("Pathname was truncated.\n");
5529663SMark.Logan@Sun.COM 			break;
5539663SMark.Logan@Sun.COM 		}
5549663SMark.Logan@Sun.COM 
5559663SMark.Logan@Sun.COM 		offset += len;
5569663SMark.Logan@Sun.COM 	}
5579663SMark.Logan@Sun.COM 
5589663SMark.Logan@Sun.COM 	/* Free all the allocated memory */
5599663SMark.Logan@Sun.COM 	for (i = 0; i < max_path; i++)
5609663SMark.Logan@Sun.COM 		free(names[i]);
5619663SMark.Logan@Sun.COM 
5629663SMark.Logan@Sun.COM 	ntfs_log_debug("Pathname: %s\n", buffer);
5639663SMark.Logan@Sun.COM 
5649663SMark.Logan@Sun.COM 	return 1;
5659663SMark.Logan@Sun.COM }
5669663SMark.Logan@Sun.COM #undef max_path
5679663SMark.Logan@Sun.COM 
5689663SMark.Logan@Sun.COM /**
5699663SMark.Logan@Sun.COM  * utils_attr_get_name
5709663SMark.Logan@Sun.COM  */
utils_attr_get_name(ntfs_volume * vol,ATTR_RECORD * attr,char * buffer,int bufsize)5719663SMark.Logan@Sun.COM int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize)
5729663SMark.Logan@Sun.COM {
5739663SMark.Logan@Sun.COM 	int len, namelen;
5749663SMark.Logan@Sun.COM 	char *name;
5759663SMark.Logan@Sun.COM 	ATTR_DEF *attrdef;
5769663SMark.Logan@Sun.COM 
5779663SMark.Logan@Sun.COM 	// flags: attr, name, or both
5789663SMark.Logan@Sun.COM 	if (!attr || !buffer) {
5799663SMark.Logan@Sun.COM 		errno = EINVAL;
5809663SMark.Logan@Sun.COM 		return 0;
5819663SMark.Logan@Sun.COM 	}
5829663SMark.Logan@Sun.COM 
5839663SMark.Logan@Sun.COM 	attrdef = ntfs_attr_find_in_attrdef(vol, attr->type);
5849663SMark.Logan@Sun.COM 	if (attrdef) {
5859663SMark.Logan@Sun.COM 		name    = NULL;
5869663SMark.Logan@Sun.COM 		namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name));
5879663SMark.Logan@Sun.COM 		if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) {
5889663SMark.Logan@Sun.COM 			ntfs_log_error("Couldn't translate attribute type to "
5899663SMark.Logan@Sun.COM 					"current locale.\n");
5909663SMark.Logan@Sun.COM 			// <UNKNOWN>?
5919663SMark.Logan@Sun.COM 			return 0;
5929663SMark.Logan@Sun.COM 		}
5939663SMark.Logan@Sun.COM 		len = snprintf(buffer, bufsize, "%s", name);
5949663SMark.Logan@Sun.COM 	} else {
5959663SMark.Logan@Sun.COM 		ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type);
5969663SMark.Logan@Sun.COM 		len = snprintf(buffer, bufsize, "<UNKNOWN>");
5979663SMark.Logan@Sun.COM 	}
5989663SMark.Logan@Sun.COM 
5999663SMark.Logan@Sun.COM 	if (len >= bufsize) {
6009663SMark.Logan@Sun.COM 		ntfs_log_error("Attribute type was truncated.\n");
6019663SMark.Logan@Sun.COM 		return 0;
6029663SMark.Logan@Sun.COM 	}
6039663SMark.Logan@Sun.COM 
6049663SMark.Logan@Sun.COM 	if (!attr->name_length) {
6059663SMark.Logan@Sun.COM 		return 0;
6069663SMark.Logan@Sun.COM 	}
6079663SMark.Logan@Sun.COM 
6089663SMark.Logan@Sun.COM 	buffer  += len;
6099663SMark.Logan@Sun.COM 	bufsize -= len;
6109663SMark.Logan@Sun.COM 
6119663SMark.Logan@Sun.COM 	name    = NULL;
6129663SMark.Logan@Sun.COM 	namelen = attr->name_length;
6139663SMark.Logan@Sun.COM 	if (ntfs_ucstombs((ntfschar *)((char *)attr + le16_to_cpu(
6149663SMark.Logan@Sun.COM 			attr->name_offset)), namelen, &name, 0) < 0) {
6159663SMark.Logan@Sun.COM 		ntfs_log_error("Couldn't translate attribute name to current "
6169663SMark.Logan@Sun.COM 				"locale.\n");
6179663SMark.Logan@Sun.COM 		// <UNKNOWN>?
6189663SMark.Logan@Sun.COM 		len = snprintf(buffer, bufsize, "<UNKNOWN>");
6199663SMark.Logan@Sun.COM 		return 0;
6209663SMark.Logan@Sun.COM 	}
6219663SMark.Logan@Sun.COM 
6229663SMark.Logan@Sun.COM 	len = snprintf(buffer, bufsize, "(%s)", name);
6239663SMark.Logan@Sun.COM 	free(name);
6249663SMark.Logan@Sun.COM 
6259663SMark.Logan@Sun.COM 	if (len >= bufsize) {
6269663SMark.Logan@Sun.COM 		ntfs_log_error("Attribute name was truncated.\n");
6279663SMark.Logan@Sun.COM 		return 0;
6289663SMark.Logan@Sun.COM 	}
6299663SMark.Logan@Sun.COM 
6309663SMark.Logan@Sun.COM 	return 0;
6319663SMark.Logan@Sun.COM }
6329663SMark.Logan@Sun.COM 
6339663SMark.Logan@Sun.COM /**
6349663SMark.Logan@Sun.COM  * utils_cluster_in_use - Determine if a cluster is in use
6359663SMark.Logan@Sun.COM  * @vol:  An ntfs volume obtained from ntfs_mount
6369663SMark.Logan@Sun.COM  * @lcn:  The Logical Cluster Number to test
6379663SMark.Logan@Sun.COM  *
6389663SMark.Logan@Sun.COM  * The metadata file $Bitmap has one binary bit representing each cluster on
6399663SMark.Logan@Sun.COM  * disk.  The bit will be set for each cluster that is in use.  The function
6409663SMark.Logan@Sun.COM  * reads the relevant part of $Bitmap into a buffer and tests the bit.
6419663SMark.Logan@Sun.COM  *
6429663SMark.Logan@Sun.COM  * This function has a static buffer in which it caches a section of $Bitmap.
6439663SMark.Logan@Sun.COM  * If the lcn, being tested, lies outside the range, the buffer will be
6449663SMark.Logan@Sun.COM  * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the
6459663SMark.Logan@Sun.COM  * buffer.
6469663SMark.Logan@Sun.COM  *
6479663SMark.Logan@Sun.COM  * NOTE: Be very carefull with shifts by 3 everywhere in this function.
6489663SMark.Logan@Sun.COM  *
6499663SMark.Logan@Sun.COM  * Return:  1  Cluster is in use
6509663SMark.Logan@Sun.COM  *	    0  Cluster is free space
6519663SMark.Logan@Sun.COM  *	   -1  Error occurred
6529663SMark.Logan@Sun.COM  */
utils_cluster_in_use(ntfs_volume * vol,long long lcn)6539663SMark.Logan@Sun.COM int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
6549663SMark.Logan@Sun.COM {
6559663SMark.Logan@Sun.COM 	static unsigned char buffer[512];
6569663SMark.Logan@Sun.COM 	static long long bmplcn = -(sizeof(buffer) << 3);
6579663SMark.Logan@Sun.COM 	int byte, bit;
6589663SMark.Logan@Sun.COM 	ntfs_attr *attr;
6599663SMark.Logan@Sun.COM 
6609663SMark.Logan@Sun.COM 	if (!vol) {
6619663SMark.Logan@Sun.COM 		errno = EINVAL;
6629663SMark.Logan@Sun.COM 		return -1;
6639663SMark.Logan@Sun.COM 	}
6649663SMark.Logan@Sun.COM 
6659663SMark.Logan@Sun.COM 	/* Does lcn lie in the section of $Bitmap we already have cached? */
6669663SMark.Logan@Sun.COM 	if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof(buffer) << 3)))) {
6679663SMark.Logan@Sun.COM 		ntfs_log_debug("Bit lies outside cache.\n");
6689663SMark.Logan@Sun.COM 		attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
6699663SMark.Logan@Sun.COM 		if (!attr) {
6709663SMark.Logan@Sun.COM 			ntfs_log_perror("Couldn't open $Bitmap");
6719663SMark.Logan@Sun.COM 			return -1;
6729663SMark.Logan@Sun.COM 		}
6739663SMark.Logan@Sun.COM 
6749663SMark.Logan@Sun.COM 		/* Mark the buffer as in use, in case the read is shorter. */
6759663SMark.Logan@Sun.COM 		memset(buffer, 0xFF, sizeof(buffer));
6769663SMark.Logan@Sun.COM 		bmplcn = lcn & (~((sizeof(buffer) << 3) - 1));
6779663SMark.Logan@Sun.COM 
6789663SMark.Logan@Sun.COM 		if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer),
6799663SMark.Logan@Sun.COM 					buffer) < 0) {
6809663SMark.Logan@Sun.COM 			ntfs_log_perror("Couldn't read $Bitmap");
6819663SMark.Logan@Sun.COM 			ntfs_attr_close(attr);
6829663SMark.Logan@Sun.COM 			return -1;
6839663SMark.Logan@Sun.COM 		}
6849663SMark.Logan@Sun.COM 
6859663SMark.Logan@Sun.COM 		ntfs_log_debug("Reloaded bitmap buffer.\n");
6869663SMark.Logan@Sun.COM 		ntfs_attr_close(attr);
6879663SMark.Logan@Sun.COM 	}
6889663SMark.Logan@Sun.COM 
6899663SMark.Logan@Sun.COM 	bit  = 1 << (lcn & 7);
6909663SMark.Logan@Sun.COM 	byte = (lcn >> 3) & (sizeof(buffer) - 1);
6919663SMark.Logan@Sun.COM 	ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, "
6929663SMark.Logan@Sun.COM 			"in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] &
6939663SMark.Logan@Sun.COM 			bit);
6949663SMark.Logan@Sun.COM 
6959663SMark.Logan@Sun.COM 	return (buffer[byte] & bit);
6969663SMark.Logan@Sun.COM }
6979663SMark.Logan@Sun.COM 
6989663SMark.Logan@Sun.COM /**
6999663SMark.Logan@Sun.COM  * utils_mftrec_in_use - Determine if a MFT Record is in use
7009663SMark.Logan@Sun.COM  * @vol:   An ntfs volume obtained from ntfs_mount
7019663SMark.Logan@Sun.COM  * @mref:  MFT Reference (inode number)
7029663SMark.Logan@Sun.COM  *
7039663SMark.Logan@Sun.COM  * The metadata file $BITMAP has one binary bit representing each record in the
7049663SMark.Logan@Sun.COM  * MFT.  The bit will be set for each record that is in use.  The function
7059663SMark.Logan@Sun.COM  * reads the relevant part of $BITMAP into a buffer and tests the bit.
7069663SMark.Logan@Sun.COM  *
7079663SMark.Logan@Sun.COM  * This function has a static buffer in which it caches a section of $BITMAP.
7089663SMark.Logan@Sun.COM  * If the mref, being tested, lies outside the range, the buffer will be
7099663SMark.Logan@Sun.COM  * refreshed.
7109663SMark.Logan@Sun.COM  *
7119663SMark.Logan@Sun.COM  * Return:  1  MFT Record is in use
7129663SMark.Logan@Sun.COM  *	    0  MFT Record is unused
7139663SMark.Logan@Sun.COM  *	   -1  Error occurred
7149663SMark.Logan@Sun.COM  */
utils_mftrec_in_use(ntfs_volume * vol,MFT_REF mref)7159663SMark.Logan@Sun.COM int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
7169663SMark.Logan@Sun.COM {
7179663SMark.Logan@Sun.COM 	static u8 buffer[512];
7189663SMark.Logan@Sun.COM 	static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */
7199663SMark.Logan@Sun.COM 	int byte, bit;
7209663SMark.Logan@Sun.COM 
7219663SMark.Logan@Sun.COM 	ntfs_log_trace("Entering.\n");
7229663SMark.Logan@Sun.COM 
7239663SMark.Logan@Sun.COM 	if (!vol) {
7249663SMark.Logan@Sun.COM 		errno = EINVAL;
7259663SMark.Logan@Sun.COM 		return -1;
7269663SMark.Logan@Sun.COM 	}
7279663SMark.Logan@Sun.COM 
7289663SMark.Logan@Sun.COM 	/* Does mref lie in the section of $Bitmap we already have cached? */
7299663SMark.Logan@Sun.COM 	if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref +
7309663SMark.Logan@Sun.COM 			(sizeof(buffer) << 3)))) {
7319663SMark.Logan@Sun.COM 		ntfs_log_debug("Bit lies outside cache.\n");
7329663SMark.Logan@Sun.COM 
7339663SMark.Logan@Sun.COM 		/* Mark the buffer as not in use, in case the read is shorter. */
7349663SMark.Logan@Sun.COM 		memset(buffer, 0, sizeof(buffer));
7359663SMark.Logan@Sun.COM 		bmpmref = mref & (~((sizeof(buffer) << 3) - 1));
7369663SMark.Logan@Sun.COM 
7379663SMark.Logan@Sun.COM 		if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) {
7389663SMark.Logan@Sun.COM 			ntfs_log_perror("Couldn't read $MFT/$BITMAP");
7399663SMark.Logan@Sun.COM 			return -1;
7409663SMark.Logan@Sun.COM 		}
7419663SMark.Logan@Sun.COM 
7429663SMark.Logan@Sun.COM 		ntfs_log_debug("Reloaded bitmap buffer.\n");
7439663SMark.Logan@Sun.COM 	}
7449663SMark.Logan@Sun.COM 
7459663SMark.Logan@Sun.COM 	bit  = 1 << (mref & 7);
7469663SMark.Logan@Sun.COM 	byte = (mref >> 3) & (sizeof(buffer) - 1);
7479663SMark.Logan@Sun.COM 	ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit);
7489663SMark.Logan@Sun.COM 
7499663SMark.Logan@Sun.COM 	return (buffer[byte] & bit);
7509663SMark.Logan@Sun.COM }
7519663SMark.Logan@Sun.COM 
7529663SMark.Logan@Sun.COM /**
7539663SMark.Logan@Sun.COM  * __metadata
7549663SMark.Logan@Sun.COM  */
__metadata(ntfs_volume * vol,u64 num)7559663SMark.Logan@Sun.COM static int __metadata(ntfs_volume *vol, u64 num)
7569663SMark.Logan@Sun.COM {
7579663SMark.Logan@Sun.COM 	if (num <= FILE_UpCase)
7589663SMark.Logan@Sun.COM 		return 1;
7599663SMark.Logan@Sun.COM 	if (!vol)
7609663SMark.Logan@Sun.COM 		return -1;
7619663SMark.Logan@Sun.COM 	if ((vol->major_ver == 3) && (num == FILE_Extend))
7629663SMark.Logan@Sun.COM 		return 1;
7639663SMark.Logan@Sun.COM 
7649663SMark.Logan@Sun.COM 	return 0;
7659663SMark.Logan@Sun.COM }
7669663SMark.Logan@Sun.COM 
7679663SMark.Logan@Sun.COM /**
7689663SMark.Logan@Sun.COM  * utils_is_metadata - Determine if an inode represents a metadata file
7699663SMark.Logan@Sun.COM  * @inode:  An ntfs inode to be tested
7709663SMark.Logan@Sun.COM  *
7719663SMark.Logan@Sun.COM  * A handful of files in the volume contain filesystem data - metadata.
7729663SMark.Logan@Sun.COM  * They can be identified by their inode number (offset in MFT/$DATA) or by
7739663SMark.Logan@Sun.COM  * their parent.
7749663SMark.Logan@Sun.COM  *
7759663SMark.Logan@Sun.COM  * Return:  1  inode is a metadata file
7769663SMark.Logan@Sun.COM  *	    0  inode is not a metadata file
7779663SMark.Logan@Sun.COM  *	   -1  Error occurred
7789663SMark.Logan@Sun.COM  */
utils_is_metadata(ntfs_inode * inode)7799663SMark.Logan@Sun.COM int utils_is_metadata(ntfs_inode *inode)
7809663SMark.Logan@Sun.COM {
7819663SMark.Logan@Sun.COM 	ntfs_volume *vol;
7829663SMark.Logan@Sun.COM 	ATTR_RECORD *rec;
7839663SMark.Logan@Sun.COM 	FILE_NAME_ATTR *attr;
7849663SMark.Logan@Sun.COM 	MFT_RECORD *file;
7859663SMark.Logan@Sun.COM 	u64 num;
7869663SMark.Logan@Sun.COM 
7879663SMark.Logan@Sun.COM 	if (!inode) {
7889663SMark.Logan@Sun.COM 		errno = EINVAL;
7899663SMark.Logan@Sun.COM 		return -1;
7909663SMark.Logan@Sun.COM 	}
7919663SMark.Logan@Sun.COM 
7929663SMark.Logan@Sun.COM 	vol = inode->vol;
7939663SMark.Logan@Sun.COM 	if (!vol)
7949663SMark.Logan@Sun.COM 		return -1;
7959663SMark.Logan@Sun.COM 
7969663SMark.Logan@Sun.COM 	num = inode->mft_no;
7979663SMark.Logan@Sun.COM 	if (__metadata(vol, num) == 1)
7989663SMark.Logan@Sun.COM 		return 1;
7999663SMark.Logan@Sun.COM 
8009663SMark.Logan@Sun.COM 	file = inode->mrec;
8019663SMark.Logan@Sun.COM 	if (file && (file->base_mft_record != 0)) {
8029663SMark.Logan@Sun.COM 		num = MREF_LE(file->base_mft_record);
8039663SMark.Logan@Sun.COM 		if (__metadata(vol, num) == 1)
8049663SMark.Logan@Sun.COM 			return 1;
8059663SMark.Logan@Sun.COM 	}
8069663SMark.Logan@Sun.COM 	file = inode->mrec;
8079663SMark.Logan@Sun.COM 
8089663SMark.Logan@Sun.COM 	rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
8099663SMark.Logan@Sun.COM 	if (!rec)
8109663SMark.Logan@Sun.COM 		return -1;
8119663SMark.Logan@Sun.COM 
8129663SMark.Logan@Sun.COM 	/* We know this will always be resident. */
8139663SMark.Logan@Sun.COM 	attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->u.res.value_offset));
8149663SMark.Logan@Sun.COM 
8159663SMark.Logan@Sun.COM 	num = MREF_LE(attr->parent_directory);
8169663SMark.Logan@Sun.COM 	if ((num != FILE_root) && (__metadata(vol, num) == 1))
8179663SMark.Logan@Sun.COM 		return 1;
8189663SMark.Logan@Sun.COM 
8199663SMark.Logan@Sun.COM 	return 0;
8209663SMark.Logan@Sun.COM }
8219663SMark.Logan@Sun.COM 
8229663SMark.Logan@Sun.COM /**
8239663SMark.Logan@Sun.COM  * utils_dump_mem - Display a block of memory in hex and ascii
8249663SMark.Logan@Sun.COM  * @buf:     Buffer to be displayed
8259663SMark.Logan@Sun.COM  * @start:   Offset into @buf to start from
8269663SMark.Logan@Sun.COM  * @length:  Number of bytes to display
8279663SMark.Logan@Sun.COM  * @flags:   Options to change the style of the output
8289663SMark.Logan@Sun.COM  *
8299663SMark.Logan@Sun.COM  * Display a block of memory in a tradition hex-dump manner.
8309663SMark.Logan@Sun.COM  * Optionally the ascii part can be turned off.
8319663SMark.Logan@Sun.COM  *
8329663SMark.Logan@Sun.COM  * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS).
8339663SMark.Logan@Sun.COM  * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the
8349663SMark.Logan@Sun.COM  * output); DM_NO_ASCII (only print the hex values).
8359663SMark.Logan@Sun.COM  */
utils_dump_mem(void * buf,int start,int length,int flags)8369663SMark.Logan@Sun.COM void utils_dump_mem(void *buf, int start, int length, int flags)
8379663SMark.Logan@Sun.COM {
8389663SMark.Logan@Sun.COM 	int off, i, s, e, col;
8399663SMark.Logan@Sun.COM 	u8 *mem = buf;
8409663SMark.Logan@Sun.COM 
8419663SMark.Logan@Sun.COM 	s =  start                & ~15;	// round down
8429663SMark.Logan@Sun.COM 	e = (start + length + 15) & ~15;	// round up
8439663SMark.Logan@Sun.COM 
8449663SMark.Logan@Sun.COM 	for (off = s; off < e; off += 16) {
8459663SMark.Logan@Sun.COM 		col = 30;
8469663SMark.Logan@Sun.COM 		if (flags & DM_RED)
8479663SMark.Logan@Sun.COM 			col += 1;
8489663SMark.Logan@Sun.COM 		if (flags & DM_GREEN)
8499663SMark.Logan@Sun.COM 			col += 2;
8509663SMark.Logan@Sun.COM 		if (flags & DM_BLUE)
8519663SMark.Logan@Sun.COM 			col += 4;
8529663SMark.Logan@Sun.COM 		if (flags & DM_INDENT)
8539663SMark.Logan@Sun.COM 			ntfs_log_debug("\t");
8549663SMark.Logan@Sun.COM 		if (flags & DM_BOLD)
8559663SMark.Logan@Sun.COM 			ntfs_log_debug("\e[01m");
8569663SMark.Logan@Sun.COM 		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
8579663SMark.Logan@Sun.COM 			ntfs_log_debug("\e[%dm", col);
8589663SMark.Logan@Sun.COM 		if (off == s)
8599663SMark.Logan@Sun.COM 			ntfs_log_debug("%6.6x ", start);
8609663SMark.Logan@Sun.COM 		else
8619663SMark.Logan@Sun.COM 			ntfs_log_debug("%6.6x ", off);
8629663SMark.Logan@Sun.COM 
8639663SMark.Logan@Sun.COM 		for (i = 0; i < 16; i++) {
8649663SMark.Logan@Sun.COM 			if ((i == 8) && (!(flags & DM_NO_DIVIDER)))
8659663SMark.Logan@Sun.COM 				ntfs_log_debug(" -");
8669663SMark.Logan@Sun.COM 			if (((off+i) >= start) && ((off+i) < (start+length)))
8679663SMark.Logan@Sun.COM 				ntfs_log_debug(" %02X", mem[off+i]);
8689663SMark.Logan@Sun.COM 			else
8699663SMark.Logan@Sun.COM 				ntfs_log_debug("   ");
8709663SMark.Logan@Sun.COM 		}
8719663SMark.Logan@Sun.COM 		if (!(flags & DM_NO_ASCII)) {
8729663SMark.Logan@Sun.COM 			ntfs_log_debug("  ");
8739663SMark.Logan@Sun.COM 			for (i = 0; i < 16; i++) {
8749663SMark.Logan@Sun.COM 				if (((off+i) < start) || ((off+i) >= (start+length)))
8759663SMark.Logan@Sun.COM 					ntfs_log_debug(" ");
8769663SMark.Logan@Sun.COM 				else if (isprint(mem[off + i]))
8779663SMark.Logan@Sun.COM 					ntfs_log_debug("%c", mem[off + i]);
8789663SMark.Logan@Sun.COM 				else
8799663SMark.Logan@Sun.COM 					ntfs_log_debug(".");
8809663SMark.Logan@Sun.COM 			}
8819663SMark.Logan@Sun.COM 		}
8829663SMark.Logan@Sun.COM 		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
8839663SMark.Logan@Sun.COM 			ntfs_log_debug("\e[0m");
8849663SMark.Logan@Sun.COM 		ntfs_log_debug("\n");
8859663SMark.Logan@Sun.COM 	}
8869663SMark.Logan@Sun.COM }
8879663SMark.Logan@Sun.COM 
8889663SMark.Logan@Sun.COM 
8899663SMark.Logan@Sun.COM /**
8909663SMark.Logan@Sun.COM  * mft_get_search_ctx
8919663SMark.Logan@Sun.COM  */
mft_get_search_ctx(ntfs_volume * vol)8929663SMark.Logan@Sun.COM struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
8939663SMark.Logan@Sun.COM {
8949663SMark.Logan@Sun.COM 	struct mft_search_ctx *ctx;
8959663SMark.Logan@Sun.COM 
8969663SMark.Logan@Sun.COM 	if (!vol) {
8979663SMark.Logan@Sun.COM 		errno = EINVAL;
8989663SMark.Logan@Sun.COM 		return NULL;
8999663SMark.Logan@Sun.COM 	}
9009663SMark.Logan@Sun.COM 
9019663SMark.Logan@Sun.COM 	ctx = calloc(1, sizeof *ctx);
9029663SMark.Logan@Sun.COM 
9039663SMark.Logan@Sun.COM 	ctx->mft_num = -1;
9049663SMark.Logan@Sun.COM 	ctx->vol = vol;
9059663SMark.Logan@Sun.COM 
9069663SMark.Logan@Sun.COM 	return ctx;
9079663SMark.Logan@Sun.COM }
9089663SMark.Logan@Sun.COM 
9099663SMark.Logan@Sun.COM /**
9109663SMark.Logan@Sun.COM  * mft_put_search_ctx
9119663SMark.Logan@Sun.COM  */
mft_put_search_ctx(struct mft_search_ctx * ctx)9129663SMark.Logan@Sun.COM void mft_put_search_ctx(struct mft_search_ctx *ctx)
9139663SMark.Logan@Sun.COM {
9149663SMark.Logan@Sun.COM 	if (!ctx)
9159663SMark.Logan@Sun.COM 		return;
9169663SMark.Logan@Sun.COM 	if (ctx->inode)
9179663SMark.Logan@Sun.COM 		ntfs_inode_close(ctx->inode);
9189663SMark.Logan@Sun.COM 	free(ctx);
9199663SMark.Logan@Sun.COM }
9209663SMark.Logan@Sun.COM 
9219663SMark.Logan@Sun.COM /**
9229663SMark.Logan@Sun.COM  * mft_next_record
9239663SMark.Logan@Sun.COM  */
mft_next_record(struct mft_search_ctx * ctx)9249663SMark.Logan@Sun.COM int mft_next_record(struct mft_search_ctx *ctx)
9259663SMark.Logan@Sun.COM {
9269663SMark.Logan@Sun.COM 	s64 nr_mft_records;
9279663SMark.Logan@Sun.COM 	ATTR_RECORD *attr10 = NULL;
9289663SMark.Logan@Sun.COM 	ATTR_RECORD *attr20 = NULL;
9299663SMark.Logan@Sun.COM 	ATTR_RECORD *attr80 = NULL;
9309663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *attr_ctx;
9319663SMark.Logan@Sun.COM 
9329663SMark.Logan@Sun.COM 	if (!ctx) {
9339663SMark.Logan@Sun.COM 		errno = EINVAL;
9349663SMark.Logan@Sun.COM 		return -1;
9359663SMark.Logan@Sun.COM 	}
9369663SMark.Logan@Sun.COM 
9379663SMark.Logan@Sun.COM 	if (ctx->inode) {
9389663SMark.Logan@Sun.COM 		ntfs_inode_close(ctx->inode);
9399663SMark.Logan@Sun.COM 		ctx->inode = NULL;
9409663SMark.Logan@Sun.COM 	}
9419663SMark.Logan@Sun.COM 
9429663SMark.Logan@Sun.COM 	nr_mft_records = ctx->vol->mft_na->initialized_size >>
9439663SMark.Logan@Sun.COM 			ctx->vol->mft_record_size_bits;
9449663SMark.Logan@Sun.COM 
9459663SMark.Logan@Sun.COM 	for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) {
9469663SMark.Logan@Sun.COM 		int in_use;
9479663SMark.Logan@Sun.COM 
9489663SMark.Logan@Sun.COM 		ctx->flags_match = 0;
9499663SMark.Logan@Sun.COM 		in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num);
9509663SMark.Logan@Sun.COM 		if (in_use == -1) {
9519663SMark.Logan@Sun.COM 			ntfs_log_error("Error reading inode %llu.  Aborting.\n",
9529663SMark.Logan@Sun.COM 					(unsigned long long)ctx->mft_num);
9539663SMark.Logan@Sun.COM 			return -1;
9549663SMark.Logan@Sun.COM 		}
9559663SMark.Logan@Sun.COM 
9569663SMark.Logan@Sun.COM 		if (in_use) {
9579663SMark.Logan@Sun.COM 			ctx->flags_match |= FEMR_IN_USE;
9589663SMark.Logan@Sun.COM 
9599663SMark.Logan@Sun.COM 			ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num);
9609663SMark.Logan@Sun.COM 			if (ctx->inode == NULL) {
9619663SMark.Logan@Sun.COM 				ntfs_log_error("Error reading inode %llu.\n", (unsigned
9629663SMark.Logan@Sun.COM 						long long) ctx->mft_num);
9639663SMark.Logan@Sun.COM 				continue;
9649663SMark.Logan@Sun.COM 			}
9659663SMark.Logan@Sun.COM 
9669663SMark.Logan@Sun.COM 			attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec);
9679663SMark.Logan@Sun.COM 			attr20 = find_first_attribute(AT_ATTRIBUTE_LIST,       ctx->inode->mrec);
9689663SMark.Logan@Sun.COM 			attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec);
9699663SMark.Logan@Sun.COM 
9709663SMark.Logan@Sun.COM 			if (attr10)
9719663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_BASE_RECORD;
9729663SMark.Logan@Sun.COM 			else
9739663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_NOT_BASE_RECORD;
9749663SMark.Logan@Sun.COM 
9759663SMark.Logan@Sun.COM 			if (attr20)
9769663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_BASE_RECORD;
9779663SMark.Logan@Sun.COM 
9789663SMark.Logan@Sun.COM 			if (attr80)
9799663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_FILE;
9809663SMark.Logan@Sun.COM 
9819663SMark.Logan@Sun.COM 			if (ctx->flags_search & FEMR_DIR) {
9829663SMark.Logan@Sun.COM 				attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL);
9839663SMark.Logan@Sun.COM 				if (attr_ctx) {
9849663SMark.Logan@Sun.COM 					if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0)
9859663SMark.Logan@Sun.COM 						ctx->flags_match |= FEMR_DIR;
9869663SMark.Logan@Sun.COM 
9879663SMark.Logan@Sun.COM 					ntfs_attr_put_search_ctx(attr_ctx);
9889663SMark.Logan@Sun.COM 				} else {
9899663SMark.Logan@Sun.COM 					ntfs_log_error("Couldn't create a search context.\n");
9909663SMark.Logan@Sun.COM 					return -1;
9919663SMark.Logan@Sun.COM 				}
9929663SMark.Logan@Sun.COM 			}
9939663SMark.Logan@Sun.COM 
9949663SMark.Logan@Sun.COM 			switch (utils_is_metadata(ctx->inode)) {
9959663SMark.Logan@Sun.COM 				case 1: ctx->flags_match |= FEMR_METADATA;     break;
9969663SMark.Logan@Sun.COM 				case 0: ctx->flags_match |= FEMR_NOT_METADATA; break;
9979663SMark.Logan@Sun.COM 				default:
9989663SMark.Logan@Sun.COM 					ctx->flags_match |= FEMR_NOT_METADATA; break;
9999663SMark.Logan@Sun.COM 					//ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num);
10009663SMark.Logan@Sun.COM 					//return -1;
10019663SMark.Logan@Sun.COM 			}
10029663SMark.Logan@Sun.COM 
10039663SMark.Logan@Sun.COM 		} else {		// !in_use
10049663SMark.Logan@Sun.COM 			ntfs_attr *mft;
10059663SMark.Logan@Sun.COM 
10069663SMark.Logan@Sun.COM 			ctx->flags_match |= FEMR_NOT_IN_USE;
10079663SMark.Logan@Sun.COM 
10089663SMark.Logan@Sun.COM 			ctx->inode = calloc(1, sizeof(*ctx->inode));
10099663SMark.Logan@Sun.COM 			if (!ctx->inode) {
10109663SMark.Logan@Sun.COM 				ntfs_log_error("Out of memory.  Aborting.\n");
10119663SMark.Logan@Sun.COM 				return -1;
10129663SMark.Logan@Sun.COM 			}
10139663SMark.Logan@Sun.COM 
10149663SMark.Logan@Sun.COM 			ctx->inode->mft_no = ctx->mft_num;
10159663SMark.Logan@Sun.COM 			ctx->inode->vol    = ctx->vol;
10169663SMark.Logan@Sun.COM 			ctx->inode->mrec   = ntfs_malloc(ctx->vol->mft_record_size);
10179663SMark.Logan@Sun.COM 			if (!ctx->inode->mrec) {
10189663SMark.Logan@Sun.COM 				free(ctx->inode); // == ntfs_inode_close
10199663SMark.Logan@Sun.COM 				return -1;
10209663SMark.Logan@Sun.COM 			}
10219663SMark.Logan@Sun.COM 
10229663SMark.Logan@Sun.COM 			mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA,
10239663SMark.Logan@Sun.COM 					AT_UNNAMED, 0);
10249663SMark.Logan@Sun.COM 			if (!mft) {
10259663SMark.Logan@Sun.COM 				ntfs_log_perror("Couldn't open $MFT/$DATA");
10269663SMark.Logan@Sun.COM 				// free / close
10279663SMark.Logan@Sun.COM 				return -1;
10289663SMark.Logan@Sun.COM 			}
10299663SMark.Logan@Sun.COM 
10309663SMark.Logan@Sun.COM 			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) {
10319663SMark.Logan@Sun.COM 				ntfs_log_perror("Couldn't read MFT Record %llu",
10329663SMark.Logan@Sun.COM 					(unsigned long long) ctx->mft_num);
10339663SMark.Logan@Sun.COM 				// free / close
10349663SMark.Logan@Sun.COM 				ntfs_attr_close(mft);
10359663SMark.Logan@Sun.COM 				return -1;
10369663SMark.Logan@Sun.COM 			}
10379663SMark.Logan@Sun.COM 
10389663SMark.Logan@Sun.COM 			ntfs_attr_close(mft);
10399663SMark.Logan@Sun.COM 		}
10409663SMark.Logan@Sun.COM 
10419663SMark.Logan@Sun.COM 		if (ctx->flags_match & ctx->flags_search) {
10429663SMark.Logan@Sun.COM 			break;
10439663SMark.Logan@Sun.COM 		}
10449663SMark.Logan@Sun.COM 
10459663SMark.Logan@Sun.COM 		if (ntfs_inode_close(ctx->inode)) {
10469663SMark.Logan@Sun.COM 			ntfs_log_error("Error closing inode %llu.\n",
10479663SMark.Logan@Sun.COM 					(unsigned long long)ctx->mft_num);
10489663SMark.Logan@Sun.COM 			return -errno;
10499663SMark.Logan@Sun.COM 		}
10509663SMark.Logan@Sun.COM 
10519663SMark.Logan@Sun.COM 		ctx->inode = NULL;
10529663SMark.Logan@Sun.COM 	}
10539663SMark.Logan@Sun.COM 
10549663SMark.Logan@Sun.COM 	return (ctx->inode == NULL);
10559663SMark.Logan@Sun.COM }
10569663SMark.Logan@Sun.COM 
10579663SMark.Logan@Sun.COM 
1058