utils.c revision 9663:ace9a2ac3683
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 76const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n"; 77const char *ntfs_home = "Linux NTFS homepage: http://www.linux-ntfs.org\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 * utils_valid_device - Perform some safety checks on the device, before start 149 * @name: Full pathname of the device/file to work with 150 * @force: Continue regardless of problems 151 * 152 * Check that the name refers to a device and that is isn't already mounted. 153 * These checks can be overridden by using the force option. 154 * 155 * Return: 1 Success, we can continue 156 * 0 Error, we cannot use this device 157 */ 158int utils_valid_device(const char *name, int force) 159{ 160 unsigned long mnt_flags = 0; 161 struct stat st; 162 163#ifdef __CYGWIN32__ 164 /* FIXME: This doesn't work for Cygwin, so just return success. */ 165 return 1; 166#endif 167 if (!name) { 168 errno = EINVAL; 169 return 0; 170 } 171 172 if (stat(name, &st) == -1) { 173 if (errno == ENOENT) 174 ntfs_log_error("The device %s doesn't exist\n", name); 175 else 176 ntfs_log_perror("Error getting information about %s", 177 name); 178 return 0; 179 } 180 181 /* Make sure the file system is not mounted. */ 182 if (ntfs_check_if_mounted(name, &mnt_flags)) { 183 ntfs_log_perror("Failed to determine whether %s is mounted", 184 name); 185 if (!force) { 186 ntfs_log_error("Use the force option to ignore this " 187 "error.\n"); 188 return 0; 189 } 190 ntfs_log_warning("Forced to continue.\n"); 191 } else if (mnt_flags & NTFS_MF_MOUNTED) { 192 if (!force) { 193 ntfs_log_error("%s", opened_volume_msg); 194 ntfs_log_error("You can use force option to avoid this " 195 "check, but this is not recommended\n" 196 "and may lead to data corruption.\n"); 197 return 0; 198 } 199 ntfs_log_warning("Forced to continue.\n"); 200 } 201 202 return 1; 203} 204 205/** 206 * utils_mount_volume - Mount an NTFS volume 207 */ 208ntfs_volume * utils_mount_volume(const char *device, ntfs_mount_flags flags) 209{ 210 ntfs_volume *vol; 211 212 if (!device) { 213 errno = EINVAL; 214 return NULL; 215 } 216 217 if (!utils_valid_device(device, flags & NTFS_MNT_FORCE)) 218 return NULL; 219 220 vol = ntfs_mount(device, flags); 221 if (!vol) { 222 ntfs_log_perror("Failed to mount '%s'", device); 223 if (errno == EINVAL) 224 ntfs_log_error(invalid_ntfs_msg, device); 225 else if (errno == EIO) 226 ntfs_log_error("%s", corrupt_volume_msg); 227 else if (errno == EPERM) 228 ntfs_log_error("%s", hibernated_volume_msg); 229 else if (errno == EOPNOTSUPP) 230 ntfs_log_error("%s", unclean_journal_msg); 231 else if (errno == EBUSY) 232 ntfs_log_error("%s", opened_volume_msg); 233 else if (errno == ENXIO) 234 ntfs_log_error("%s", fakeraid_msg); 235 return NULL; 236 } 237 238 if (NVolWasDirty(vol)) { 239 if (!(flags & NTFS_MNT_FORCE)) { 240 ntfs_log_error("%s", dirty_volume_msg); 241 ntfs_umount(vol, FALSE); 242 return NULL; 243 } 244 ntfs_log_error("WARNING: Dirty volume mount was forced by the " 245 "'force' mount option.\n"); 246 } 247 return vol; 248} 249 250/** 251 * utils_parse_size - Convert a string representing a size 252 * @value: String to be parsed 253 * @size: Parsed size 254 * @scale: Whether or not to allow a suffix to scale the value 255 * 256 * Read a string and convert it to a number. Strings may be suffixed to scale 257 * them. Any number without a suffix is assumed to be in bytes. 258 * 259 * Suffix Description Multiple 260 * [tT] Terabytes 10^12 261 * [gG] Gigabytes 10^9 262 * [mM] Megabytes 10^6 263 * [kK] Kilobytes 10^3 264 * 265 * Notes: 266 * Only the first character of the suffix is read. 267 * The multipliers are decimal thousands, not binary: 1000, not 1024. 268 * If parse_size fails, @size will not be changed 269 * 270 * Return: 1 Success 271 * 0 Error, the string was malformed 272 */ 273int utils_parse_size(const char *value, s64 *size, BOOL scale) 274{ 275 long long result; 276 char *suffix = NULL; 277 278 if (!value || !size) { 279 errno = EINVAL; 280 return 0; 281 } 282 283 ntfs_log_debug("Parsing size '%s'.\n", value); 284 285 result = strtoll(value, &suffix, 0); 286 if (result < 0 || errno == ERANGE) { 287 ntfs_log_error("Invalid size '%s'.\n", value); 288 return 0; 289 } 290 291 if (!suffix) { 292 ntfs_log_error("Internal error, strtoll didn't return a suffix.\n"); 293 return 0; 294 } 295 296 if (scale) { 297 switch (suffix[0]) { 298 case 't': case 'T': result *= 1000; 299 case 'g': case 'G': result *= 1000; 300 case 'm': case 'M': result *= 1000; 301 case 'k': case 'K': result *= 1000; 302 case '-': case 0: 303 break; 304 default: 305 ntfs_log_error("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix); 306 return 0; 307 } 308 } else { 309 if ((suffix[0] != '-') && (suffix[0] != 0)) { 310 ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value); 311 return 0; 312 } 313 } 314 315 ntfs_log_debug("Parsed size = %lld.\n", result); 316 *size = result; 317 return 1; 318} 319 320/** 321 * utils_parse_range - Convert a string representing a range of numbers 322 * @string: The string to be parsed 323 * @start: The beginning of the range will be stored here 324 * @finish: The end of the range will be stored here 325 * 326 * Read a string of the form n-m. If the lower end is missing, zero will be 327 * substituted. If the upper end is missing LONG_MAX will be used. If the 328 * string cannot be parsed correctly, @start and @finish will not be changed. 329 * 330 * Return: 1 Success, a valid string was found 331 * 0 Error, the string was not a valid range 332 */ 333int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale) 334{ 335 s64 a, b; 336 char *middle; 337 338 if (!string || !start || !finish) { 339 errno = EINVAL; 340 return 0; 341 } 342 343 middle = strchr(string, '-'); 344 if (string == middle) { 345 ntfs_log_debug("Range has no beginning, defaulting to 0.\n"); 346 a = 0; 347 } else { 348 if (!utils_parse_size(string, &a, scale)) 349 return 0; 350 } 351 352 if (middle) { 353 if (middle[1] == 0) { 354 b = LONG_MAX; // XXX ULLONG_MAX 355 ntfs_log_debug("Range has no end, defaulting to %lld.\n", b); 356 } else { 357 if (!utils_parse_size(middle+1, &b, scale)) 358 return 0; 359 } 360 } else { 361 b = a; 362 } 363 364 ntfs_log_debug("Range '%s' = %lld - %lld\n", string, a, b); 365 366 *start = a; 367 *finish = b; 368 return 1; 369} 370 371/** 372 * find_attribute - Find an attribute of the given type 373 * @type: An attribute type, e.g. AT_FILE_NAME 374 * @ctx: A search context, created using ntfs_get_attr_search_ctx 375 * 376 * Using the search context to keep track, find the first/next occurrence of a 377 * given attribute type. 378 * 379 * N.B. This will return a pointer into @mft. As long as the search context 380 * has been created without an inode, it won't overflow the buffer. 381 * 382 * Return: Pointer Success, an attribute was found 383 * NULL Error, no matching attributes were found 384 */ 385ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) 386{ 387 if (!ctx) { 388 errno = EINVAL; 389 return NULL; 390 } 391 392 if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) { 393 ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", type); 394 return NULL; /* None / no more of that type */ 395 } 396 397 ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", type); 398 return ctx->attr; 399} 400 401/** 402 * find_first_attribute - Find the first attribute of a given type 403 * @type: An attribute type, e.g. AT_FILE_NAME 404 * @mft: A buffer containing a raw MFT record 405 * 406 * Search through a raw MFT record for an attribute of a given type. 407 * The return value is a pointer into the MFT record that was supplied. 408 * 409 * N.B. This will return a pointer into @mft. The pointer won't stray outside 410 * the buffer, since we created the search context without an inode. 411 * 412 * Return: Pointer Success, an attribute was found 413 * NULL Error, no matching attributes were found 414 */ 415ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) 416{ 417 ntfs_attr_search_ctx *ctx; 418 ATTR_RECORD *rec; 419 420 if (!mft) { 421 errno = EINVAL; 422 return NULL; 423 } 424 425 ctx = ntfs_attr_get_search_ctx(NULL, mft); 426 if (!ctx) { 427 ntfs_log_error("Couldn't create a search context.\n"); 428 return NULL; 429 } 430 431 rec = find_attribute(type, ctx); 432 ntfs_attr_put_search_ctx(ctx); 433 if (rec) 434 ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", type); 435 else 436 ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", type); 437 return rec; 438} 439 440/** 441 * utils_inode_get_name 442 * 443 * using inode 444 * get filename 445 * add name to list 446 * get parent 447 * if parent is 5 (/) stop 448 * get inode of parent 449 */ 450#define max_path 20 451int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) 452{ 453 // XXX option: names = posix/win32 or dos 454 // flags: path, filename, or both 455 456 457 ntfs_volume *vol; 458 ntfs_attr_search_ctx *ctx; 459 ATTR_RECORD *rec; 460 FILE_NAME_ATTR *attr; 461 int name_space; 462 MFT_REF parent = FILE_root; 463 char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger? 464 int i, len, offset = 0; 465 466 if (!inode || !buffer) { 467 errno = EINVAL; 468 return 0; 469 } 470 471 vol = inode->vol; 472 473 //ntfs_log_debug("sizeof(char*) = %d, sizeof(names) = %d\n", sizeof(char*), sizeof(names)); 474 memset(names, 0, sizeof(names)); 475 476 for (i = 0; i < max_path; i++) { 477 478 ctx = ntfs_attr_get_search_ctx(inode, NULL); 479 if (!ctx) { 480 ntfs_log_error("Couldn't create a search context.\n"); 481 return 0; 482 } 483 484 //ntfs_log_debug("i = %d, inode = %p (%lld)\n", i, inode, inode->mft_no); 485 486 name_space = 4; 487 while ((rec = find_attribute(AT_FILE_NAME, ctx))) { 488 /* We know this will always be resident. */ 489 attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->u.res.value_offset)); 490 491 if (attr->file_name_type > name_space) { //XXX find the ... 492 continue; 493 } 494 495 name_space = attr->file_name_type; 496 parent = le64_to_cpu(attr->parent_directory); 497 498 if (names[i]) { 499 free(names[i]); 500 names[i] = NULL; 501 } 502 503 if (ntfs_ucstombs(attr->file_name, attr->file_name_length, 504 &names[i], 0) < 0) { 505 char *temp; 506 ntfs_log_error("Couldn't translate filename to current locale.\n"); 507 temp = ntfs_malloc(30); 508 if (!temp) 509 return 0; 510 snprintf(temp, 30, "<MFT%llu>", (unsigned 511 long long)inode->mft_no); 512 names[i] = temp; 513 } 514 515 //ntfs_log_debug("names[%d] %s\n", i, names[i]); 516 //ntfs_log_debug("parent = %lld\n", MREF(parent)); 517 } 518 519 ntfs_attr_put_search_ctx(ctx); 520 521 if (i > 0) /* Don't close the original inode */ 522 ntfs_inode_close(inode); 523 524 if (MREF(parent) == FILE_root) { /* The root directory, stop. */ 525 //ntfs_log_debug("inode 5\n"); 526 break; 527 } 528 529 inode = ntfs_inode_open(vol, parent); 530 if (!inode) { 531 ntfs_log_error("Couldn't open inode %llu.\n", 532 (unsigned long long)MREF(parent)); 533 break; 534 } 535 } 536 537 if (i >= max_path) { 538 /* If we get into an infinite loop, we'll end up here. */ 539 ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path); 540 return 0; 541 } 542 543 /* Assemble the names in the correct order. */ 544 for (i = max_path; i >= 0; i--) { 545 if (!names[i]) 546 continue; 547 548 len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]); 549 if (len >= (bufsize - offset)) { 550 ntfs_log_error("Pathname was truncated.\n"); 551 break; 552 } 553 554 offset += len; 555 } 556 557 /* Free all the allocated memory */ 558 for (i = 0; i < max_path; i++) 559 free(names[i]); 560 561 ntfs_log_debug("Pathname: %s\n", buffer); 562 563 return 1; 564} 565#undef max_path 566 567/** 568 * utils_attr_get_name 569 */ 570int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize) 571{ 572 int len, namelen; 573 char *name; 574 ATTR_DEF *attrdef; 575 576 // flags: attr, name, or both 577 if (!attr || !buffer) { 578 errno = EINVAL; 579 return 0; 580 } 581 582 attrdef = ntfs_attr_find_in_attrdef(vol, attr->type); 583 if (attrdef) { 584 name = NULL; 585 namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name)); 586 if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) { 587 ntfs_log_error("Couldn't translate attribute type to " 588 "current locale.\n"); 589 // <UNKNOWN>? 590 return 0; 591 } 592 len = snprintf(buffer, bufsize, "%s", name); 593 } else { 594 ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type); 595 len = snprintf(buffer, bufsize, "<UNKNOWN>"); 596 } 597 598 if (len >= bufsize) { 599 ntfs_log_error("Attribute type was truncated.\n"); 600 return 0; 601 } 602 603 if (!attr->name_length) { 604 return 0; 605 } 606 607 buffer += len; 608 bufsize -= len; 609 610 name = NULL; 611 namelen = attr->name_length; 612 if (ntfs_ucstombs((ntfschar *)((char *)attr + le16_to_cpu( 613 attr->name_offset)), namelen, &name, 0) < 0) { 614 ntfs_log_error("Couldn't translate attribute name to current " 615 "locale.\n"); 616 // <UNKNOWN>? 617 len = snprintf(buffer, bufsize, "<UNKNOWN>"); 618 return 0; 619 } 620 621 len = snprintf(buffer, bufsize, "(%s)", name); 622 free(name); 623 624 if (len >= bufsize) { 625 ntfs_log_error("Attribute name was truncated.\n"); 626 return 0; 627 } 628 629 return 0; 630} 631 632/** 633 * utils_cluster_in_use - Determine if a cluster is in use 634 * @vol: An ntfs volume obtained from ntfs_mount 635 * @lcn: The Logical Cluster Number to test 636 * 637 * The metadata file $Bitmap has one binary bit representing each cluster on 638 * disk. The bit will be set for each cluster that is in use. The function 639 * reads the relevant part of $Bitmap into a buffer and tests the bit. 640 * 641 * This function has a static buffer in which it caches a section of $Bitmap. 642 * If the lcn, being tested, lies outside the range, the buffer will be 643 * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the 644 * buffer. 645 * 646 * NOTE: Be very carefull with shifts by 3 everywhere in this function. 647 * 648 * Return: 1 Cluster is in use 649 * 0 Cluster is free space 650 * -1 Error occurred 651 */ 652int utils_cluster_in_use(ntfs_volume *vol, long long lcn) 653{ 654 static unsigned char buffer[512]; 655 static long long bmplcn = -(sizeof(buffer) << 3); 656 int byte, bit; 657 ntfs_attr *attr; 658 659 if (!vol) { 660 errno = EINVAL; 661 return -1; 662 } 663 664 /* Does lcn lie in the section of $Bitmap we already have cached? */ 665 if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof(buffer) << 3)))) { 666 ntfs_log_debug("Bit lies outside cache.\n"); 667 attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); 668 if (!attr) { 669 ntfs_log_perror("Couldn't open $Bitmap"); 670 return -1; 671 } 672 673 /* Mark the buffer as in use, in case the read is shorter. */ 674 memset(buffer, 0xFF, sizeof(buffer)); 675 bmplcn = lcn & (~((sizeof(buffer) << 3) - 1)); 676 677 if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer), 678 buffer) < 0) { 679 ntfs_log_perror("Couldn't read $Bitmap"); 680 ntfs_attr_close(attr); 681 return -1; 682 } 683 684 ntfs_log_debug("Reloaded bitmap buffer.\n"); 685 ntfs_attr_close(attr); 686 } 687 688 bit = 1 << (lcn & 7); 689 byte = (lcn >> 3) & (sizeof(buffer) - 1); 690 ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, " 691 "in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & 692 bit); 693 694 return (buffer[byte] & bit); 695} 696 697/** 698 * utils_mftrec_in_use - Determine if a MFT Record is in use 699 * @vol: An ntfs volume obtained from ntfs_mount 700 * @mref: MFT Reference (inode number) 701 * 702 * The metadata file $BITMAP has one binary bit representing each record in the 703 * MFT. The bit will be set for each record that is in use. The function 704 * reads the relevant part of $BITMAP into a buffer and tests the bit. 705 * 706 * This function has a static buffer in which it caches a section of $BITMAP. 707 * If the mref, being tested, lies outside the range, the buffer will be 708 * refreshed. 709 * 710 * Return: 1 MFT Record is in use 711 * 0 MFT Record is unused 712 * -1 Error occurred 713 */ 714int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref) 715{ 716 static u8 buffer[512]; 717 static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */ 718 int byte, bit; 719 720 ntfs_log_trace("Entering.\n"); 721 722 if (!vol) { 723 errno = EINVAL; 724 return -1; 725 } 726 727 /* Does mref lie in the section of $Bitmap we already have cached? */ 728 if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref + 729 (sizeof(buffer) << 3)))) { 730 ntfs_log_debug("Bit lies outside cache.\n"); 731 732 /* Mark the buffer as not in use, in case the read is shorter. */ 733 memset(buffer, 0, sizeof(buffer)); 734 bmpmref = mref & (~((sizeof(buffer) << 3) - 1)); 735 736 if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) { 737 ntfs_log_perror("Couldn't read $MFT/$BITMAP"); 738 return -1; 739 } 740 741 ntfs_log_debug("Reloaded bitmap buffer.\n"); 742 } 743 744 bit = 1 << (mref & 7); 745 byte = (mref >> 3) & (sizeof(buffer) - 1); 746 ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, in use %d\n", mref, bmpmref, byte, bit, buffer[byte] & bit); 747 748 return (buffer[byte] & bit); 749} 750 751/** 752 * __metadata 753 */ 754static int __metadata(ntfs_volume *vol, u64 num) 755{ 756 if (num <= FILE_UpCase) 757 return 1; 758 if (!vol) 759 return -1; 760 if ((vol->major_ver == 3) && (num == FILE_Extend)) 761 return 1; 762 763 return 0; 764} 765 766/** 767 * utils_is_metadata - Determine if an inode represents a metadata file 768 * @inode: An ntfs inode to be tested 769 * 770 * A handful of files in the volume contain filesystem data - metadata. 771 * They can be identified by their inode number (offset in MFT/$DATA) or by 772 * their parent. 773 * 774 * Return: 1 inode is a metadata file 775 * 0 inode is not a metadata file 776 * -1 Error occurred 777 */ 778int utils_is_metadata(ntfs_inode *inode) 779{ 780 ntfs_volume *vol; 781 ATTR_RECORD *rec; 782 FILE_NAME_ATTR *attr; 783 MFT_RECORD *file; 784 u64 num; 785 786 if (!inode) { 787 errno = EINVAL; 788 return -1; 789 } 790 791 vol = inode->vol; 792 if (!vol) 793 return -1; 794 795 num = inode->mft_no; 796 if (__metadata(vol, num) == 1) 797 return 1; 798 799 file = inode->mrec; 800 if (file && (file->base_mft_record != 0)) { 801 num = MREF_LE(file->base_mft_record); 802 if (__metadata(vol, num) == 1) 803 return 1; 804 } 805 file = inode->mrec; 806 807 rec = find_first_attribute(AT_FILE_NAME, inode->mrec); 808 if (!rec) 809 return -1; 810 811 /* We know this will always be resident. */ 812 attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->u.res.value_offset)); 813 814 num = MREF_LE(attr->parent_directory); 815 if ((num != FILE_root) && (__metadata(vol, num) == 1)) 816 return 1; 817 818 return 0; 819} 820 821/** 822 * utils_dump_mem - Display a block of memory in hex and ascii 823 * @buf: Buffer to be displayed 824 * @start: Offset into @buf to start from 825 * @length: Number of bytes to display 826 * @flags: Options to change the style of the output 827 * 828 * Display a block of memory in a tradition hex-dump manner. 829 * Optionally the ascii part can be turned off. 830 * 831 * The flags, described fully in utils.h, default to 0 (DM_DEFAULTS). 832 * Examples are: DM_INDENT (indent the output by one tab); DM_RED (colour the 833 * output); DM_NO_ASCII (only print the hex values). 834 */ 835void utils_dump_mem(void *buf, int start, int length, int flags) 836{ 837 int off, i, s, e, col; 838 u8 *mem = buf; 839 840 s = start & ~15; // round down 841 e = (start + length + 15) & ~15; // round up 842 843 for (off = s; off < e; off += 16) { 844 col = 30; 845 if (flags & DM_RED) 846 col += 1; 847 if (flags & DM_GREEN) 848 col += 2; 849 if (flags & DM_BLUE) 850 col += 4; 851 if (flags & DM_INDENT) 852 ntfs_log_debug("\t"); 853 if (flags & DM_BOLD) 854 ntfs_log_debug("\e[01m"); 855 if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) 856 ntfs_log_debug("\e[%dm", col); 857 if (off == s) 858 ntfs_log_debug("%6.6x ", start); 859 else 860 ntfs_log_debug("%6.6x ", off); 861 862 for (i = 0; i < 16; i++) { 863 if ((i == 8) && (!(flags & DM_NO_DIVIDER))) 864 ntfs_log_debug(" -"); 865 if (((off+i) >= start) && ((off+i) < (start+length))) 866 ntfs_log_debug(" %02X", mem[off+i]); 867 else 868 ntfs_log_debug(" "); 869 } 870 if (!(flags & DM_NO_ASCII)) { 871 ntfs_log_debug(" "); 872 for (i = 0; i < 16; i++) { 873 if (((off+i) < start) || ((off+i) >= (start+length))) 874 ntfs_log_debug(" "); 875 else if (isprint(mem[off + i])) 876 ntfs_log_debug("%c", mem[off + i]); 877 else 878 ntfs_log_debug("."); 879 } 880 } 881 if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD)) 882 ntfs_log_debug("\e[0m"); 883 ntfs_log_debug("\n"); 884 } 885} 886 887 888/** 889 * mft_get_search_ctx 890 */ 891struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol) 892{ 893 struct mft_search_ctx *ctx; 894 895 if (!vol) { 896 errno = EINVAL; 897 return NULL; 898 } 899 900 ctx = calloc(1, sizeof *ctx); 901 902 ctx->mft_num = -1; 903 ctx->vol = vol; 904 905 return ctx; 906} 907 908/** 909 * mft_put_search_ctx 910 */ 911void mft_put_search_ctx(struct mft_search_ctx *ctx) 912{ 913 if (!ctx) 914 return; 915 if (ctx->inode) 916 ntfs_inode_close(ctx->inode); 917 free(ctx); 918} 919 920/** 921 * mft_next_record 922 */ 923int mft_next_record(struct mft_search_ctx *ctx) 924{ 925 s64 nr_mft_records; 926 ATTR_RECORD *attr10 = NULL; 927 ATTR_RECORD *attr20 = NULL; 928 ATTR_RECORD *attr80 = NULL; 929 ntfs_attr_search_ctx *attr_ctx; 930 931 if (!ctx) { 932 errno = EINVAL; 933 return -1; 934 } 935 936 if (ctx->inode) { 937 ntfs_inode_close(ctx->inode); 938 ctx->inode = NULL; 939 } 940 941 nr_mft_records = ctx->vol->mft_na->initialized_size >> 942 ctx->vol->mft_record_size_bits; 943 944 for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) { 945 int in_use; 946 947 ctx->flags_match = 0; 948 in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num); 949 if (in_use == -1) { 950 ntfs_log_error("Error reading inode %llu. Aborting.\n", 951 (unsigned long long)ctx->mft_num); 952 return -1; 953 } 954 955 if (in_use) { 956 ctx->flags_match |= FEMR_IN_USE; 957 958 ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num); 959 if (ctx->inode == NULL) { 960 ntfs_log_error("Error reading inode %llu.\n", (unsigned 961 long long) ctx->mft_num); 962 continue; 963 } 964 965 attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec); 966 attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, ctx->inode->mrec); 967 attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec); 968 969 if (attr10) 970 ctx->flags_match |= FEMR_BASE_RECORD; 971 else 972 ctx->flags_match |= FEMR_NOT_BASE_RECORD; 973 974 if (attr20) 975 ctx->flags_match |= FEMR_BASE_RECORD; 976 977 if (attr80) 978 ctx->flags_match |= FEMR_FILE; 979 980 if (ctx->flags_search & FEMR_DIR) { 981 attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL); 982 if (attr_ctx) { 983 if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0) 984 ctx->flags_match |= FEMR_DIR; 985 986 ntfs_attr_put_search_ctx(attr_ctx); 987 } else { 988 ntfs_log_error("Couldn't create a search context.\n"); 989 return -1; 990 } 991 } 992 993 switch (utils_is_metadata(ctx->inode)) { 994 case 1: ctx->flags_match |= FEMR_METADATA; break; 995 case 0: ctx->flags_match |= FEMR_NOT_METADATA; break; 996 default: 997 ctx->flags_match |= FEMR_NOT_METADATA; break; 998 //ntfs_log_error("Error reading inode %lld.\n", ctx->mft_num); 999 //return -1; 1000 } 1001 1002 } else { // !in_use 1003 ntfs_attr *mft; 1004 1005 ctx->flags_match |= FEMR_NOT_IN_USE; 1006 1007 ctx->inode = calloc(1, sizeof(*ctx->inode)); 1008 if (!ctx->inode) { 1009 ntfs_log_error("Out of memory. Aborting.\n"); 1010 return -1; 1011 } 1012 1013 ctx->inode->mft_no = ctx->mft_num; 1014 ctx->inode->vol = ctx->vol; 1015 ctx->inode->mrec = ntfs_malloc(ctx->vol->mft_record_size); 1016 if (!ctx->inode->mrec) { 1017 free(ctx->inode); // == ntfs_inode_close 1018 return -1; 1019 } 1020 1021 mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA, 1022 AT_UNNAMED, 0); 1023 if (!mft) { 1024 ntfs_log_perror("Couldn't open $MFT/$DATA"); 1025 // free / close 1026 return -1; 1027 } 1028 1029 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) { 1030 ntfs_log_perror("Couldn't read MFT Record %llu", 1031 (unsigned long long) ctx->mft_num); 1032 // free / close 1033 ntfs_attr_close(mft); 1034 return -1; 1035 } 1036 1037 ntfs_attr_close(mft); 1038 } 1039 1040 if (ctx->flags_match & ctx->flags_search) { 1041 break; 1042 } 1043 1044 if (ntfs_inode_close(ctx->inode)) { 1045 ntfs_log_error("Error closing inode %llu.\n", 1046 (unsigned long long)ctx->mft_num); 1047 return -errno; 1048 } 1049 1050 ctx->inode = NULL; 1051 } 1052 1053 return (ctx->inode == NULL); 1054} 1055 1056 1057