xref: /onnv-gate/usr/src/cmd/ntfsprogs/utils.c (revision 9663)
1*9663SMark.Logan@Sun.COM /**
2*9663SMark.Logan@Sun.COM  * utils.c - Part of the Linux-NTFS project.
3*9663SMark.Logan@Sun.COM  *
4*9663SMark.Logan@Sun.COM  * Copyright (c) 2002-2005 Richard Russon
5*9663SMark.Logan@Sun.COM  * Copyright (c) 2003-2006 Anton Altaparmakov
6*9663SMark.Logan@Sun.COM  * Copyright (c) 2003 Lode Leroy
7*9663SMark.Logan@Sun.COM  * Copyright (c) 2005-2007 Yura Pakhuchiy
8*9663SMark.Logan@Sun.COM  *
9*9663SMark.Logan@Sun.COM  * A set of shared functions for ntfs utilities
10*9663SMark.Logan@Sun.COM  *
11*9663SMark.Logan@Sun.COM  * This program is free software; you can redistribute it and/or modify
12*9663SMark.Logan@Sun.COM  * it under the terms of the GNU General Public License as published by
13*9663SMark.Logan@Sun.COM  * the Free Software Foundation; either version 2 of the License, or
14*9663SMark.Logan@Sun.COM  * (at your option) any later version.
15*9663SMark.Logan@Sun.COM  *
16*9663SMark.Logan@Sun.COM  * This program is distributed in the hope that it will be useful,
17*9663SMark.Logan@Sun.COM  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18*9663SMark.Logan@Sun.COM  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19*9663SMark.Logan@Sun.COM  * GNU General Public License for more details.
20*9663SMark.Logan@Sun.COM  *
21*9663SMark.Logan@Sun.COM  * You should have received a copy of the GNU General Public License
22*9663SMark.Logan@Sun.COM  * along with this program (in the main directory of the Linux-NTFS
23*9663SMark.Logan@Sun.COM  * distribution in the file COPYING); if not, write to the Free Software
24*9663SMark.Logan@Sun.COM  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25*9663SMark.Logan@Sun.COM  */
26*9663SMark.Logan@Sun.COM 
27*9663SMark.Logan@Sun.COM #ifdef HAVE_CONFIG_H
28*9663SMark.Logan@Sun.COM #include "config.h"
29*9663SMark.Logan@Sun.COM #endif
30*9663SMark.Logan@Sun.COM 
31*9663SMark.Logan@Sun.COM #ifdef HAVE_STDIO_H
32*9663SMark.Logan@Sun.COM #include <stdio.h>
33*9663SMark.Logan@Sun.COM #endif
34*9663SMark.Logan@Sun.COM #ifdef HAVE_STDARG_H
35*9663SMark.Logan@Sun.COM #include <stdarg.h>
36*9663SMark.Logan@Sun.COM #endif
37*9663SMark.Logan@Sun.COM #ifdef HAVE_ERRNO_H
38*9663SMark.Logan@Sun.COM #include <errno.h>
39*9663SMark.Logan@Sun.COM #endif
40*9663SMark.Logan@Sun.COM #ifdef HAVE_SYS_TYPES_H
41*9663SMark.Logan@Sun.COM #include <sys/types.h>
42*9663SMark.Logan@Sun.COM #endif
43*9663SMark.Logan@Sun.COM #ifdef HAVE_SYS_STAT_H
44*9663SMark.Logan@Sun.COM #include <sys/stat.h>
45*9663SMark.Logan@Sun.COM #endif
46*9663SMark.Logan@Sun.COM #ifdef HAVE_UNISTD_H
47*9663SMark.Logan@Sun.COM #include <unistd.h>
48*9663SMark.Logan@Sun.COM #endif
49*9663SMark.Logan@Sun.COM #ifdef HAVE_STRING_H
50*9663SMark.Logan@Sun.COM #include <string.h>
51*9663SMark.Logan@Sun.COM #endif
52*9663SMark.Logan@Sun.COM #ifdef HAVE_LOCALE_H
53*9663SMark.Logan@Sun.COM #include <locale.h>
54*9663SMark.Logan@Sun.COM #endif
55*9663SMark.Logan@Sun.COM #ifdef HAVE_LIBINTL_H
56*9663SMark.Logan@Sun.COM #include <libintl.h>
57*9663SMark.Logan@Sun.COM #endif
58*9663SMark.Logan@Sun.COM #ifdef HAVE_STDLIB_H
59*9663SMark.Logan@Sun.COM #include <stdlib.h>
60*9663SMark.Logan@Sun.COM #endif
61*9663SMark.Logan@Sun.COM #ifdef HAVE_LIMITS_H
62*9663SMark.Logan@Sun.COM #include <limits.h>
63*9663SMark.Logan@Sun.COM #endif
64*9663SMark.Logan@Sun.COM #ifdef HAVE_CTYPE_H
65*9663SMark.Logan@Sun.COM #include <ctype.h>
66*9663SMark.Logan@Sun.COM #endif
67*9663SMark.Logan@Sun.COM 
68*9663SMark.Logan@Sun.COM #include "utils.h"
69*9663SMark.Logan@Sun.COM #include "types.h"
70*9663SMark.Logan@Sun.COM #include "volume.h"
71*9663SMark.Logan@Sun.COM #include "debug.h"
72*9663SMark.Logan@Sun.COM #include "dir.h"
73*9663SMark.Logan@Sun.COM #include "version.h"
74*9663SMark.Logan@Sun.COM #include "logging.h"
75*9663SMark.Logan@Sun.COM 
76*9663SMark.Logan@Sun.COM const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n";
77*9663SMark.Logan@Sun.COM const char *ntfs_home = "Linux NTFS homepage: http://www.linux-ntfs.org\n";
78*9663SMark.Logan@Sun.COM const char *ntfs_gpl = "This program is free software, released under the GNU "
79*9663SMark.Logan@Sun.COM 	"General Public License\nand you are welcome to redistribute it under "
80*9663SMark.Logan@Sun.COM 	"certain conditions.  It comes with\nABSOLUTELY NO WARRANTY; for "
81*9663SMark.Logan@Sun.COM 	"details read the GNU General Public License to be\nfound in the file "
82*9663SMark.Logan@Sun.COM 	"\"COPYING\" distributed with this program, or online at:\n"
83*9663SMark.Logan@Sun.COM 	"http://www.gnu.org/copyleft/gpl.html\n";
84*9663SMark.Logan@Sun.COM 
85*9663SMark.Logan@Sun.COM static const char *invalid_ntfs_msg =
86*9663SMark.Logan@Sun.COM "The device '%s' doesn't have a valid NTFS.\n"
87*9663SMark.Logan@Sun.COM "Maybe you selected the wrong device? Or the whole disk instead of a\n"
88*9663SMark.Logan@Sun.COM "partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n";
89*9663SMark.Logan@Sun.COM 
90*9663SMark.Logan@Sun.COM static const char *corrupt_volume_msg =
91*9663SMark.Logan@Sun.COM "NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n"
92*9663SMark.Logan@Sun.COM "The usage of the /f parameter is very IMPORTANT! No modification was\n"
93*9663SMark.Logan@Sun.COM "made to NTFS by this software.\n";
94*9663SMark.Logan@Sun.COM 
95*9663SMark.Logan@Sun.COM static const char *hibernated_volume_msg =
96*9663SMark.Logan@Sun.COM "The NTFS partition is hibernated. Please resume Windows and turned it \n"
97*9663SMark.Logan@Sun.COM "off properly, so mounting could be done safely.\n";
98*9663SMark.Logan@Sun.COM 
99*9663SMark.Logan@Sun.COM static const char *unclean_journal_msg =
100*9663SMark.Logan@Sun.COM "Access is denied because the NTFS journal file is unclean. Choices are:\n"
101*9663SMark.Logan@Sun.COM " A) Shutdown Windows properly.\n"
102*9663SMark.Logan@Sun.COM " B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n"
103*9663SMark.Logan@Sun.COM "    notification area before disconnecting the device.\n"
104*9663SMark.Logan@Sun.COM " C) Use 'Eject' from Windows Explorer to safely remove the device.\n"
105*9663SMark.Logan@Sun.COM " D) If you ran chkdsk previously then boot Windows again which will\n"
106*9663SMark.Logan@Sun.COM "    automatically initialize the journal.\n"
107*9663SMark.Logan@Sun.COM " E) Submit 'force' option (WARNING: This solution it not recommended).\n"
108*9663SMark.Logan@Sun.COM " F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n";
109*9663SMark.Logan@Sun.COM 
110*9663SMark.Logan@Sun.COM static const char *opened_volume_msg =
111*9663SMark.Logan@Sun.COM "Access is denied because the NTFS volume is already exclusively opened.\n"
112*9663SMark.Logan@Sun.COM "The volume may be already mounted, or another software may use it which\n"
113*9663SMark.Logan@Sun.COM "could be identified for example by the help of the 'fuser' command.\n";
114*9663SMark.Logan@Sun.COM 
115*9663SMark.Logan@Sun.COM static const char *dirty_volume_msg =
116*9663SMark.Logan@Sun.COM "Volume is scheduled for check.\n"
117*9663SMark.Logan@Sun.COM "Please boot into Windows TWICE, or use the 'force' option.\n"
118*9663SMark.Logan@Sun.COM "NOTE: If you had not scheduled check and last time accessed this volume\n"
119*9663SMark.Logan@Sun.COM "using ntfsmount and shutdown system properly, then init scripts in your\n"
120*9663SMark.Logan@Sun.COM "distribution are broken. Please report to your distribution developers\n"
121*9663SMark.Logan@Sun.COM "(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n"
122*9663SMark.Logan@Sun.COM "shutdown instead of proper umount.\n";
123*9663SMark.Logan@Sun.COM 
124*9663SMark.Logan@Sun.COM static const char *fakeraid_msg =
125*9663SMark.Logan@Sun.COM "You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n"
126*9663SMark.Logan@Sun.COM "different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n"
127*9663SMark.Logan@Sun.COM "to mount NTFS. Please see the 'dmraid' documentation for help.\n";
128*9663SMark.Logan@Sun.COM 
129*9663SMark.Logan@Sun.COM /**
130*9663SMark.Logan@Sun.COM  * utils_set_locale
131*9663SMark.Logan@Sun.COM  */
132*9663SMark.Logan@Sun.COM int utils_set_locale(void)
133*9663SMark.Logan@Sun.COM {
134*9663SMark.Logan@Sun.COM 	const char *locale;
135*9663SMark.Logan@Sun.COM 
136*9663SMark.Logan@Sun.COM 	locale = setlocale(LC_ALL, "");
137*9663SMark.Logan@Sun.COM 	if (!locale) {
138*9663SMark.Logan@Sun.COM 		locale = setlocale(LC_ALL, NULL);
139*9663SMark.Logan@Sun.COM 		ntfs_log_error("Failed to set locale, using default '%s'.\n",
140*9663SMark.Logan@Sun.COM 				locale);
141*9663SMark.Logan@Sun.COM 		return 1;
142*9663SMark.Logan@Sun.COM 	} else {
143*9663SMark.Logan@Sun.COM 		return 0;
144*9663SMark.Logan@Sun.COM 	}
145*9663SMark.Logan@Sun.COM }
146*9663SMark.Logan@Sun.COM 
147*9663SMark.Logan@Sun.COM /**
148*9663SMark.Logan@Sun.COM  * utils_valid_device - Perform some safety checks on the device, before start
149*9663SMark.Logan@Sun.COM  * @name:   Full pathname of the device/file to work with
150*9663SMark.Logan@Sun.COM  * @force:  Continue regardless of problems
151*9663SMark.Logan@Sun.COM  *
152*9663SMark.Logan@Sun.COM  * Check that the name refers to a device and that is isn't already mounted.
153*9663SMark.Logan@Sun.COM  * These checks can be overridden by using the force option.
154*9663SMark.Logan@Sun.COM  *
155*9663SMark.Logan@Sun.COM  * Return:  1  Success, we can continue
156*9663SMark.Logan@Sun.COM  *	    0  Error, we cannot use this device
157*9663SMark.Logan@Sun.COM  */
158*9663SMark.Logan@Sun.COM int utils_valid_device(const char *name, int force)
159*9663SMark.Logan@Sun.COM {
160*9663SMark.Logan@Sun.COM 	unsigned long mnt_flags = 0;
161*9663SMark.Logan@Sun.COM 	struct stat st;
162*9663SMark.Logan@Sun.COM 
163*9663SMark.Logan@Sun.COM #ifdef __CYGWIN32__
164*9663SMark.Logan@Sun.COM 	/* FIXME: This doesn't work for Cygwin, so just return success. */
165*9663SMark.Logan@Sun.COM 	return 1;
166*9663SMark.Logan@Sun.COM #endif
167*9663SMark.Logan@Sun.COM 	if (!name) {
168*9663SMark.Logan@Sun.COM 		errno = EINVAL;
169*9663SMark.Logan@Sun.COM 		return 0;
170*9663SMark.Logan@Sun.COM 	}
171*9663SMark.Logan@Sun.COM 
172*9663SMark.Logan@Sun.COM 	if (stat(name, &st) == -1) {
173*9663SMark.Logan@Sun.COM 		if (errno == ENOENT)
174*9663SMark.Logan@Sun.COM 			ntfs_log_error("The device %s doesn't exist\n", name);
175*9663SMark.Logan@Sun.COM 		else
176*9663SMark.Logan@Sun.COM 			ntfs_log_perror("Error getting information about %s",
177*9663SMark.Logan@Sun.COM 					name);
178*9663SMark.Logan@Sun.COM 		return 0;
179*9663SMark.Logan@Sun.COM 	}
180*9663SMark.Logan@Sun.COM 
181*9663SMark.Logan@Sun.COM 	/* Make sure the file system is not mounted. */
182*9663SMark.Logan@Sun.COM 	if (ntfs_check_if_mounted(name, &mnt_flags)) {
183*9663SMark.Logan@Sun.COM 		ntfs_log_perror("Failed to determine whether %s is mounted",
184*9663SMark.Logan@Sun.COM 				name);
185*9663SMark.Logan@Sun.COM 		if (!force) {
186*9663SMark.Logan@Sun.COM 			ntfs_log_error("Use the force option to ignore this "
187*9663SMark.Logan@Sun.COM 					"error.\n");
188*9663SMark.Logan@Sun.COM 			return 0;
189*9663SMark.Logan@Sun.COM 		}
190*9663SMark.Logan@Sun.COM 		ntfs_log_warning("Forced to continue.\n");
191*9663SMark.Logan@Sun.COM 	} else if (mnt_flags & NTFS_MF_MOUNTED) {
192*9663SMark.Logan@Sun.COM 		if (!force) {
193*9663SMark.Logan@Sun.COM 			ntfs_log_error("%s", opened_volume_msg);
194*9663SMark.Logan@Sun.COM 			ntfs_log_error("You can use force option to avoid this "
195*9663SMark.Logan@Sun.COM 					"check, but this is not recommended\n"
196*9663SMark.Logan@Sun.COM 					"and may lead to data corruption.\n");
197*9663SMark.Logan@Sun.COM 			return 0;
198*9663SMark.Logan@Sun.COM 		}
199*9663SMark.Logan@Sun.COM 		ntfs_log_warning("Forced to continue.\n");
200*9663SMark.Logan@Sun.COM 	}
201*9663SMark.Logan@Sun.COM 
202*9663SMark.Logan@Sun.COM 	return 1;
203*9663SMark.Logan@Sun.COM }
204*9663SMark.Logan@Sun.COM 
205*9663SMark.Logan@Sun.COM /**
206*9663SMark.Logan@Sun.COM  * utils_mount_volume - Mount an NTFS volume
207*9663SMark.Logan@Sun.COM  */
208*9663SMark.Logan@Sun.COM ntfs_volume * utils_mount_volume(const char *device, ntfs_mount_flags flags)
209*9663SMark.Logan@Sun.COM {
210*9663SMark.Logan@Sun.COM 	ntfs_volume *vol;
211*9663SMark.Logan@Sun.COM 
212*9663SMark.Logan@Sun.COM 	if (!device) {
213*9663SMark.Logan@Sun.COM 		errno = EINVAL;
214*9663SMark.Logan@Sun.COM 		return NULL;
215*9663SMark.Logan@Sun.COM 	}
216*9663SMark.Logan@Sun.COM 
217*9663SMark.Logan@Sun.COM 	if (!utils_valid_device(device, flags & NTFS_MNT_FORCE))
218*9663SMark.Logan@Sun.COM 		return NULL;
219*9663SMark.Logan@Sun.COM 
220*9663SMark.Logan@Sun.COM 	vol = ntfs_mount(device, flags);
221*9663SMark.Logan@Sun.COM 	if (!vol) {
222*9663SMark.Logan@Sun.COM 		ntfs_log_perror("Failed to mount '%s'", device);
223*9663SMark.Logan@Sun.COM 		if (errno == EINVAL)
224*9663SMark.Logan@Sun.COM 			ntfs_log_error(invalid_ntfs_msg, device);
225*9663SMark.Logan@Sun.COM 		else if (errno == EIO)
226*9663SMark.Logan@Sun.COM 			ntfs_log_error("%s", corrupt_volume_msg);
227*9663SMark.Logan@Sun.COM 		else if (errno == EPERM)
228*9663SMark.Logan@Sun.COM 			ntfs_log_error("%s", hibernated_volume_msg);
229*9663SMark.Logan@Sun.COM 		else if (errno == EOPNOTSUPP)
230*9663SMark.Logan@Sun.COM 			ntfs_log_error("%s", unclean_journal_msg);
231*9663SMark.Logan@Sun.COM 		else if (errno == EBUSY)
232*9663SMark.Logan@Sun.COM 			ntfs_log_error("%s", opened_volume_msg);
233*9663SMark.Logan@Sun.COM 		else if (errno == ENXIO)
234*9663SMark.Logan@Sun.COM 			ntfs_log_error("%s", fakeraid_msg);
235*9663SMark.Logan@Sun.COM 		return NULL;
236*9663SMark.Logan@Sun.COM 	}
237*9663SMark.Logan@Sun.COM 
238*9663SMark.Logan@Sun.COM 	if (NVolWasDirty(vol)) {
239*9663SMark.Logan@Sun.COM 		if (!(flags & NTFS_MNT_FORCE)) {
240*9663SMark.Logan@Sun.COM 			ntfs_log_error("%s", dirty_volume_msg);
241*9663SMark.Logan@Sun.COM 			ntfs_umount(vol, FALSE);
242*9663SMark.Logan@Sun.COM 			return NULL;
243*9663SMark.Logan@Sun.COM 		}
244*9663SMark.Logan@Sun.COM 		ntfs_log_error("WARNING: Dirty volume mount was forced by the "
245*9663SMark.Logan@Sun.COM 				"'force' mount option.\n");
246*9663SMark.Logan@Sun.COM 	}
247*9663SMark.Logan@Sun.COM 	return vol;
248*9663SMark.Logan@Sun.COM }
249*9663SMark.Logan@Sun.COM 
250*9663SMark.Logan@Sun.COM /**
251*9663SMark.Logan@Sun.COM  * utils_parse_size - Convert a string representing a size
252*9663SMark.Logan@Sun.COM  * @value:  String to be parsed
253*9663SMark.Logan@Sun.COM  * @size:   Parsed size
254*9663SMark.Logan@Sun.COM  * @scale:  Whether or not to allow a suffix to scale the value
255*9663SMark.Logan@Sun.COM  *
256*9663SMark.Logan@Sun.COM  * Read a string and convert it to a number.  Strings may be suffixed to scale
257*9663SMark.Logan@Sun.COM  * them.  Any number without a suffix is assumed to be in bytes.
258*9663SMark.Logan@Sun.COM  *
259*9663SMark.Logan@Sun.COM  * Suffix  Description  Multiple
260*9663SMark.Logan@Sun.COM  *  [tT]    Terabytes     10^12
261*9663SMark.Logan@Sun.COM  *  [gG]    Gigabytes     10^9
262*9663SMark.Logan@Sun.COM  *  [mM]    Megabytes     10^6
263*9663SMark.Logan@Sun.COM  *  [kK]    Kilobytes     10^3
264*9663SMark.Logan@Sun.COM  *
265*9663SMark.Logan@Sun.COM  * Notes:
266*9663SMark.Logan@Sun.COM  *     Only the first character of the suffix is read.
267*9663SMark.Logan@Sun.COM  *     The multipliers are decimal thousands, not binary: 1000, not 1024.
268*9663SMark.Logan@Sun.COM  *     If parse_size fails, @size will not be changed
269*9663SMark.Logan@Sun.COM  *
270*9663SMark.Logan@Sun.COM  * Return:  1  Success
271*9663SMark.Logan@Sun.COM  *	    0  Error, the string was malformed
272*9663SMark.Logan@Sun.COM  */
273*9663SMark.Logan@Sun.COM int utils_parse_size(const char *value, s64 *size, BOOL scale)
274*9663SMark.Logan@Sun.COM {
275*9663SMark.Logan@Sun.COM 	long long result;
276*9663SMark.Logan@Sun.COM 	char *suffix = NULL;
277*9663SMark.Logan@Sun.COM 
278*9663SMark.Logan@Sun.COM 	if (!value || !size) {
279*9663SMark.Logan@Sun.COM 		errno = EINVAL;
280*9663SMark.Logan@Sun.COM 		return 0;
281*9663SMark.Logan@Sun.COM 	}
282*9663SMark.Logan@Sun.COM 
283*9663SMark.Logan@Sun.COM 	ntfs_log_debug("Parsing size '%s'.\n", value);
284*9663SMark.Logan@Sun.COM 
285*9663SMark.Logan@Sun.COM 	result = strtoll(value, &suffix, 0);
286*9663SMark.Logan@Sun.COM 	if (result < 0 || errno == ERANGE) {
287*9663SMark.Logan@Sun.COM 		ntfs_log_error("Invalid size '%s'.\n", value);
288*9663SMark.Logan@Sun.COM 		return 0;
289*9663SMark.Logan@Sun.COM 	}
290*9663SMark.Logan@Sun.COM 
291*9663SMark.Logan@Sun.COM 	if (!suffix) {
292*9663SMark.Logan@Sun.COM 		ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
293*9663SMark.Logan@Sun.COM 		return 0;
294*9663SMark.Logan@Sun.COM 	}
295*9663SMark.Logan@Sun.COM 
296*9663SMark.Logan@Sun.COM 	if (scale) {
297*9663SMark.Logan@Sun.COM 		switch (suffix[0]) {
298*9663SMark.Logan@Sun.COM 			case 't': case 'T': result *= 1000;
299*9663SMark.Logan@Sun.COM 			case 'g': case 'G': result *= 1000;
300*9663SMark.Logan@Sun.COM 			case 'm': case 'M': result *= 1000;
301*9663SMark.Logan@Sun.COM 			case 'k': case 'K': result *= 1000;
302*9663SMark.Logan@Sun.COM 			case '-': case 0:
303*9663SMark.Logan@Sun.COM 				break;
304*9663SMark.Logan@Sun.COM 			default:
305*9663SMark.Logan@Sun.COM 				ntfs_log_error("Invalid size suffix '%s'.  Use T, G, M, or K.\n", suffix);
306*9663SMark.Logan@Sun.COM 				return 0;
307*9663SMark.Logan@Sun.COM 		}
308*9663SMark.Logan@Sun.COM 	} else {
309*9663SMark.Logan@Sun.COM 		if ((suffix[0] != '-') && (suffix[0] != 0)) {
310*9663SMark.Logan@Sun.COM 			ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value);
311*9663SMark.Logan@Sun.COM 			return 0;
312*9663SMark.Logan@Sun.COM 		}
313*9663SMark.Logan@Sun.COM 	}
314*9663SMark.Logan@Sun.COM 
315*9663SMark.Logan@Sun.COM 	ntfs_log_debug("Parsed size = %lld.\n", result);
316*9663SMark.Logan@Sun.COM 	*size = result;
317*9663SMark.Logan@Sun.COM 	return 1;
318*9663SMark.Logan@Sun.COM }
319*9663SMark.Logan@Sun.COM 
320*9663SMark.Logan@Sun.COM /**
321*9663SMark.Logan@Sun.COM  * utils_parse_range - Convert a string representing a range of numbers
322*9663SMark.Logan@Sun.COM  * @string:  The string to be parsed
323*9663SMark.Logan@Sun.COM  * @start:   The beginning of the range will be stored here
324*9663SMark.Logan@Sun.COM  * @finish:  The end of the range will be stored here
325*9663SMark.Logan@Sun.COM  *
326*9663SMark.Logan@Sun.COM  * Read a string of the form n-m.  If the lower end is missing, zero will be
327*9663SMark.Logan@Sun.COM  * substituted.  If the upper end is missing LONG_MAX will be used.  If the
328*9663SMark.Logan@Sun.COM  * string cannot be parsed correctly, @start and @finish will not be changed.
329*9663SMark.Logan@Sun.COM  *
330*9663SMark.Logan@Sun.COM  * Return:  1  Success, a valid string was found
331*9663SMark.Logan@Sun.COM  *	    0  Error, the string was not a valid range
332*9663SMark.Logan@Sun.COM  */
333*9663SMark.Logan@Sun.COM int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale)
334*9663SMark.Logan@Sun.COM {
335*9663SMark.Logan@Sun.COM 	s64 a, b;
336*9663SMark.Logan@Sun.COM 	char *middle;
337*9663SMark.Logan@Sun.COM 
338*9663SMark.Logan@Sun.COM 	if (!string || !start || !finish) {
339*9663SMark.Logan@Sun.COM 		errno = EINVAL;
340*9663SMark.Logan@Sun.COM 		return 0;
341*9663SMark.Logan@Sun.COM 	}
342*9663SMark.Logan@Sun.COM 
343*9663SMark.Logan@Sun.COM 	middle = strchr(string, '-');
344*9663SMark.Logan@Sun.COM 	if (string == middle) {
345*9663SMark.Logan@Sun.COM 		ntfs_log_debug("Range has no beginning, defaulting to 0.\n");
346*9663SMark.Logan@Sun.COM 		a = 0;
347*9663SMark.Logan@Sun.COM 	} else {
348*9663SMark.Logan@Sun.COM 		if (!utils_parse_size(string, &a, scale))
349*9663SMark.Logan@Sun.COM 			return 0;
350*9663SMark.Logan@Sun.COM 	}
351*9663SMark.Logan@Sun.COM 
352*9663SMark.Logan@Sun.COM 	if (middle) {
353*9663SMark.Logan@Sun.COM 		if (middle[1] == 0) {
354*9663SMark.Logan@Sun.COM 			b = LONG_MAX;		// XXX ULLONG_MAX
355*9663SMark.Logan@Sun.COM 			ntfs_log_debug("Range has no end, defaulting to %lld.\n", b);
356*9663SMark.Logan@Sun.COM 		} else {
357*9663SMark.Logan@Sun.COM 			if (!utils_parse_size(middle+1, &b, scale))
358*9663SMark.Logan@Sun.COM 				return 0;
359*9663SMark.Logan@Sun.COM 		}
360*9663SMark.Logan@Sun.COM 	} else {
361*9663SMark.Logan@Sun.COM 		b = a;
362*9663SMark.Logan@Sun.COM 	}
363*9663SMark.Logan@Sun.COM 
364*9663SMark.Logan@Sun.COM 	ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b);
365*9663SMark.Logan@Sun.COM 
366*9663SMark.Logan@Sun.COM 	*start  = a;
367*9663SMark.Logan@Sun.COM 	*finish = b;
368*9663SMark.Logan@Sun.COM 	return 1;
369*9663SMark.Logan@Sun.COM }
370*9663SMark.Logan@Sun.COM 
371*9663SMark.Logan@Sun.COM /**
372*9663SMark.Logan@Sun.COM  * find_attribute - Find an attribute of the given type
373*9663SMark.Logan@Sun.COM  * @type:  An attribute type, e.g. AT_FILE_NAME
374*9663SMark.Logan@Sun.COM  * @ctx:   A search context, created using ntfs_get_attr_search_ctx
375*9663SMark.Logan@Sun.COM  *
376*9663SMark.Logan@Sun.COM  * Using the search context to keep track, find the first/next occurrence of a
377*9663SMark.Logan@Sun.COM  * given attribute type.
378*9663SMark.Logan@Sun.COM  *
379*9663SMark.Logan@Sun.COM  * N.B.  This will return a pointer into @mft.  As long as the search context
380*9663SMark.Logan@Sun.COM  *       has been created without an inode, it won't overflow the buffer.
381*9663SMark.Logan@Sun.COM  *
382*9663SMark.Logan@Sun.COM  * Return:  Pointer  Success, an attribute was found
383*9663SMark.Logan@Sun.COM  *	    NULL     Error, no matching attributes were found
384*9663SMark.Logan@Sun.COM  */
385*9663SMark.Logan@Sun.COM ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
386*9663SMark.Logan@Sun.COM {
387*9663SMark.Logan@Sun.COM 	if (!ctx) {
388*9663SMark.Logan@Sun.COM 		errno = EINVAL;
389*9663SMark.Logan@Sun.COM 		return NULL;
390*9663SMark.Logan@Sun.COM 	}
391*9663SMark.Logan@Sun.COM 
392*9663SMark.Logan@Sun.COM 	if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) {
393*9663SMark.Logan@Sun.COM 		ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type);
394*9663SMark.Logan@Sun.COM 		return NULL;	/* None / no more of that type */
395*9663SMark.Logan@Sun.COM 	}
396*9663SMark.Logan@Sun.COM 
397*9663SMark.Logan@Sun.COM 	ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type);
398*9663SMark.Logan@Sun.COM 	return ctx->attr;
399*9663SMark.Logan@Sun.COM }
400*9663SMark.Logan@Sun.COM 
401*9663SMark.Logan@Sun.COM /**
402*9663SMark.Logan@Sun.COM  * find_first_attribute - Find the first attribute of a given type
403*9663SMark.Logan@Sun.COM  * @type:  An attribute type, e.g. AT_FILE_NAME
404*9663SMark.Logan@Sun.COM  * @mft:   A buffer containing a raw MFT record
405*9663SMark.Logan@Sun.COM  *
406*9663SMark.Logan@Sun.COM  * Search through a raw MFT record for an attribute of a given type.
407*9663SMark.Logan@Sun.COM  * The return value is a pointer into the MFT record that was supplied.
408*9663SMark.Logan@Sun.COM  *
409*9663SMark.Logan@Sun.COM  * N.B.  This will return a pointer into @mft.  The pointer won't stray outside
410*9663SMark.Logan@Sun.COM  *       the buffer, since we created the search context without an inode.
411*9663SMark.Logan@Sun.COM  *
412*9663SMark.Logan@Sun.COM  * Return:  Pointer  Success, an attribute was found
413*9663SMark.Logan@Sun.COM  *	    NULL     Error, no matching attributes were found
414*9663SMark.Logan@Sun.COM  */
415*9663SMark.Logan@Sun.COM ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft)
416*9663SMark.Logan@Sun.COM {
417*9663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ctx;
418*9663SMark.Logan@Sun.COM 	ATTR_RECORD *rec;
419*9663SMark.Logan@Sun.COM 
420*9663SMark.Logan@Sun.COM 	if (!mft) {
421*9663SMark.Logan@Sun.COM 		errno = EINVAL;
422*9663SMark.Logan@Sun.COM 		return NULL;
423*9663SMark.Logan@Sun.COM 	}
424*9663SMark.Logan@Sun.COM 
425*9663SMark.Logan@Sun.COM 	ctx = ntfs_attr_get_search_ctx(NULL, mft);
426*9663SMark.Logan@Sun.COM 	if (!ctx) {
427*9663SMark.Logan@Sun.COM 		ntfs_log_error("Couldn't create a search context.\n");
428*9663SMark.Logan@Sun.COM 		return NULL;
429*9663SMark.Logan@Sun.COM 	}
430*9663SMark.Logan@Sun.COM 
431*9663SMark.Logan@Sun.COM 	rec = find_attribute(type, ctx);
432*9663SMark.Logan@Sun.COM 	ntfs_attr_put_search_ctx(ctx);
433*9663SMark.Logan@Sun.COM 	if (rec)
434*9663SMark.Logan@Sun.COM 		ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type);
435*9663SMark.Logan@Sun.COM 	else
436*9663SMark.Logan@Sun.COM 		ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type);
437*9663SMark.Logan@Sun.COM 	return rec;
438*9663SMark.Logan@Sun.COM }
439*9663SMark.Logan@Sun.COM 
440*9663SMark.Logan@Sun.COM /**
441*9663SMark.Logan@Sun.COM  * utils_inode_get_name
442*9663SMark.Logan@Sun.COM  *
443*9663SMark.Logan@Sun.COM  * using inode
444*9663SMark.Logan@Sun.COM  * get filename
445*9663SMark.Logan@Sun.COM  * add name to list
446*9663SMark.Logan@Sun.COM  * get parent
447*9663SMark.Logan@Sun.COM  * if parent is 5 (/) stop
448*9663SMark.Logan@Sun.COM  * get inode of parent
449*9663SMark.Logan@Sun.COM  */
450*9663SMark.Logan@Sun.COM #define max_path 20
451*9663SMark.Logan@Sun.COM int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
452*9663SMark.Logan@Sun.COM {
453*9663SMark.Logan@Sun.COM 	// XXX option: names = posix/win32 or dos
454*9663SMark.Logan@Sun.COM 	// flags: path, filename, or both
455*9663SMark.Logan@Sun.COM 
456*9663SMark.Logan@Sun.COM 
457*9663SMark.Logan@Sun.COM 	ntfs_volume *vol;
458*9663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *ctx;
459*9663SMark.Logan@Sun.COM 	ATTR_RECORD *rec;
460*9663SMark.Logan@Sun.COM 	FILE_NAME_ATTR *attr;
461*9663SMark.Logan@Sun.COM 	int name_space;
462*9663SMark.Logan@Sun.COM 	MFT_REF parent = FILE_root;
463*9663SMark.Logan@Sun.COM 	char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger?
464*9663SMark.Logan@Sun.COM 	int i, len, offset = 0;
465*9663SMark.Logan@Sun.COM 
466*9663SMark.Logan@Sun.COM 	if (!inode || !buffer) {
467*9663SMark.Logan@Sun.COM 		errno = EINVAL;
468*9663SMark.Logan@Sun.COM 		return 0;
469*9663SMark.Logan@Sun.COM 	}
470*9663SMark.Logan@Sun.COM 
471*9663SMark.Logan@Sun.COM 	vol = inode->vol;
472*9663SMark.Logan@Sun.COM 
473*9663SMark.Logan@Sun.COM 	//ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names));
474*9663SMark.Logan@Sun.COM 	memset(names, 0, sizeof(names));
475*9663SMark.Logan@Sun.COM 
476*9663SMark.Logan@Sun.COM 	for (i = 0; i < max_path; i++) {
477*9663SMark.Logan@Sun.COM 
478*9663SMark.Logan@Sun.COM 		ctx = ntfs_attr_get_search_ctx(inode, NULL);
479*9663SMark.Logan@Sun.COM 		if (!ctx) {
480*9663SMark.Logan@Sun.COM 			ntfs_log_error("Couldn't create a search context.\n");
481*9663SMark.Logan@Sun.COM 			return 0;
482*9663SMark.Logan@Sun.COM 		}
483*9663SMark.Logan@Sun.COM 
484*9663SMark.Logan@Sun.COM 		//ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no);
485*9663SMark.Logan@Sun.COM 
486*9663SMark.Logan@Sun.COM 		name_space = 4;
487*9663SMark.Logan@Sun.COM 		while ((rec = find_attribute(AT_FILE_NAME, ctx))) {
488*9663SMark.Logan@Sun.COM 			/* We know this will always be resident. */
489*9663SMark.Logan@Sun.COM 			attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->u.res.value_offset));
490*9663SMark.Logan@Sun.COM 
491*9663SMark.Logan@Sun.COM 			if (attr->file_name_type > name_space) { //XXX find the ...
492*9663SMark.Logan@Sun.COM 				continue;
493*9663SMark.Logan@Sun.COM 			}
494*9663SMark.Logan@Sun.COM 
495*9663SMark.Logan@Sun.COM 			name_space = attr->file_name_type;
496*9663SMark.Logan@Sun.COM 			parent     = le64_to_cpu(attr->parent_directory);
497*9663SMark.Logan@Sun.COM 
498*9663SMark.Logan@Sun.COM 			if (names[i]) {
499*9663SMark.Logan@Sun.COM 				free(names[i]);
500*9663SMark.Logan@Sun.COM 				names[i] = NULL;
501*9663SMark.Logan@Sun.COM 			}
502*9663SMark.Logan@Sun.COM 
503*9663SMark.Logan@Sun.COM 			if (ntfs_ucstombs(attr->file_name, attr->file_name_length,
504*9663SMark.Logan@Sun.COM 			    &names[i], 0) < 0) {
505*9663SMark.Logan@Sun.COM 				char *temp;
506*9663SMark.Logan@Sun.COM 				ntfs_log_error("Couldn't translate filename to current locale.\n");
507*9663SMark.Logan@Sun.COM 				temp = ntfs_malloc(30);
508*9663SMark.Logan@Sun.COM 				if (!temp)
509*9663SMark.Logan@Sun.COM 					return 0;
510*9663SMark.Logan@Sun.COM 				snprintf(temp, 30, "<MFT%llu>", (unsigned
511*9663SMark.Logan@Sun.COM 						long long)inode->mft_no);
512*9663SMark.Logan@Sun.COM 				names[i] = temp;
513*9663SMark.Logan@Sun.COM 			}
514*9663SMark.Logan@Sun.COM 
515*9663SMark.Logan@Sun.COM 			//ntfs_log_debug("names[%d] %s\n", i, names[i]);
516*9663SMark.Logan@Sun.COM 			//ntfs_log_debug("parent = %lld\n", MREF(parent));
517*9663SMark.Logan@Sun.COM 		}
518*9663SMark.Logan@Sun.COM 
519*9663SMark.Logan@Sun.COM 		ntfs_attr_put_search_ctx(ctx);
520*9663SMark.Logan@Sun.COM 
521*9663SMark.Logan@Sun.COM 		if (i > 0)			/* Don't close the original inode */
522*9663SMark.Logan@Sun.COM 			ntfs_inode_close(inode);
523*9663SMark.Logan@Sun.COM 
524*9663SMark.Logan@Sun.COM 		if (MREF(parent) == FILE_root) {	/* The root directory, stop. */
525*9663SMark.Logan@Sun.COM 			//ntfs_log_debug("inode 5\n");
526*9663SMark.Logan@Sun.COM 			break;
527*9663SMark.Logan@Sun.COM 		}
528*9663SMark.Logan@Sun.COM 
529*9663SMark.Logan@Sun.COM 		inode = ntfs_inode_open(vol, parent);
530*9663SMark.Logan@Sun.COM 		if (!inode) {
531*9663SMark.Logan@Sun.COM 			ntfs_log_error("Couldn't open inode %llu.\n",
532*9663SMark.Logan@Sun.COM 					(unsigned long long)MREF(parent));
533*9663SMark.Logan@Sun.COM 			break;
534*9663SMark.Logan@Sun.COM 		}
535*9663SMark.Logan@Sun.COM 	}
536*9663SMark.Logan@Sun.COM 
537*9663SMark.Logan@Sun.COM 	if (i >= max_path) {
538*9663SMark.Logan@Sun.COM 		/* If we get into an infinite loop, we'll end up here. */
539*9663SMark.Logan@Sun.COM 		ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path);
540*9663SMark.Logan@Sun.COM 		return 0;
541*9663SMark.Logan@Sun.COM 	}
542*9663SMark.Logan@Sun.COM 
543*9663SMark.Logan@Sun.COM 	/* Assemble the names in the correct order. */
544*9663SMark.Logan@Sun.COM 	for (i = max_path; i >= 0; i--) {
545*9663SMark.Logan@Sun.COM 		if (!names[i])
546*9663SMark.Logan@Sun.COM 			continue;
547*9663SMark.Logan@Sun.COM 
548*9663SMark.Logan@Sun.COM 		len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]);
549*9663SMark.Logan@Sun.COM 		if (len >= (bufsize - offset)) {
550*9663SMark.Logan@Sun.COM 			ntfs_log_error("Pathname was truncated.\n");
551*9663SMark.Logan@Sun.COM 			break;
552*9663SMark.Logan@Sun.COM 		}
553*9663SMark.Logan@Sun.COM 
554*9663SMark.Logan@Sun.COM 		offset += len;
555*9663SMark.Logan@Sun.COM 	}
556*9663SMark.Logan@Sun.COM 
557*9663SMark.Logan@Sun.COM 	/* Free all the allocated memory */
558*9663SMark.Logan@Sun.COM 	for (i = 0; i < max_path; i++)
559*9663SMark.Logan@Sun.COM 		free(names[i]);
560*9663SMark.Logan@Sun.COM 
561*9663SMark.Logan@Sun.COM 	ntfs_log_debug("Pathname: %s\n", buffer);
562*9663SMark.Logan@Sun.COM 
563*9663SMark.Logan@Sun.COM 	return 1;
564*9663SMark.Logan@Sun.COM }
565*9663SMark.Logan@Sun.COM #undef max_path
566*9663SMark.Logan@Sun.COM 
567*9663SMark.Logan@Sun.COM /**
568*9663SMark.Logan@Sun.COM  * utils_attr_get_name
569*9663SMark.Logan@Sun.COM  */
570*9663SMark.Logan@Sun.COM int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize)
571*9663SMark.Logan@Sun.COM {
572*9663SMark.Logan@Sun.COM 	int len, namelen;
573*9663SMark.Logan@Sun.COM 	char *name;
574*9663SMark.Logan@Sun.COM 	ATTR_DEF *attrdef;
575*9663SMark.Logan@Sun.COM 
576*9663SMark.Logan@Sun.COM 	// flags: attr, name, or both
577*9663SMark.Logan@Sun.COM 	if (!attr || !buffer) {
578*9663SMark.Logan@Sun.COM 		errno = EINVAL;
579*9663SMark.Logan@Sun.COM 		return 0;
580*9663SMark.Logan@Sun.COM 	}
581*9663SMark.Logan@Sun.COM 
582*9663SMark.Logan@Sun.COM 	attrdef = ntfs_attr_find_in_attrdef(vol, attr->type);
583*9663SMark.Logan@Sun.COM 	if (attrdef) {
584*9663SMark.Logan@Sun.COM 		name    = NULL;
585*9663SMark.Logan@Sun.COM 		namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name));
586*9663SMark.Logan@Sun.COM 		if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) {
587*9663SMark.Logan@Sun.COM 			ntfs_log_error("Couldn't translate attribute type to "
588*9663SMark.Logan@Sun.COM 					"current locale.\n");
589*9663SMark.Logan@Sun.COM 			// <UNKNOWN>?
590*9663SMark.Logan@Sun.COM 			return 0;
591*9663SMark.Logan@Sun.COM 		}
592*9663SMark.Logan@Sun.COM 		len = snprintf(buffer, bufsize, "%s", name);
593*9663SMark.Logan@Sun.COM 	} else {
594*9663SMark.Logan@Sun.COM 		ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type);
595*9663SMark.Logan@Sun.COM 		len = snprintf(buffer, bufsize, "<UNKNOWN>");
596*9663SMark.Logan@Sun.COM 	}
597*9663SMark.Logan@Sun.COM 
598*9663SMark.Logan@Sun.COM 	if (len >= bufsize) {
599*9663SMark.Logan@Sun.COM 		ntfs_log_error("Attribute type was truncated.\n");
600*9663SMark.Logan@Sun.COM 		return 0;
601*9663SMark.Logan@Sun.COM 	}
602*9663SMark.Logan@Sun.COM 
603*9663SMark.Logan@Sun.COM 	if (!attr->name_length) {
604*9663SMark.Logan@Sun.COM 		return 0;
605*9663SMark.Logan@Sun.COM 	}
606*9663SMark.Logan@Sun.COM 
607*9663SMark.Logan@Sun.COM 	buffer  += len;
608*9663SMark.Logan@Sun.COM 	bufsize -= len;
609*9663SMark.Logan@Sun.COM 
610*9663SMark.Logan@Sun.COM 	name    = NULL;
611*9663SMark.Logan@Sun.COM 	namelen = attr->name_length;
612*9663SMark.Logan@Sun.COM 	if (ntfs_ucstombs((ntfschar *)((char *)attr + le16_to_cpu(
613*9663SMark.Logan@Sun.COM 			attr->name_offset)), namelen, &name, 0) < 0) {
614*9663SMark.Logan@Sun.COM 		ntfs_log_error("Couldn't translate attribute name to current "
615*9663SMark.Logan@Sun.COM 				"locale.\n");
616*9663SMark.Logan@Sun.COM 		// <UNKNOWN>?
617*9663SMark.Logan@Sun.COM 		len = snprintf(buffer, bufsize, "<UNKNOWN>");
618*9663SMark.Logan@Sun.COM 		return 0;
619*9663SMark.Logan@Sun.COM 	}
620*9663SMark.Logan@Sun.COM 
621*9663SMark.Logan@Sun.COM 	len = snprintf(buffer, bufsize, "(%s)", name);
622*9663SMark.Logan@Sun.COM 	free(name);
623*9663SMark.Logan@Sun.COM 
624*9663SMark.Logan@Sun.COM 	if (len >= bufsize) {
625*9663SMark.Logan@Sun.COM 		ntfs_log_error("Attribute name was truncated.\n");
626*9663SMark.Logan@Sun.COM 		return 0;
627*9663SMark.Logan@Sun.COM 	}
628*9663SMark.Logan@Sun.COM 
629*9663SMark.Logan@Sun.COM 	return 0;
630*9663SMark.Logan@Sun.COM }
631*9663SMark.Logan@Sun.COM 
632*9663SMark.Logan@Sun.COM /**
633*9663SMark.Logan@Sun.COM  * utils_cluster_in_use - Determine if a cluster is in use
634*9663SMark.Logan@Sun.COM  * @vol:  An ntfs volume obtained from ntfs_mount
635*9663SMark.Logan@Sun.COM  * @lcn:  The Logical Cluster Number to test
636*9663SMark.Logan@Sun.COM  *
637*9663SMark.Logan@Sun.COM  * The metadata file $Bitmap has one binary bit representing each cluster on
638*9663SMark.Logan@Sun.COM  * disk.  The bit will be set for each cluster that is in use.  The function
639*9663SMark.Logan@Sun.COM  * reads the relevant part of $Bitmap into a buffer and tests the bit.
640*9663SMark.Logan@Sun.COM  *
641*9663SMark.Logan@Sun.COM  * This function has a static buffer in which it caches a section of $Bitmap.
642*9663SMark.Logan@Sun.COM  * If the lcn, being tested, lies outside the range, the buffer will be
643*9663SMark.Logan@Sun.COM  * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the
644*9663SMark.Logan@Sun.COM  * buffer.
645*9663SMark.Logan@Sun.COM  *
646*9663SMark.Logan@Sun.COM  * NOTE: Be very carefull with shifts by 3 everywhere in this function.
647*9663SMark.Logan@Sun.COM  *
648*9663SMark.Logan@Sun.COM  * Return:  1  Cluster is in use
649*9663SMark.Logan@Sun.COM  *	    0  Cluster is free space
650*9663SMark.Logan@Sun.COM  *	   -1  Error occurred
651*9663SMark.Logan@Sun.COM  */
652*9663SMark.Logan@Sun.COM int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
653*9663SMark.Logan@Sun.COM {
654*9663SMark.Logan@Sun.COM 	static unsigned char buffer[512];
655*9663SMark.Logan@Sun.COM 	static long long bmplcn = -(sizeof(buffer) << 3);
656*9663SMark.Logan@Sun.COM 	int byte, bit;
657*9663SMark.Logan@Sun.COM 	ntfs_attr *attr;
658*9663SMark.Logan@Sun.COM 
659*9663SMark.Logan@Sun.COM 	if (!vol) {
660*9663SMark.Logan@Sun.COM 		errno = EINVAL;
661*9663SMark.Logan@Sun.COM 		return -1;
662*9663SMark.Logan@Sun.COM 	}
663*9663SMark.Logan@Sun.COM 
664*9663SMark.Logan@Sun.COM 	/* Does lcn lie in the section of $Bitmap we already have cached? */
665*9663SMark.Logan@Sun.COM 	if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof(buffer) << 3)))) {
666*9663SMark.Logan@Sun.COM 		ntfs_log_debug("Bit lies outside cache.\n");
667*9663SMark.Logan@Sun.COM 		attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
668*9663SMark.Logan@Sun.COM 		if (!attr) {
669*9663SMark.Logan@Sun.COM 			ntfs_log_perror("Couldn't open $Bitmap");
670*9663SMark.Logan@Sun.COM 			return -1;
671*9663SMark.Logan@Sun.COM 		}
672*9663SMark.Logan@Sun.COM 
673*9663SMark.Logan@Sun.COM 		/* Mark the buffer as in use, in case the read is shorter. */
674*9663SMark.Logan@Sun.COM 		memset(buffer, 0xFF, sizeof(buffer));
675*9663SMark.Logan@Sun.COM 		bmplcn = lcn & (~((sizeof(buffer) << 3) - 1));
676*9663SMark.Logan@Sun.COM 
677*9663SMark.Logan@Sun.COM 		if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer),
678*9663SMark.Logan@Sun.COM 					buffer) < 0) {
679*9663SMark.Logan@Sun.COM 			ntfs_log_perror("Couldn't read $Bitmap");
680*9663SMark.Logan@Sun.COM 			ntfs_attr_close(attr);
681*9663SMark.Logan@Sun.COM 			return -1;
682*9663SMark.Logan@Sun.COM 		}
683*9663SMark.Logan@Sun.COM 
684*9663SMark.Logan@Sun.COM 		ntfs_log_debug("Reloaded bitmap buffer.\n");
685*9663SMark.Logan@Sun.COM 		ntfs_attr_close(attr);
686*9663SMark.Logan@Sun.COM 	}
687*9663SMark.Logan@Sun.COM 
688*9663SMark.Logan@Sun.COM 	bit  = 1 << (lcn & 7);
689*9663SMark.Logan@Sun.COM 	byte = (lcn >> 3) & (sizeof(buffer) - 1);
690*9663SMark.Logan@Sun.COM 	ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, "
691*9663SMark.Logan@Sun.COM 			"in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] &
692*9663SMark.Logan@Sun.COM 			bit);
693*9663SMark.Logan@Sun.COM 
694*9663SMark.Logan@Sun.COM 	return (buffer[byte] & bit);
695*9663SMark.Logan@Sun.COM }
696*9663SMark.Logan@Sun.COM 
697*9663SMark.Logan@Sun.COM /**
698*9663SMark.Logan@Sun.COM  * utils_mftrec_in_use - Determine if a MFT Record is in use
699*9663SMark.Logan@Sun.COM  * @vol:   An ntfs volume obtained from ntfs_mount
700*9663SMark.Logan@Sun.COM  * @mref:  MFT Reference (inode number)
701*9663SMark.Logan@Sun.COM  *
702*9663SMark.Logan@Sun.COM  * The metadata file $BITMAP has one binary bit representing each record in the
703*9663SMark.Logan@Sun.COM  * MFT.  The bit will be set for each record that is in use.  The function
704*9663SMark.Logan@Sun.COM  * reads the relevant part of $BITMAP into a buffer and tests the bit.
705*9663SMark.Logan@Sun.COM  *
706*9663SMark.Logan@Sun.COM  * This function has a static buffer in which it caches a section of $BITMAP.
707*9663SMark.Logan@Sun.COM  * If the mref, being tested, lies outside the range, the buffer will be
708*9663SMark.Logan@Sun.COM  * refreshed.
709*9663SMark.Logan@Sun.COM  *
710*9663SMark.Logan@Sun.COM  * Return:  1  MFT Record is in use
711*9663SMark.Logan@Sun.COM  *	    0  MFT Record is unused
712*9663SMark.Logan@Sun.COM  *	   -1  Error occurred
713*9663SMark.Logan@Sun.COM  */
714*9663SMark.Logan@Sun.COM int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
715*9663SMark.Logan@Sun.COM {
716*9663SMark.Logan@Sun.COM 	static u8 buffer[512];
717*9663SMark.Logan@Sun.COM 	static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */
718*9663SMark.Logan@Sun.COM 	int byte, bit;
719*9663SMark.Logan@Sun.COM 
720*9663SMark.Logan@Sun.COM 	ntfs_log_trace("Entering.\n");
721*9663SMark.Logan@Sun.COM 
722*9663SMark.Logan@Sun.COM 	if (!vol) {
723*9663SMark.Logan@Sun.COM 		errno = EINVAL;
724*9663SMark.Logan@Sun.COM 		return -1;
725*9663SMark.Logan@Sun.COM 	}
726*9663SMark.Logan@Sun.COM 
727*9663SMark.Logan@Sun.COM 	/* Does mref lie in the section of $Bitmap we already have cached? */
728*9663SMark.Logan@Sun.COM 	if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref +
729*9663SMark.Logan@Sun.COM 			(sizeof(buffer) << 3)))) {
730*9663SMark.Logan@Sun.COM 		ntfs_log_debug("Bit lies outside cache.\n");
731*9663SMark.Logan@Sun.COM 
732*9663SMark.Logan@Sun.COM 		/* Mark the buffer as not in use, in case the read is shorter. */
733*9663SMark.Logan@Sun.COM 		memset(buffer, 0, sizeof(buffer));
734*9663SMark.Logan@Sun.COM 		bmpmref = mref & (~((sizeof(buffer) << 3) - 1));
735*9663SMark.Logan@Sun.COM 
736*9663SMark.Logan@Sun.COM 		if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) {
737*9663SMark.Logan@Sun.COM 			ntfs_log_perror("Couldn't read $MFT/$BITMAP");
738*9663SMark.Logan@Sun.COM 			return -1;
739*9663SMark.Logan@Sun.COM 		}
740*9663SMark.Logan@Sun.COM 
741*9663SMark.Logan@Sun.COM 		ntfs_log_debug("Reloaded bitmap buffer.\n");
742*9663SMark.Logan@Sun.COM 	}
743*9663SMark.Logan@Sun.COM 
744*9663SMark.Logan@Sun.COM 	bit  = 1 << (mref & 7);
745*9663SMark.Logan@Sun.COM 	byte = (mref >> 3) & (sizeof(buffer) - 1);
746*9663SMark.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);
747*9663SMark.Logan@Sun.COM 
748*9663SMark.Logan@Sun.COM 	return (buffer[byte] & bit);
749*9663SMark.Logan@Sun.COM }
750*9663SMark.Logan@Sun.COM 
751*9663SMark.Logan@Sun.COM /**
752*9663SMark.Logan@Sun.COM  * __metadata
753*9663SMark.Logan@Sun.COM  */
754*9663SMark.Logan@Sun.COM static int __metadata(ntfs_volume *vol, u64 num)
755*9663SMark.Logan@Sun.COM {
756*9663SMark.Logan@Sun.COM 	if (num <= FILE_UpCase)
757*9663SMark.Logan@Sun.COM 		return 1;
758*9663SMark.Logan@Sun.COM 	if (!vol)
759*9663SMark.Logan@Sun.COM 		return -1;
760*9663SMark.Logan@Sun.COM 	if ((vol->major_ver == 3) && (num == FILE_Extend))
761*9663SMark.Logan@Sun.COM 		return 1;
762*9663SMark.Logan@Sun.COM 
763*9663SMark.Logan@Sun.COM 	return 0;
764*9663SMark.Logan@Sun.COM }
765*9663SMark.Logan@Sun.COM 
766*9663SMark.Logan@Sun.COM /**
767*9663SMark.Logan@Sun.COM  * utils_is_metadata - Determine if an inode represents a metadata file
768*9663SMark.Logan@Sun.COM  * @inode:  An ntfs inode to be tested
769*9663SMark.Logan@Sun.COM  *
770*9663SMark.Logan@Sun.COM  * A handful of files in the volume contain filesystem data - metadata.
771*9663SMark.Logan@Sun.COM  * They can be identified by their inode number (offset in MFT/$DATA) or by
772*9663SMark.Logan@Sun.COM  * their parent.
773*9663SMark.Logan@Sun.COM  *
774*9663SMark.Logan@Sun.COM  * Return:  1  inode is a metadata file
775*9663SMark.Logan@Sun.COM  *	    0  inode is not a metadata file
776*9663SMark.Logan@Sun.COM  *	   -1  Error occurred
777*9663SMark.Logan@Sun.COM  */
778*9663SMark.Logan@Sun.COM int utils_is_metadata(ntfs_inode *inode)
779*9663SMark.Logan@Sun.COM {
780*9663SMark.Logan@Sun.COM 	ntfs_volume *vol;
781*9663SMark.Logan@Sun.COM 	ATTR_RECORD *rec;
782*9663SMark.Logan@Sun.COM 	FILE_NAME_ATTR *attr;
783*9663SMark.Logan@Sun.COM 	MFT_RECORD *file;
784*9663SMark.Logan@Sun.COM 	u64 num;
785*9663SMark.Logan@Sun.COM 
786*9663SMark.Logan@Sun.COM 	if (!inode) {
787*9663SMark.Logan@Sun.COM 		errno = EINVAL;
788*9663SMark.Logan@Sun.COM 		return -1;
789*9663SMark.Logan@Sun.COM 	}
790*9663SMark.Logan@Sun.COM 
791*9663SMark.Logan@Sun.COM 	vol = inode->vol;
792*9663SMark.Logan@Sun.COM 	if (!vol)
793*9663SMark.Logan@Sun.COM 		return -1;
794*9663SMark.Logan@Sun.COM 
795*9663SMark.Logan@Sun.COM 	num = inode->mft_no;
796*9663SMark.Logan@Sun.COM 	if (__metadata(vol, num) == 1)
797*9663SMark.Logan@Sun.COM 		return 1;
798*9663SMark.Logan@Sun.COM 
799*9663SMark.Logan@Sun.COM 	file = inode->mrec;
800*9663SMark.Logan@Sun.COM 	if (file && (file->base_mft_record != 0)) {
801*9663SMark.Logan@Sun.COM 		num = MREF_LE(file->base_mft_record);
802*9663SMark.Logan@Sun.COM 		if (__metadata(vol, num) == 1)
803*9663SMark.Logan@Sun.COM 			return 1;
804*9663SMark.Logan@Sun.COM 	}
805*9663SMark.Logan@Sun.COM 	file = inode->mrec;
806*9663SMark.Logan@Sun.COM 
807*9663SMark.Logan@Sun.COM 	rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
808*9663SMark.Logan@Sun.COM 	if (!rec)
809*9663SMark.Logan@Sun.COM 		return -1;
810*9663SMark.Logan@Sun.COM 
811*9663SMark.Logan@Sun.COM 	/* We know this will always be resident. */
812*9663SMark.Logan@Sun.COM 	attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->u.res.value_offset));
813*9663SMark.Logan@Sun.COM 
814*9663SMark.Logan@Sun.COM 	num = MREF_LE(attr->parent_directory);
815*9663SMark.Logan@Sun.COM 	if ((num != FILE_root) && (__metadata(vol, num) == 1))
816*9663SMark.Logan@Sun.COM 		return 1;
817*9663SMark.Logan@Sun.COM 
818*9663SMark.Logan@Sun.COM 	return 0;
819*9663SMark.Logan@Sun.COM }
820*9663SMark.Logan@Sun.COM 
821*9663SMark.Logan@Sun.COM /**
822*9663SMark.Logan@Sun.COM  * utils_dump_mem - Display a block of memory in hex and ascii
823*9663SMark.Logan@Sun.COM  * @buf:     Buffer to be displayed
824*9663SMark.Logan@Sun.COM  * @start:   Offset into @buf to start from
825*9663SMark.Logan@Sun.COM  * @length:  Number of bytes to display
826*9663SMark.Logan@Sun.COM  * @flags:   Options to change the style of the output
827*9663SMark.Logan@Sun.COM  *
828*9663SMark.Logan@Sun.COM  * Display a block of memory in a tradition hex-dump manner.
829*9663SMark.Logan@Sun.COM  * Optionally the ascii part can be turned off.
830*9663SMark.Logan@Sun.COM  *
831*9663SMark.Logan@Sun.COM  * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS).
832*9663SMark.Logan@Sun.COM  * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the
833*9663SMark.Logan@Sun.COM  * output); DM_NO_ASCII (only print the hex values).
834*9663SMark.Logan@Sun.COM  */
835*9663SMark.Logan@Sun.COM void utils_dump_mem(void *buf, int start, int length, int flags)
836*9663SMark.Logan@Sun.COM {
837*9663SMark.Logan@Sun.COM 	int off, i, s, e, col;
838*9663SMark.Logan@Sun.COM 	u8 *mem = buf;
839*9663SMark.Logan@Sun.COM 
840*9663SMark.Logan@Sun.COM 	s =  start                & ~15;	// round down
841*9663SMark.Logan@Sun.COM 	e = (start + length + 15) & ~15;	// round up
842*9663SMark.Logan@Sun.COM 
843*9663SMark.Logan@Sun.COM 	for (off = s; off < e; off += 16) {
844*9663SMark.Logan@Sun.COM 		col = 30;
845*9663SMark.Logan@Sun.COM 		if (flags & DM_RED)
846*9663SMark.Logan@Sun.COM 			col += 1;
847*9663SMark.Logan@Sun.COM 		if (flags & DM_GREEN)
848*9663SMark.Logan@Sun.COM 			col += 2;
849*9663SMark.Logan@Sun.COM 		if (flags & DM_BLUE)
850*9663SMark.Logan@Sun.COM 			col += 4;
851*9663SMark.Logan@Sun.COM 		if (flags & DM_INDENT)
852*9663SMark.Logan@Sun.COM 			ntfs_log_debug("\t");
853*9663SMark.Logan@Sun.COM 		if (flags & DM_BOLD)
854*9663SMark.Logan@Sun.COM 			ntfs_log_debug("\e[01m");
855*9663SMark.Logan@Sun.COM 		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
856*9663SMark.Logan@Sun.COM 			ntfs_log_debug("\e[%dm", col);
857*9663SMark.Logan@Sun.COM 		if (off == s)
858*9663SMark.Logan@Sun.COM 			ntfs_log_debug("%6.6x ", start);
859*9663SMark.Logan@Sun.COM 		else
860*9663SMark.Logan@Sun.COM 			ntfs_log_debug("%6.6x ", off);
861*9663SMark.Logan@Sun.COM 
862*9663SMark.Logan@Sun.COM 		for (i = 0; i < 16; i++) {
863*9663SMark.Logan@Sun.COM 			if ((i == 8) && (!(flags & DM_NO_DIVIDER)))
864*9663SMark.Logan@Sun.COM 				ntfs_log_debug(" -");
865*9663SMark.Logan@Sun.COM 			if (((off+i) >= start) && ((off+i) < (start+length)))
866*9663SMark.Logan@Sun.COM 				ntfs_log_debug(" %02X", mem[off+i]);
867*9663SMark.Logan@Sun.COM 			else
868*9663SMark.Logan@Sun.COM 				ntfs_log_debug("   ");
869*9663SMark.Logan@Sun.COM 		}
870*9663SMark.Logan@Sun.COM 		if (!(flags & DM_NO_ASCII)) {
871*9663SMark.Logan@Sun.COM 			ntfs_log_debug("  ");
872*9663SMark.Logan@Sun.COM 			for (i = 0; i < 16; i++) {
873*9663SMark.Logan@Sun.COM 				if (((off+i) < start) || ((off+i) >= (start+length)))
874*9663SMark.Logan@Sun.COM 					ntfs_log_debug(" ");
875*9663SMark.Logan@Sun.COM 				else if (isprint(mem[off + i]))
876*9663SMark.Logan@Sun.COM 					ntfs_log_debug("%c", mem[off + i]);
877*9663SMark.Logan@Sun.COM 				else
878*9663SMark.Logan@Sun.COM 					ntfs_log_debug(".");
879*9663SMark.Logan@Sun.COM 			}
880*9663SMark.Logan@Sun.COM 		}
881*9663SMark.Logan@Sun.COM 		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
882*9663SMark.Logan@Sun.COM 			ntfs_log_debug("\e[0m");
883*9663SMark.Logan@Sun.COM 		ntfs_log_debug("\n");
884*9663SMark.Logan@Sun.COM 	}
885*9663SMark.Logan@Sun.COM }
886*9663SMark.Logan@Sun.COM 
887*9663SMark.Logan@Sun.COM 
888*9663SMark.Logan@Sun.COM /**
889*9663SMark.Logan@Sun.COM  * mft_get_search_ctx
890*9663SMark.Logan@Sun.COM  */
891*9663SMark.Logan@Sun.COM struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
892*9663SMark.Logan@Sun.COM {
893*9663SMark.Logan@Sun.COM 	struct mft_search_ctx *ctx;
894*9663SMark.Logan@Sun.COM 
895*9663SMark.Logan@Sun.COM 	if (!vol) {
896*9663SMark.Logan@Sun.COM 		errno = EINVAL;
897*9663SMark.Logan@Sun.COM 		return NULL;
898*9663SMark.Logan@Sun.COM 	}
899*9663SMark.Logan@Sun.COM 
900*9663SMark.Logan@Sun.COM 	ctx = calloc(1, sizeof *ctx);
901*9663SMark.Logan@Sun.COM 
902*9663SMark.Logan@Sun.COM 	ctx->mft_num = -1;
903*9663SMark.Logan@Sun.COM 	ctx->vol = vol;
904*9663SMark.Logan@Sun.COM 
905*9663SMark.Logan@Sun.COM 	return ctx;
906*9663SMark.Logan@Sun.COM }
907*9663SMark.Logan@Sun.COM 
908*9663SMark.Logan@Sun.COM /**
909*9663SMark.Logan@Sun.COM  * mft_put_search_ctx
910*9663SMark.Logan@Sun.COM  */
911*9663SMark.Logan@Sun.COM void mft_put_search_ctx(struct mft_search_ctx *ctx)
912*9663SMark.Logan@Sun.COM {
913*9663SMark.Logan@Sun.COM 	if (!ctx)
914*9663SMark.Logan@Sun.COM 		return;
915*9663SMark.Logan@Sun.COM 	if (ctx->inode)
916*9663SMark.Logan@Sun.COM 		ntfs_inode_close(ctx->inode);
917*9663SMark.Logan@Sun.COM 	free(ctx);
918*9663SMark.Logan@Sun.COM }
919*9663SMark.Logan@Sun.COM 
920*9663SMark.Logan@Sun.COM /**
921*9663SMark.Logan@Sun.COM  * mft_next_record
922*9663SMark.Logan@Sun.COM  */
923*9663SMark.Logan@Sun.COM int mft_next_record(struct mft_search_ctx *ctx)
924*9663SMark.Logan@Sun.COM {
925*9663SMark.Logan@Sun.COM 	s64 nr_mft_records;
926*9663SMark.Logan@Sun.COM 	ATTR_RECORD *attr10 = NULL;
927*9663SMark.Logan@Sun.COM 	ATTR_RECORD *attr20 = NULL;
928*9663SMark.Logan@Sun.COM 	ATTR_RECORD *attr80 = NULL;
929*9663SMark.Logan@Sun.COM 	ntfs_attr_search_ctx *attr_ctx;
930*9663SMark.Logan@Sun.COM 
931*9663SMark.Logan@Sun.COM 	if (!ctx) {
932*9663SMark.Logan@Sun.COM 		errno = EINVAL;
933*9663SMark.Logan@Sun.COM 		return -1;
934*9663SMark.Logan@Sun.COM 	}
935*9663SMark.Logan@Sun.COM 
936*9663SMark.Logan@Sun.COM 	if (ctx->inode) {
937*9663SMark.Logan@Sun.COM 		ntfs_inode_close(ctx->inode);
938*9663SMark.Logan@Sun.COM 		ctx->inode = NULL;
939*9663SMark.Logan@Sun.COM 	}
940*9663SMark.Logan@Sun.COM 
941*9663SMark.Logan@Sun.COM 	nr_mft_records = ctx->vol->mft_na->initialized_size >>
942*9663SMark.Logan@Sun.COM 			ctx->vol->mft_record_size_bits;
943*9663SMark.Logan@Sun.COM 
944*9663SMark.Logan@Sun.COM 	for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) {
945*9663SMark.Logan@Sun.COM 		int in_use;
946*9663SMark.Logan@Sun.COM 
947*9663SMark.Logan@Sun.COM 		ctx->flags_match = 0;
948*9663SMark.Logan@Sun.COM 		in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num);
949*9663SMark.Logan@Sun.COM 		if (in_use == -1) {
950*9663SMark.Logan@Sun.COM 			ntfs_log_error("Error reading inode %llu.  Aborting.\n",
951*9663SMark.Logan@Sun.COM 					(unsigned long long)ctx->mft_num);
952*9663SMark.Logan@Sun.COM 			return -1;
953*9663SMark.Logan@Sun.COM 		}
954*9663SMark.Logan@Sun.COM 
955*9663SMark.Logan@Sun.COM 		if (in_use) {
956*9663SMark.Logan@Sun.COM 			ctx->flags_match |= FEMR_IN_USE;
957*9663SMark.Logan@Sun.COM 
958*9663SMark.Logan@Sun.COM 			ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num);
959*9663SMark.Logan@Sun.COM 			if (ctx->inode == NULL) {
960*9663SMark.Logan@Sun.COM 				ntfs_log_error("Error reading inode %llu.\n", (unsigned
961*9663SMark.Logan@Sun.COM 						long long) ctx->mft_num);
962*9663SMark.Logan@Sun.COM 				continue;
963*9663SMark.Logan@Sun.COM 			}
964*9663SMark.Logan@Sun.COM 
965*9663SMark.Logan@Sun.COM 			attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec);
966*9663SMark.Logan@Sun.COM 			attr20 = find_first_attribute(AT_ATTRIBUTE_LIST,       ctx->inode->mrec);
967*9663SMark.Logan@Sun.COM 			attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec);
968*9663SMark.Logan@Sun.COM 
969*9663SMark.Logan@Sun.COM 			if (attr10)
970*9663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_BASE_RECORD;
971*9663SMark.Logan@Sun.COM 			else
972*9663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_NOT_BASE_RECORD;
973*9663SMark.Logan@Sun.COM 
974*9663SMark.Logan@Sun.COM 			if (attr20)
975*9663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_BASE_RECORD;
976*9663SMark.Logan@Sun.COM 
977*9663SMark.Logan@Sun.COM 			if (attr80)
978*9663SMark.Logan@Sun.COM 				ctx->flags_match |= FEMR_FILE;
979*9663SMark.Logan@Sun.COM 
980*9663SMark.Logan@Sun.COM 			if (ctx->flags_search & FEMR_DIR) {
981*9663SMark.Logan@Sun.COM 				attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL);
982*9663SMark.Logan@Sun.COM 				if (attr_ctx) {
983*9663SMark.Logan@Sun.COM 					if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0)
984*9663SMark.Logan@Sun.COM 						ctx->flags_match |= FEMR_DIR;
985*9663SMark.Logan@Sun.COM 
986*9663SMark.Logan@Sun.COM 					ntfs_attr_put_search_ctx(attr_ctx);
987*9663SMark.Logan@Sun.COM 				} else {
988*9663SMark.Logan@Sun.COM 					ntfs_log_error("Couldn't create a search context.\n");
989*9663SMark.Logan@Sun.COM 					return -1;
990*9663SMark.Logan@Sun.COM 				}
991*9663SMark.Logan@Sun.COM 			}
992*9663SMark.Logan@Sun.COM 
993*9663SMark.Logan@Sun.COM 			switch (utils_is_metadata(ctx->inode)) {
994*9663SMark.Logan@Sun.COM 				case 1: ctx->flags_match |= FEMR_METADATA;     break;
995*9663SMark.Logan@Sun.COM 				case 0: ctx->flags_match |= FEMR_NOT_METADATA; break;
996*9663SMark.Logan@Sun.COM 				default:
997*9663SMark.Logan@Sun.COM 					ctx->flags_match |= FEMR_NOT_METADATA; break;
998*9663SMark.Logan@Sun.COM 					//ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num);
999*9663SMark.Logan@Sun.COM 					//return -1;
1000*9663SMark.Logan@Sun.COM 			}
1001*9663SMark.Logan@Sun.COM 
1002*9663SMark.Logan@Sun.COM 		} else {		// !in_use
1003*9663SMark.Logan@Sun.COM 			ntfs_attr *mft;
1004*9663SMark.Logan@Sun.COM 
1005*9663SMark.Logan@Sun.COM 			ctx->flags_match |= FEMR_NOT_IN_USE;
1006*9663SMark.Logan@Sun.COM 
1007*9663SMark.Logan@Sun.COM 			ctx->inode = calloc(1, sizeof(*ctx->inode));
1008*9663SMark.Logan@Sun.COM 			if (!ctx->inode) {
1009*9663SMark.Logan@Sun.COM 				ntfs_log_error("Out of memory.  Aborting.\n");
1010*9663SMark.Logan@Sun.COM 				return -1;
1011*9663SMark.Logan@Sun.COM 			}
1012*9663SMark.Logan@Sun.COM 
1013*9663SMark.Logan@Sun.COM 			ctx->inode->mft_no = ctx->mft_num;
1014*9663SMark.Logan@Sun.COM 			ctx->inode->vol    = ctx->vol;
1015*9663SMark.Logan@Sun.COM 			ctx->inode->mrec   = ntfs_malloc(ctx->vol->mft_record_size);
1016*9663SMark.Logan@Sun.COM 			if (!ctx->inode->mrec) {
1017*9663SMark.Logan@Sun.COM 				free(ctx->inode); // == ntfs_inode_close
1018*9663SMark.Logan@Sun.COM 				return -1;
1019*9663SMark.Logan@Sun.COM 			}
1020*9663SMark.Logan@Sun.COM 
1021*9663SMark.Logan@Sun.COM 			mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA,
1022*9663SMark.Logan@Sun.COM 					AT_UNNAMED, 0);
1023*9663SMark.Logan@Sun.COM 			if (!mft) {
1024*9663SMark.Logan@Sun.COM 				ntfs_log_perror("Couldn't open $MFT/$DATA");
1025*9663SMark.Logan@Sun.COM 				// free / close
1026*9663SMark.Logan@Sun.COM 				return -1;
1027*9663SMark.Logan@Sun.COM 			}
1028*9663SMark.Logan@Sun.COM 
1029*9663SMark.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) {
1030*9663SMark.Logan@Sun.COM 				ntfs_log_perror("Couldn't read MFT Record %llu",
1031*9663SMark.Logan@Sun.COM 					(unsigned long long) ctx->mft_num);
1032*9663SMark.Logan@Sun.COM 				// free / close
1033*9663SMark.Logan@Sun.COM 				ntfs_attr_close(mft);
1034*9663SMark.Logan@Sun.COM 				return -1;
1035*9663SMark.Logan@Sun.COM 			}
1036*9663SMark.Logan@Sun.COM 
1037*9663SMark.Logan@Sun.COM 			ntfs_attr_close(mft);
1038*9663SMark.Logan@Sun.COM 		}
1039*9663SMark.Logan@Sun.COM 
1040*9663SMark.Logan@Sun.COM 		if (ctx->flags_match & ctx->flags_search) {
1041*9663SMark.Logan@Sun.COM 			break;
1042*9663SMark.Logan@Sun.COM 		}
1043*9663SMark.Logan@Sun.COM 
1044*9663SMark.Logan@Sun.COM 		if (ntfs_inode_close(ctx->inode)) {
1045*9663SMark.Logan@Sun.COM 			ntfs_log_error("Error closing inode %llu.\n",
1046*9663SMark.Logan@Sun.COM 					(unsigned long long)ctx->mft_num);
1047*9663SMark.Logan@Sun.COM 			return -errno;
1048*9663SMark.Logan@Sun.COM 		}
1049*9663SMark.Logan@Sun.COM 
1050*9663SMark.Logan@Sun.COM 		ctx->inode = NULL;
1051*9663SMark.Logan@Sun.COM 	}
1052*9663SMark.Logan@Sun.COM 
1053*9663SMark.Logan@Sun.COM 	return (ctx->inode == NULL);
1054*9663SMark.Logan@Sun.COM }
1055*9663SMark.Logan@Sun.COM 
1056*9663SMark.Logan@Sun.COM 
1057