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
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
43 #ifdef HAVE_SYS_STAT_H
73 /* #include "version.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";
132 int utils_set_locale(void)
136 locale
= setlocale(LC_ALL
, "");
138 locale
= setlocale(LC_ALL
, NULL
);
139 ntfs_log_error("Failed to set locale, using default '%s'.\n",
148 * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with
151 int ntfs_mbstoucs_libntfscompat(const char *ins
,
152 ntfschar
**outs
, int outs_len
)
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
;
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
172 ntfschar
*re_outs
= realloc(*outs
,
173 sizeof(ntfschar
)*(tmpstr_len
+ 1));
180 if(tmpstr_len
>= 0) {
181 /* The extra character is the \0 terminator. */
182 memcpy(*outs
, tmpstr
,
183 sizeof(ntfschar
)*(tmpstr_len
+ 1));
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;
212 /* FIXME: This doesn't work for Cygwin, so just return success. */
220 if (stat(name
, &st
) == -1) {
222 ntfs_log_error("The device %s doesn't exist\n", name
);
224 ntfs_log_perror("Error getting information about %s",
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",
234 ntfs_log_error("Use the force option to ignore this "
238 ntfs_log_warning("Forced to continue.\n");
239 } else if (mnt_flags
& NTFS_MF_MOUNTED
) {
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");
247 ntfs_log_warning("Forced to continue.\n");
254 * utils_mount_volume - Mount an NTFS volume
256 ntfs_volume
* utils_mount_volume(const char *device
, unsigned long flags
)
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
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
))
283 vol
= ntfs_mount(device
, flags
);
285 ntfs_log_perror("Failed to mount '%s'", device
);
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
);
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
);
311 ntfs_log_error("WARNING: Dirty volume mount was forced by the "
312 "'force' mount option.\n");
318 * utils_parse_size - Convert a string representing a size
319 * @value: String to be parsed
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
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
338 * 0 Error, the string was malformed
340 int utils_parse_size(const char *value
, s64
*size
, BOOL scale
)
345 if (!value
|| !size
) {
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
);
359 ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
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;
372 ntfs_log_error("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix
);
376 if ((suffix
[0] != '-') && (suffix
[0] != 0)) {
377 ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix
- value
+ 1), value
);
382 ntfs_log_debug("Parsed size = %lld.\n", result
);
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
)
405 if (!string
|| !start
|| !finish
) {
410 middle
= strchr(string
, '-');
411 if (string
== middle
) {
412 ntfs_log_debug("Range has no beginning, defaulting to 0.\n");
415 if (!utils_parse_size(string
, &a
, scale
))
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
);
424 if (!utils_parse_size(middle
+1, &b
, scale
))
431 ntfs_log_debug("Range '%s' = %lld - %lld\n", string
, a
, b
);
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
)
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
);
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
;
492 ctx
= ntfs_attr_get_search_ctx(NULL
, mft
);
494 ntfs_log_error("Couldn't create a search context.\n");
498 rec
= find_attribute(type
, ctx
);
499 ntfs_attr_put_search_ctx(ctx
);
501 ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type
);
503 ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type
);
508 * utils_inode_get_name
514 * if parent is 5 (/) stop
515 * get inode of parent
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
525 ntfs_attr_search_ctx
*ctx
;
527 FILE_NAME_ATTR
*attr
;
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
) {
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
);
547 ntfs_log_error("Couldn't create a search context.\n");
551 //ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no);
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 ...
562 name_space
= attr
->file_name_type
;
563 parent
= le64_to_cpu(attr
->parent_directory
);
570 if (ntfs_ucstombs(attr
->file_name
, attr
->file_name_length
,
573 ntfs_log_error("Couldn't translate filename to current locale.\n");
574 temp
= ntfs_malloc(30);
577 snprintf(temp
, 30, "<MFT%llu>", (unsigned
578 long long)inode
->mft_no
);
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");
596 inode
= ntfs_inode_open(vol
, parent
);
598 ntfs_log_error("Couldn't open inode %llu.\n",
599 (unsigned long long)MREF(parent
));
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
);
610 /* Assemble the names in the correct order. */
611 for (i
= max_path
; i
>= 0; i
--) {
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");
624 /* Free all the allocated memory */
625 for (i
= 0; i
< max_path
; i
++)
628 ntfs_log_debug("Pathname: %s\n", buffer
);
635 * utils_attr_get_name
637 int utils_attr_get_name(ntfs_volume
*vol
, ATTR_RECORD
*attr
, char *buffer
, int bufsize
)
643 // flags: attr, name, or both
644 if (!attr
|| !buffer
) {
649 attrdef
= ntfs_attr_find_in_attrdef(vol
, attr
->type
);
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");
659 len
= snprintf(buffer
, bufsize
, "%s", name
);
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");
670 if (!attr
->name_length
) {
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 "
684 len
= snprintf(buffer
, bufsize
, "<UNKNOWN>");
688 len
= snprintf(buffer
, bufsize
, "(%s)", name
);
691 if (len
>= bufsize
) {
692 ntfs_log_error("Attribute name was truncated.\n");
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
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
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);
731 /* Does lcn lie in the section of $Bitmap we already have cached? */
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);
737 ntfs_log_perror("Couldn't open $Bitmap");
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
),
747 ntfs_log_perror("Couldn't read $Bitmap");
748 ntfs_attr_close(attr
);
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
] &
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
778 * Return: 1 MFT Record is in use
779 * 0 MFT Record is unused
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 */
788 ntfs_log_trace("Entering.\n");
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");
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
);
822 static int __metadata(ntfs_volume
*vol
, u64 num
)
824 if (num
<= FILE_UpCase
)
828 if ((vol
->major_ver
== 3) && (num
== FILE_Extend
))
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
842 * Return: 1 inode is a metadata file
843 * 0 inode is not a metadata file
846 int utils_is_metadata(ntfs_inode
*inode
)
850 FILE_NAME_ATTR
*attr
;
864 if (__metadata(vol
, num
) == 1)
868 if (file
&& (file
->base_mft_record
!= 0)) {
869 num
= MREF_LE(file
->base_mft_record
);
870 if (__metadata(vol
, num
) == 1)
874 rec
= find_first_attribute(AT_FILE_NAME
, inode
->mrec
);
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))
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
;
907 s
= start
& ~15; // round down
908 e
= (start
+ length
+ 15) & ~15; // round up
910 for (off
= s
; off
< e
; off
+= 16) {
914 if (flags
& DM_GREEN
)
918 if (flags
& DM_INDENT
)
919 ntfs_log_debug("\t");
921 ntfs_log_debug("\e[01m");
922 if (flags
& (DM_RED
| DM_BLUE
| DM_GREEN
| DM_BOLD
))
923 ntfs_log_debug("\e[%dm", col
);
925 ntfs_log_debug("%6.6x ", start
);
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
]);
937 if (!(flags
& DM_NO_ASCII
)) {
939 for (i
= 0; i
< 16; i
++) {
940 if (((off
+i
) < start
) || ((off
+i
) >= (start
+length
)))
942 else if (isprint(mem
[off
+ i
]))
943 ntfs_log_debug("%c", mem
[off
+ i
]);
948 if (flags
& (DM_RED
| DM_BLUE
| DM_GREEN
| DM_BOLD
))
949 ntfs_log_debug("\e[0m");
950 ntfs_log_debug("\n");
958 struct mft_search_ctx
* mft_get_search_ctx(ntfs_volume
*vol
)
960 struct mft_search_ctx
*ctx
;
967 ctx
= (struct mft_search_ctx
*)calloc(1, sizeof *ctx
);
978 void mft_put_search_ctx(struct mft_search_ctx
*ctx
)
983 ntfs_inode_close(ctx
->inode
);
990 int mft_next_record(struct mft_search_ctx
*ctx
)
993 ATTR_RECORD
*attr10
= NULL
;
994 ATTR_RECORD
*attr20
= NULL
;
995 ATTR_RECORD
*attr80
= NULL
;
996 ntfs_attr_search_ctx
*attr_ctx
;
1004 ntfs_inode_close(ctx
->inode
);
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
++) {
1014 ctx
->flags_match
= 0;
1015 in_use
= utils_mftrec_in_use(ctx
->vol
, (MFT_REF
) ctx
->mft_num
);
1017 ntfs_log_error("Error reading inode %llu. Aborting.\n",
1018 (unsigned long long)ctx
->mft_num
);
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
);
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
);
1037 ctx
->flags_match
|= FEMR_BASE_RECORD
;
1039 ctx
->flags_match
|= FEMR_NOT_BASE_RECORD
;
1042 ctx
->flags_match
|= FEMR_BASE_RECORD
;
1045 ctx
->flags_match
|= FEMR_FILE
;
1047 if (ctx
->flags_search
& FEMR_DIR
) {
1048 attr_ctx
= ntfs_attr_get_search_ctx(ctx
->inode
, NULL
);
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
);
1055 ntfs_log_error("Couldn't create a search context.\n");
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;
1064 ctx
->flags_match
|= FEMR_NOT_METADATA
; break;
1065 //ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num);
1072 ctx
->flags_match
|= FEMR_NOT_IN_USE
;
1074 ctx
->inode
= (ntfs_inode
*)calloc(1, sizeof(*ctx
->inode
));
1076 ntfs_log_error("Out of memory. Aborting.\n");
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
1088 mft
= ntfs_attr_open(ctx
->vol
->mft_ni
, AT_DATA
,
1091 ntfs_log_perror("Couldn't open $MFT/$DATA");
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
);
1100 ntfs_attr_close(mft
);
1104 ntfs_attr_close(mft
);
1107 if (ctx
->flags_match
& ctx
->flags_search
) {
1111 if (ntfs_inode_close(ctx
->inode
)) {
1112 ntfs_log_error("Error closing inode %llu.\n",
1113 (unsigned long long)ctx
->mft_num
);
1120 return (ctx
->inode
== NULL
);