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