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