Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / ntfs-3g / ntfsprogs / utils.c
blob0a97cecae66bfd959b180d3292fa8325429ee5d4
1 /**
2 * utils.c - Part of the Linux-NTFS project.
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
9 * A set of shared functions for ntfs utilities
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.
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.
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
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
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
68 #include "utils.h"
69 #include "types.h"
70 #include "volume.h"
71 #include "debug.h"
72 #include "dir.h"
73 /* #include "version.h" */
74 #include "logging.h"
75 #include "misc.h"
77 const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n";
78 const char *ntfs_gpl = "This program is free software, released under the GNU "
79 "General Public License\nand you are welcome to redistribute it under "
80 "certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for "
81 "details read the GNU General Public License to be\nfound in the file "
82 "\"COPYING\" distributed with this program, or online at:\n"
83 "http://www.gnu.org/copyleft/gpl.html\n";
85 static const char *invalid_ntfs_msg =
86 "The device '%s' doesn't have a valid NTFS.\n"
87 "Maybe you selected the wrong device? Or the whole disk instead of a\n"
88 "partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n";
90 static const char *corrupt_volume_msg =
91 "NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n"
92 "The usage of the /f parameter is very IMPORTANT! No modification was\n"
93 "made to NTFS by this software.\n";
95 static const char *hibernated_volume_msg =
96 "The NTFS partition is hibernated. Please resume Windows and turned it \n"
97 "off properly, so mounting could be done safely.\n";
99 static const char *unclean_journal_msg =
100 "Access is denied because the NTFS journal file is unclean. Choices are:\n"
101 " A) Shutdown Windows properly.\n"
102 " B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n"
103 " notification area before disconnecting the device.\n"
104 " C) Use 'Eject' from Windows Explorer to safely remove the device.\n"
105 " D) If you ran chkdsk previously then boot Windows again which will\n"
106 " automatically initialize the journal.\n"
107 " E) Submit 'force' option (WARNING: This solution it not recommended).\n"
108 " F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n";
110 static const char *opened_volume_msg =
111 "Access is denied because the NTFS volume is already exclusively opened.\n"
112 "The volume may be already mounted, or another software may use it which\n"
113 "could be identified for example by the help of the 'fuser' command.\n";
115 static const char *dirty_volume_msg =
116 "Volume is scheduled for check.\n"
117 "Please boot into Windows TWICE, or use the 'force' option.\n"
118 "NOTE: If you had not scheduled check and last time accessed this volume\n"
119 "using ntfsmount and shutdown system properly, then init scripts in your\n"
120 "distribution are broken. Please report to your distribution developers\n"
121 "(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n"
122 "shutdown instead of proper umount.\n";
124 static const char *fakeraid_msg =
125 "You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n"
126 "different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n"
127 "to mount NTFS. Please see the 'dmraid' documentation for help.\n";
130 * utils_set_locale
132 int utils_set_locale(void)
134 const char *locale;
136 locale = setlocale(LC_ALL, "");
137 if (!locale) {
138 locale = setlocale(LC_ALL, NULL);
139 ntfs_log_error("Failed to set locale, using default '%s'.\n",
140 locale);
141 return 1;
142 } else {
143 return 0;
148 * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with
149 * ntfs-3g's.
151 int ntfs_mbstoucs_libntfscompat(const char *ins,
152 ntfschar **outs, int outs_len)
154 if(!outs) {
155 errno = EINVAL;
156 return -1;
158 else if(*outs != NULL) {
159 /* Note: libntfs's mbstoucs implementation allows the caller to
160 * specify a preallocated buffer while libntfs-3g's always
161 * allocates the output buffer.
163 ntfschar *tmpstr = NULL;
164 int tmpstr_len;
166 tmpstr_len = ntfs_mbstoucs(ins, &tmpstr);
167 if(tmpstr_len >= 0) {
168 if((tmpstr_len + 1) > outs_len) {
169 /* Doing a realloc instead of reusing tmpstr
170 * because it emulates libntfs's mbstoucs more
171 * closely. */
172 ntfschar *re_outs = realloc(*outs,
173 sizeof(ntfschar)*(tmpstr_len + 1));
174 if(!re_outs)
175 tmpstr_len = -1;
176 else
177 *outs = re_outs;
180 if(tmpstr_len >= 0) {
181 /* The extra character is the \0 terminator. */
182 memcpy(*outs, tmpstr,
183 sizeof(ntfschar)*(tmpstr_len + 1));
186 free(tmpstr);
189 return tmpstr_len;
191 else
192 return ntfs_mbstoucs(ins, outs);
196 * utils_valid_device - Perform some safety checks on the device, before start
197 * @name: Full pathname of the device/file to work with
198 * @force: Continue regardless of problems
200 * Check that the name refers to a device and that is isn't already mounted.
201 * These checks can be overridden by using the force option.
203 * Return: 1 Success, we can continue
204 * 0 Error, we cannot use this device
206 int utils_valid_device(const char *name, int force)
208 unsigned long mnt_flags = 0;
209 struct stat st;
211 #ifdef __CYGWIN32__
212 /* FIXME: This doesn't work for Cygwin, so just return success. */
213 return 1;
214 #endif
215 if (!name) {
216 errno = EINVAL;
217 return 0;
220 if (stat(name, &st) == -1) {
221 if (errno == ENOENT)
222 ntfs_log_error("The device %s doesn't exist\n", name);
223 else
224 ntfs_log_perror("Error getting information about %s",
225 name);
226 return 0;
229 /* Make sure the file system is not mounted. */
230 if (ntfs_check_if_mounted(name, &mnt_flags)) {
231 ntfs_log_perror("Failed to determine whether %s is mounted",
232 name);
233 if (!force) {
234 ntfs_log_error("Use the force option to ignore this "
235 "error.\n");
236 return 0;
238 ntfs_log_warning("Forced to continue.\n");
239 } else if (mnt_flags & NTFS_MF_MOUNTED) {
240 if (!force) {
241 ntfs_log_error("%s", opened_volume_msg);
242 ntfs_log_error("You can use force option to avoid this "
243 "check, but this is not recommended\n"
244 "and may lead to data corruption.\n");
245 return 0;
247 ntfs_log_warning("Forced to continue.\n");
250 return 1;
254 * utils_mount_volume - Mount an NTFS volume
256 ntfs_volume * utils_mount_volume(const char *device, unsigned long flags)
258 ntfs_volume *vol;
260 if (!device) {
261 errno = EINVAL;
262 return NULL;
265 /* Porting notes:
267 * libntfs-3g does not have the 'force' flag in ntfs_mount_flags.
268 * The 'force' flag in libntfs bypasses two safety checks when mounting
269 * read/write:
270 * 1. Do not mount when the VOLUME_IS_DIRTY flag in
271 * VOLUME_INFORMATION is set.
272 * 2. Do not mount when the logfile is unclean.
274 * libntfs-3g only has safety check number 2. The dirty flag is simply
275 * ignored because we are confident that we can handle a dirty volume.
276 * So we treat NTFS_MNT_RECOVER like NTFS_MNT_FORCE, knowing that the
277 * first check is always bypassed.
280 if (!utils_valid_device(device, flags & NTFS_MNT_RECOVER))
281 return NULL;
283 vol = ntfs_mount(device, flags);
284 if (!vol) {
285 ntfs_log_perror("Failed to mount '%s'", device);
286 if (errno == EINVAL)
287 ntfs_log_error(invalid_ntfs_msg, device);
288 else if (errno == EIO)
289 ntfs_log_error("%s", corrupt_volume_msg);
290 else if (errno == EPERM)
291 ntfs_log_error("%s", hibernated_volume_msg);
292 else if (errno == EOPNOTSUPP)
293 ntfs_log_error("%s", unclean_journal_msg);
294 else if (errno == EBUSY)
295 ntfs_log_error("%s", opened_volume_msg);
296 else if (errno == ENXIO)
297 ntfs_log_error("%s", fakeraid_msg);
298 return NULL;
301 /* Porting notes:
302 * libntfs-3g does not record whether the volume log file was dirty
303 * before mount, so we can only warn if the VOLUME_IS_DIRTY flag is set
304 * in VOLUME_INFORMATION. */
305 if (vol->flags & VOLUME_IS_DIRTY) {
306 if (!(flags & NTFS_MNT_RECOVER)) {
307 ntfs_log_error("%s", dirty_volume_msg);
308 ntfs_umount(vol, FALSE);
309 return NULL;
311 ntfs_log_error("WARNING: Dirty volume mount was forced by the "
312 "'force' mount option.\n");
314 return vol;
318 * utils_parse_size - Convert a string representing a size
319 * @value: String to be parsed
320 * @size: Parsed size
321 * @scale: Whether or not to allow a suffix to scale the value
323 * Read a string and convert it to a number. Strings may be suffixed to scale
324 * them. Any number without a suffix is assumed to be in bytes.
326 * Suffix Description Multiple
327 * [tT] Terabytes 10^12
328 * [gG] Gigabytes 10^9
329 * [mM] Megabytes 10^6
330 * [kK] Kilobytes 10^3
332 * Notes:
333 * Only the first character of the suffix is read.
334 * The multipliers are decimal thousands, not binary: 1000, not 1024.
335 * If parse_size fails, @size will not be changed
337 * Return: 1 Success
338 * 0 Error, the string was malformed
340 int utils_parse_size(const char *value, s64 *size, BOOL scale)
342 long long result;
343 char *suffix = NULL;
345 if (!value || !size) {
346 errno = EINVAL;
347 return 0;
350 ntfs_log_debug("Parsing size '%s'.\n", value);
352 result = strtoll(value, &suffix, 0);
353 if (result < 0 || errno == ERANGE) {
354 ntfs_log_error("Invalid size '%s'.\n", value);
355 return 0;
358 if (!suffix) {
359 ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
360 return 0;
363 if (scale) {
364 switch (suffix[0]) {
365 case 't': case 'T': result *= 1000;
366 case 'g': case 'G': result *= 1000;
367 case 'm': case 'M': result *= 1000;
368 case 'k': case 'K': result *= 1000;
369 case '-': case 0:
370 break;
371 default:
372 ntfs_log_error("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix);
373 return 0;
375 } else {
376 if ((suffix[0] != '-') && (suffix[0] != 0)) {
377 ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value);
378 return 0;
382 ntfs_log_debug("Parsed size = %lld.\n", result);
383 *size = result;
384 return 1;
388 * utils_parse_range - Convert a string representing a range of numbers
389 * @string: The string to be parsed
390 * @start: The beginning of the range will be stored here
391 * @finish: The end of the range will be stored here
393 * Read a string of the form n-m. If the lower end is missing, zero will be
394 * substituted. If the upper end is missing LONG_MAX will be used. If the
395 * string cannot be parsed correctly, @start and @finish will not be changed.
397 * Return: 1 Success, a valid string was found
398 * 0 Error, the string was not a valid range
400 int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale)
402 s64 a, b;
403 char *middle;
405 if (!string || !start || !finish) {
406 errno = EINVAL;
407 return 0;
410 middle = strchr(string, '-');
411 if (string == middle) {
412 ntfs_log_debug("Range has no beginning, defaulting to 0.\n");
413 a = 0;
414 } else {
415 if (!utils_parse_size(string, &a, scale))
416 return 0;
419 if (middle) {
420 if (middle[1] == 0) {
421 b = LONG_MAX; // XXX ULLONG_MAX
422 ntfs_log_debug("Range has no end, defaulting to %lld.\n", b);
423 } else {
424 if (!utils_parse_size(middle+1, &b, scale))
425 return 0;
427 } else {
428 b = a;
431 ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b);
433 *start = a;
434 *finish = b;
435 return 1;
439 * find_attribute - Find an attribute of the given type
440 * @type: An attribute type, e.g. AT_FILE_NAME
441 * @ctx: A search context, created using ntfs_get_attr_search_ctx
443 * Using the search context to keep track, find the first/next occurrence of a
444 * given attribute type.
446 * N.B. This will return a pointer into @mft. As long as the search context
447 * has been created without an inode, it won't overflow the buffer.
449 * Return: Pointer Success, an attribute was found
450 * NULL Error, no matching attributes were found
452 ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
454 if (!ctx) {
455 errno = EINVAL;
456 return NULL;
459 if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) {
460 ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type);
461 return NULL; /* None / no more of that type */
464 ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type);
465 return ctx->attr;
469 * find_first_attribute - Find the first attribute of a given type
470 * @type: An attribute type, e.g. AT_FILE_NAME
471 * @mft: A buffer containing a raw MFT record
473 * Search through a raw MFT record for an attribute of a given type.
474 * The return value is a pointer into the MFT record that was supplied.
476 * N.B. This will return a pointer into @mft. The pointer won't stray outside
477 * the buffer, since we created the search context without an inode.
479 * Return: Pointer Success, an attribute was found
480 * NULL Error, no matching attributes were found
482 ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft)
484 ntfs_attr_search_ctx *ctx;
485 ATTR_RECORD *rec;
487 if (!mft) {
488 errno = EINVAL;
489 return NULL;
492 ctx = ntfs_attr_get_search_ctx(NULL, mft);
493 if (!ctx) {
494 ntfs_log_error("Couldn't create a search context.\n");
495 return NULL;
498 rec = find_attribute(type, ctx);
499 ntfs_attr_put_search_ctx(ctx);
500 if (rec)
501 ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type);
502 else
503 ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type);
504 return rec;
508 * utils_inode_get_name
510 * using inode
511 * get filename
512 * add name to list
513 * get parent
514 * if parent is 5 (/) stop
515 * get inode of parent
517 #define max_path 20
518 int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
520 // XXX option: names = posix/win32 or dos
521 // flags: path, filename, or both
524 ntfs_volume *vol;
525 ntfs_attr_search_ctx *ctx;
526 ATTR_RECORD *rec;
527 FILE_NAME_ATTR *attr;
528 int name_space;
529 MFT_REF parent = FILE_root;
530 char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger?
531 int i, len, offset = 0;
533 if (!inode || !buffer) {
534 errno = EINVAL;
535 return 0;
538 vol = inode->vol;
540 //ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names));
541 memset(names, 0, sizeof(names));
543 for (i = 0; i < max_path; i++) {
545 ctx = ntfs_attr_get_search_ctx(inode, NULL);
546 if (!ctx) {
547 ntfs_log_error("Couldn't create a search context.\n");
548 return 0;
551 //ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no);
553 name_space = 4;
554 while ((rec = find_attribute(AT_FILE_NAME, ctx))) {
555 /* We know this will always be resident. */
556 attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset));
558 if (attr->file_name_type > name_space) { //XXX find the ...
559 continue;
562 name_space = attr->file_name_type;
563 parent = le64_to_cpu(attr->parent_directory);
565 if (names[i]) {
566 free(names[i]);
567 names[i] = NULL;
570 if (ntfs_ucstombs(attr->file_name, attr->file_name_length,
571 &names[i], 0) < 0) {
572 char *temp;
573 ntfs_log_error("Couldn't translate filename to current locale.\n");
574 temp = ntfs_malloc(30);
575 if (!temp)
576 return 0;
577 snprintf(temp, 30, "<MFT%llu>", (unsigned
578 long long)inode->mft_no);
579 names[i] = temp;
582 //ntfs_log_debug("names[%d] %s\n", i, names[i]);
583 //ntfs_log_debug("parent = %lld\n", MREF(parent));
586 ntfs_attr_put_search_ctx(ctx);
588 if (i > 0) /* Don't close the original inode */
589 ntfs_inode_close(inode);
591 if (MREF(parent) == FILE_root) { /* The root directory, stop. */
592 //ntfs_log_debug("inode 5\n");
593 break;
596 inode = ntfs_inode_open(vol, parent);
597 if (!inode) {
598 ntfs_log_error("Couldn't open inode %llu.\n",
599 (unsigned long long)MREF(parent));
600 break;
604 if (i >= max_path) {
605 /* If we get into an infinite loop, we'll end up here. */
606 ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path);
607 return 0;
610 /* Assemble the names in the correct order. */
611 for (i = max_path; i >= 0; i--) {
612 if (!names[i])
613 continue;
615 len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]);
616 if (len >= (bufsize - offset)) {
617 ntfs_log_error("Pathname was truncated.\n");
618 break;
621 offset += len;
624 /* Free all the allocated memory */
625 for (i = 0; i < max_path; i++)
626 free(names[i]);
628 ntfs_log_debug("Pathname: %s\n", buffer);
630 return 1;
632 #undef max_path
635 * utils_attr_get_name
637 int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize)
639 int len, namelen;
640 char *name;
641 ATTR_DEF *attrdef;
643 // flags: attr, name, or both
644 if (!attr || !buffer) {
645 errno = EINVAL;
646 return 0;
649 attrdef = ntfs_attr_find_in_attrdef(vol, attr->type);
650 if (attrdef) {
651 name = NULL;
652 namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name));
653 if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) {
654 ntfs_log_error("Couldn't translate attribute type to "
655 "current locale.\n");
656 // <UNKNOWN>?
657 return 0;
659 len = snprintf(buffer, bufsize, "%s", name);
660 } else {
661 ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type);
662 len = snprintf(buffer, bufsize, "<UNKNOWN>");
665 if (len >= bufsize) {
666 ntfs_log_error("Attribute type was truncated.\n");
667 return 0;
670 if (!attr->name_length) {
671 return 0;
674 buffer += len;
675 bufsize -= len;
677 name = NULL;
678 namelen = attr->name_length;
679 if (ntfs_ucstombs((ntfschar *)((char *)attr + attr->name_offset),
680 namelen, &name, 0) < 0) {
681 ntfs_log_error("Couldn't translate attribute name to current "
682 "locale.\n");
683 // <UNKNOWN>?
684 len = snprintf(buffer, bufsize, "<UNKNOWN>");
685 return 0;
688 len = snprintf(buffer, bufsize, "(%s)", name);
689 free(name);
691 if (len >= bufsize) {
692 ntfs_log_error("Attribute name was truncated.\n");
693 return 0;
696 return 0;
700 * utils_cluster_in_use - Determine if a cluster is in use
701 * @vol: An ntfs volume obtained from ntfs_mount
702 * @lcn: The Logical Cluster Number to test
704 * The metadata file $Bitmap has one binary bit representing each cluster on
705 * disk. The bit will be set for each cluster that is in use. The function
706 * reads the relevant part of $Bitmap into a buffer and tests the bit.
708 * This function has a static buffer in which it caches a section of $Bitmap.
709 * If the lcn, being tested, lies outside the range, the buffer will be
710 * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the
711 * buffer.
713 * NOTE: Be very carefull with shifts by 3 everywhere in this function.
715 * Return: 1 Cluster is in use
716 * 0 Cluster is free space
717 * -1 Error occurred
719 int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
721 static unsigned char buffer[512];
722 static long long bmplcn = -(sizeof(buffer) << 3);
723 int byte, bit;
724 ntfs_attr *attr;
726 if (!vol) {
727 errno = EINVAL;
728 return -1;
731 /* Does lcn lie in the section of $Bitmap we already have cached? */
732 if ((lcn < bmplcn)
733 || (lcn >= (long long)(bmplcn + (sizeof(buffer) << 3)))) {
734 ntfs_log_debug("Bit lies outside cache.\n");
735 attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
736 if (!attr) {
737 ntfs_log_perror("Couldn't open $Bitmap");
738 return -1;
741 /* Mark the buffer as in use, in case the read is shorter. */
742 memset(buffer, 0xFF, sizeof(buffer));
743 bmplcn = lcn & (~((sizeof(buffer) << 3) - 1));
745 if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer),
746 buffer) < 0) {
747 ntfs_log_perror("Couldn't read $Bitmap");
748 ntfs_attr_close(attr);
749 return -1;
752 ntfs_log_debug("Reloaded bitmap buffer.\n");
753 ntfs_attr_close(attr);
756 bit = 1 << (lcn & 7);
757 byte = (lcn >> 3) & (sizeof(buffer) - 1);
758 ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, "
759 "in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] &
760 bit);
762 return (buffer[byte] & bit);
766 * utils_mftrec_in_use - Determine if a MFT Record is in use
767 * @vol: An ntfs volume obtained from ntfs_mount
768 * @mref: MFT Reference (inode number)
770 * The metadata file $BITMAP has one binary bit representing each record in the
771 * MFT. The bit will be set for each record that is in use. The function
772 * reads the relevant part of $BITMAP into a buffer and tests the bit.
774 * This function has a static buffer in which it caches a section of $BITMAP.
775 * If the mref, being tested, lies outside the range, the buffer will be
776 * refreshed.
778 * Return: 1 MFT Record is in use
779 * 0 MFT Record is unused
780 * -1 Error occurred
782 int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
784 static u8 buffer[512];
785 static s64 bmpmref = -(sizeof(buffer) << 3) - 1; /* Which bit of $BITMAP is in the buffer */
786 int byte, bit;
788 ntfs_log_trace("Entering.\n");
790 if (!vol) {
791 errno = EINVAL;
792 return -1;
795 /* Does mref lie in the section of $Bitmap we already have cached? */
796 if (((s64)MREF(mref) < bmpmref)
797 || ((s64)MREF(mref) >= (s64)(bmpmref + (sizeof(buffer) << 3)))) {
798 ntfs_log_debug("Bit lies outside cache.\n");
800 /* Mark the buffer as not in use, in case the read is shorter. */
801 memset(buffer, 0, sizeof(buffer));
802 bmpmref = mref & (~((sizeof(buffer) << 3) - 1));
804 if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) {
805 ntfs_log_perror("Couldn't read $MFT/$BITMAP");
806 return -1;
809 ntfs_log_debug("Reloaded bitmap buffer.\n");
812 bit = 1 << (mref & 7);
813 byte = (mref >> 3) & (sizeof(buffer) - 1);
814 ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit);
816 return (buffer[byte] & bit);
820 * __metadata
822 static int __metadata(ntfs_volume *vol, u64 num)
824 if (num <= FILE_UpCase)
825 return 1;
826 if (!vol)
827 return -1;
828 if ((vol->major_ver == 3) && (num == FILE_Extend))
829 return 1;
831 return 0;
835 * utils_is_metadata - Determine if an inode represents a metadata file
836 * @inode: An ntfs inode to be tested
838 * A handful of files in the volume contain filesystem data - metadata.
839 * They can be identified by their inode number (offset in MFT/$DATA) or by
840 * their parent.
842 * Return: 1 inode is a metadata file
843 * 0 inode is not a metadata file
844 * -1 Error occurred
846 int utils_is_metadata(ntfs_inode *inode)
848 ntfs_volume *vol;
849 ATTR_RECORD *rec;
850 FILE_NAME_ATTR *attr;
851 MFT_RECORD *file;
852 u64 num;
854 if (!inode) {
855 errno = EINVAL;
856 return -1;
859 vol = inode->vol;
860 if (!vol)
861 return -1;
863 num = inode->mft_no;
864 if (__metadata(vol, num) == 1)
865 return 1;
867 file = inode->mrec;
868 if (file && (file->base_mft_record != 0)) {
869 num = MREF_LE(file->base_mft_record);
870 if (__metadata(vol, num) == 1)
871 return 1;
874 rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
875 if (!rec)
876 return -1;
878 /* We know this will always be resident. */
879 attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset));
881 num = MREF_LE(attr->parent_directory);
882 if ((num != FILE_root) && (__metadata(vol, num) == 1))
883 return 1;
885 return 0;
889 * utils_dump_mem - Display a block of memory in hex and ascii
890 * @buf: Buffer to be displayed
891 * @start: Offset into @buf to start from
892 * @length: Number of bytes to display
893 * @flags: Options to change the style of the output
895 * Display a block of memory in a tradition hex-dump manner.
896 * Optionally the ascii part can be turned off.
898 * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS).
899 * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the
900 * output); DM_NO_ASCII (only print the hex values).
902 void utils_dump_mem(void *buf, int start, int length, int flags)
904 int off, i, s, e, col;
905 u8 *mem = buf;
907 s = start & ~15; // round down
908 e = (start + length + 15) & ~15; // round up
910 for (off = s; off < e; off += 16) {
911 col = 30;
912 if (flags & DM_RED)
913 col += 1;
914 if (flags & DM_GREEN)
915 col += 2;
916 if (flags & DM_BLUE)
917 col += 4;
918 if (flags & DM_INDENT)
919 ntfs_log_debug("\t");
920 if (flags & DM_BOLD)
921 ntfs_log_debug("\e[01m");
922 if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
923 ntfs_log_debug("\e[%dm", col);
924 if (off == s)
925 ntfs_log_debug("%6.6x ", start);
926 else
927 ntfs_log_debug("%6.6x ", off);
929 for (i = 0; i < 16; i++) {
930 if ((i == 8) && (!(flags & DM_NO_DIVIDER)))
931 ntfs_log_debug(" -");
932 if (((off+i) >= start) && ((off+i) < (start+length)))
933 ntfs_log_debug(" %02X", mem[off+i]);
934 else
935 ntfs_log_debug(" ");
937 if (!(flags & DM_NO_ASCII)) {
938 ntfs_log_debug(" ");
939 for (i = 0; i < 16; i++) {
940 if (((off+i) < start) || ((off+i) >= (start+length)))
941 ntfs_log_debug(" ");
942 else if (isprint(mem[off + i]))
943 ntfs_log_debug("%c", mem[off + i]);
944 else
945 ntfs_log_debug(".");
948 if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
949 ntfs_log_debug("\e[0m");
950 ntfs_log_debug("\n");
956 * mft_get_search_ctx
958 struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
960 struct mft_search_ctx *ctx;
962 if (!vol) {
963 errno = EINVAL;
964 return NULL;
967 ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx);
969 ctx->mft_num = -1;
970 ctx->vol = vol;
972 return ctx;
976 * mft_put_search_ctx
978 void mft_put_search_ctx(struct mft_search_ctx *ctx)
980 if (!ctx)
981 return;
982 if (ctx->inode)
983 ntfs_inode_close(ctx->inode);
984 free(ctx);
988 * mft_next_record
990 int mft_next_record(struct mft_search_ctx *ctx)
992 s64 nr_mft_records;
993 ATTR_RECORD *attr10 = NULL;
994 ATTR_RECORD *attr20 = NULL;
995 ATTR_RECORD *attr80 = NULL;
996 ntfs_attr_search_ctx *attr_ctx;
998 if (!ctx) {
999 errno = EINVAL;
1000 return -1;
1003 if (ctx->inode) {
1004 ntfs_inode_close(ctx->inode);
1005 ctx->inode = NULL;
1008 nr_mft_records = ctx->vol->mft_na->initialized_size >>
1009 ctx->vol->mft_record_size_bits;
1011 for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) {
1012 int in_use;
1014 ctx->flags_match = 0;
1015 in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num);
1016 if (in_use == -1) {
1017 ntfs_log_error("Error reading inode %llu. Aborting.\n",
1018 (unsigned long long)ctx->mft_num);
1019 return -1;
1022 if (in_use) {
1023 ctx->flags_match |= FEMR_IN_USE;
1025 ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num);
1026 if (ctx->inode == NULL) {
1027 ntfs_log_error("Error reading inode %llu.\n", (unsigned
1028 long long) ctx->mft_num);
1029 continue;
1032 attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec);
1033 attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, ctx->inode->mrec);
1034 attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec);
1036 if (attr10)
1037 ctx->flags_match |= FEMR_BASE_RECORD;
1038 else
1039 ctx->flags_match |= FEMR_NOT_BASE_RECORD;
1041 if (attr20)
1042 ctx->flags_match |= FEMR_BASE_RECORD;
1044 if (attr80)
1045 ctx->flags_match |= FEMR_FILE;
1047 if (ctx->flags_search & FEMR_DIR) {
1048 attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL);
1049 if (attr_ctx) {
1050 if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0)
1051 ctx->flags_match |= FEMR_DIR;
1053 ntfs_attr_put_search_ctx(attr_ctx);
1054 } else {
1055 ntfs_log_error("Couldn't create a search context.\n");
1056 return -1;
1060 switch (utils_is_metadata(ctx->inode)) {
1061 case 1: ctx->flags_match |= FEMR_METADATA; break;
1062 case 0: ctx->flags_match |= FEMR_NOT_METADATA; break;
1063 default:
1064 ctx->flags_match |= FEMR_NOT_METADATA; break;
1065 //ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num);
1066 //return -1;
1069 } else { // !in_use
1070 ntfs_attr *mft;
1072 ctx->flags_match |= FEMR_NOT_IN_USE;
1074 ctx->inode = (ntfs_inode*)calloc(1, sizeof(*ctx->inode));
1075 if (!ctx->inode) {
1076 ntfs_log_error("Out of memory. Aborting.\n");
1077 return -1;
1080 ctx->inode->mft_no = ctx->mft_num;
1081 ctx->inode->vol = ctx->vol;
1082 ctx->inode->mrec = ntfs_malloc(ctx->vol->mft_record_size);
1083 if (!ctx->inode->mrec) {
1084 free(ctx->inode); // == ntfs_inode_close
1085 return -1;
1088 mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA,
1089 AT_UNNAMED, 0);
1090 if (!mft) {
1091 ntfs_log_perror("Couldn't open $MFT/$DATA");
1092 // free / close
1093 return -1;
1096 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) {
1097 ntfs_log_perror("Couldn't read MFT Record %llu",
1098 (unsigned long long) ctx->mft_num);
1099 // free / close
1100 ntfs_attr_close(mft);
1101 return -1;
1104 ntfs_attr_close(mft);
1107 if (ctx->flags_match & ctx->flags_search) {
1108 break;
1111 if (ntfs_inode_close(ctx->inode)) {
1112 ntfs_log_error("Error closing inode %llu.\n",
1113 (unsigned long long)ctx->mft_num);
1114 return -errno;
1117 ctx->inode = NULL;
1120 return (ctx->inode == NULL);