summaryrefslogtreecommitdiff
path: root/ntfsprogs/utils.c (plain)
blob: c5e9e233b7c9e69348307806ffda178f153c5648
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 "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"
76
77const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n";
78const 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";
84
85static 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";
89
90static 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";
94
95static 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";
98
99static 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";
109
110static 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";
114
115static 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";
123
124static 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";
128
129/**
130 * utils_set_locale
131 */
132int utils_set_locale(void)
133{
134 const char *locale;
135
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;
144 }
145}
146
147/**
148 * linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with
149 * ntfs-3g's.
150 */
151int ntfs_mbstoucs_libntfscompat(const char *ins,
152 ntfschar **outs, int outs_len)
153{
154 if(!outs) {
155 errno = EINVAL;
156 return -1;
157 }
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.
162 */
163 ntfschar *tmpstr = NULL;
164 int tmpstr_len;
165
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;
178 }
179
180 if(tmpstr_len >= 0) {
181 /* The extra character is the \0 terminator. */
182 memcpy(*outs, tmpstr,
183 sizeof(ntfschar)*(tmpstr_len + 1));
184 }
185
186 free(tmpstr);
187 }
188
189 return tmpstr_len;
190 }
191 else
192 return ntfs_mbstoucs(ins, outs);
193}
194
195/**
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
199 *
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.
202 *
203 * Return: 1 Success, we can continue
204 * 0 Error, we cannot use this device
205 */
206int utils_valid_device(const char *name, int force)
207{
208 unsigned long mnt_flags = 0;
209 struct stat st;
210
211#if defined(HAVE_WINDOWS_H) | defined(__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;
218 }
219
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;
227 }
228
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;
237 }
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;
246 }
247 ntfs_log_warning("Forced to continue.\n");
248 }
249
250 return 1;
251}
252
253/**
254 * utils_mount_volume - Mount an NTFS volume
255 */
256ntfs_volume * utils_mount_volume(const char *device, unsigned long flags)
257{
258 ntfs_volume *vol;
259
260 if (!device) {
261 errno = EINVAL;
262 return NULL;
263 }
264
265 /* Porting notes:
266 *
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.
273 *
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.
278 */
279
280 if (!utils_valid_device(device, flags & NTFS_MNT_RECOVER))
281 return NULL;
282
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;
299 }
300
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;
310 }
311 ntfs_log_error("WARNING: Dirty volume mount was forced by the "
312 "'force' mount option.\n");
313 }
314 return vol;
315}
316
317/**
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
322 *
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.
325 *
326 * Suffix Description Multiple
327 * [tT] Terabytes 10^12
328 * [gG] Gigabytes 10^9
329 * [mM] Megabytes 10^6
330 * [kK] Kilobytes 10^3
331 *
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
336 *
337 * Return: 1 Success
338 * 0 Error, the string was malformed
339 */
340int utils_parse_size(const char *value, s64 *size, BOOL scale)
341{
342 long long result;
343 char *suffix = NULL;
344
345 if (!value || !size) {
346 errno = EINVAL;
347 return 0;
348 }
349
350 ntfs_log_debug("Parsing size '%s'.\n", value);
351
352 result = strtoll(value, &suffix, 0);
353 if (result < 0 || errno == ERANGE) {
354 ntfs_log_error("Invalid size '%s'.\n", value);
355 return 0;
356 }
357
358 if (!suffix) {
359 ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
360 return 0;
361 }
362
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;
374 }
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;
379 }
380 }
381
382 ntfs_log_debug("Parsed size = %lld.\n", result);
383 *size = result;
384 return 1;
385}
386
387/**
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
392 *
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.
396 *
397 * Return: 1 Success, a valid string was found
398 * 0 Error, the string was not a valid range
399 */
400int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale)
401{
402 s64 a, b;
403 char *middle;
404
405 if (!string || !start || !finish) {
406 errno = EINVAL;
407 return 0;
408 }
409
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;
417 }
418
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;
426 }
427 } else {
428 b = a;
429 }
430
431 ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b);
432
433 *start = a;
434 *finish = b;
435 return 1;
436}
437
438/**
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
442 *
443 * Using the search context to keep track, find the first/next occurrence of a
444 * given attribute type.
445 *
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.
448 *
449 * Return: Pointer Success, an attribute was found
450 * NULL Error, no matching attributes were found
451 */
452ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
453{
454 if (!ctx) {
455 errno = EINVAL;
456 return NULL;
457 }
458
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 */
462 }
463
464 ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type);
465 return ctx->attr;
466}
467
468/**
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
472 *
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.
475 *
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.
478 *
479 * Return: Pointer Success, an attribute was found
480 * NULL Error, no matching attributes were found
481 */
482ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft)
483{
484 ntfs_attr_search_ctx *ctx;
485 ATTR_RECORD *rec;
486
487 if (!mft) {
488 errno = EINVAL;
489 return NULL;
490 }
491
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;
496 }
497
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;
505}
506
507/**
508 * utils_inode_get_name
509 *
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
516 */
517#define max_path 20
518int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
519{
520 // XXX option: names = posix/win32 or dos
521 // flags: path, filename, or both
522
523
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;
532
533 if (!inode || !buffer) {
534 errno = EINVAL;
535 return 0;
536 }
537
538 vol = inode->vol;
539
540 //ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names));
541 memset(names, 0, sizeof(names));
542
543 for (i = 0; i < max_path; i++) {
544
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;
549 }
550
551 //ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no);
552
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));
557
558 if (attr->file_name_type > name_space) { //XXX find the ...
559 continue;
560 }
561
562 name_space = attr->file_name_type;
563 parent = le64_to_cpu(attr->parent_directory);
564
565 if (names[i]) {
566 free(names[i]);
567 names[i] = NULL;
568 }
569
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;
580 }
581
582 //ntfs_log_debug("names[%d] %s\n", i, names[i]);
583 //ntfs_log_debug("parent = %lld\n", MREF(parent));
584 }
585
586 ntfs_attr_put_search_ctx(ctx);
587
588 if (i > 0) /* Don't close the original inode */
589 ntfs_inode_close(inode);
590
591 if (MREF(parent) == FILE_root) { /* The root directory, stop. */
592 //ntfs_log_debug("inode 5\n");
593 break;
594 }
595
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;
601 }
602 }
603
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;
608 }
609
610 /* Assemble the names in the correct order. */
611 for (i = max_path; i >= 0; i--) {
612 if (!names[i])
613 continue;
614
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;
619 }
620
621 offset += len;
622 }
623
624 /* Free all the allocated memory */
625 for (i = 0; i < max_path; i++)
626 free(names[i]);
627
628 ntfs_log_debug("Pathname: %s\n", buffer);
629
630 return 1;
631}
632#undef max_path
633
634/**
635 * utils_attr_get_name
636 */
637int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize)
638{
639 int len, namelen;
640 char *name;
641 ATTR_DEF *attrdef;
642
643 // flags: attr, name, or both
644 if (!attr || !buffer) {
645 errno = EINVAL;
646 return 0;
647 }
648
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;
658 }
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>");
663 }
664
665 if (len >= bufsize) {
666 ntfs_log_error("Attribute type was truncated.\n");
667 return 0;
668 }
669
670 if (!attr->name_length) {
671 return 0;
672 }
673
674 buffer += len;
675 bufsize -= len;
676
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;
686 }
687
688 len = snprintf(buffer, bufsize, "(%s)", name);
689 free(name);
690
691 if (len >= bufsize) {
692 ntfs_log_error("Attribute name was truncated.\n");
693 return 0;
694 }
695
696 return 0;
697}
698
699/**
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
703 *
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.
707 *
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.
712 *
713 * NOTE: Be very carefull with shifts by 3 everywhere in this function.
714 *
715 * Return: 1 Cluster is in use
716 * 0 Cluster is free space
717 * -1 Error occurred
718 */
719int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
720{
721 static unsigned char buffer[512];
722 static long long bmplcn = -(sizeof(buffer) << 3);
723 int byte, bit;
724 ntfs_attr *attr;
725
726 if (!vol) {
727 errno = EINVAL;
728 return -1;
729 }
730
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;
739 }
740
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));
744
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;
750 }
751
752 ntfs_log_debug("Reloaded bitmap buffer.\n");
753 ntfs_attr_close(attr);
754 }
755
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);
761
762 return (buffer[byte] & bit);
763}
764
765/**
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)
769 *
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.
773 *
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.
777 *
778 * Return: 1 MFT Record is in use
779 * 0 MFT Record is unused
780 * -1 Error occurred
781 */
782int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
783{
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;
787
788 ntfs_log_trace("Entering.\n");
789
790 if (!vol) {
791 errno = EINVAL;
792 return -1;
793 }
794
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");
799
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));
803
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;
807 }
808
809 ntfs_log_debug("Reloaded bitmap buffer.\n");
810 }
811
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);
815
816 return (buffer[byte] & bit);
817}
818
819/**
820 * __metadata
821 */
822static int __metadata(ntfs_volume *vol, u64 num)
823{
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;
830
831 return 0;
832}
833
834/**
835 * utils_is_metadata - Determine if an inode represents a metadata file
836 * @inode: An ntfs inode to be tested
837 *
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.
841 *
842 * Return: 1 inode is a metadata file
843 * 0 inode is not a metadata file
844 * -1 Error occurred
845 */
846int utils_is_metadata(ntfs_inode *inode)
847{
848 ntfs_volume *vol;
849 ATTR_RECORD *rec;
850 FILE_NAME_ATTR *attr;
851 MFT_RECORD *file;
852 u64 num;
853
854 if (!inode) {
855 errno = EINVAL;
856 return -1;
857 }
858
859 vol = inode->vol;
860 if (!vol)
861 return -1;
862
863 num = inode->mft_no;
864 if (__metadata(vol, num) == 1)
865 return 1;
866
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;
872 }
873
874 rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
875 if (!rec)
876 return -1;
877
878 /* We know this will always be resident. */
879 attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset));
880
881 num = MREF_LE(attr->parent_directory);
882 if ((num != FILE_root) && (__metadata(vol, num) == 1))
883 return 1;
884
885 return 0;
886}
887
888/**
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
894 *
895 * Display a block of memory in a tradition hex-dump manner.
896 * Optionally the ascii part can be turned off.
897 *
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).
901 */
902void utils_dump_mem(void *buf, int start, int length, int flags)
903{
904 int off, i, s, e, col;
905 u8 *mem = buf;
906
907 s = start & ~15; // round down
908 e = (start + length + 15) & ~15; // round up
909
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);
928
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(" ");
936 }
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(".");
946 }
947 }
948 if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
949 ntfs_log_debug("\e[0m");
950 ntfs_log_debug("\n");
951 }
952}
953
954
955/**
956 * mft_get_search_ctx
957 */
958struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
959{
960 struct mft_search_ctx *ctx;
961
962 if (!vol) {
963 errno = EINVAL;
964 return NULL;
965 }
966
967 ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx);
968
969 ctx->mft_num = -1;
970 ctx->vol = vol;
971
972 return ctx;
973}
974
975/**
976 * mft_put_search_ctx
977 */
978void mft_put_search_ctx(struct mft_search_ctx *ctx)
979{
980 if (!ctx)
981 return;
982 if (ctx->inode)
983 ntfs_inode_close(ctx->inode);
984 free(ctx);
985}
986
987/**
988 * mft_next_record
989 */
990int mft_next_record(struct mft_search_ctx *ctx)
991{
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;
997
998 if (!ctx) {
999 errno = EINVAL;
1000 return -1;
1001 }
1002
1003 if (ctx->inode) {
1004 ntfs_inode_close(ctx->inode);
1005 ctx->inode = NULL;
1006 }
1007
1008 nr_mft_records = ctx->vol->mft_na->initialized_size >>
1009 ctx->vol->mft_record_size_bits;
1010
1011 for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) {
1012 int in_use;
1013
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;
1020 }
1021
1022 if (in_use) {
1023 ctx->flags_match |= FEMR_IN_USE;
1024
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;
1030 }
1031
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);
1035
1036 if (attr10)
1037 ctx->flags_match |= FEMR_BASE_RECORD;
1038 else
1039 ctx->flags_match |= FEMR_NOT_BASE_RECORD;
1040
1041 if (attr20)
1042 ctx->flags_match |= FEMR_BASE_RECORD;
1043
1044 if (attr80)
1045 ctx->flags_match |= FEMR_FILE;
1046
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;
1052
1053 ntfs_attr_put_search_ctx(attr_ctx);
1054 } else {
1055 ntfs_log_error("Couldn't create a search context.\n");
1056 return -1;
1057 }
1058 }
1059
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;
1067 }
1068
1069 } else { // !in_use
1070 ntfs_attr *mft;
1071
1072 ctx->flags_match |= FEMR_NOT_IN_USE;
1073
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;
1078 }
1079
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;
1086 }
1087
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;
1094 }
1095
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;
1102 }
1103
1104 ntfs_attr_close(mft);
1105 }
1106
1107 if (ctx->flags_match & ctx->flags_search) {
1108 break;
1109 }
1110
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;
1115 }
1116
1117 ctx->inode = NULL;
1118 }
1119
1120 return (ctx->inode == NULL);
1121}
1122
1123#ifdef HAVE_WINDOWS_H
1124
1125/*
1126 * Translate formats for older Windows
1127 *
1128 * Up to Windows XP, msvcrt.dll does not support long long format
1129 * specifications (%lld, %llx, etc). We have to translate them
1130 * to %I64.
1131 */
1132
1133char *ntfs_utils_reformat(char *out, int sz, const char *fmt)
1134{
1135 const char *f;
1136 char *p;
1137 int i;
1138 enum { F_INIT, F_PERCENT, F_FIRST } state;
1139
1140 i = 0;
1141 f = fmt;
1142 p = out;
1143 state = F_INIT;
1144 while (*f && ((i + 3) < sz)) {
1145 switch (state) {
1146 case F_INIT :
1147 if (*f == '%')
1148 state = F_PERCENT;
1149 *p++ = *f++;
1150 i++;
1151 break;
1152 case F_PERCENT :
1153 if (*f == 'l') {
1154 state = F_FIRST;
1155 f++;
1156 } else {
1157 if (((*f < '0') || (*f > '9'))
1158 && (*f != '*') && (*f != '-'))
1159 state = F_INIT;
1160 *p++ = *f++;
1161 i++;
1162 }
1163 break;
1164 case F_FIRST :
1165 if (*f == 'l') {
1166 *p++ = 'I';
1167 *p++ = '6';
1168 *p++ = '4';
1169 f++;
1170 i += 3;
1171 } else {
1172 *p++ = 'l';
1173 *p++ = *f++;
1174 i += 2;
1175 }
1176 state = F_INIT;
1177 break;
1178 }
1179 }
1180 *p++ = 0;
1181 return (out);
1182}
1183
1184#endif
1185