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