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