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