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