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#include "misc.h"
76
77const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\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 */
132#ifndef __HAIKU__
133int utils_set_locale(void)
134{
135	const char *locale;
136
137	locale = setlocale(LC_ALL, "");
138	if (!locale) {
139		locale = setlocale(LC_ALL, NULL);
140		ntfs_log_error("Failed to set locale, using default '%s'.\n",
141				locale);
142		return 1;
143	} else {
144		return 0;
145	}
146}
147#endif
148
149/**
150 * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with
151 * ntfs-3g's.
152 */
153int ntfs_mbstoucs_libntfscompat(const char *ins,
154		ntfschar **outs, int outs_len)
155{
156	if(!outs) {
157		errno = EINVAL;
158		return -1;
159	}
160	else if(*outs != NULL) {
161		/* Note: libntfs's mbstoucs implementation allows the caller to
162		 * specify a preallocated buffer while libntfs-3g's always
163		 * allocates the output buffer.
164		 */
165		ntfschar *tmpstr = NULL;
166		int tmpstr_len;
167
168		tmpstr_len = ntfs_mbstoucs(ins, &tmpstr);
169		if(tmpstr_len >= 0) {
170			if((tmpstr_len + 1) > outs_len) {
171				/* Doing a realloc instead of reusing tmpstr
172				 * because it emulates libntfs's mbstoucs more
173				 * closely. */
174				ntfschar *re_outs = realloc(*outs,
175					sizeof(ntfschar)*(tmpstr_len + 1));
176				if(!re_outs)
177					tmpstr_len = -1;
178				else
179					*outs = re_outs;
180			}
181
182			if(tmpstr_len >= 0) {
183				/* The extra character is the \0 terminator. */
184				memcpy(*outs, tmpstr,
185					sizeof(ntfschar)*(tmpstr_len + 1));
186			}
187
188			free(tmpstr);
189		}
190
191		return tmpstr_len;
192	}
193	else
194		return ntfs_mbstoucs(ins, outs);
195}
196
197/**
198 * utils_valid_device - Perform some safety checks on the device, before start
199 * @name:   Full pathname of the device/file to work with
200 * @force:  Continue regardless of problems
201 *
202 * Check that the name refers to a device and that is isn't already mounted.
203 * These checks can be overridden by using the force option.
204 *
205 * Return:  1  Success, we can continue
206 *	    0  Error, we cannot use this device
207 */
208int utils_valid_device(const char *name, int force)
209{
210	unsigned long mnt_flags = 0;
211	struct stat st;
212
213#ifdef __CYGWIN32__
214	/* FIXME: This doesn't work for Cygwin, so just return success. */
215	return 1;
216#endif
217	if (!name) {
218		errno = EINVAL;
219		return 0;
220	}
221
222	if (stat(name, &st) == -1) {
223		if (errno == ENOENT)
224			ntfs_log_error("The device %s doesn't exist\n", name);
225		else
226			ntfs_log_perror("Error getting information about %s",
227					name);
228		return 0;
229	}
230
231	/* Make sure the file system is not mounted. */
232	if (ntfs_check_if_mounted(name, &mnt_flags)) {
233		ntfs_log_perror("Failed to determine whether %s is mounted",
234				name);
235		if (!force) {
236			ntfs_log_error("Use the force option to ignore this "
237					"error.\n");
238			return 0;
239		}
240		ntfs_log_warning("Forced to continue.\n");
241	} else if (mnt_flags & NTFS_MF_MOUNTED) {
242		if (!force) {
243			ntfs_log_error("%s", opened_volume_msg);
244			ntfs_log_error("You can use force option to avoid this "
245					"check, but this is not recommended\n"
246					"and may lead to data corruption.\n");
247			return 0;
248		}
249		ntfs_log_warning("Forced to continue.\n");
250	}
251
252	return 1;
253}
254
255/**
256 * utils_mount_volume - Mount an NTFS volume
257 */
258ntfs_volume * utils_mount_volume(const char *device, unsigned long flags)
259{
260	ntfs_volume *vol;
261
262	if (!device) {
263		errno = EINVAL;
264		return NULL;
265	}
266
267	/* Porting notes:
268	 *
269	 * libntfs-3g does not have the 'force' flag in ntfs_mount_flags.
270	 * The 'force' flag in libntfs bypasses two safety checks when mounting
271	 * read/write:
272	 *   1. Do not mount when the VOLUME_IS_DIRTY flag in
273	 *      VOLUME_INFORMATION is set.
274	 *   2. Do not mount when the logfile is unclean.
275	 *
276	 * libntfs-3g only has safety check number 2. The dirty flag is simply
277	 * ignored because we are confident that we can handle a dirty volume.
278	 * So we treat MS_RECOVER like NTFS_MNT_FORCE, knowing that the first
279	 * check is always bypassed.
280	 */
281
282	if (!utils_valid_device(device, flags & MS_RECOVER))
283		return NULL;
284
285	vol = ntfs_mount(device, flags);
286	if (!vol) {
287		ntfs_log_perror("Failed to mount '%s'", device);
288		if (errno == EINVAL)
289			ntfs_log_error(invalid_ntfs_msg, device);
290		else if (errno == EIO)
291			ntfs_log_error("%s", corrupt_volume_msg);
292		else if (errno == EPERM)
293			ntfs_log_error("%s", hibernated_volume_msg);
294		else if (errno == EOPNOTSUPP)
295			ntfs_log_error("%s", unclean_journal_msg);
296		else if (errno == EBUSY)
297			ntfs_log_error("%s", opened_volume_msg);
298		else if (errno == ENXIO)
299			ntfs_log_error("%s", fakeraid_msg);
300		return NULL;
301	}
302
303	/* Porting notes:
304	 * libntfs-3g does not record whether the volume log file was dirty
305	 * before mount, so we can only warn if the VOLUME_IS_DIRTY flag is set
306	 * in VOLUME_INFORMATION. */
307	if (vol->flags & VOLUME_IS_DIRTY) {
308		if (!(flags & MS_RECOVER)) {
309			ntfs_log_error("%s", dirty_volume_msg);
310			ntfs_umount(vol, FALSE);
311			return NULL;
312		}
313		ntfs_log_error("WARNING: Dirty volume mount was forced by the "
314				"'force' mount option.\n");
315	}
316	return vol;
317}
318
319/**
320 * utils_parse_size - Convert a string representing a size
321 * @value:  String to be parsed
322 * @size:   Parsed size
323 * @scale:  Whether or not to allow a suffix to scale the value
324 *
325 * Read a string and convert it to a number.  Strings may be suffixed to scale
326 * them.  Any number without a suffix is assumed to be in bytes.
327 *
328 * Suffix  Description  Multiple
329 *  [tT]    Terabytes     10^12
330 *  [gG]    Gigabytes     10^9
331 *  [mM]    Megabytes     10^6
332 *  [kK]    Kilobytes     10^3
333 *
334 * Notes:
335 *     Only the first character of the suffix is read.
336 *     The multipliers are decimal thousands, not binary: 1000, not 1024.
337 *     If parse_size fails, @size will not be changed
338 *
339 * Return:  1  Success
340 *	    0  Error, the string was malformed
341 */
342int utils_parse_size(const char *value, s64 *size, BOOL scale)
343{
344	long long result;
345	char *suffix = NULL;
346
347	if (!value || !size) {
348		errno = EINVAL;
349		return 0;
350	}
351
352	ntfs_log_debug("Parsing size '%s'.\n", value);
353
354	result = strtoll(value, &suffix, 0);
355	if (result < 0 || errno == ERANGE) {
356		ntfs_log_error("Invalid size '%s'.\n", value);
357		return 0;
358	}
359
360	if (!suffix) {
361		ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
362		return 0;
363	}
364
365	if (scale) {
366		switch (suffix[0]) {
367			case 't': case 'T': result *= 1000;
368			case 'g': case 'G': result *= 1000;
369			case 'm': case 'M': result *= 1000;
370			case 'k': case 'K': result *= 1000;
371			case '-': case 0:
372				break;
373			default:
374				ntfs_log_error("Invalid size suffix '%s'.  Use T, G, M, or K.\n", suffix);
375				return 0;
376		}
377	} else {
378		if ((suffix[0] != '-') && (suffix[0] != 0)) {
379			ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value);
380			return 0;
381		}
382	}
383
384	ntfs_log_debug("Parsed size = %lld.\n", result);
385	*size = result;
386	return 1;
387}
388
389/**
390 * utils_parse_range - Convert a string representing a range of numbers
391 * @string:  The string to be parsed
392 * @start:   The beginning of the range will be stored here
393 * @finish:  The end of the range will be stored here
394 *
395 * Read a string of the form n-m.  If the lower end is missing, zero will be
396 * substituted.  If the upper end is missing LONG_MAX will be used.  If the
397 * string cannot be parsed correctly, @start and @finish will not be changed.
398 *
399 * Return:  1  Success, a valid string was found
400 *	    0  Error, the string was not a valid range
401 */
402int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale)
403{
404	s64 a, b;
405	char *middle;
406
407	if (!string || !start || !finish) {
408		errno = EINVAL;
409		return 0;
410	}
411
412	middle = strchr(string, '-');
413	if (string == middle) {
414		ntfs_log_debug("Range has no beginning, defaulting to 0.\n");
415		a = 0;
416	} else {
417		if (!utils_parse_size(string, &a, scale))
418			return 0;
419	}
420
421	if (middle) {
422		if (middle[1] == 0) {
423			b = LONG_MAX;		// XXX ULLONG_MAX
424			ntfs_log_debug("Range has no end, defaulting to %lld.\n", b);
425		} else {
426			if (!utils_parse_size(middle+1, &b, scale))
427				return 0;
428		}
429	} else {
430		b = a;
431	}
432
433	ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b);
434
435	*start  = a;
436	*finish = b;
437	return 1;
438}
439
440/**
441 * find_attribute - Find an attribute of the given type
442 * @type:  An attribute type, e.g. AT_FILE_NAME
443 * @ctx:   A search context, created using ntfs_get_attr_search_ctx
444 *
445 * Using the search context to keep track, find the first/next occurrence of a
446 * given attribute type.
447 *
448 * N.B.  This will return a pointer into @mft.  As long as the search context
449 *       has been created without an inode, it won't overflow the buffer.
450 *
451 * Return:  Pointer  Success, an attribute was found
452 *	    NULL     Error, no matching attributes were found
453 */
454ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
455{
456	if (!ctx) {
457		errno = EINVAL;
458		return NULL;
459	}
460
461	if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) {
462		ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type);
463		return NULL;	/* None / no more of that type */
464	}
465
466	ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type);
467	return ctx->attr;
468}
469
470/**
471 * find_first_attribute - Find the first attribute of a given type
472 * @type:  An attribute type, e.g. AT_FILE_NAME
473 * @mft:   A buffer containing a raw MFT record
474 *
475 * Search through a raw MFT record for an attribute of a given type.
476 * The return value is a pointer into the MFT record that was supplied.
477 *
478 * N.B.  This will return a pointer into @mft.  The pointer won't stray outside
479 *       the buffer, since we created the search context without an inode.
480 *
481 * Return:  Pointer  Success, an attribute was found
482 *	    NULL     Error, no matching attributes were found
483 */
484ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft)
485{
486	ntfs_attr_search_ctx *ctx;
487	ATTR_RECORD *rec;
488
489	if (!mft) {
490		errno = EINVAL;
491		return NULL;
492	}
493
494	ctx = ntfs_attr_get_search_ctx(NULL, mft);
495	if (!ctx) {
496		ntfs_log_error("Couldn't create a search context.\n");
497		return NULL;
498	}
499
500	rec = find_attribute(type, ctx);
501	ntfs_attr_put_search_ctx(ctx);
502	if (rec)
503		ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type);
504	else
505		ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type);
506	return rec;
507}
508
509/**
510 * utils_inode_get_name
511 *
512 * using inode
513 * get filename
514 * add name to list
515 * get parent
516 * if parent is 5 (/) stop
517 * get inode of parent
518 */
519#define max_path 20
520int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
521{
522	// XXX option: names = posix/win32 or dos
523	// flags: path, filename, or both
524
525
526	ntfs_volume *vol;
527	ntfs_attr_search_ctx *ctx;
528	ATTR_RECORD *rec;
529	FILE_NAME_ATTR *attr;
530	int name_space;
531	MFT_REF parent = FILE_root;
532	char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger?
533	int i, len, offset = 0;
534
535	if (!inode || !buffer) {
536		errno = EINVAL;
537		return 0;
538	}
539
540	vol = inode->vol;
541
542	//ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names));
543	memset(names, 0, sizeof(names));
544
545	for (i = 0; i < max_path; i++) {
546
547		ctx = ntfs_attr_get_search_ctx(inode, NULL);
548		if (!ctx) {
549			ntfs_log_error("Couldn't create a search context.\n");
550			return 0;
551		}
552
553		//ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no);
554
555		name_space = 4;
556		while ((rec = find_attribute(AT_FILE_NAME, ctx))) {
557			/* We know this will always be resident. */
558			attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset));
559
560			if (attr->file_name_type > name_space) { //XXX find the ...
561				continue;
562			}
563
564			name_space = attr->file_name_type;
565			parent     = le64_to_cpu(attr->parent_directory);
566
567			if (names[i]) {
568				free(names[i]);
569				names[i] = NULL;
570			}
571
572			if (ntfs_ucstombs(attr->file_name, attr->file_name_length,
573			    &names[i], 0) < 0) {
574				char *temp;
575				ntfs_log_error("Couldn't translate filename to current locale.\n");
576				temp = ntfs_malloc(30);
577				if (!temp)
578					return 0;
579				snprintf(temp, 30, "<MFT%llu>", (unsigned
580						long long)inode->mft_no);
581				names[i] = temp;
582			}
583
584			//ntfs_log_debug("names[%d] %s\n", i, names[i]);
585			//ntfs_log_debug("parent = %lld\n", MREF(parent));
586		}
587
588		ntfs_attr_put_search_ctx(ctx);
589
590		if (i > 0)			/* Don't close the original inode */
591			ntfs_inode_close(inode);
592
593		if (MREF(parent) == FILE_root) {	/* The root directory, stop. */
594			//ntfs_log_debug("inode 5\n");
595			break;
596		}
597
598		inode = ntfs_inode_open(vol, parent);
599		if (!inode) {
600			ntfs_log_error("Couldn't open inode %llu.\n",
601					(unsigned long long)MREF(parent));
602			break;
603		}
604	}
605
606	if (i >= max_path) {
607		/* If we get into an infinite loop, we'll end up here. */
608		ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path);
609		return 0;
610	}
611
612	/* Assemble the names in the correct order. */
613	for (i = max_path; i >= 0; i--) {
614		if (!names[i])
615			continue;
616
617		len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]);
618		if (len >= (bufsize - offset)) {
619			ntfs_log_error("Pathname was truncated.\n");
620			break;
621		}
622
623		offset += len;
624	}
625
626	/* Free all the allocated memory */
627	for (i = 0; i < max_path; i++)
628		free(names[i]);
629
630	ntfs_log_debug("Pathname: %s\n", buffer);
631
632	return 1;
633}
634#undef max_path
635
636/**
637 * utils_attr_get_name
638 */
639int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize)
640{
641	int len, namelen;
642	char *name;
643	ATTR_DEF *attrdef;
644
645	// flags: attr, name, or both
646	if (!attr || !buffer) {
647		errno = EINVAL;
648		return 0;
649	}
650
651	attrdef = ntfs_attr_find_in_attrdef(vol, attr->type);
652	if (attrdef) {
653		name    = NULL;
654		namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name));
655		if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) {
656			ntfs_log_error("Couldn't translate attribute type to "
657					"current locale.\n");
658			// <UNKNOWN>?
659			return 0;
660		}
661		len = snprintf(buffer, bufsize, "%s", name);
662	} else {
663		ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type);
664		len = snprintf(buffer, bufsize, "<UNKNOWN>");
665	}
666
667	if (len >= bufsize) {
668		ntfs_log_error("Attribute type was truncated.\n");
669		return 0;
670	}
671
672	if (!attr->name_length) {
673		return 0;
674	}
675
676	buffer  += len;
677	bufsize -= len;
678
679	name    = NULL;
680	namelen = attr->name_length;
681	if (ntfs_ucstombs((ntfschar *)((char *)attr + attr->name_offset),
682				namelen, &name, 0) < 0) {
683		ntfs_log_error("Couldn't translate attribute name to current "
684				"locale.\n");
685		// <UNKNOWN>?
686		len = snprintf(buffer, bufsize, "<UNKNOWN>");
687		return 0;
688	}
689
690	len = snprintf(buffer, bufsize, "(%s)", name);
691	free(name);
692
693	if (len >= bufsize) {
694		ntfs_log_error("Attribute name was truncated.\n");
695		return 0;
696	}
697
698	return 0;
699}
700
701/**
702 * utils_cluster_in_use - Determine if a cluster is in use
703 * @vol:  An ntfs volume obtained from ntfs_mount
704 * @lcn:  The Logical Cluster Number to test
705 *
706 * The metadata file $Bitmap has one binary bit representing each cluster on
707 * disk.  The bit will be set for each cluster that is in use.  The function
708 * reads the relevant part of $Bitmap into a buffer and tests the bit.
709 *
710 * This function has a static buffer in which it caches a section of $Bitmap.
711 * If the lcn, being tested, lies outside the range, the buffer will be
712 * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the
713 * buffer.
714 *
715 * NOTE: Be very carefull with shifts by 3 everywhere in this function.
716 *
717 * Return:  1  Cluster is in use
718 *	    0  Cluster is free space
719 *	   -1  Error occurred
720 */
721int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
722{
723	static unsigned char buffer[512];
724	static long long bmplcn = -(sizeof(buffer) << 3);
725	int byte, bit;
726	ntfs_attr *attr;
727
728	if (!vol) {
729		errno = EINVAL;
730		return -1;
731	}
732
733	/* Does lcn lie in the section of $Bitmap we already have cached? */
734	if ((lcn < bmplcn)
735	    || (lcn >= (long long)(bmplcn + (sizeof(buffer) << 3)))) {
736		ntfs_log_debug("Bit lies outside cache.\n");
737		attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
738		if (!attr) {
739			ntfs_log_perror("Couldn't open $Bitmap");
740			return -1;
741		}
742
743		/* Mark the buffer as in use, in case the read is shorter. */
744		memset(buffer, 0xFF, sizeof(buffer));
745		bmplcn = lcn & (~((sizeof(buffer) << 3) - 1));
746
747		if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer),
748					buffer) < 0) {
749			ntfs_log_perror("Couldn't read $Bitmap");
750			ntfs_attr_close(attr);
751			return -1;
752		}
753
754		ntfs_log_debug("Reloaded bitmap buffer.\n");
755		ntfs_attr_close(attr);
756	}
757
758	bit  = 1 << (lcn & 7);
759	byte = (lcn >> 3) & (sizeof(buffer) - 1);
760	ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, "
761			"in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] &
762			bit);
763
764	return (buffer[byte] & bit);
765}
766
767/**
768 * utils_mftrec_in_use - Determine if a MFT Record is in use
769 * @vol:   An ntfs volume obtained from ntfs_mount
770 * @mref:  MFT Reference (inode number)
771 *
772 * The metadata file $BITMAP has one binary bit representing each record in the
773 * MFT.  The bit will be set for each record that is in use.  The function
774 * reads the relevant part of $BITMAP into a buffer and tests the bit.
775 *
776 * This function has a static buffer in which it caches a section of $BITMAP.
777 * If the mref, being tested, lies outside the range, the buffer will be
778 * refreshed.
779 *
780 * Return:  1  MFT Record is in use
781 *	    0  MFT Record is unused
782 *	   -1  Error occurred
783 */
784int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
785{
786	static u8 buffer[512];
787	static s64 bmpmref = -(sizeof(buffer) << 3) - 1; /* Which bit of $BITMAP is in the buffer */
788	int byte, bit;
789
790	ntfs_log_trace("Entering.\n");
791
792	if (!vol) {
793		errno = EINVAL;
794		return -1;
795	}
796
797	/* Does mref lie in the section of $Bitmap we already have cached? */
798	if (((s64)MREF(mref) < bmpmref)
799	    || ((s64)MREF(mref) >= (s64)(bmpmref + (sizeof(buffer) << 3)))) {
800		ntfs_log_debug("Bit lies outside cache.\n");
801
802		/* Mark the buffer as not in use, in case the read is shorter. */
803		memset(buffer, 0, sizeof(buffer));
804		bmpmref = mref & (~((sizeof(buffer) << 3) - 1));
805
806		if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) {
807			ntfs_log_perror("Couldn't read $MFT/$BITMAP");
808			return -1;
809		}
810
811		ntfs_log_debug("Reloaded bitmap buffer.\n");
812	}
813
814	bit  = 1 << (mref & 7);
815	byte = (mref >> 3) & (sizeof(buffer) - 1);
816	ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit);
817
818	return (buffer[byte] & bit);
819}
820
821/**
822 * __metadata
823 */
824static int __metadata(ntfs_volume *vol, u64 num)
825{
826	if (num <= FILE_UpCase)
827		return 1;
828	if (!vol)
829		return -1;
830	if ((vol->major_ver == 3) && (num == FILE_Extend))
831		return 1;
832
833	return 0;
834}
835
836/**
837 * utils_is_metadata - Determine if an inode represents a metadata file
838 * @inode:  An ntfs inode to be tested
839 *
840 * A handful of files in the volume contain filesystem data - metadata.
841 * They can be identified by their inode number (offset in MFT/$DATA) or by
842 * their parent.
843 *
844 * Return:  1  inode is a metadata file
845 *	    0  inode is not a metadata file
846 *	   -1  Error occurred
847 */
848int utils_is_metadata(ntfs_inode *inode)
849{
850	ntfs_volume *vol;
851	ATTR_RECORD *rec;
852	FILE_NAME_ATTR *attr;
853	MFT_RECORD *file;
854	u64 num;
855
856	if (!inode) {
857		errno = EINVAL;
858		return -1;
859	}
860
861	vol = inode->vol;
862	if (!vol)
863		return -1;
864
865	num = inode->mft_no;
866	if (__metadata(vol, num) == 1)
867		return 1;
868
869	file = inode->mrec;
870	if (file && (file->base_mft_record != 0)) {
871		num = MREF_LE(file->base_mft_record);
872		if (__metadata(vol, num) == 1)
873			return 1;
874	}
875
876	rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
877	if (!rec)
878		return -1;
879
880	/* We know this will always be resident. */
881	attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset));
882
883	num = MREF_LE(attr->parent_directory);
884	if ((num != FILE_root) && (__metadata(vol, num) == 1))
885		return 1;
886
887	return 0;
888}
889
890/**
891 * utils_dump_mem - Display a block of memory in hex and ascii
892 * @buf:     Buffer to be displayed
893 * @start:   Offset into @buf to start from
894 * @length:  Number of bytes to display
895 * @flags:   Options to change the style of the output
896 *
897 * Display a block of memory in a tradition hex-dump manner.
898 * Optionally the ascii part can be turned off.
899 *
900 * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS).
901 * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the
902 * output); DM_NO_ASCII (only print the hex values).
903 */
904void utils_dump_mem(void *buf, int start, int length, int flags)
905{
906	int off, i, s, e, col;
907	u8 *mem = buf;
908
909	s =  start                & ~15;	// round down
910	e = (start + length + 15) & ~15;	// round up
911
912	for (off = s; off < e; off += 16) {
913		col = 30;
914		if (flags & DM_RED)
915			col += 1;
916		if (flags & DM_GREEN)
917			col += 2;
918		if (flags & DM_BLUE)
919			col += 4;
920		if (flags & DM_INDENT)
921			ntfs_log_debug("\t");
922		if (flags & DM_BOLD)
923			ntfs_log_debug("\e[01m");
924		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
925			ntfs_log_debug("\e[%dm", col);
926		if (off == s)
927			ntfs_log_debug("%6.6x ", start);
928		else
929			ntfs_log_debug("%6.6x ", off);
930
931		for (i = 0; i < 16; i++) {
932			if ((i == 8) && (!(flags & DM_NO_DIVIDER)))
933				ntfs_log_debug(" -");
934			if (((off+i) >= start) && ((off+i) < (start+length)))
935				ntfs_log_debug(" %02X", mem[off+i]);
936			else
937				ntfs_log_debug("   ");
938		}
939		if (!(flags & DM_NO_ASCII)) {
940			ntfs_log_debug("  ");
941			for (i = 0; i < 16; i++) {
942				if (((off+i) < start) || ((off+i) >= (start+length)))
943					ntfs_log_debug(" ");
944				else if (isprint(mem[off + i]))
945					ntfs_log_debug("%c", mem[off + i]);
946				else
947					ntfs_log_debug(".");
948			}
949		}
950		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
951			ntfs_log_debug("\e[0m");
952		ntfs_log_debug("\n");
953	}
954}
955
956
957/**
958 * mft_get_search_ctx
959 */
960struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
961{
962	struct mft_search_ctx *ctx;
963
964	if (!vol) {
965		errno = EINVAL;
966		return NULL;
967	}
968
969	ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx);
970
971	ctx->mft_num = -1;
972	ctx->vol = vol;
973
974	return ctx;
975}
976
977/**
978 * mft_put_search_ctx
979 */
980void mft_put_search_ctx(struct mft_search_ctx *ctx)
981{
982	if (!ctx)
983		return;
984	if (ctx->inode)
985		ntfs_inode_close(ctx->inode);
986	free(ctx);
987}
988
989/**
990 * mft_next_record
991 */
992int mft_next_record(struct mft_search_ctx *ctx)
993{
994	s64 nr_mft_records;
995	ATTR_RECORD *attr10 = NULL;
996	ATTR_RECORD *attr20 = NULL;
997	ATTR_RECORD *attr80 = NULL;
998	ntfs_attr_search_ctx *attr_ctx;
999
1000	if (!ctx) {
1001		errno = EINVAL;
1002		return -1;
1003	}
1004
1005	if (ctx->inode) {
1006		ntfs_inode_close(ctx->inode);
1007		ctx->inode = NULL;
1008	}
1009
1010	nr_mft_records = ctx->vol->mft_na->initialized_size >>
1011			ctx->vol->mft_record_size_bits;
1012
1013	for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) {
1014		int in_use;
1015
1016		ctx->flags_match = 0;
1017		in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num);
1018		if (in_use == -1) {
1019			ntfs_log_error("Error reading inode %llu.  Aborting.\n",
1020					(unsigned long long)ctx->mft_num);
1021			return -1;
1022		}
1023
1024		if (in_use) {
1025			ctx->flags_match |= FEMR_IN_USE;
1026
1027			ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num);
1028			if (ctx->inode == NULL) {
1029				ntfs_log_error("Error reading inode %llu.\n", (unsigned
1030						long long) ctx->mft_num);
1031				continue;
1032			}
1033
1034			attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec);
1035			attr20 = find_first_attribute(AT_ATTRIBUTE_LIST,       ctx->inode->mrec);
1036			attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec);
1037
1038			if (attr10)
1039				ctx->flags_match |= FEMR_BASE_RECORD;
1040			else
1041				ctx->flags_match |= FEMR_NOT_BASE_RECORD;
1042
1043			if (attr20)
1044				ctx->flags_match |= FEMR_BASE_RECORD;
1045
1046			if (attr80)
1047				ctx->flags_match |= FEMR_FILE;
1048
1049			if (ctx->flags_search & FEMR_DIR) {
1050				attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL);
1051				if (attr_ctx) {
1052					if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0)
1053						ctx->flags_match |= FEMR_DIR;
1054
1055					ntfs_attr_put_search_ctx(attr_ctx);
1056				} else {
1057					ntfs_log_error("Couldn't create a search context.\n");
1058					return -1;
1059				}
1060			}
1061
1062			switch (utils_is_metadata(ctx->inode)) {
1063				case 1: ctx->flags_match |= FEMR_METADATA;     break;
1064				case 0: ctx->flags_match |= FEMR_NOT_METADATA; break;
1065				default:
1066					ctx->flags_match |= FEMR_NOT_METADATA; break;
1067					//ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num);
1068					//return -1;
1069			}
1070
1071		} else {		// !in_use
1072			ntfs_attr *mft;
1073
1074			ctx->flags_match |= FEMR_NOT_IN_USE;
1075
1076			ctx->inode = (ntfs_inode*)calloc(1, sizeof(*ctx->inode));
1077			if (!ctx->inode) {
1078				ntfs_log_error("Out of memory.  Aborting.\n");
1079				return -1;
1080			}
1081
1082			ctx->inode->mft_no = ctx->mft_num;
1083			ctx->inode->vol    = ctx->vol;
1084			ctx->inode->mrec   = ntfs_malloc(ctx->vol->mft_record_size);
1085			if (!ctx->inode->mrec) {
1086				free(ctx->inode); // == ntfs_inode_close
1087				return -1;
1088			}
1089
1090			mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA,
1091					AT_UNNAMED, 0);
1092			if (!mft) {
1093				ntfs_log_perror("Couldn't open $MFT/$DATA");
1094				// free / close
1095				return -1;
1096			}
1097
1098			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) {
1099				ntfs_log_perror("Couldn't read MFT Record %llu",
1100					(unsigned long long) ctx->mft_num);
1101				// free / close
1102				ntfs_attr_close(mft);
1103				return -1;
1104			}
1105
1106			ntfs_attr_close(mft);
1107		}
1108
1109		if (ctx->flags_match & ctx->flags_search) {
1110			break;
1111		}
1112
1113		if (ntfs_inode_close(ctx->inode)) {
1114			ntfs_log_error("Error closing inode %llu.\n",
1115					(unsigned long long)ctx->mft_num);
1116			return -errno;
1117		}
1118
1119		ctx->inode = NULL;
1120	}
1121
1122	return (ctx->inode == NULL);
1123}
1124
1125/**
1126 * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode
1127 * @ni:		ntfs inode whose parent directory to find
1128 *
1129 * Find the parent directory of the ntfs inode @ni. To do this, find the first
1130 * file name attribute in the mft record of @ni and return the parent mft
1131 * reference from that.
1132 *
1133 * Note this only makes sense for directories, since files can be hard linked
1134 * from multiple directories and there is no way for us to tell which one is
1135 * being looked for.
1136 *
1137 * Technically directories can have hard links, too, but we consider that as
1138 * illegal as Linux/UNIX do not support directory hard links.
1139 *
1140 * Return the mft reference of the parent directory on success or -1 on error
1141 * with errno set to the error code.
1142 */
1143MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
1144{
1145	MFT_REF mref;
1146	ntfs_attr_search_ctx *ctx;
1147	FILE_NAME_ATTR *fn;
1148	int eo;
1149
1150	ntfs_log_trace("Entering.\n");
1151
1152	if (!ni) {
1153		errno = EINVAL;
1154		return ERR_MREF(-1);
1155	}
1156
1157	ctx = ntfs_attr_get_search_ctx(ni, NULL);
1158	if (!ctx)
1159		return ERR_MREF(-1);
1160	if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
1161		ntfs_log_error("No file name found in inode %lld\n",
1162			       (unsigned long long)ni->mft_no);
1163		goto err_out;
1164	}
1165	if (ctx->attr->non_resident) {
1166		ntfs_log_error("File name attribute must be resident (inode "
1167			       "%lld)\n", (unsigned long long)ni->mft_no);
1168		goto io_err_out;
1169	}
1170	fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
1171			le16_to_cpu(ctx->attr->value_offset));
1172	if ((u8*)fn +	le32_to_cpu(ctx->attr->value_length) >
1173			(u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
1174		ntfs_log_error("Corrupt file name attribute in inode %lld.\n",
1175			       (unsigned long long)ni->mft_no);
1176		goto io_err_out;
1177	}
1178	mref = le64_to_cpu(fn->parent_directory);
1179	ntfs_attr_put_search_ctx(ctx);
1180	return mref;
1181io_err_out:
1182	errno = EIO;
1183err_out:
1184	eo = errno;
1185	ntfs_attr_put_search_ctx(ctx);
1186	errno = eo;
1187	return ERR_MREF(-1);
1188}
1189
1190
1191