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