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