#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <time.h>
#include "compat.h"
#include "types.h"
#include "device.h"
#include "debug.h"
#include "bitmap.h"
#include "attrib.h"
#include "inode.h"
#include "volume.h"
#include "layout.h"
#include "lcnalloc.h"
#include "mft.h"
#include "logging.h"
#include "misc.h"
int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref,
const s64 count, MFT_RECORD *b)
{
s64 br;
VCN m;
ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref));
if (!vol || !vol->mft_na || !b || count < 0) {
errno = EINVAL;
ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__,
b, (long long)count, (unsigned long long)MREF(mref));
return -1;
}
m = MREF(mref);
if (m + count > vol->mft_na->initialized_size >>
vol->mft_record_size_bits) {
errno = ESPIPE;
ntfs_log_perror("Trying to read non-allocated mft records "
"(%lld > %lld)", (long long)m + count,
(long long)vol->mft_na->initialized_size >>
vol->mft_record_size_bits);
return -1;
}
br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits,
count, vol->mft_record_size, b);
if (br != count) {
if (br != -1)
errno = EIO;
ntfs_log_perror("Failed to read of MFT, mft=%llu count=%lld "
"br=%lld", (long long)m, (long long)count,
(long long)br);
return -1;
}
return 0;
}
int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
const s64 count, MFT_RECORD *b)
{
s64 bw;
VCN m;
void *bmirr = NULL;
int cnt = 0, res = 0;
if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) {
errno = EINVAL;
return -1;
}
m = MREF(mref);
if (m + count > vol->mft_na->initialized_size >>
vol->mft_record_size_bits) {
errno = ESPIPE;
ntfs_log_perror("Trying to write non-allocated mft records "
"(%lld > %lld)", (long long)m + count,
(long long)vol->mft_na->initialized_size >>
vol->mft_record_size_bits);
return -1;
}
if (m < vol->mftmirr_size) {
if (!vol->mftmirr_na) {
errno = EINVAL;
return -1;
}
cnt = vol->mftmirr_size - m;
if (cnt > count)
cnt = count;
if ((m + cnt) > vol->mftmirr_na->initialized_size >>
vol->mft_record_size_bits) {
errno = ESPIPE;
ntfs_log_perror("Trying to write non-allocated mftmirr"
" records (%lld > %lld)", (long long)m + cnt,
(long long)vol->mftmirr_na->initialized_size >>
vol->mft_record_size_bits);
return -1;
}
bmirr = ntfs_malloc(cnt * vol->mft_record_size);
if (!bmirr)
return -1;
memcpy(bmirr, b, cnt * vol->mft_record_size);
}
bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits,
count, vol->mft_record_size, b);
if (bw != count) {
if (bw != -1)
errno = EIO;
if (bw >= 0)
ntfs_log_debug("Error: partial write while writing $Mft "
"record(s)!\n");
else
ntfs_log_perror("Error writing $Mft record(s)");
res = errno;
}
if (bmirr && bw > 0) {
if (bw < cnt)
cnt = bw;
bw = ntfs_attr_mst_pwrite(vol->mftmirr_na,
m << vol->mft_record_size_bits, cnt,
vol->mft_record_size, bmirr);
if (bw != cnt) {
if (bw != -1)
errno = EIO;
ntfs_log_debug("Error: failed to sync $MFTMirr! Run "
"chkdsk.\n");
res = errno;
}
}
free(bmirr);
if (!res)
return res;
errno = res;
return -1;
}
int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
MFT_RECORD *m)
{
ATTR_RECORD *a;
ATTR_TYPES previous_type;
int ret = -1;
u32 offset;
s32 space;
if (!ntfs_is_file_record(m->magic)) {
if (!NVolNoFixupWarn(vol))
ntfs_log_error("Record %llu has no FILE magic (0x%x)\n",
(unsigned long long)MREF(mref),
(int)le32_to_cpu(*(le32*)m));
goto err_out;
}
if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) {
ntfs_log_error("Record %llu has corrupt allocation size "
"(%u <> %u)\n", (unsigned long long)MREF(mref),
vol->mft_record_size,
le32_to_cpu(m->bytes_allocated));
goto err_out;
}
if (!NVolNoFixupWarn(vol)
&& (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size)) {
ntfs_log_error("Record %llu has corrupt in-use size "
"(%u > %u)\n", (unsigned long long)MREF(mref),
(int)le32_to_cpu(m->bytes_in_use),
(int)vol->mft_record_size);
goto err_out;
}
if (le16_to_cpu(m->attrs_offset) & 7) {
ntfs_log_error("Attributes badly aligned in record %llu\n",
(unsigned long long)MREF(mref));
goto err_out;
}
a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset));
if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) {
ntfs_log_error("Record %llu is corrupt\n",
(unsigned long long)MREF(mref));
goto err_out;
}
if (!NVolNoFixupWarn(vol)) {
offset = le16_to_cpu(m->attrs_offset);
space = le32_to_cpu(m->bytes_in_use) - offset;
a = (ATTR_RECORD*)((char*)m + offset);
previous_type = AT_STANDARD_INFORMATION;
while ((space >= (s32)offsetof(ATTR_RECORD, resident_end))
&& (a->type != AT_END)
&& (le32_to_cpu(a->type) >= le32_to_cpu(previous_type))) {
if ((le32_to_cpu(a->length) <= (u32)space)
&& !(le32_to_cpu(a->length) & 7)) {
if (!ntfs_attr_inconsistent(a, mref)) {
previous_type = a->type;
offset += le32_to_cpu(a->length);
space -= le32_to_cpu(a->length);
a = (ATTR_RECORD*)((char*)m + offset);
} else
goto err_out;
} else {
ntfs_log_error("Corrupted MFT record %llu\n",
(unsigned long long)MREF(mref));
goto err_out;
}
}
if ((space < 4) || (a->type != AT_END)) {
ntfs_log_error("Bad end of MFT record %llu\n",
(unsigned long long)MREF(mref));
goto err_out;
}
}
ret = 0;
err_out:
if (ret)
errno = EIO;
return ret;
}
int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref,
MFT_RECORD **mrec, ATTR_RECORD **attr)
{
MFT_RECORD *m;
if (!vol || !mrec) {
errno = EINVAL;
ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec);
return -1;
}
m = *mrec;
if (!m) {
m = ntfs_malloc(vol->mft_record_size);
if (!m)
return -1;
}
if (ntfs_mft_record_read(vol, mref, m))
goto err_out;
if (ntfs_mft_record_check(vol, mref, m))
goto err_out;
if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) {
ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n",
(unsigned long long)MREF(mref), MSEQNO(mref),
le16_to_cpu(m->sequence_number));
errno = EIO;
goto err_out;
}
*mrec = m;
if (attr)
*attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
return 0;
err_out:
if (m != *mrec)
free(m);
return -1;
}
int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref,
MFT_RECORD *mrec)
{
ATTR_RECORD *a;
if (!vol || !mrec) {
errno = EINVAL;
ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec);
return -1;
}
if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver))
mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1);
else {
if (MREF(mref) & 0x0000ffff00000000ull) {
errno = ERANGE;
ntfs_log_perror("Mft reference exceeds 32 bits");
return -1;
}
mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1);
mrec->reserved = const_cpu_to_le16(0);
mrec->mft_record_number = cpu_to_le32(MREF(mref));
}
mrec->magic = magic_FILE;
if (vol->mft_record_size >= NTFS_BLOCK_SIZE)
mrec->usa_count = cpu_to_le16(vol->mft_record_size /
NTFS_BLOCK_SIZE + 1);
else {
mrec->usa_count = const_cpu_to_le16(1);
ntfs_log_error("Sector size is bigger than MFT record size. "
"Setting usa_count to 1. If Windows chkdsk "
"reports this as corruption, please email %s "
"stating that you saw this message and that "
"the file system created was corrupt. "
"Thank you.\n", NTFS_DEV_LIST);
}
*(le16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = const_cpu_to_le16(1);
mrec->lsn = const_cpu_to_sle64(0ll);
mrec->sequence_number = const_cpu_to_le16(1);
mrec->link_count = const_cpu_to_le16(0);
mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) +
(le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7);
mrec->flags = const_cpu_to_le16(0);
mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 +
7) & ~7);
mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size);
mrec->base_mft_record = const_cpu_to_le64((MFT_REF)0);
mrec->next_attr_instance = const_cpu_to_le16(0);
a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
a->type = AT_END;
a->length = const_cpu_to_le32(0);
memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec));
return 0;
}
int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref)
{
MFT_RECORD *m;
int ret = -1;
ntfs_log_enter("Entering\n");
m = ntfs_calloc(vol->mft_record_size);
if (!m)
goto out;
if (ntfs_mft_record_layout(vol, mref, m))
goto free_m;
if (ntfs_mft_record_write(vol, mref, m))
goto free_m;
ret = 0;
free_m:
free(m);
out:
ntfs_log_leave("\n");
return ret;
}
static const char *es = " Leaving inconsistent metadata. Run chkdsk.";
static inline unsigned int ntfs_ffz(unsigned int word)
{
return ffs(~word) - 1;
}
static int ntfs_is_mft(ntfs_inode *ni)
{
if (ni && ni->mft_no == FILE_MFT)
return 1;
return 0;
}
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
#define RESERVED_MFT_RECORDS 64
static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni)
{
s64 pass_end, ll, data_pos, pass_start, ofs, bit;
ntfs_attr *mftbmp_na;
u8 *buf, *byte;
unsigned int size;
u8 pass, b;
int ret = -1;
ntfs_log_enter("Entering\n");
mftbmp_na = vol->mftbmp_na;
size = PAGE_SIZE;
pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits;
ll = mftbmp_na->initialized_size << 3;
if (pass_end > ll)
pass_end = ll;
pass = 1;
if (!base_ni)
data_pos = vol->mft_data_pos;
else
data_pos = base_ni->mft_no + 1;
if (data_pos < RESERVED_MFT_RECORDS)
data_pos = RESERVED_MFT_RECORDS;
if (data_pos >= pass_end) {
data_pos = RESERVED_MFT_RECORDS;
pass = 2;
if (data_pos >= pass_end) {
errno = ENOSPC;
goto leave;
}
}
if (ntfs_is_mft(base_ni)) {
data_pos = 0;
pass = 2;
}
pass_start = data_pos;
buf = ntfs_malloc(PAGE_SIZE);
if (!buf)
goto leave;
ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, "
"pass_end 0x%llx, data_pos 0x%llx.\n", pass,
(long long)pass_start, (long long)pass_end,
(long long)data_pos);
#ifdef DEBUG
byte = NULL;
b = 0;
#endif
for (; pass <= 2; size = PAGE_SIZE) {
ofs = data_pos >> 3;
ll = ((pass_end + 7) >> 3) - ofs;
if (size > ll)
size = ll;
ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf);
if (ll < 0) {
ntfs_log_perror("Failed to read $MFT bitmap");
free(buf);
goto leave;
}
ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll);
if (ll) {
size = ll << 3;
bit = data_pos & 7;
data_pos &= ~7ull;
ntfs_log_debug("Before inner for loop: size 0x%x, "
"data_pos 0x%llx, bit 0x%llx, "
"*byte 0x%hhx, b %u.\n", size,
(long long)data_pos, (long long)bit,
(u8) (byte ? *byte : -1), b);
for (; bit < size && data_pos + bit < pass_end;
bit &= ~7ull, bit += 8) {
if (ntfs_is_mft(base_ni) && bit > 400)
goto out;
byte = buf + (bit >> 3);
if (*byte == 0xff)
continue;
b = ntfs_ffz((unsigned long)*byte);
if (b < 8 && b >= (bit & 7)) {
free(buf);
ret = data_pos + (bit & ~7ull) + b;
goto leave;
}
}
ntfs_log_debug("After inner for loop: size 0x%x, "
"data_pos 0x%llx, bit 0x%llx, "
"*byte 0x%hhx, b %u.\n", size,
(long long)data_pos, (long long)bit,
(u8) (byte ? *byte : -1), b);
data_pos += size;
if (data_pos < pass_end)
continue;
}
pass++;
if (pass == 2) {
pass_end = pass_start;
data_pos = pass_start = RESERVED_MFT_RECORDS;
ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end "
"0x%llx.\n", pass, (long long)pass_start,
(long long)pass_end);
if (data_pos >= pass_end)
break;
}
}
out:
free(buf);
errno = ENOSPC;
leave:
ntfs_log_leave("\n");
return ret;
}
static int ntfs_mft_attr_extend(ntfs_attr *na)
{
int ret = STATUS_ERROR;
ntfs_log_enter("Entering\n");
if (!NInoAttrList(na->ni)) {
if (ntfs_inode_add_attrlist(na->ni)) {
ntfs_log_perror("%s: Can not add attrlist #3", __FUNCTION__);
goto out;
}
ret = STATUS_KEEP_SEARCHING;
goto out;
}
if (ntfs_attr_update_mapping_pairs(na, 0)) {
ntfs_log_perror("%s: MP update failed", __FUNCTION__);
goto out;
}
ret = STATUS_OK;
out:
ntfs_log_leave("\n");
return ret;
}
static int ntfs_mft_bitmap_extend_allocation_i(ntfs_volume *vol)
{
LCN lcn;
s64 ll = 0;
ntfs_attr *mftbmp_na;
runlist_element *rl, *rl2 = NULL;
ntfs_attr_search_ctx *ctx;
MFT_RECORD *m = NULL;
ATTR_RECORD *a = NULL;
int err, mp_size;
int ret = STATUS_ERROR;
u32 old_alen = 0;
BOOL mp_rebuilt = FALSE;
BOOL update_mp = FALSE;
mftbmp_na = vol->mftbmp_na;
rl = ntfs_attr_find_vcn(mftbmp_na, (mftbmp_na->allocated_size - 1) >>
vol->cluster_size_bits);
if (!rl || !rl->length || rl->lcn < 0) {
ntfs_log_error("Failed to determine last allocated "
"cluster of mft bitmap attribute.\n");
if (rl)
errno = EIO;
return STATUS_ERROR;
}
lcn = rl->lcn + rl->length;
rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE);
if (!rl2) {
ntfs_log_error("Failed to allocate a cluster for "
"the mft bitmap.\n");
return STATUS_ERROR;
}
rl = ntfs_runlists_merge(mftbmp_na->rl, rl2);
if (!rl) {
err = errno;
ntfs_log_error("Failed to merge runlists for mft "
"bitmap.\n");
if (ntfs_cluster_free_from_rl(vol, rl2))
ntfs_log_error("Failed to deallocate "
"cluster.%s\n", es);
free(rl2);
errno = err;
return STATUS_ERROR;
}
mftbmp_na->rl = rl;
ntfs_log_debug("Adding one run to mft bitmap.\n");
for (; rl[1].length; rl++)
;
ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL);
if (!ctx)
goto undo_alloc;
if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) {
ntfs_log_error("Failed to find last attribute extent of "
"mft bitmap attribute.\n");
goto undo_alloc;
}
m = ctx->mrec;
a = ctx->attr;
ll = sle64_to_cpu(a->lowest_vcn);
rl2 = ntfs_attr_find_vcn(mftbmp_na, ll);
if (!rl2 || !rl2->length) {
ntfs_log_error("Failed to determine previous last "
"allocated cluster of mft bitmap attribute.\n");
if (rl2)
errno = EIO;
goto undo_alloc;
}
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX);
if (mp_size <= 0) {
ntfs_log_error("Get size for mapping pairs failed for "
"mft bitmap attribute extent.\n");
goto undo_alloc;
}
old_alen = le32_to_cpu(a->length);
if (ntfs_attr_record_resize(m, a, mp_size +
le16_to_cpu(a->mapping_pairs_offset))) {
ntfs_log_info("extending $MFT bitmap\n");
ret = ntfs_mft_attr_extend(vol->mftbmp_na);
if (ret == STATUS_OK)
goto ok;
if (ret == STATUS_ERROR) {
ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__);
update_mp = TRUE;
}
goto undo_alloc;
}
mp_rebuilt = TRUE;
if (ntfs_mapping_pairs_build(vol, (u8*)a +
le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll,
NULL)) {
ntfs_log_error("Failed to build mapping pairs array for "
"mft bitmap attribute.\n");
errno = EIO;
goto undo_alloc;
}
a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1);
if (a->lowest_vcn) {
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_reinit_search_ctx(ctx);
if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) {
ntfs_log_error("Failed to find first attribute "
"extent of mft bitmap attribute.\n");
goto restore_undo_alloc;
}
a = ctx->attr;
}
ok:
mftbmp_na->allocated_size += vol->cluster_size;
a->allocated_size = cpu_to_sle64(mftbmp_na->allocated_size);
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
return STATUS_OK;
restore_undo_alloc:
err = errno;
ntfs_attr_reinit_search_ctx(ctx);
if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) {
ntfs_log_error("Failed to find last attribute extent of "
"mft bitmap attribute.%s\n", es);
ntfs_attr_put_search_ctx(ctx);
mftbmp_na->allocated_size += vol->cluster_size;
errno = err;
return STATUS_ERROR;
}
m = ctx->mrec;
a = ctx->attr;
a->highest_vcn = cpu_to_sle64(rl[1].vcn - 2);
errno = err;
undo_alloc:
err = errno;
lcn = rl->lcn;
rl->lcn = rl[1].lcn;
rl->length = 0;
if (ntfs_bitmap_clear_bit(vol->lcnbmp_na, lcn))
ntfs_log_error("Failed to free cluster.%s\n", es);
else
vol->free_clusters++;
if (mp_rebuilt) {
if (ntfs_mapping_pairs_build(vol, (u8*)a +
le16_to_cpu(a->mapping_pairs_offset),
old_alen - le16_to_cpu(a->mapping_pairs_offset),
rl2, ll, NULL))
ntfs_log_error("Failed to restore mapping "
"pairs array.%s\n", es);
if (ntfs_attr_record_resize(m, a, old_alen))
ntfs_log_error("Failed to restore attribute "
"record.%s\n", es);
ntfs_inode_mark_dirty(ctx->ntfs_ino);
}
if (update_mp) {
if (ntfs_attr_update_mapping_pairs(vol->mftbmp_na, 0))
ntfs_log_perror("%s: MP update failed", __FUNCTION__);
}
if (ctx)
ntfs_attr_put_search_ctx(ctx);
errno = err;
return ret;
}
static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol)
{
int ret;
ntfs_log_enter("Entering\n");
ret = ntfs_mft_bitmap_extend_allocation_i(vol);
ntfs_log_leave("\n");
return ret;
}
static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol)
{
s64 old_data_size, old_initialized_size, ll;
ntfs_attr *mftbmp_na;
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *a;
int err;
int ret = -1;
ntfs_log_enter("Entering\n");
mftbmp_na = vol->mftbmp_na;
ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL);
if (!ctx)
goto out;
if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) {
ntfs_log_error("Failed to find first attribute extent of "
"mft bitmap attribute.\n");
err = errno;
goto put_err_out;
}
a = ctx->attr;
old_data_size = mftbmp_na->data_size;
old_initialized_size = mftbmp_na->initialized_size;
mftbmp_na->initialized_size += 8;
a->initialized_size = cpu_to_sle64(mftbmp_na->initialized_size);
if (mftbmp_na->initialized_size > mftbmp_na->data_size) {
mftbmp_na->data_size = mftbmp_na->initialized_size;
a->data_size = cpu_to_sle64(mftbmp_na->data_size);
}
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
ll = 0;
ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll);
if (ll == 8) {
ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n");
vol->free_mft_records += (8 * 8);
ret = 0;
goto out;
}
ntfs_log_error("Failed to write to mft bitmap.\n");
err = errno;
if (ll >= 0)
err = EIO;
ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL);
if (!ctx)
goto err_out;
if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) {
ntfs_log_error("Failed to find first attribute extent of "
"mft bitmap attribute.%s\n", es);
put_err_out:
ntfs_attr_put_search_ctx(ctx);
goto err_out;
}
a = ctx->attr;
mftbmp_na->initialized_size = old_initialized_size;
a->initialized_size = cpu_to_sle64(old_initialized_size);
if (mftbmp_na->data_size != old_data_size) {
mftbmp_na->data_size = old_data_size;
a->data_size = cpu_to_sle64(old_data_size);
}
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
ntfs_log_debug("Restored status of mftbmp: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.\n",
(long long)mftbmp_na->allocated_size,
(long long)mftbmp_na->data_size,
(long long)mftbmp_na->initialized_size);
err_out:
errno = err;
out:
ntfs_log_leave("\n");
return ret;
}
static int ntfs_mft_data_extend_allocation(ntfs_volume *vol)
{
LCN lcn;
VCN old_last_vcn;
s64 min_nr, nr, ll = 0;
ntfs_attr *mft_na;
runlist_element *rl, *rl2;
ntfs_attr_search_ctx *ctx;
MFT_RECORD *m = NULL;
ATTR_RECORD *a = NULL;
int err, mp_size;
int ret = STATUS_ERROR;
u32 old_alen = 0;
BOOL mp_rebuilt = FALSE;
BOOL update_mp = FALSE;
ntfs_log_enter("Extending mft data allocation.\n");
mft_na = vol->mft_na;
rl = ntfs_attr_find_vcn(mft_na,
(mft_na->allocated_size - 1) >> vol->cluster_size_bits);
if (!rl || !rl->length || rl->lcn < 0) {
ntfs_log_error("Failed to determine last allocated "
"cluster of mft data attribute.\n");
if (rl)
errno = EIO;
goto out;
}
lcn = rl->lcn + rl->length;
ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn);
min_nr = vol->mft_record_size >> vol->cluster_size_bits;
if (!min_nr)
min_nr = 1;
nr = vol->mft_record_size << 4 >> vol->cluster_size_bits;
if (!nr)
nr = min_nr;
old_last_vcn = rl[1].vcn;
do {
rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE);
if (rl2)
break;
if (errno != ENOSPC || nr == min_nr) {
ntfs_log_perror("Failed to allocate (%lld) clusters "
"for $MFT", (long long)nr);
goto out;
}
nr = min_nr;
ntfs_log_debug("Retrying mft data allocation with minimal cluster "
"count %lli.\n", (long long)nr);
} while (1);
ntfs_log_debug("Allocated %lld clusters.\n", (long long)nr);
rl = ntfs_runlists_merge(mft_na->rl, rl2);
if (!rl) {
err = errno;
ntfs_log_error("Failed to merge runlists for mft data "
"attribute.\n");
if (ntfs_cluster_free_from_rl(vol, rl2))
ntfs_log_error("Failed to deallocate clusters "
"from the mft data attribute.%s\n", es);
free(rl2);
errno = err;
goto out;
}
mft_na->rl = rl;
for (; rl[1].length; rl++)
;
ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL);
if (!ctx)
goto undo_alloc;
if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0,
rl[1].vcn, NULL, 0, ctx)) {
ntfs_log_error("Failed to find last attribute extent of "
"mft data attribute.\n");
goto undo_alloc;
}
m = ctx->mrec;
a = ctx->attr;
ll = sle64_to_cpu(a->lowest_vcn);
rl2 = ntfs_attr_find_vcn(mft_na, ll);
if (!rl2 || !rl2->length) {
ntfs_log_error("Failed to determine previous last "
"allocated cluster of mft data attribute.\n");
if (rl2)
errno = EIO;
goto undo_alloc;
}
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, INT_MAX);
if (mp_size <= 0) {
ntfs_log_error("Get size for mapping pairs failed for "
"mft data attribute extent.\n");
goto undo_alloc;
}
old_alen = le32_to_cpu(a->length);
if (ntfs_attr_record_resize(m, a,
mp_size + le16_to_cpu(a->mapping_pairs_offset))) {
ret = ntfs_mft_attr_extend(vol->mft_na);
if (ret == STATUS_OK)
goto ok;
if (ret == STATUS_ERROR) {
ntfs_log_perror("%s: ntfs_mft_attr_extend failed", __FUNCTION__);
update_mp = TRUE;
}
goto undo_alloc;
}
mp_rebuilt = TRUE;
if (ntfs_mapping_pairs_build(vol,
(u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size,
rl2, ll, NULL)) {
ntfs_log_error("Failed to build mapping pairs array of "
"mft data attribute.\n");
errno = EIO;
goto undo_alloc;
}
a->highest_vcn = cpu_to_sle64(rl[1].vcn - 1);
if (a->lowest_vcn) {
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_reinit_search_ctx(ctx);
if (ntfs_attr_lookup(mft_na->type, mft_na->name,
mft_na->name_len, 0, 0, NULL, 0, ctx)) {
ntfs_log_error("Failed to find first attribute "
"extent of mft data attribute.\n");
goto restore_undo_alloc;
}
a = ctx->attr;
}
ok:
mft_na->allocated_size += nr << vol->cluster_size_bits;
a->allocated_size = cpu_to_sle64(mft_na->allocated_size);
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
ret = STATUS_OK;
out:
ntfs_log_leave("\n");
return ret;
restore_undo_alloc:
err = errno;
ntfs_attr_reinit_search_ctx(ctx);
if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0,
rl[1].vcn, NULL, 0, ctx)) {
ntfs_log_error("Failed to find last attribute extent of "
"mft data attribute.%s\n", es);
ntfs_attr_put_search_ctx(ctx);
mft_na->allocated_size += nr << vol->cluster_size_bits;
errno = err;
ret = STATUS_ERROR;
goto out;
}
m = ctx->mrec;
a = ctx->attr;
a->highest_vcn = cpu_to_sle64(old_last_vcn - 1);
errno = err;
undo_alloc:
err = errno;
if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0)
ntfs_log_error("Failed to free clusters from mft data "
"attribute.%s\n", es);
if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn))
ntfs_log_error("Failed to truncate mft data attribute "
"runlist.%s\n", es);
if (mp_rebuilt) {
if (ntfs_mapping_pairs_build(vol, (u8*)a +
le16_to_cpu(a->mapping_pairs_offset),
old_alen - le16_to_cpu(a->mapping_pairs_offset),
rl2, ll, NULL))
ntfs_log_error("Failed to restore mapping pairs "
"array.%s\n", es);
if (ntfs_attr_record_resize(m, a, old_alen))
ntfs_log_error("Failed to restore attribute "
"record.%s\n", es);
ntfs_inode_mark_dirty(ctx->ntfs_ino);
}
if (update_mp) {
if (ntfs_attr_update_mapping_pairs(vol->mft_na, 0))
ntfs_log_perror("%s: MP update failed", __FUNCTION__);
}
if (ctx)
ntfs_attr_put_search_ctx(ctx);
errno = err;
goto out;
}
static int ntfs_mft_record_init(ntfs_volume *vol, s64 size)
{
int ret = -1;
ntfs_attr *mft_na;
s64 old_data_initialized, old_data_size;
ntfs_attr_search_ctx *ctx;
ntfs_log_enter("Entering\n");
mft_na = vol->mft_na;
ntfs_log_debug("Status of mft data before extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.\n",
(long long)mft_na->allocated_size,
(long long)mft_na->data_size,
(long long)mft_na->initialized_size);
while (size > mft_na->allocated_size) {
if (ntfs_mft_data_extend_allocation(vol) == STATUS_ERROR)
goto out;
ntfs_log_debug("Status of mft data after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.\n",
(long long)mft_na->allocated_size,
(long long)mft_na->data_size,
(long long)mft_na->initialized_size);
}
old_data_initialized = mft_na->initialized_size;
old_data_size = mft_na->data_size;
while (size > mft_na->initialized_size) {
s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits;
mft_na->initialized_size += vol->mft_record_size;
if (mft_na->initialized_size > mft_na->data_size)
mft_na->data_size = mft_na->initialized_size;
ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2);
if (ntfs_mft_record_format(vol, ll2) < 0) {
ntfs_log_perror("Failed to format mft record");
goto undo_data_init;
}
}
ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL);
if (!ctx)
goto undo_data_init;
if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0,
0, NULL, 0, ctx)) {
ntfs_log_error("Failed to find first attribute extent of "
"mft data attribute.\n");
ntfs_attr_put_search_ctx(ctx);
goto undo_data_init;
}
ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size);
ctx->attr->data_size = cpu_to_sle64(mft_na->data_size);
ctx->attr->allocated_size = cpu_to_sle64(mft_na->allocated_size);
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
ntfs_log_debug("Status of mft data after mft record initialization: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.\n",
(long long)mft_na->allocated_size,
(long long)mft_na->data_size,
(long long)mft_na->initialized_size);
if (mft_na->data_size > mft_na->allocated_size ||
mft_na->initialized_size > mft_na->data_size)
NTFS_BUG("mft_na sanity checks failed");
if (ntfs_inode_sync(mft_na->ni))
goto undo_data_init;
ret = 0;
out:
ntfs_log_leave("\n");
return ret;
undo_data_init:
mft_na->initialized_size = old_data_initialized;
mft_na->data_size = old_data_size;
goto out;
}
static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size)
{
int ret = -1;
ntfs_attr *mft_na;
s64 old_data_initialized, old_data_size;
ntfs_attr_search_ctx *ctx;
ntfs_log_enter("Entering\n");
mft_na = vol->mft_na;
if (size > mft_na->allocated_size || size > mft_na->initialized_size) {
errno = EIO;
ntfs_log_perror("%s: unexpected $MFT sizes, see below", __FUNCTION__);
ntfs_log_error("$MFT: size=%lld allocated_size=%lld "
"data_size=%lld initialized_size=%lld\n",
(long long)size,
(long long)mft_na->allocated_size,
(long long)mft_na->data_size,
(long long)mft_na->initialized_size);
goto out;
}
old_data_initialized = mft_na->initialized_size;
old_data_size = mft_na->data_size;
ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL);
if (!ctx)
goto undo_data_init;
if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0,
0, NULL, 0, ctx)) {
ntfs_log_error("Failed to find first attribute extent of "
"mft data attribute.\n");
ntfs_attr_put_search_ctx(ctx);
goto undo_data_init;
}
ctx->attr->initialized_size = cpu_to_sle64(mft_na->initialized_size);
ctx->attr->data_size = cpu_to_sle64(mft_na->data_size);
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
if (mft_na->data_size > mft_na->allocated_size ||
mft_na->initialized_size > mft_na->data_size)
NTFS_BUG("mft_na sanity checks failed");
out:
ntfs_log_leave("\n");
return ret;
undo_data_init:
mft_na->initialized_size = old_data_initialized;
mft_na->data_size = old_data_size;
goto out;
}
ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data)
{
s64 ll, bit;
ntfs_attr *mft_na, *mftbmp_na;
MFT_RECORD *m;
ntfs_inode *ni = NULL;
ntfs_inode *base_ni;
int err;
le16 seq_no, usn;
BOOL forced_mft_data;
ntfs_log_enter("Entering\n");
mft_na = vol->mft_na;
mftbmp_na = vol->mftbmp_na;
base_ni = mft_na->ni;
forced_mft_data = FALSE;
if (mft_data) {
ntfs_inode *ext_ni = ntfs_inode_open(vol, FILE_mft_data);
if (ext_ni) {
if (!ext_ni->mrec->base_mft_record
&& !ext_ni->mrec->link_count)
forced_mft_data = TRUE;
ntfs_inode_close(ext_ni);
if (forced_mft_data && base_ni->nr_extents) {
int i;
for (i=0; i<base_ni->nr_extents; i++) {
if (base_ni->extent_nis[i]
&& (base_ni->extent_nis[i]->mft_no
== FILE_mft_data))
forced_mft_data = FALSE;
}
}
}
}
if (forced_mft_data)
bit = FILE_mft_data;
else
bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni);
if (bit >= 0)
goto found_free_rec;
if (errno != ENOSPC)
goto out;
errno = ENOSPC;
ntfs_log_error("No free mft record for $MFT: %s\n", strerror(errno));
goto err_out;
found_free_rec:
if (ntfs_bitmap_set_bit(mftbmp_na, bit)) {
ntfs_log_error("Failed to allocate bit in mft bitmap #2\n");
goto err_out;
}
ll = (bit + 1) << vol->mft_record_size_bits;
if (ll > mft_na->initialized_size)
if (ntfs_mft_rec_init(vol, ll) < 0)
goto undo_mftbmp_alloc;
m = ntfs_malloc(vol->mft_record_size);
if (!m)
goto undo_mftbmp_alloc;
if (ntfs_mft_record_read(vol, bit, m)) {
free(m);
goto undo_mftbmp_alloc;
}
if (!forced_mft_data
&& (ntfs_is_file_record(m->magic)
&& (m->flags & MFT_RECORD_IN_USE))) {
ntfs_log_error("Inode %lld is used but it wasn't marked in "
"$MFT bitmap. Fixed.\n", (long long)bit);
free(m);
goto undo_mftbmp_alloc;
}
seq_no = m->sequence_number;
if (le16_to_cpu(m->usa_ofs) <= (NTFS_BLOCK_SIZE - 2))
usn = *(le16*)((u8*)m + (le16_to_cpu(m->usa_ofs) & -2));
else
usn = const_cpu_to_le16(1);
if (ntfs_mft_record_layout(vol, bit, m)) {
ntfs_log_error("Failed to re-format mft record.\n");
free(m);
goto undo_mftbmp_alloc;
}
if (seq_no)
m->sequence_number = seq_no;
seq_no = usn;
if (seq_no && seq_no != const_cpu_to_le16(0xffff))
*(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
m->flags |= MFT_RECORD_IN_USE;
ni = ntfs_inode_allocate(vol);
if (!ni) {
ntfs_log_error("Failed to allocate buffer for inode.\n");
free(m);
goto undo_mftbmp_alloc;
}
ni->mft_no = bit;
ni->mrec = m;
ni->nr_extents = -1;
ni->base_ni = base_ni;
m->base_mft_record = MK_LE_MREF(base_ni->mft_no,
le16_to_cpu(base_ni->mrec->sequence_number));
if (!(base_ni->nr_extents & 3)) {
ntfs_inode **extent_nis;
int i;
i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
extent_nis = ntfs_malloc(i);
if (!extent_nis) {
free(m);
free(ni);
goto undo_mftbmp_alloc;
}
if (base_ni->nr_extents) {
memcpy(extent_nis, base_ni->extent_nis,
i - 4 * sizeof(ntfs_inode *));
free(base_ni->extent_nis);
}
base_ni->extent_nis = extent_nis;
}
base_ni->extent_nis[base_ni->nr_extents++] = ni;
ntfs_inode_mark_dirty(ni);
ni->data_size = ni->allocated_size = 0;
ni->flags = const_cpu_to_le32(0);
ni->creation_time = ni->last_data_change_time =
ni->last_mft_change_time =
ni->last_access_time = ntfs_current_time();
if (!base_ni)
vol->mft_data_pos = bit + 1;
ntfs_log_error("allocated %sinode %lld\n",
base_ni ? "extent " : "", (long long)bit);
out:
ntfs_log_leave("\n");
return ni;
undo_mftbmp_alloc:
err = errno;
if (ntfs_bitmap_clear_bit(mftbmp_na, bit))
ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es);
errno = err;
err_out:
if (!errno)
errno = EIO;
ni = NULL;
goto out;
}
ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni)
{
s64 ll, bit;
ntfs_attr *mft_na, *mftbmp_na;
MFT_RECORD *m;
ntfs_inode *ni = NULL;
int err;
u32 usa_ofs;
le16 seq_no, usn;
BOOL oldwarn;
if (base_ni)
ntfs_log_enter("Entering (allocating an extent mft record for "
"base mft record %lld).\n",
(long long)base_ni->mft_no);
else
ntfs_log_enter("Entering (allocating a base mft record)\n");
if (!vol || !vol->mft_na || !vol->mftbmp_na) {
errno = EINVAL;
goto out;
}
if (ntfs_is_mft(base_ni)) {
ni = ntfs_mft_rec_alloc(vol, FALSE);
goto out;
}
mft_na = vol->mft_na;
mftbmp_na = vol->mftbmp_na;
retry:
bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni);
if (bit >= 0) {
ntfs_log_debug("found free record (#1) at %lld\n",
(long long)bit);
goto found_free_rec;
}
if (errno != ENOSPC)
goto out;
ll = mft_na->initialized_size >> vol->mft_record_size_bits;
if (mftbmp_na->initialized_size << 3 > ll &&
mftbmp_na->initialized_size > RESERVED_MFT_RECORDS / 8) {
bit = ll;
if (bit < RESERVED_MFT_RECORDS)
bit = RESERVED_MFT_RECORDS;
ntfs_log_debug("found free record (#2) at %lld\n",
(long long)bit);
goto found_free_rec;
}
ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, "
"data_size 0x%llx, initialized_size 0x%llx.\n",
(long long)mftbmp_na->allocated_size,
(long long)mftbmp_na->data_size,
(long long)mftbmp_na->initialized_size);
if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) {
int ret = ntfs_mft_bitmap_extend_allocation(vol);
if (ret == STATUS_ERROR)
goto err_out;
if (ret == STATUS_KEEP_SEARCHING) {
ret = ntfs_mft_bitmap_extend_allocation(vol);
if (ret != STATUS_OK)
goto err_out;
}
ntfs_log_debug("Status of mftbmp after allocation extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.\n",
(long long)mftbmp_na->allocated_size,
(long long)mftbmp_na->data_size,
(long long)mftbmp_na->initialized_size);
}
bit = mftbmp_na->initialized_size << 3;
if (ntfs_mft_bitmap_extend_initialized(vol))
goto err_out;
ntfs_log_debug("Status of mftbmp after initialized extension: "
"allocated_size 0x%llx, data_size 0x%llx, "
"initialized_size 0x%llx.\n",
(long long)mftbmp_na->allocated_size,
(long long)mftbmp_na->data_size,
(long long)mftbmp_na->initialized_size);
ntfs_log_debug("found free record (#3) at %lld\n", (long long)bit);
found_free_rec:
if (ntfs_bitmap_set_bit(mftbmp_na, bit)) {
ntfs_log_error("Failed to allocate bit in mft bitmap.\n");
goto err_out;
}
ll = (bit + 1) << vol->mft_record_size_bits;
if (ll > mft_na->initialized_size)
if (ntfs_mft_record_init(vol, ll) < 0)
goto undo_mftbmp_alloc;
m = ntfs_malloc(vol->mft_record_size);
if (!m)
goto undo_mftbmp_alloc;
oldwarn = !NVolNoFixupWarn(vol);
NVolSetNoFixupWarn(vol);
if (ntfs_mft_record_read(vol, bit, m)) {
if (oldwarn)
NVolClearNoFixupWarn(vol);
free(m);
goto undo_mftbmp_alloc;
}
if (oldwarn)
NVolClearNoFixupWarn(vol);
if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) {
ntfs_log_error("Inode %lld is used but it wasn't marked in "
"$MFT bitmap. Fixed.\n", (long long)bit);
free(m);
goto retry;
}
seq_no = m->sequence_number;
usa_ofs = le16_to_cpu(m->usa_ofs);
if (!(usa_ofs & 1) && (usa_ofs < NTFS_BLOCK_SIZE)) {
usn = *(le16*)((u8*)m + usa_ofs);
} else
usn = const_cpu_to_le16(1);
if (ntfs_mft_record_layout(vol, bit, m)) {
ntfs_log_error("Failed to re-format mft record.\n");
free(m);
goto undo_mftbmp_alloc;
}
if (seq_no)
m->sequence_number = seq_no;
seq_no = usn;
if (seq_no && seq_no != const_cpu_to_le16(0xffff))
*(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
m->flags |= MFT_RECORD_IN_USE;
ni = ntfs_inode_allocate(vol);
if (!ni) {
ntfs_log_error("Failed to allocate buffer for inode.\n");
free(m);
goto undo_mftbmp_alloc;
}
ni->mft_no = bit;
ni->mrec = m;
if (base_ni) {
ni->nr_extents = -1;
ni->base_ni = base_ni;
m->base_mft_record = MK_LE_MREF(base_ni->mft_no,
le16_to_cpu(base_ni->mrec->sequence_number));
if (!(base_ni->nr_extents & 3)) {
ntfs_inode **extent_nis;
int i;
i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
extent_nis = ntfs_malloc(i);
if (!extent_nis) {
free(m);
free(ni);
goto undo_mftbmp_alloc;
}
if (base_ni->nr_extents) {
memcpy(extent_nis, base_ni->extent_nis,
i - 4 * sizeof(ntfs_inode *));
free(base_ni->extent_nis);
}
base_ni->extent_nis = extent_nis;
}
base_ni->extent_nis[base_ni->nr_extents++] = ni;
}
ntfs_inode_mark_dirty(ni);
ni->data_size = ni->allocated_size = 0;
ni->flags = const_cpu_to_le32(0);
ni->creation_time = ni->last_data_change_time =
ni->last_mft_change_time =
ni->last_access_time = ntfs_current_time();
if (!base_ni)
vol->mft_data_pos = bit + 1;
ntfs_log_debug("allocated %sinode 0x%llx.\n",
base_ni ? "extent " : "", (long long)bit);
vol->free_mft_records--;
out:
ntfs_log_leave("\n");
return ni;
undo_mftbmp_alloc:
err = errno;
if (ntfs_bitmap_clear_bit(mftbmp_na, bit))
ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es);
errno = err;
err_out:
if (!errno)
errno = EIO;
ni = NULL;
goto out;
}
int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni)
{
u64 mft_no;
int err;
u16 seq_no;
le16 old_seq_no;
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
if (!vol || !vol->mftbmp_na || !ni) {
errno = EINVAL;
return -1;
}
mft_no = ni->mft_no;
ni->mrec->flags &= ~MFT_RECORD_IN_USE;
old_seq_no = ni->mrec->sequence_number;
seq_no = le16_to_cpu(old_seq_no);
if (seq_no == 0xffff)
seq_no = 1;
else if (seq_no)
seq_no++;
ni->mrec->sequence_number = cpu_to_le16(seq_no);
ntfs_inode_mark_dirty(ni);
if (ntfs_inode_sync(ni)) {
err = errno;
goto sync_rollback;
}
if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) {
err = errno;
goto bitmap_rollback;
}
#if CACHE_NIDATA_SIZE
if (!ntfs_inode_real_close(ni)) {
#else
if (!ntfs_inode_close(ni)) {
#endif
vol->free_mft_records++;
return 0;
}
err = errno;
bitmap_rollback:
if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no))
ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). "
"Leaving inconsistent metadata!\n");
sync_rollback:
ni->mrec->flags |= MFT_RECORD_IN_USE;
ni->mrec->sequence_number = old_seq_no;
ntfs_inode_mark_dirty(ni);
errno = err;
return -1;
}
int ntfs_mft_usn_dec(MFT_RECORD *mrec)
{
u16 usn;
le16 *usnp;
if (!mrec) {
errno = EINVAL;
return -1;
}
usnp = (le16*)((char*)mrec + le16_to_cpu(mrec->usa_ofs));
usn = le16_to_cpup(usnp);
if (usn-- <= 1)
usn = 0xfffe;
*usnp = cpu_to_le16(usn);
return 0;
}