xref: /haiku/src/add-ons/kernel/file_systems/ntfs/utils/utils.c (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 #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  */
652 int 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  */
735 int 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  */
798 int 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  */
840 static 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  */
864 int 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  */
920 void 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  */
976 struct 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  */
996 void 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  */
1008 int 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 
1169 char *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 
1229 char *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