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