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