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