#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#ifdef HAVE_LIBINTL_H
#include <libintl.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#include "utils.h"
#include "types.h"
#include "volume.h"
#include "debug.h"
#include "dir.h"
#include "logging.h"
#include "misc.h"
const char *ntfs_bugs = "Developers' email address: " NTFS_DEV_LIST "\n";
const char *ntfs_gpl = "This program is free software, released under the GNU "
"General Public License\nand you are welcome to redistribute it under "
"certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for "
"details read the GNU General Public License to be\nfound in the file "
"\"COPYING\" distributed with this program, or online at:\n"
"http://www.gnu.org/copyleft/gpl.html\n";
static const char *invalid_ntfs_msg =
"The device '%s' doesn't have a valid NTFS.\n"
"Maybe you selected the wrong device? Or the whole disk instead of a\n"
"partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n";
static const char *corrupt_volume_msg =
"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n"
"The usage of the /f parameter is very IMPORTANT! No modification was\n"
"made to NTFS by this software.\n";
static const char *hibernated_volume_msg =
"The NTFS partition is hibernated. Please resume Windows and turned it \n"
"off properly, so mounting could be done safely.\n";
static const char *unclean_journal_msg =
"Access is denied because the NTFS journal file is unclean. Choices are:\n"
" A) Shutdown Windows properly.\n"
" B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n"
" notification area before disconnecting the device.\n"
" C) Use 'Eject' from Windows Explorer to safely remove the device.\n"
" D) If you ran chkdsk previously then boot Windows again which will\n"
" automatically initialize the journal.\n"
" E) Submit 'force' option (WARNING: This solution it not recommended).\n"
" F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n";
static const char *opened_volume_msg =
"Access is denied because the NTFS volume is already exclusively opened.\n"
"The volume may be already mounted, or another software may use it which\n"
"could be identified for example by the help of the 'fuser' command.\n";
static const char *dirty_volume_msg =
"Volume is scheduled for check.\n"
"Please boot into Windows TWICE, or use the 'force' option.\n"
"NOTE: If you had not scheduled check and last time accessed this volume\n"
"using ntfsmount and shutdown system properly, then init scripts in your\n"
"distribution are broken. Please report to your distribution developers\n"
"(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n"
"shutdown instead of proper umount.\n";
static const char *fakeraid_msg =
"You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n"
"different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n"
"to mount NTFS. Please see the 'dmraid' documentation for help.\n";
#ifndef __HAIKU__
int utils_set_locale(void)
{
const char *locale;
locale = setlocale(LC_ALL, "");
if (!locale) {
locale = setlocale(LC_ALL, NULL);
ntfs_log_error("Failed to set locale, using default '%s'.\n",
locale);
return 1;
} else {
return 0;
}
}
#endif
int ntfs_mbstoucs_libntfscompat(const char *ins,
ntfschar **outs, int outs_len)
{
if(!outs) {
errno = EINVAL;
return -1;
}
else if(*outs != NULL) {
ntfschar *tmpstr = NULL;
int tmpstr_len;
tmpstr_len = ntfs_mbstoucs(ins, &tmpstr);
if(tmpstr_len >= 0) {
if((tmpstr_len + 1) > outs_len) {
ntfschar *re_outs = realloc(*outs,
sizeof(ntfschar)*(tmpstr_len + 1));
if(!re_outs)
tmpstr_len = -1;
else
*outs = re_outs;
}
if(tmpstr_len >= 0) {
memcpy(*outs, tmpstr,
sizeof(ntfschar)*(tmpstr_len + 1));
}
free(tmpstr);
}
return tmpstr_len;
}
else
return ntfs_mbstoucs(ins, outs);
}
int utils_valid_device(const char *name, int force)
{
unsigned long mnt_flags = 0;
struct stat st;
#if defined(HAVE_WINDOWS_H) | defined(__CYGWIN32__)
return 1;
#endif
if (!name) {
errno = EINVAL;
return 0;
}
if (stat(name, &st) == -1) {
if (errno == ENOENT)
ntfs_log_error("The device %s doesn't exist\n", name);
else
ntfs_log_perror("Error getting information about %s",
name);
return 0;
}
if (ntfs_check_if_mounted(name, &mnt_flags)) {
ntfs_log_perror("Failed to determine whether %s is mounted",
name);
if (!force) {
ntfs_log_error("Use the force option to ignore this "
"error.\n");
return 0;
}
ntfs_log_warning("Forced to continue.\n");
} else if (mnt_flags & NTFS_MF_MOUNTED) {
if (!force) {
ntfs_log_error("%s", opened_volume_msg);
ntfs_log_error("You can use force option to avoid this "
"check, but this is not recommended\n"
"and may lead to data corruption.\n");
return 0;
}
ntfs_log_warning("Forced to continue.\n");
}
return 1;
}
ntfs_volume * utils_mount_volume(const char *device, unsigned long flags)
{
ntfs_volume *vol;
if (!device) {
errno = EINVAL;
return NULL;
}
if (!utils_valid_device(device, flags & NTFS_MNT_RECOVER))
return NULL;
vol = ntfs_mount(device, flags);
if (!vol) {
ntfs_log_perror("Failed to mount '%s'", device);
if (errno == EINVAL)
ntfs_log_error(invalid_ntfs_msg, device);
else if (errno == EIO)
ntfs_log_error("%s", corrupt_volume_msg);
else if (errno == EPERM)
ntfs_log_error("%s", hibernated_volume_msg);
else if (errno == EOPNOTSUPP)
ntfs_log_error("%s", unclean_journal_msg);
else if (errno == EBUSY)
ntfs_log_error("%s", opened_volume_msg);
else if (errno == ENXIO)
ntfs_log_error("%s", fakeraid_msg);
return NULL;
}
if (vol->flags & VOLUME_IS_DIRTY) {
if (!(flags & NTFS_MNT_RECOVER)) {
ntfs_log_error("%s", dirty_volume_msg);
ntfs_umount(vol, FALSE);
return NULL;
}
ntfs_log_error("WARNING: Dirty volume mount was forced by the "
"'force' mount option.\n");
}
return vol;
}
int utils_parse_size(const char *value, s64 *size, BOOL scale)
{
long long result;
char *suffix = NULL;
if (!value || !size) {
errno = EINVAL;
return 0;
}
ntfs_log_debug("Parsing size '%s'.\n", value);
result = strtoll(value, &suffix, 0);
if (result < 0 || errno == ERANGE) {
ntfs_log_error("Invalid size '%s'.\n", value);
return 0;
}
if (!suffix) {
ntfs_log_error("Internal error, strtoll didn't return a suffix.\n");
return 0;
}
if (scale) {
switch (suffix[0]) {
case 't': case 'T': result *= 1000;
case 'g': case 'G': result *= 1000;
case 'm': case 'M': result *= 1000;
case 'k': case 'K': result *= 1000;
case '-': case 0:
break;
default:
ntfs_log_error("Invalid size suffix '%s'. Use T, G, M, or K.\n", suffix);
return 0;
}
} else {
if ((suffix[0] != '-') && (suffix[0] != 0)) {
ntfs_log_error("Invalid number '%.*s'.\n", (int)(suffix - value + 1), value);
return 0;
}
}
ntfs_log_debug("Parsed size = %lld.\n", result);
*size = result;
return 1;
}
int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale)
{
s64 a, b;
char *middle;
if (!string || !start || !finish) {
errno = EINVAL;
return 0;
}
middle = strchr(string, '-');
if (string == middle) {
ntfs_log_debug("Range has no beginning, defaulting to 0.\n");
a = 0;
} else {
if (!utils_parse_size(string, &a, scale))
return 0;
}
if (middle) {
if (middle[1] == 0) {
b = LONG_MAX;
ntfs_log_debug("Range has no end, defaulting to "
"%lld.\n", (long long)b);
} else {
if (!utils_parse_size(middle+1, &b, scale))
return 0;
}
} else {
b = a;
}
ntfs_log_debug("Range '%s' = %lld - %lld\n", string, (long long)a,
(long long)b);
*start = a;
*finish = b;
return 1;
}
ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
{
if (!ctx) {
errno = EINVAL;
return NULL;
}
if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) {
ntfs_log_debug("find_attribute didn't find an attribute of type: 0x%02x.\n", le32_to_cpu(type));
return NULL;
}
ntfs_log_debug("find_attribute found an attribute of type: 0x%02x.\n", le32_to_cpu(type));
return ctx->attr;
}
ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft)
{
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *rec;
if (!mft) {
errno = EINVAL;
return NULL;
}
ctx = ntfs_attr_get_search_ctx(NULL, mft);
if (!ctx) {
ntfs_log_error("Couldn't create a search context.\n");
return NULL;
}
rec = find_attribute(type, ctx);
ntfs_attr_put_search_ctx(ctx);
if (rec)
ntfs_log_debug("find_first_attribute: found attr of type 0x%02x.\n", le32_to_cpu(type));
else
ntfs_log_debug("find_first_attribute: didn't find attr of type 0x%02x.\n", le32_to_cpu(type));
return rec;
}
#define max_path 20
int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
{
ntfs_volume *vol;
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *rec;
FILE_NAME_ATTR *attr;
int name_space;
MFT_REF parent = FILE_root;
char *names[max_path + 1];
int i, len, offset = 0;
if (!inode || !buffer) {
errno = EINVAL;
return 0;
}
vol = inode->vol;
memset(names, 0, sizeof(names));
for (i = 0; i < max_path; i++) {
ctx = ntfs_attr_get_search_ctx(inode, NULL);
if (!ctx) {
ntfs_log_error("Couldn't create a search context.\n");
return 0;
}
name_space = 4;
while ((rec = find_attribute(AT_FILE_NAME, ctx))) {
attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset));
if (attr->file_name_type > name_space) {
continue;
}
name_space = attr->file_name_type;
parent = le64_to_cpu(attr->parent_directory);
if (names[i]) {
free(names[i]);
names[i] = NULL;
}
if (ntfs_ucstombs(attr->file_name, attr->file_name_length,
&names[i], 0) < 0) {
char *temp;
ntfs_log_error("Couldn't translate filename to current locale.\n");
temp = ntfs_malloc(30);
if (!temp)
return 0;
snprintf(temp, 30, "<MFT%llu>", (unsigned
long long)inode->mft_no);
names[i] = temp;
}
}
ntfs_attr_put_search_ctx(ctx);
if (i > 0)
ntfs_inode_close(inode);
if (MREF(parent) == FILE_root) {
break;
}
inode = ntfs_inode_open(vol, parent);
if (!inode) {
ntfs_log_error("Couldn't open inode %llu.\n",
(unsigned long long)MREF(parent));
break;
}
}
if (i >= max_path) {
ntfs_log_error("The directory structure is too deep (over %d) nested directories.\n", max_path);
return 0;
}
for (i = max_path; i >= 0; i--) {
if (!names[i])
continue;
len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]);
if (len >= (bufsize - offset)) {
ntfs_log_error("Pathname was truncated.\n");
#ifdef __HAIKU__
for (i = 0; i < max_path; i++)
free(names[i]);
return 0;
#else
break;
#endif
}
offset += len;
}
for (i = 0; i < max_path; i++)
free(names[i]);
ntfs_log_debug("Pathname: %s\n", buffer);
return 1;
}
#undef max_path
int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize)
{
int len, namelen;
char *name;
ATTR_DEF *attrdef;
if (!attr || !buffer) {
errno = EINVAL;
return 0;
}
attrdef = ntfs_attr_find_in_attrdef(vol, attr->type);
if (attrdef) {
name = NULL;
namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name));
if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) {
ntfs_log_error("Couldn't translate attribute type to "
"current locale.\n");
return 0;
}
len = snprintf(buffer, bufsize, "%s", name);
} else {
ntfs_log_error("Unknown attribute type 0x%02x\n", le32_to_cpu(attr->type));
len = snprintf(buffer, bufsize, "<UNKNOWN>");
}
if (len >= bufsize) {
ntfs_log_error("Attribute type was truncated.\n");
return 0;
}
if (!attr->name_length) {
return 0;
}
buffer += len;
bufsize -= len;
name = NULL;
namelen = attr->name_length;
if (ntfs_ucstombs((ntfschar *)((char *)attr
+ le16_to_cpu(attr->name_offset)),
namelen, &name, 0) < 0) {
ntfs_log_error("Couldn't translate attribute name to current "
"locale.\n");
len = snprintf(buffer, bufsize, "<UNKNOWN>");
return 0;
}
len = snprintf(buffer, bufsize, "(%s)", name);
free(name);
if (len >= bufsize) {
ntfs_log_error("Attribute name was truncated.\n");
return 0;
}
return 0;
}
int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
{
static unsigned char buffer[512];
static long long bmplcn = -(sizeof(buffer) << 3);
int byte, bit;
ntfs_attr *attr;
if (!vol) {
errno = EINVAL;
return -1;
}
if ((lcn < bmplcn)
|| (lcn >= (long long)(bmplcn + (sizeof(buffer) << 3)))) {
ntfs_log_debug("Bit lies outside cache.\n");
attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
if (!attr) {
ntfs_log_perror("Couldn't open $Bitmap");
return -1;
}
memset(buffer, 0xFF, sizeof(buffer));
bmplcn = lcn & (~((sizeof(buffer) << 3) - 1));
if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer),
buffer) < 0) {
ntfs_log_perror("Couldn't read $Bitmap");
ntfs_attr_close(attr);
return -1;
}
ntfs_log_debug("Reloaded bitmap buffer.\n");
ntfs_attr_close(attr);
}
bit = 1 << (lcn & 7);
byte = (lcn >> 3) & (sizeof(buffer) - 1);
ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, "
"in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] &
bit);
return (buffer[byte] & bit);
}
int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
{
static u8 buffer[512];
static s64 bmpmref = -(sizeof(buffer) << 3) - 1;
int byte, bit;
ntfs_log_trace("Entering.\n");
if (!vol) {
errno = EINVAL;
return -1;
}
if (((s64)MREF(mref) < bmpmref)
|| ((s64)MREF(mref) >= (s64)(bmpmref + (sizeof(buffer) << 3)))) {
ntfs_log_debug("Bit lies outside cache.\n");
memset(buffer, 0, sizeof(buffer));
bmpmref = mref & (~((sizeof(buffer) << 3) - 1));
if (ntfs_attr_pread(vol->mftbmp_na, (bmpmref>>3), sizeof(buffer), buffer) < 0) {
ntfs_log_perror("Couldn't read $MFT/$BITMAP");
return -1;
}
ntfs_log_debug("Reloaded bitmap buffer.\n");
}
bit = 1 << (mref & 7);
byte = (mref >> 3) & (sizeof(buffer) - 1);
ntfs_log_debug("cluster = %lld, bmpmref = %lld, byte = %d, bit = %d, "
"in use %d\n", (long long) mref, (long long) bmpmref,
byte, bit, buffer[byte] & bit);
return (buffer[byte] & bit);
}
static int __metadata(ntfs_volume *vol, u64 num)
{
if (num <= FILE_UpCase)
return 1;
if (!vol)
return -1;
if ((vol->major_ver == 3) && (num == FILE_Extend))
return 1;
return 0;
}
int utils_is_metadata(ntfs_inode *inode)
{
ntfs_volume *vol;
ATTR_RECORD *rec;
FILE_NAME_ATTR *attr;
MFT_RECORD *file;
u64 num;
if (!inode) {
errno = EINVAL;
return -1;
}
vol = inode->vol;
if (!vol)
return -1;
num = inode->mft_no;
if (__metadata(vol, num) == 1)
return 1;
file = inode->mrec;
if (file && (file->base_mft_record != 0)) {
num = MREF_LE(file->base_mft_record);
if (__metadata(vol, num) == 1)
return 1;
}
rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
if (!rec)
return -1;
attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset));
num = MREF_LE(attr->parent_directory);
if ((num != FILE_root) && (__metadata(vol, num) == 1))
return 1;
return 0;
}
void utils_dump_mem(void *buf, int start, int length, int flags)
{
int off, i, s, e, col;
u8 *mem = buf;
s = start & ~15;
e = (start + length + 15) & ~15;
for (off = s; off < e; off += 16) {
col = 30;
if (flags & DM_RED)
col += 1;
if (flags & DM_GREEN)
col += 2;
if (flags & DM_BLUE)
col += 4;
if (flags & DM_INDENT)
ntfs_log_debug("\t");
if (flags & DM_BOLD)
ntfs_log_debug("\e[01m");
if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
ntfs_log_debug("\e[%dm", col);
if (off == s)
ntfs_log_debug("%6.6x ", start);
else
ntfs_log_debug("%6.6x ", off);
for (i = 0; i < 16; i++) {
if ((i == 8) && (!(flags & DM_NO_DIVIDER)))
ntfs_log_debug(" -");
if (((off+i) >= start) && ((off+i) < (start+length)))
ntfs_log_debug(" %02X", mem[off+i]);
else
ntfs_log_debug(" ");
}
if (!(flags & DM_NO_ASCII)) {
ntfs_log_debug(" ");
for (i = 0; i < 16; i++) {
if (((off+i) < start) || ((off+i) >= (start+length)))
ntfs_log_debug(" ");
else if (isprint(mem[off + i]))
ntfs_log_debug("%c", mem[off + i]);
else
ntfs_log_debug(".");
}
}
if (flags & (DM_RED | DM_BLUE | DM_GREEN | DM_BOLD))
ntfs_log_debug("\e[0m");
ntfs_log_debug("\n");
}
}
struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
{
struct mft_search_ctx *ctx;
if (!vol) {
errno = EINVAL;
return NULL;
}
ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx);
ctx->mft_num = -1;
ctx->vol = vol;
return ctx;
}
void mft_put_search_ctx(struct mft_search_ctx *ctx)
{
if (!ctx)
return;
if (ctx->inode)
ntfs_inode_close(ctx->inode);
free(ctx);
}
int mft_next_record(struct mft_search_ctx *ctx)
{
s64 nr_mft_records;
ATTR_RECORD *attr10 = NULL;
ATTR_RECORD *attr20 = NULL;
ATTR_RECORD *attr80 = NULL;
ntfs_attr_search_ctx *attr_ctx;
if (!ctx) {
errno = EINVAL;
return -1;
}
if (ctx->inode) {
ntfs_inode_close(ctx->inode);
ctx->inode = NULL;
}
nr_mft_records = ctx->vol->mft_na->initialized_size >>
ctx->vol->mft_record_size_bits;
for (ctx->mft_num++; (s64)ctx->mft_num < nr_mft_records; ctx->mft_num++) {
int in_use;
ctx->flags_match = 0;
in_use = utils_mftrec_in_use(ctx->vol, (MFT_REF) ctx->mft_num);
if (in_use == -1) {
ntfs_log_error("Error reading inode %llu. Aborting.\n",
(unsigned long long)ctx->mft_num);
return -1;
}
if (in_use) {
ctx->flags_match |= FEMR_IN_USE;
ctx->inode = ntfs_inode_open(ctx->vol, (MFT_REF) ctx->mft_num);
if (ctx->inode == NULL) {
MFT_RECORD *mrec;
int r;
MFT_REF base_inode;
mrec = (MFT_RECORD*)NULL;
r = ntfs_file_record_read(ctx->vol,
(MFT_REF) ctx->mft_num, &mrec, NULL);
if (r || !mrec || !mrec->base_mft_record)
ntfs_log_error(
"Error reading inode %lld.\n",
(long long)ctx->mft_num);
else {
base_inode = le64_to_cpu(
mrec->base_mft_record);
ntfs_log_error("Inode %lld is an "
"extent of inode %lld.\n",
(long long)ctx->mft_num,
(long long)MREF(base_inode));
}
free (mrec);
continue;
}
attr10 = find_first_attribute(AT_STANDARD_INFORMATION, ctx->inode->mrec);
attr20 = find_first_attribute(AT_ATTRIBUTE_LIST, ctx->inode->mrec);
attr80 = find_first_attribute(AT_DATA, ctx->inode->mrec);
if (attr10)
ctx->flags_match |= FEMR_BASE_RECORD;
else
ctx->flags_match |= FEMR_NOT_BASE_RECORD;
if (attr20)
ctx->flags_match |= FEMR_BASE_RECORD;
if (attr80)
ctx->flags_match |= FEMR_FILE;
if (ctx->flags_search & FEMR_DIR) {
attr_ctx = ntfs_attr_get_search_ctx(ctx->inode, NULL);
if (attr_ctx) {
if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, 0, 0, NULL, 0, attr_ctx) == 0)
ctx->flags_match |= FEMR_DIR;
ntfs_attr_put_search_ctx(attr_ctx);
} else {
ntfs_log_error("Couldn't create a search context.\n");
return -1;
}
}
switch (utils_is_metadata(ctx->inode)) {
case 1: ctx->flags_match |= FEMR_METADATA; break;
case 0: ctx->flags_match |= FEMR_NOT_METADATA; break;
default:
ctx->flags_match |= FEMR_NOT_METADATA; break;
}
} else {
ntfs_attr *mft;
ctx->flags_match |= FEMR_NOT_IN_USE;
ctx->inode = (ntfs_inode*)calloc(1, sizeof(*ctx->inode));
if (!ctx->inode) {
ntfs_log_error("Out of memory. Aborting.\n");
return -1;
}
ctx->inode->mft_no = ctx->mft_num;
ctx->inode->vol = ctx->vol;
ctx->inode->mrec = ntfs_malloc(ctx->vol->mft_record_size);
if (!ctx->inode->mrec) {
free(ctx->inode);
return -1;
}
mft = ntfs_attr_open(ctx->vol->mft_ni, AT_DATA,
AT_UNNAMED, 0);
if (!mft) {
ntfs_log_perror("Couldn't open $MFT/$DATA");
return -1;
}
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) {
ntfs_log_perror("Couldn't read MFT Record %llu",
(unsigned long long) ctx->mft_num);
ntfs_attr_close(mft);
return -1;
}
ntfs_attr_close(mft);
}
if (ctx->flags_match & ctx->flags_search) {
break;
}
if (ntfs_inode_close(ctx->inode)) {
ntfs_log_error("Error closing inode %llu.\n",
(unsigned long long)ctx->mft_num);
return -errno;
}
ctx->inode = NULL;
}
return (ctx->inode == NULL);
}
#ifdef HAVE_WINDOWS_H
char *ntfs_utils_reformat(char *out, int sz, const char *fmt)
{
const char *f;
char *p;
int i;
enum { F_INIT, F_PERCENT, F_FIRST } state;
i = 0;
f = fmt;
p = out;
state = F_INIT;
while (*f && ((i + 3) < sz)) {
switch (state) {
case F_INIT :
if (*f == '%')
state = F_PERCENT;
*p++ = *f++;
i++;
break;
case F_PERCENT :
if (*f == 'l') {
state = F_FIRST;
f++;
} else {
if (((*f < '0') || (*f > '9'))
&& (*f != '*') && (*f != '-'))
state = F_INIT;
*p++ = *f++;
i++;
}
break;
case F_FIRST :
if (*f == 'l') {
*p++ = 'I';
*p++ = '6';
*p++ = '4';
f++;
i += 3;
} else {
*p++ = 'l';
*p++ = *f++;
i += 2;
}
state = F_INIT;
break;
}
}
*p++ = 0;
return (out);
}
char *ntfs_utils_unix_path(const char *in)
{
char *out;
int i;
out = strdup(in);
if (out) {
for (i=0; in[i]; i++)
if (in[i] == '\\')
out[i] = '/';
} else
errno = ENOMEM;
return (out);
}
#endif