xref: /haiku/src/add-ons/kernel/file_systems/ntfs/utils/utils.c (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 
78 const char *ntfs_bugs = "Developers' email address: " NTFS_DEV_LIST "\n";
79 const 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 
86 static 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 
91 static 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 
96 static 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 
100 static 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 
111 static 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 
116 static 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 
125 static 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__
134 int 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  */
154 int 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  */
209 int 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  */
259 ntfs_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  */
343 int 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  */
407 int 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  */
461 ATTR_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  */
491 ATTR_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
527 int 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 			break;
628 		}
629 
630 		offset += len;
631 	}
632 
633 	/* Free all the allocated memory */
634 	for (i = 0; i < max_path; i++)
635 		free(names[i]);
636 
637 	ntfs_log_debug("Pathname: %s\n", buffer);
638 
639 	return 1;
640 }
641 #undef max_path
642 
643 /**
644  * utils_attr_get_name
645  */
646 int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize)
647 {
648 	int len, namelen;
649 	char *name;
650 	ATTR_DEF *attrdef;
651 
652 	// flags: attr, name, or both
653 	if (!attr || !buffer) {
654 		errno = EINVAL;
655 		return 0;
656 	}
657 
658 	attrdef = ntfs_attr_find_in_attrdef(vol, attr->type);
659 	if (attrdef) {
660 		name    = NULL;
661 		namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name));
662 		if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) {
663 			ntfs_log_error("Couldn't translate attribute type to "
664 					"current locale.\n");
665 			// <UNKNOWN>?
666 			return 0;
667 		}
668 		len = snprintf(buffer, bufsize, "%s", name);
669 	} else {
670 		ntfs_log_error("Unknown attribute type 0x%02x\n", le32_to_cpu(attr->type));
671 		len = snprintf(buffer, bufsize, "<UNKNOWN>");
672 	}
673 
674 	if (len >= bufsize) {
675 		ntfs_log_error("Attribute type was truncated.\n");
676 		return 0;
677 	}
678 
679 	if (!attr->name_length) {
680 		return 0;
681 	}
682 
683 	buffer  += len;
684 	bufsize -= len;
685 
686 	name    = NULL;
687 	namelen = attr->name_length;
688 	if (ntfs_ucstombs((ntfschar *)((char *)attr
689 					+ le16_to_cpu(attr->name_offset)),
690 				namelen, &name, 0) < 0) {
691 		ntfs_log_error("Couldn't translate attribute name to current "
692 				"locale.\n");
693 		// <UNKNOWN>?
694 		len = snprintf(buffer, bufsize, "<UNKNOWN>");
695 		return 0;
696 	}
697 
698 	len = snprintf(buffer, bufsize, "(%s)", name);
699 	free(name);
700 
701 	if (len >= bufsize) {
702 		ntfs_log_error("Attribute name was truncated.\n");
703 		return 0;
704 	}
705 
706 	return 0;
707 }
708 
709 /**
710  * utils_cluster_in_use - Determine if a cluster is in use
711  * @vol:  An ntfs volume obtained from ntfs_mount
712  * @lcn:  The Logical Cluster Number to test
713  *
714  * The metadata file $Bitmap has one binary bit representing each cluster on
715  * disk.  The bit will be set for each cluster that is in use.  The function
716  * reads the relevant part of $Bitmap into a buffer and tests the bit.
717  *
718  * This function has a static buffer in which it caches a section of $Bitmap.
719  * If the lcn, being tested, lies outside the range, the buffer will be
720  * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the
721  * buffer.
722  *
723  * NOTE: Be very carefull with shifts by 3 everywhere in this function.
724  *
725  * Return:  1  Cluster is in use
726  *	    0  Cluster is free space
727  *	   -1  Error occurred
728  */
729 int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
730 {
731 	static unsigned char buffer[512];
732 	static long long bmplcn = -(sizeof(buffer) << 3);
733 	int byte, bit;
734 	ntfs_attr *attr;
735 
736 	if (!vol) {
737 		errno = EINVAL;
738 		return -1;
739 	}
740 
741 	/* Does lcn lie in the section of $Bitmap we already have cached? */
742 	if ((lcn < bmplcn)
743 	    || (lcn >= (long long)(bmplcn + (sizeof(buffer) << 3)))) {
744 		ntfs_log_debug("Bit lies outside cache.\n");
745 		attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
746 		if (!attr) {
747 			ntfs_log_perror("Couldn't open $Bitmap");
748 			return -1;
749 		}
750 
751 		/* Mark the buffer as in use, in case the read is shorter. */
752 		memset(buffer, 0xFF, sizeof(buffer));
753 		bmplcn = lcn & (~((sizeof(buffer) << 3) - 1));
754 
755 		if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer),
756 					buffer) < 0) {
757 			ntfs_log_perror("Couldn't read $Bitmap");
758 			ntfs_attr_close(attr);
759 			return -1;
760 		}
761 
762 		ntfs_log_debug("Reloaded bitmap buffer.\n");
763 		ntfs_attr_close(attr);
764 	}
765 
766 	bit  = 1 << (lcn & 7);
767 	byte = (lcn >> 3) & (sizeof(buffer) - 1);
768 	ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, "
769 			"in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] &
770 			bit);
771 
772 	return (buffer[byte] & bit);
773 }
774 
775 /**
776  * utils_mftrec_in_use - Determine if a MFT Record is in use
777  * @vol:   An ntfs volume obtained from ntfs_mount
778  * @mref:  MFT Reference (inode number)
779  *
780  * The metadata file $BITMAP has one binary bit representing each record in the
781  * MFT.  The bit will be set for each record that is in use.  The function
782  * reads the relevant part of $BITMAP into a buffer and tests the bit.
783  *
784  * This function has a static buffer in which it caches a section of $BITMAP.
785  * If the mref, being tested, lies outside the range, the buffer will be
786  * refreshed.
787  *
788  * Return:  1  MFT Record is in use
789  *	    0  MFT Record is unused
790  *	   -1  Error occurred
791  */
792 int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
793 {
794 	static u8 buffer[512];
795 	static s64 bmpmref = -(sizeof(buffer) << 3) - 1; /* Which bit of $BITMAP is in the buffer */
796 	int byte, bit;
797 
798 	ntfs_log_trace("Entering.\n");
799 
800 	if (!vol) {
801 		errno = EINVAL;
802 		return -1;
803 	}
804 
805 	/* Does mref lie in the section of $Bitmap we already have cached? */
806 	if (((s64)MREF(mref) < bmpmref)
807 	    || ((s64)MREF(mref) >= (s64)(bmpmref + (sizeof(buffer) << 3)))) {
808 		ntfs_log_debug("Bit lies outside cache.\n");
809 
810 		/* Mark the buffer as not in use, in case the read is shorter. */
811 		memset(buffer, 0, sizeof(buffer));
812 		bmpmref = mref & (~((sizeof(buffer) << 3) - 1));
813 
814 		if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) {
815 			ntfs_log_perror("Couldn't read $MFT/$BITMAP");
816 			return -1;
817 		}
818 
819 		ntfs_log_debug("Reloaded bitmap buffer.\n");
820 	}
821 
822 	bit  = 1 << (mref & 7);
823 	byte = (mref >> 3) & (sizeof(buffer) - 1);
824 	ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, "
825 			"in use %d\n", (long long) mref, (long long) bmpmref,
826 			byte, bit, buffer[byte] & bit);
827 
828 	return (buffer[byte] & bit);
829 }
830 
831 /**
832  * __metadata
833  */
834 static int __metadata(ntfs_volume *vol, u64 num)
835 {
836 	if (num <= FILE_UpCase)
837 		return 1;
838 	if (!vol)
839 		return -1;
840 	if ((vol->major_ver == 3) && (num == FILE_Extend))
841 		return 1;
842 
843 	return 0;
844 }
845 
846 /**
847  * utils_is_metadata - Determine if an inode represents a metadata file
848  * @inode:  An ntfs inode to be tested
849  *
850  * A handful of files in the volume contain filesystem data - metadata.
851  * They can be identified by their inode number (offset in MFT/$DATA) or by
852  * their parent.
853  *
854  * Return:  1  inode is a metadata file
855  *	    0  inode is not a metadata file
856  *	   -1  Error occurred
857  */
858 int utils_is_metadata(ntfs_inode *inode)
859 {
860 	ntfs_volume *vol;
861 	ATTR_RECORD *rec;
862 	FILE_NAME_ATTR *attr;
863 	MFT_RECORD *file;
864 	u64 num;
865 
866 	if (!inode) {
867 		errno = EINVAL;
868 		return -1;
869 	}
870 
871 	vol = inode->vol;
872 	if (!vol)
873 		return -1;
874 
875 	num = inode->mft_no;
876 	if (__metadata(vol, num) == 1)
877 		return 1;
878 
879 	file = inode->mrec;
880 	if (file && (file->base_mft_record != 0)) {
881 		num = MREF_LE(file->base_mft_record);
882 		if (__metadata(vol, num) == 1)
883 			return 1;
884 	}
885 
886 	rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
887 	if (!rec)
888 		return -1;
889 
890 	/* We know this will always be resident. */
891 	attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset));
892 
893 	num = MREF_LE(attr->parent_directory);
894 	if ((num != FILE_root) && (__metadata(vol, num) == 1))
895 		return 1;
896 
897 	return 0;
898 }
899 
900 /**
901  * utils_dump_mem - Display a block of memory in hex and ascii
902  * @buf:     Buffer to be displayed
903  * @start:   Offset into @buf to start from
904  * @length:  Number of bytes to display
905  * @flags:   Options to change the style of the output
906  *
907  * Display a block of memory in a tradition hex-dump manner.
908  * Optionally the ascii part can be turned off.
909  *
910  * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS).
911  * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the
912  * output); DM_NO_ASCII (only print the hex values).
913  */
914 void utils_dump_mem(void *buf, int start, int length, int flags)
915 {
916 	int off, i, s, e, col;
917 	u8 *mem = buf;
918 
919 	s =  start                & ~15;	// round down
920 	e = (start + length + 15) & ~15;	// round up
921 
922 	for (off = s; off < e; off += 16) {
923 		col = 30;
924 		if (flags & DM_RED)
925 			col += 1;
926 		if (flags & DM_GREEN)
927 			col += 2;
928 		if (flags & DM_BLUE)
929 			col += 4;
930 		if (flags & DM_INDENT)
931 			ntfs_log_debug("\t");
932 		if (flags & DM_BOLD)
933 			ntfs_log_debug("\e[01m");
934 		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
935 			ntfs_log_debug("\e[%dm", col);
936 		if (off == s)
937 			ntfs_log_debug("%6.6x ", start);
938 		else
939 			ntfs_log_debug("%6.6x ", off);
940 
941 		for (i = 0; i < 16; i++) {
942 			if ((i == 8) && (!(flags & DM_NO_DIVIDER)))
943 				ntfs_log_debug(" -");
944 			if (((off+i) >= start) && ((off+i) < (start+length)))
945 				ntfs_log_debug(" %02X", mem[off+i]);
946 			else
947 				ntfs_log_debug("   ");
948 		}
949 		if (!(flags & DM_NO_ASCII)) {
950 			ntfs_log_debug("  ");
951 			for (i = 0; i < 16; i++) {
952 				if (((off+i) < start) || ((off+i) >= (start+length)))
953 					ntfs_log_debug(" ");
954 				else if (isprint(mem[off + i]))
955 					ntfs_log_debug("%c", mem[off + i]);
956 				else
957 					ntfs_log_debug(".");
958 			}
959 		}
960 		if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
961 			ntfs_log_debug("\e[0m");
962 		ntfs_log_debug("\n");
963 	}
964 }
965 
966 
967 /**
968  * mft_get_search_ctx
969  */
970 struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
971 {
972 	struct mft_search_ctx *ctx;
973 
974 	if (!vol) {
975 		errno = EINVAL;
976 		return NULL;
977 	}
978 
979 	ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx);
980 
981 	ctx->mft_num = -1;
982 	ctx->vol = vol;
983 
984 	return ctx;
985 }
986 
987 /**
988  * mft_put_search_ctx
989  */
990 void mft_put_search_ctx(struct mft_search_ctx *ctx)
991 {
992 	if (!ctx)
993 		return;
994 	if (ctx->inode)
995 		ntfs_inode_close(ctx->inode);
996 	free(ctx);
997 }
998 
999 /**
1000  * mft_next_record
1001  */
1002 int mft_next_record(struct mft_search_ctx *ctx)
1003 {
1004 	s64 nr_mft_records;
1005 	ATTR_RECORD *attr10 = NULL;
1006 	ATTR_RECORD *attr20 = NULL;
1007 	ATTR_RECORD *attr80 = NULL;
1008 	ntfs_attr_search_ctx *attr_ctx;
1009 
1010 	if (!ctx) {
1011 		errno = EINVAL;
1012 		return -1;
1013 	}
1014 
1015 	if (ctx->inode) {
1016 		ntfs_inode_close(ctx->inode);
1017 		ctx->inode = NULL;
1018 	}
1019 
1020 	nr_mft_records = ctx->vol->mft_na->initialized_size >>
1021 			ctx->vol->mft_record_size_bits;
1022 
1023 	for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) {
1024 		int in_use;
1025 
1026 		ctx->flags_match = 0;
1027 		in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num);
1028 		if (in_use == -1) {
1029 			ntfs_log_error("Error reading inode %llu.  Aborting.\n",
1030 					(unsigned long long)ctx->mft_num);
1031 			return -1;
1032 		}
1033 
1034 		if (in_use) {
1035 			ctx->flags_match |= FEMR_IN_USE;
1036 
1037 			ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num);
1038 			if (ctx->inode == NULL) {
1039 				MFT_RECORD *mrec;
1040 				int r;
1041 				MFT_REF base_inode;
1042 
1043 				mrec = (MFT_RECORD*)NULL;
1044 				r = ntfs_file_record_read(ctx->vol,
1045 					(MFT_REF) ctx->mft_num, &mrec, NULL);
1046 				if (r || !mrec || !mrec->base_mft_record)
1047 					ntfs_log_error(
1048 						"Error reading inode %lld.\n",
1049 						(long long)ctx->mft_num);
1050 				else {
1051 					base_inode = le64_to_cpu(
1052 						mrec->base_mft_record);
1053 					ntfs_log_error("Inode %lld is an "
1054 						"extent of inode %lld.\n",
1055 						(long long)ctx->mft_num,
1056 						(long long)MREF(base_inode));
1057 				}
1058 				free (mrec);
1059 				continue;
1060 			}
1061 
1062 			attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec);
1063 			attr20 = find_first_attribute(AT_ATTRIBUTE_LIST,       ctx->inode->mrec);
1064 			attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec);
1065 
1066 			if (attr10)
1067 				ctx->flags_match |= FEMR_BASE_RECORD;
1068 			else
1069 				ctx->flags_match |= FEMR_NOT_BASE_RECORD;
1070 
1071 			if (attr20)
1072 				ctx->flags_match |= FEMR_BASE_RECORD;
1073 
1074 			if (attr80)
1075 				ctx->flags_match |= FEMR_FILE;
1076 
1077 			if (ctx->flags_search & FEMR_DIR) {
1078 				attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL);
1079 				if (attr_ctx) {
1080 					if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0)
1081 						ctx->flags_match |= FEMR_DIR;
1082 
1083 					ntfs_attr_put_search_ctx(attr_ctx);
1084 				} else {
1085 					ntfs_log_error("Couldn't create a search context.\n");
1086 					return -1;
1087 				}
1088 			}
1089 
1090 			switch (utils_is_metadata(ctx->inode)) {
1091 				case 1: ctx->flags_match |= FEMR_METADATA;     break;
1092 				case 0: ctx->flags_match |= FEMR_NOT_METADATA; break;
1093 				default:
1094 					ctx->flags_match |= FEMR_NOT_METADATA; break;
1095 					//ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num);
1096 					//return -1;
1097 			}
1098 
1099 		} else {		// !in_use
1100 			ntfs_attr *mft;
1101 
1102 			ctx->flags_match |= FEMR_NOT_IN_USE;
1103 
1104 			ctx->inode = (ntfs_inode*)calloc(1, sizeof(*ctx->inode));
1105 			if (!ctx->inode) {
1106 				ntfs_log_error("Out of memory.  Aborting.\n");
1107 				return -1;
1108 			}
1109 
1110 			ctx->inode->mft_no = ctx->mft_num;
1111 			ctx->inode->vol    = ctx->vol;
1112 			ctx->inode->mrec   = ntfs_malloc(ctx->vol->mft_record_size);
1113 			if (!ctx->inode->mrec) {
1114 				free(ctx->inode); // == ntfs_inode_close
1115 				return -1;
1116 			}
1117 
1118 			mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA,
1119 					AT_UNNAMED, 0);
1120 			if (!mft) {
1121 				ntfs_log_perror("Couldn't open $MFT/$DATA");
1122 				// free / close
1123 				return -1;
1124 			}
1125 
1126 			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) {
1127 				ntfs_log_perror("Couldn't read MFT Record %llu",
1128 					(unsigned long long) ctx->mft_num);
1129 				// free / close
1130 				ntfs_attr_close(mft);
1131 				return -1;
1132 			}
1133 
1134 			ntfs_attr_close(mft);
1135 		}
1136 
1137 		if (ctx->flags_match & ctx->flags_search) {
1138 			break;
1139 		}
1140 
1141 		if (ntfs_inode_close(ctx->inode)) {
1142 			ntfs_log_error("Error closing inode %llu.\n",
1143 					(unsigned long long)ctx->mft_num);
1144 			return -errno;
1145 		}
1146 
1147 		ctx->inode = NULL;
1148 	}
1149 
1150 	return (ctx->inode == NULL);
1151 }
1152 
1153 #ifdef HAVE_WINDOWS_H
1154 
1155 /*
1156  *		Translate formats for older Windows
1157  *
1158  *	Up to Windows XP, msvcrt.dll does not support long long format
1159  *	specifications (%lld, %llx, etc). We have to translate them
1160  *	to %I64.
1161  */
1162 
1163 char *ntfs_utils_reformat(char *out, int sz, const char *fmt)
1164 {
1165 	const char *f;
1166 	char *p;
1167 	int i;
1168 	enum { F_INIT, F_PERCENT, F_FIRST } state;
1169 
1170 	i = 0;
1171 	f = fmt;
1172 	p = out;
1173 	state = F_INIT;
1174 	while (*f && ((i + 3) < sz)) {
1175 		switch (state) {
1176 		case F_INIT :
1177 			if (*f == '%')
1178 				state = F_PERCENT;
1179 			*p++ = *f++;
1180 			i++;
1181 			break;
1182 		case F_PERCENT :
1183 			if (*f == 'l') {
1184 				state = F_FIRST;
1185 				f++;
1186 			} else {
1187 				if (((*f < '0') || (*f > '9'))
1188 				    && (*f != '*') && (*f != '-'))
1189 					state = F_INIT;
1190 				*p++ = *f++;
1191 				i++;
1192 			}
1193 			break;
1194 		case F_FIRST :
1195 			if (*f == 'l') {
1196 				*p++ = 'I';
1197 				*p++ = '6';
1198 				*p++ = '4';
1199 				f++;
1200 				i += 3;
1201 			} else {
1202 				*p++ = 'l';
1203 				*p++ = *f++;
1204 				i += 2;
1205 			}
1206 			state = F_INIT;
1207 			break;
1208 		}
1209 	}
1210 	*p++ = 0;
1211 	return (out);
1212 }
1213 
1214 /*
1215  *		Translate paths to files submitted from Windows
1216  *
1217  *	Translate Windows directory separators to Unix ones
1218  *
1219  *	Returns the translated path, to be freed by caller
1220  *		NULL if there was an error, with errno set
1221  */
1222 
1223 char *ntfs_utils_unix_path(const char *in)
1224 {
1225 	char *out;
1226 	int i;
1227 
1228 	out = strdup(in);
1229 	if (out) {
1230 		for (i=0; in[i]; i++)
1231 			if (in[i] == '\\')
1232 				out[i] = '/';
1233 	} else
1234 		errno = ENOMEM;
1235 	return (out);
1236 }
1237 
1238 #endif
1239