More code refactoring in ntfs_mft_record_alloc() together with more error
recovery code. (Logical change 1.553)edge.strict_endians
parent
f4af163c83
commit
caf2f0ad97
749
libntfs/mft.c
749
libntfs/mft.c
|
@ -374,6 +374,139 @@ int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref)
|
|||
|
||||
static const char *es = " Leaving inconsistent metadata. Run chkdsk.";
|
||||
|
||||
static inline unsigned int ntfs_ffz(unsigned int word)
|
||||
{
|
||||
return ffs(~word) - 1;
|
||||
}
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap
|
||||
* @vol: volume on which to search for a free mft record
|
||||
* @base_ni: open base inode if allocating an extent mft record or NULL
|
||||
*
|
||||
* Search for a free mft record in the mft bitmap attribute on the ntfs volume
|
||||
* @vol.
|
||||
*
|
||||
* If @base_ni is NULL start the search at the default allocator position.
|
||||
*
|
||||
* If @base_ni is not NULL start the search at the mft record after the base
|
||||
* mft record @base_ni.
|
||||
*
|
||||
* Return the free mft record on success and -1 on error with errno set to the
|
||||
* error code. An error code of ENOSPC means that there are no free mft
|
||||
* records in the currently initialized mft bitmal.
|
||||
*/
|
||||
static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni)
|
||||
{
|
||||
s64 pass_end, ll, data_pos, pass_start, bit;
|
||||
ntfs_attr *mftbmp_na;
|
||||
u8 *buf, *byte;
|
||||
unsigned int size;
|
||||
u8 pass, b;
|
||||
|
||||
buf = (u8*)malloc(PAGE_SIZE);
|
||||
if (!buf)
|
||||
return -1;
|
||||
mftbmp_na = vol->mftbmp_na;
|
||||
/*
|
||||
* Set the end of the pass making sure we do not overflow the mft
|
||||
* bitmap.
|
||||
*/
|
||||
size = PAGE_SIZE;
|
||||
pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits;
|
||||
ll = pass_end >> 3;
|
||||
if (size > ll)
|
||||
size = ll;
|
||||
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 >= pass_end) {
|
||||
data_pos = 24;
|
||||
pass = 2;
|
||||
}
|
||||
pass_start = data_pos;
|
||||
ntfs_debug("Starting bitmap search: pass %u, pass_start 0x%llx, "
|
||||
"pass_end 0x%llx, data_pos 0x%llx.", pass,
|
||||
(long long)pass_start, (long long)pass_end,
|
||||
(long long)data_pos);
|
||||
#ifdef DEBUG
|
||||
byte = NULL;
|
||||
b = 0;
|
||||
#endif
|
||||
/* Loop until a free mft record is found. */
|
||||
for (; pass <= 2; size = PAGE_SIZE) {
|
||||
ll = ntfs_attr_pread(mftbmp_na, data_pos >> 3, size, buf);
|
||||
if (ll < 0) {
|
||||
ntfs_error(vol->sb, "Failed to read mft bitmap "
|
||||
"attribute, aborting.");
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
ntfs_debug("Read 0x%llx bytes.", (long long)ll);
|
||||
/* If we read at least one byte, search @buf for a zero bit. */
|
||||
if (ll) {
|
||||
size = ll << 3;
|
||||
bit = data_pos & 7;
|
||||
data_pos &= ~7ull;
|
||||
ntfs_debug("Before inner for loop: size 0x%x, "
|
||||
"data_pos 0x%llx, bit 0x%llx, "
|
||||
"*byte 0x%x, b %u.", size,
|
||||
(long long)data_pos, (long long)bit,
|
||||
byte ? *byte : -1, b);
|
||||
for (; bit < size && data_pos + bit < pass_end;
|
||||
bit &= ~7ull, bit += 8) {
|
||||
byte = buf + (bit >> 3);
|
||||
if (*byte == 0xff)
|
||||
continue;
|
||||
/* Note: ffz() result must be zero based. */
|
||||
b = ntfs_ffz((unsigned long)*byte);
|
||||
if (b < 8 && b >= (bit & 7)) {
|
||||
free(buf);
|
||||
return data_pos + (bit & ~7ull) + b;
|
||||
}
|
||||
}
|
||||
ntfs_debug("After inner for loop: size 0x%x, "
|
||||
"data_pos 0x%llx, bit 0x%llx, "
|
||||
"*byte 0x%x, b %u.", size,
|
||||
(long long)data_pos, (long long)bit,
|
||||
byte ? *byte : -1, b);
|
||||
data_pos += size;
|
||||
/*
|
||||
* If the end of the pass has not been reached yet,
|
||||
* continue searching the mft bitmap for a zero bit.
|
||||
*/
|
||||
if (data_pos < pass_end)
|
||||
continue;
|
||||
}
|
||||
/* Do the next pass. */
|
||||
pass++;
|
||||
if (pass == 2) {
|
||||
/*
|
||||
* Starting the second pass, in which we scan the first
|
||||
* part of the zone which we omitted earlier.
|
||||
*/
|
||||
pass_end = pass_start;
|
||||
data_pos = pass_start = 24;
|
||||
ntfs_debug("pass %i, pass_start 0x%llx, pass_end "
|
||||
"0x%llx.", pass, (long long)pass_start,
|
||||
(long long)pass_end);
|
||||
}
|
||||
}
|
||||
/* No free mft records in currently initialized mft bitmap. */
|
||||
free(buf);
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster
|
||||
* @vol: volume on which to extend the mft bitmap attribute
|
||||
|
@ -471,7 +604,7 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol)
|
|||
status.added_run = 1;
|
||||
ntfs_debug("Adding one run to mft bitmap.");
|
||||
/* Find the last run in the new runlist. */
|
||||
for (rl = mftbmp_na->rl; rl[1].length; rl++)
|
||||
for (; rl[1].length; rl++)
|
||||
;
|
||||
}
|
||||
/*
|
||||
|
@ -529,7 +662,8 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol)
|
|||
if (ntfs_mapping_pairs_build(vol, (u8*)a +
|
||||
le16_to_cpu(a->mapping_pairs_offset), mp_size, rl2, ll,
|
||||
NULL)) {
|
||||
ntfs_error(vol->sb, "Failed to build mapping pairs.");
|
||||
ntfs_error(vol->sb, "Failed to build mapping pairs array for "
|
||||
"mft bitmap attribute.");
|
||||
errno = EIO;
|
||||
goto undo_alloc;
|
||||
}
|
||||
|
@ -703,9 +837,234 @@ err_out:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static inline unsigned int ntfs_ffz(unsigned int word)
|
||||
/**
|
||||
* ntfs_mft_data_extend_allocation - extend mft data attribute
|
||||
* @vol: volume on which to extend the mft data attribute
|
||||
*
|
||||
* Extend the mft data attribute on the ntfs volume @vol by 16 mft records
|
||||
* worth of clusters or if not enough space for this by one mft record worth
|
||||
* of clusters.
|
||||
*
|
||||
* Note: Only changes allocated_size, i.e. does not touch initialized_size or
|
||||
* data_size.
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code.
|
||||
*/
|
||||
static int ntfs_mft_data_extend_allocation(ntfs_volume *vol)
|
||||
{
|
||||
return ffs(~word) - 1;
|
||||
LCN lcn;
|
||||
VCN old_last_vcn;
|
||||
s64 min_nr, nr, ll = 0; /* silence compiler warning */
|
||||
ntfs_attr *mft_na;
|
||||
runlist_element *rl, *rl2;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
MFT_RECORD *m = NULL; /* silence compiler warning */
|
||||
ATTR_RECORD *a = NULL; /* silence compiler warning */
|
||||
int err, mp_size;
|
||||
u32 old_alen = 0; /* silence compiler warning */
|
||||
BOOL mp_rebuilt = FALSE;
|
||||
|
||||
ntfs_debug("Extending mft data allocation.");
|
||||
mft_na = vol->mft_na;
|
||||
/*
|
||||
* Determine the preferred allocation location, i.e. the last lcn of
|
||||
* the mft data attribute. The allocated size of the mft data
|
||||
* attribute cannot be zero so we are ok to do this.
|
||||
*/
|
||||
rl = ntfs_attr_find_vcn(mft_na,
|
||||
(mft_na->allocated_size - 1) >> vol->cluster_size);
|
||||
if (!rl || !rl->length || rl->lcn < 0) {
|
||||
ntfs_error(vol->sb, "Failed to determine last allocated "
|
||||
"cluster of mft data attribute.");
|
||||
if (rl)
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
lcn = rl->lcn + rl->length;
|
||||
ntfs_debug("Last lcn of mft data attribute is 0x%llx.", (long long)lcn);
|
||||
/* Minimum allocation is one mft record worth of clusters. */
|
||||
if (vol->mft_record_size <= vol->cluster_size)
|
||||
min_nr = 1;
|
||||
else
|
||||
min_nr = vol->mft_record_size >> vol->cluster_size_bits;
|
||||
/* Want to allocate 16 mft records worth of clusters. */
|
||||
nr = vol->mft_record_size << 4 >> vol->cluster_size_bits;
|
||||
if (!nr)
|
||||
nr = 1;
|
||||
ntfs_debug("Trying mft data allocation with default cluster count "
|
||||
"%lli.", (long long)nr);
|
||||
old_last_vcn = rl[1].vcn;
|
||||
do {
|
||||
rl2 = ntfs_cluster_alloc(vol, nr, lcn, MFT_ZONE, old_last_vcn);
|
||||
if (rl2)
|
||||
break;
|
||||
if (errno != ENOSPC || nr == min_nr) {
|
||||
ntfs_error(vol->sb, "Failed to allocate the minimal "
|
||||
"number of clusters (%lli) for the "
|
||||
"mft data attribute.", (long long)nr);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* There is not enough space to do the allocation, but there
|
||||
* might be enough space to do a minimal allocation so try that
|
||||
* before failing.
|
||||
*/
|
||||
nr = min_nr;
|
||||
ntfs_debug("Retrying mft data allocation with minimal cluster "
|
||||
"count %lli.", (long long)nr);
|
||||
} while (1);
|
||||
rl = ntfs_runlists_merge(mft_na->rl, rl2);
|
||||
if (!rl) {
|
||||
err = errno;
|
||||
ntfs_error(vol->sb, "Failed to merge runlists for mft data "
|
||||
"attribute.");
|
||||
if (ntfs_cluster_free_from_rl(vol, rl2))
|
||||
ntfs_error(vol->sb, "Failed to dealocate clusters "
|
||||
"from the mft data attribute.%s", es);
|
||||
free(rl2);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
mft_na->rl = rl;
|
||||
ntfs_debug("Allocated %lli clusters.", nr);
|
||||
/* Find the last run in the new runlist. */
|
||||
for (; rl[1].length; rl++)
|
||||
;
|
||||
/* Update the attribute record as well. */
|
||||
ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_error(vol->sb, "Failed to get search context.");
|
||||
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_error(vol->sb, "Failed to find last attribute extent of "
|
||||
"mft data attribute.");
|
||||
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_error(vol->sb, "Failed to determine previous last "
|
||||
"allocated cluster of mft data attribute.");
|
||||
if (rl2)
|
||||
errno = EIO;
|
||||
goto undo_alloc;
|
||||
}
|
||||
/* Get the size for the new mapping pairs array for this extent. */
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll);
|
||||
if (mp_size <= 0) {
|
||||
ntfs_error(vol->sb, "Get size for mapping pairs failed for "
|
||||
"mft data attribute extent.");
|
||||
goto undo_alloc;
|
||||
}
|
||||
/* Expand the attribute record if necessary. */
|
||||
old_alen = le32_to_cpu(a->length);
|
||||
if (ntfs_attr_record_resize(m, a,
|
||||
mp_size + le16_to_cpu(a->mapping_pairs_offset))) {
|
||||
if (errno != ENOSPC) {
|
||||
ntfs_error(vol->sb, "Failed to resize attribute "
|
||||
"record for mft data attribute.");
|
||||
goto undo_alloc;
|
||||
}
|
||||
// TODO: Deal with this by moving this extent to a new mft
|
||||
// record or by starting a new extent in a new mft record.
|
||||
// Note: Use the special reserved mft records and ensure that
|
||||
// this extent is not required to find the mft record in
|
||||
// question.
|
||||
ntfs_error(vol->sb, "Not enough space in this mft record to "
|
||||
"accomodate extended mft data attribute "
|
||||
"extent. Cannot handle this yet.");
|
||||
errno = ENOTSUP;
|
||||
goto undo_alloc;
|
||||
}
|
||||
mp_rebuilt = TRUE;
|
||||
/*
|
||||
* Generate the mapping pairs array directly into the attribute record.
|
||||
*/
|
||||
if (ntfs_mapping_pairs_build(vol,
|
||||
(u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp_size,
|
||||
rl2, ll, NULL)) {
|
||||
ntfs_error(vol->sb, "Failed to build mapping pairs array of "
|
||||
"mft data attribute.");
|
||||
errno = EIO;
|
||||
goto undo_alloc;
|
||||
}
|
||||
/* Update the highest_vcn. */
|
||||
a->highest_vcn = scpu_to_le64(rl[1].vcn - 1);
|
||||
/*
|
||||
* We now have extended the mft data allocated_size by nr clusters.
|
||||
* Reflect this in the ntfs_attr structure and the attribute record.
|
||||
* @rl is the last (non-terminator) runlist element of mft data
|
||||
* attribute.
|
||||
*/
|
||||
if (a->lowest_vcn) {
|
||||
/*
|
||||
* We are not in the first attribute extent, switch to it, but
|
||||
* first ensure the changes will make it to disk later.
|
||||
*/
|
||||
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_error(vol->sb, "Failed to find first attribute "
|
||||
"extent of mft data attribute.");
|
||||
goto restore_undo_alloc;
|
||||
}
|
||||
a = ctx->attr;
|
||||
}
|
||||
mft_na->allocated_size += nr << vol->cluster_size_bits;
|
||||
a->allocated_size = scpu_to_le64(mft_na->allocated_size);
|
||||
/* Ensure the changes make it to disk. */
|
||||
ntfs_inode_mark_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return 0;
|
||||
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_error(vol->sb, "Failed to find last attribute extent of "
|
||||
"mft data attribute.%s", es);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
mft_na->allocated_size += nr << vol->cluster_size_bits;
|
||||
/*
|
||||
* The only thing that is now wrong is ->allocated_size of the
|
||||
* base attribute extent which chkdsk should be able to fix.
|
||||
*/
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
m = ctx->mrec;
|
||||
a = ctx->attr;
|
||||
a->highest_vcn = scpu_to_le64(old_last_vcn - 1);
|
||||
errno = err;
|
||||
undo_alloc:
|
||||
err = errno;
|
||||
if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0)
|
||||
ntfs_error(vol->sb, "Failed to free clusters from mft data "
|
||||
"attribute.%s", es);
|
||||
if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn))
|
||||
ntfs_error(vol->sb, "Failed to truncate mft data attribute "
|
||||
"runlist.%s", 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_error(vol->sb, "Failed to restore mapping pairs "
|
||||
"array.%s", es);
|
||||
if (ntfs_attr_record_resize(m, a, old_alen))
|
||||
ntfs_error(vol->sb, "Failed to restore attribute "
|
||||
"record.%s", es);
|
||||
ntfs_inode_mark_dirty(ctx->ntfs_ino);
|
||||
}
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -791,26 +1150,17 @@ static inline unsigned int ntfs_ffz(unsigned int word)
|
|||
* when reading the bitmap but if we are careful, we should be able to avoid
|
||||
* all problems.
|
||||
*/
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni)
|
||||
{
|
||||
s64 nr_allocated_mft_records, pass_end, ll, ll2, data_pos, pass_start;
|
||||
s64 last_read_pos, bit;
|
||||
LCN lcn;
|
||||
s64 ll, ll2, bit;
|
||||
ntfs_attr *mft_na, *mftbmp_na;
|
||||
u8 *buf, *byte;
|
||||
runlist_element *rl, *rl2;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
MFT_RECORD *m;
|
||||
ATTR_RECORD *a;
|
||||
ntfs_inode *ni;
|
||||
unsigned long mft_rec_size;
|
||||
unsigned int size, buf_size;
|
||||
int err, mp_size;
|
||||
int err;
|
||||
u16 seq_no;
|
||||
u8 pass, b;
|
||||
|
||||
if (base_ni)
|
||||
ntfs_debug("Entering (allocating an extent mft record for "
|
||||
|
@ -822,115 +1172,16 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni)
|
|||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
size = PAGE_SIZE;
|
||||
if (size < vol->mft_record_size)
|
||||
size = vol->mft_record_size;
|
||||
buf = (u8*)malloc(size);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
mft_na = vol->mft_na;
|
||||
mftbmp_na = vol->mftbmp_na;
|
||||
/* Determine the number of allocated mft records in the mft. */
|
||||
nr_allocated_mft_records = mft_na->allocated_size >>
|
||||
vol->mft_record_size_bits;
|
||||
ntfs_debug("nr_allocated_mft_records 0x%llx.",
|
||||
(long long)nr_allocated_mft_records);
|
||||
/*
|
||||
* Set the end of the pass making sure we do not overflow the mft
|
||||
* bitmap.
|
||||
*/
|
||||
pass_end = nr_allocated_mft_records;
|
||||
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 >= pass_end) {
|
||||
data_pos = 24;
|
||||
pass = 2;
|
||||
bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni);
|
||||
if (bit >= 0) {
|
||||
ntfs_debug("Found free record (#1), bit 0x%llx.",
|
||||
(long long)bit);
|
||||
goto found_free_rec;
|
||||
}
|
||||
pass_start = data_pos;
|
||||
ntfs_debug("Starting bitmap search: pass %u, pass_start 0x%llx, "
|
||||
"pass_end 0x%llx, data_pos 0x%llx.", pass,
|
||||
(long long)pass_start, (long long)pass_end,
|
||||
(long long)data_pos);
|
||||
#ifdef DEBUG
|
||||
byte = NULL;
|
||||
b = 0;
|
||||
#endif
|
||||
/* Loop until a free mft record is found. */
|
||||
size = PAGE_SIZE;
|
||||
ll = nr_allocated_mft_records >> 3;
|
||||
if (size > ll)
|
||||
size = ll;
|
||||
for (;; size = PAGE_SIZE) {
|
||||
last_read_pos = data_pos >> 3;
|
||||
ll = ntfs_attr_pread(mftbmp_na, last_read_pos, size, buf);
|
||||
if (ll < 0) {
|
||||
ntfs_error(vol->sb, "Failed to read mft bitmap "
|
||||
"attribute, aborting.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_debug("Read 0x%llx bytes.", (long long)ll);
|
||||
/* If we read at least one byte, search @buf for a zero bit. */
|
||||
if (ll) {
|
||||
buf_size = ll << 3;
|
||||
bit = data_pos & 7;
|
||||
data_pos &= ~7ull;
|
||||
ntfs_debug("Before inner for loop: buf_size 0x%x, "
|
||||
"data_pos 0x%llx, bit 0x%llx, "
|
||||
"*byte 0x%x, b %u.", buf_size,
|
||||
(long long)data_pos, (long long)bit,
|
||||
byte ? *byte : -1, b);
|
||||
for (; bit < buf_size && data_pos + bit < pass_end;
|
||||
bit &= ~7ull, bit += 8) {
|
||||
byte = buf + (bit >> 3);
|
||||
if (*byte == 0xff)
|
||||
continue;
|
||||
/* Note: ffz() result must be zero based. */
|
||||
b = ntfs_ffz((unsigned long)*byte);
|
||||
if (b < 8 && b >= (bit & 7)) {
|
||||
bit = data_pos + (bit & ~7ull) + b;
|
||||
ntfs_debug("Found free record (#1), "
|
||||
"bit 0x%llx.",
|
||||
(long long)bit);
|
||||
goto found_free_rec;
|
||||
}
|
||||
}
|
||||
ntfs_debug("After inner for loop: buf_size 0x%x, "
|
||||
"data_pos 0x%llx, bit 0x%llx, "
|
||||
"*byte 0x%x, b %u.", buf_size,
|
||||
(long long)data_pos, (long long)bit,
|
||||
byte ? *byte : -1, b);
|
||||
data_pos += buf_size;
|
||||
/*
|
||||
* If the end of the pass has not been reached yet,
|
||||
* continue searching the mft bitmap for a zero bit.
|
||||
*/
|
||||
if (data_pos < pass_end)
|
||||
continue;
|
||||
}
|
||||
/* Finished with the current pass. */
|
||||
ntfs_debug("At pass_done.");
|
||||
/* If finished the second pass we are done searching. */
|
||||
if (pass == 2)
|
||||
break;
|
||||
/*
|
||||
* Finished with the first pass. Now do the second pass, in
|
||||
* which we scan the first part of the zone we omitted earlier.
|
||||
*/
|
||||
ntfs_debug("Done pass 1, switching to pass 2.");
|
||||
pass = 2;
|
||||
pass_end = pass_start;
|
||||
data_pos = pass_start = 24;
|
||||
ntfs_debug("pass %i, pass_start 0x%llx, pass_end 0x%llx.",
|
||||
pass, (long long)pass_start,
|
||||
(long long)pass_end);
|
||||
}
|
||||
ntfs_debug("Done pass 2.");
|
||||
if (errno != ENOSPC)
|
||||
return NULL;
|
||||
/*
|
||||
* No free mft records left. If the mft bitmap already covers more
|
||||
* than the currently used mft records, the next records are all free,
|
||||
|
@ -988,238 +1239,39 @@ ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni)
|
|||
ntfs_debug("Found free record (#3), bit 0x%llx.", (long long)bit);
|
||||
found_free_rec:
|
||||
/* @bit is the found free mft record, allocate it in the mft bitmap. */
|
||||
vol->mft_data_pos = bit;
|
||||
ntfs_debug("At found_free_rec.");
|
||||
ll = ntfs_attr_pread(mftbmp_na, bit >> 3, 1, buf);
|
||||
if (ll < 1) {
|
||||
ntfs_error(vol->sb, "Failed to read from mft bitmap.");
|
||||
if (!ll)
|
||||
errno = EIO;
|
||||
if (ntfs_bitmap_set_bit(mftbmp_na, bit)) {
|
||||
ntfs_error(vol->sb, "Failed to allocate bit in mft bitmap.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_debug("Read 1 byte from mft bitmap.");
|
||||
/* Check our bit is really zero and set it. */
|
||||
b = 1 << (bit & 7);
|
||||
//BUG_ON(*buf & b);
|
||||
if (*buf & b)
|
||||
NTFS_BUG("Bit in mft bitmap is not zero!");
|
||||
*buf |= b;
|
||||
ll = ntfs_attr_pwrite(mftbmp_na, bit >> 3, 1, buf);
|
||||
if (ll < 1) {
|
||||
if (!ll)
|
||||
errno = EIO;
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_debug("Wrote 1 byte to mft bitmap.");
|
||||
ntfs_debug("Set bit 0x%llx in mft bitmap.", (long long)bit);
|
||||
/* The mft bitmap is now uptodate. Deal with mft data attribute now. */
|
||||
ll = (bit + 1) << vol->mft_record_size_bits;
|
||||
if (ll <= mft_na->initialized_size) {
|
||||
/* The allocated record is already initialized. We are done! */
|
||||
ntfs_debug("Allocated mft record already initialized!");
|
||||
ntfs_debug("Allocated mft record already initialized.");
|
||||
goto mft_rec_already_initialized;
|
||||
}
|
||||
ntfs_debug("Allocated mft record needs to be initialized.");
|
||||
ntfs_debug("Initializing allocated mft record.");
|
||||
/* The mft record is outside the initialized data. */
|
||||
mft_rec_size = vol->mft_record_size;
|
||||
/* Preserve old values for undo purposes. */
|
||||
//FIXME:
|
||||
// s64 old_data_allocated;
|
||||
// LCN old_data_len;
|
||||
// int old_data_rlen;
|
||||
// old_data_allocated = mft_na->allocated_size;
|
||||
// old_data_rlen = mft_na->d.r.len - 1;
|
||||
// old_data_len = mft_na->d.r.runlist[old_data_rlen].len;
|
||||
/*
|
||||
* If necessary, extend the mft until it covers the allocated record.
|
||||
* The loop is only actually used when a freshly formatted volume is
|
||||
* first written to so it optimizes away nicely in the common case.
|
||||
* Extend the mft data attribute until it covers the allocated record.
|
||||
* The loop is only actually traversed more than once when a freshly
|
||||
* formatted volume is first written to so it optimizes away nicely in
|
||||
* the common case.
|
||||
*/
|
||||
ntfs_debug("Status of mft data before extension: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(long long)mft_na->allocated_size,
|
||||
(long long)mft_na->data_size,
|
||||
(long long)mft_na->initialized_size);
|
||||
while (ll > mft_na->allocated_size) {
|
||||
s64 nr, min_nr;
|
||||
|
||||
ntfs_debug("Extending mft data allocation, status of mft "
|
||||
"data: allocated_size 0x%llx, "
|
||||
"data_size 0x%llx, initialized_size 0x%llx.",
|
||||
(long long)mft_na->allocated_size,
|
||||
(long long)mft_na->data_size,
|
||||
(long long)mft_na->initialized_size);
|
||||
/*
|
||||
* Determine the preferred allocation location, i.e. the last
|
||||
* lcn of the mft data attribute. The allocated size of the
|
||||
* mft data attribute cannot be zero so we are ok to do this.
|
||||
*/
|
||||
rl = ntfs_attr_find_vcn(mft_na, (mft_na->allocated_size - 1) >>
|
||||
vol->cluster_size);
|
||||
if (!rl || !rl->length || rl->lcn < 0) {
|
||||
ntfs_error(vol->sb, "Failed to determine last "
|
||||
"allocated cluster of mft data "
|
||||
"attribute.");
|
||||
if (!rl->length || rl->lcn < 0)
|
||||
errno = EIO;
|
||||
goto undo_mftbmp_alloc_err_out;
|
||||
}
|
||||
lcn = rl->lcn + rl->length;
|
||||
ntfs_debug("Last lcn of mft data attribute is 0x%llx.",
|
||||
(long long)lcn);
|
||||
/* Minimum allocation is one mft record worth of clusters. */
|
||||
if (mft_rec_size <= vol->cluster_size)
|
||||
min_nr = 1;
|
||||
else
|
||||
min_nr = mft_rec_size >> vol->cluster_size_bits;
|
||||
/* Want to allocate 16 mft records worth of clusters. */
|
||||
nr = mft_rec_size << 4 >> vol->cluster_size_bits;
|
||||
if (!nr)
|
||||
nr = 1;
|
||||
ntfs_debug("Trying mft data allocation with default "
|
||||
"cluster count %lli.", (long long)nr);
|
||||
retry_mft_data_allocation:
|
||||
rl2 = ntfs_cluster_alloc(vol, nr, lcn, MFT_ZONE, rl[1].vcn);
|
||||
if (!rl2) {
|
||||
/*
|
||||
* There is not enough space to do the allocation, but
|
||||
* there might be enough space to do a minimal
|
||||
* allocation so try that before failing.
|
||||
*/
|
||||
if (errno == ENOSPC && nr > min_nr) {
|
||||
nr = min_nr;
|
||||
ntfs_debug("Retrying mft data allocation with "
|
||||
"minimal cluster count %lli.",
|
||||
(long long)nr);
|
||||
goto retry_mft_data_allocation;
|
||||
}
|
||||
ntfs_error(vol->sb, "Failed to allocate a cluster for "
|
||||
"the mft data attribute.");
|
||||
goto undo_mftbmp_alloc_err_out;
|
||||
}
|
||||
rl = ntfs_runlists_merge(mft_na->rl, rl2);
|
||||
if (!rl) {
|
||||
err = errno;
|
||||
ntfs_error(vol->sb, "Failed to merge runlists for mft "
|
||||
"data attribute.");
|
||||
if (ntfs_cluster_free_from_rl(vol, rl2))
|
||||
ntfs_error(vol->sb, "Failed to dealocate "
|
||||
"clusters. Run chkdsk.");
|
||||
free(rl2);
|
||||
errno = err;
|
||||
goto undo_mftbmp_alloc_err_out;
|
||||
}
|
||||
mft_na->rl = rl;
|
||||
ntfs_debug("Allocated %lli clusters starting.", nr);
|
||||
/* Determine the last lcn of the mft data attribute. */
|
||||
rl = ntfs_attr_find_vcn(mft_na, (mft_na->allocated_size - 1) >>
|
||||
vol->cluster_size);
|
||||
if (!rl || !rl->length || rl->lcn < 0) {
|
||||
ntfs_error(vol->sb, "Failed to determine last "
|
||||
"allocated cluster of mft data "
|
||||
"attribute.");
|
||||
if (!rl->length || rl->lcn < 0)
|
||||
errno = EIO;
|
||||
// FIXME: Truncate mft_na->rl back to the old state and
|
||||
// release the clusters.
|
||||
goto undo_mftbmp_alloc_err_out;
|
||||
}
|
||||
lcn = rl->lcn + rl->length;
|
||||
/* Update the attribute record as well. */
|
||||
ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_error(vol->sb, "Failed to get search context.");
|
||||
goto trunc_mft_rl_shrink_mftbmp_alloc_err_out;
|
||||
}
|
||||
if (ntfs_attr_lookup(mft_na->type, mft_na->name,
|
||||
mft_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) {
|
||||
ntfs_error(vol->sb, "Failed to find last attribute "
|
||||
"extent of mft data attribute.");
|
||||
goto put_err_out;
|
||||
}
|
||||
m = ctx->mrec;
|
||||
a = ctx->attr;
|
||||
ll2 = sle64_to_cpu(a->lowest_vcn);
|
||||
rl2 = ntfs_attr_find_vcn(mft_na, ll2);
|
||||
if (!rl2 || !rl2->length) {
|
||||
ntfs_error(vol->sb, "Failed to determine previous "
|
||||
"last allocated cluster of mft data "
|
||||
"attribute.");
|
||||
if (!rl2->length)
|
||||
errno = EIO;
|
||||
goto put_err_out;
|
||||
}
|
||||
/*
|
||||
* Get the size for the new mapping pairs array for this
|
||||
* extent.
|
||||
*/
|
||||
mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll2);
|
||||
if (mp_size <= 0) {
|
||||
ntfs_error(vol->sb, "Get size for mapping pairs "
|
||||
"failed for mft bitmap attribute "
|
||||
"extent.");
|
||||
goto put_err_out;
|
||||
}
|
||||
/* Expand the attribute record if necessary. */
|
||||
if (ntfs_attr_record_resize(m, a, mp_size +
|
||||
le16_to_cpu(a->mapping_pairs_offset))) {
|
||||
if (errno != ENOSPC) {
|
||||
ntfs_error(vol->sb, "Failed to resize "
|
||||
"attribute record for mft "
|
||||
"data attribute.");
|
||||
goto put_err_out;
|
||||
}
|
||||
// TODO: Deal with this by moving this extent to a new
|
||||
// mft record or by starting a new extent in a new mft
|
||||
// record. Note: Use the special reserved mft records
|
||||
// and ensure that this extent is not required to find
|
||||
// the mft record in question.
|
||||
ntfs_error(vol->sb, "Not enough space in this mft "
|
||||
"record to accomodate extended mft "
|
||||
"data attribute extent. Cannot "
|
||||
"handle this yet.");
|
||||
errno = ENOTSUP;
|
||||
goto put_err_out;
|
||||
}
|
||||
/*
|
||||
* Generate the mapping pairs array directly into the attribute
|
||||
* record.
|
||||
*/
|
||||
if (ntfs_mapping_pairs_build(vol, (u8*)a +
|
||||
le16_to_cpu(a->mapping_pairs_offset), mp_size,
|
||||
rl2, ll2, NULL)) {
|
||||
ntfs_error(vol->sb, "Failed to build mapping pairs. "
|
||||
"Run chkdsk.");
|
||||
errno = EIO;
|
||||
goto put_err_out;
|
||||
}
|
||||
/* Update the highest_vcn. */
|
||||
a->highest_vcn = scpu_to_le64(rl[1].vcn - 1);
|
||||
/*
|
||||
* We now have extended the mft data allocated_size by nr
|
||||
* clusters. Reflect this in the ntfs_attr structure and the
|
||||
* attribute record. @rl is the last (non-terminator) runlist
|
||||
* element of mft data attribute.
|
||||
*/
|
||||
mft_na->allocated_size += nr << vol->cluster_size_bits;
|
||||
if (a->lowest_vcn) {
|
||||
/*
|
||||
* We are not in the first attribute extent, switch to
|
||||
* it, but first ensure the changes will make it to
|
||||
* disk later.
|
||||
*/
|
||||
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_error(vol->sb, "Failed to find first "
|
||||
"attribute extent of mft data "
|
||||
"attribute.");
|
||||
goto put_err_out;
|
||||
}
|
||||
a = ctx->attr;
|
||||
}
|
||||
a->allocated_size = scpu_to_le64(mft_na->allocated_size);
|
||||
/* Ensure the changes make it to disk. */
|
||||
ntfs_inode_mark_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
ntfs_debug("After extending mft data allocation, status of "
|
||||
"mft data: allocated_size 0x%llx, "
|
||||
"data_size 0x%llx, initialized_size 0x%llx.",
|
||||
if (ntfs_mft_data_extend_allocation(vol))
|
||||
goto undo_mftbmp_alloc;
|
||||
ntfs_debug("Status of mft data after allocation extension: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(long long)mft_na->allocated_size,
|
||||
(long long)mft_na->data_size,
|
||||
(long long)mft_na->initialized_size);
|
||||
|
@ -1229,7 +1281,7 @@ retry_mft_data_allocation:
|
|||
// old_data_size = mft_na->data_size;
|
||||
/*
|
||||
* Extend mft data initialized size (and data size of course) to reach
|
||||
* the allocated mft record formatting the mft records allong the way.
|
||||
* the allocated mft record, formatting the mft records allong the way.
|
||||
*/
|
||||
while (ll > mft_na->initialized_size) {
|
||||
ll2 = mft_na->initialized_size >> vol->mft_record_size_bits;
|
||||
|
@ -1261,7 +1313,7 @@ retry_mft_data_allocation:
|
|||
/* Ensure the changes make it to disk. */
|
||||
ntfs_inode_mark_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
ntfs_debug("After mft record initialization, status of mft data: "
|
||||
ntfs_debug("Status of mft data after mft record initialization: "
|
||||
"allocated_size 0x%llx, data_size 0x%llx, "
|
||||
"initialized_size 0x%llx.",
|
||||
(long long)mft_na->allocated_size,
|
||||
|
@ -1342,19 +1394,15 @@ mft_rec_already_initialized:
|
|||
/* Return the opened, allocated inode of the allocated mft record. */
|
||||
ntfs_debug("Returning opened, allocated %sinode 0x%llx.",
|
||||
base_ni ? "extent " : "", (long long)bit);
|
||||
free(buf);
|
||||
return ni;
|
||||
|
||||
put_err_out:
|
||||
undo_mftbmp_alloc:
|
||||
err = errno;
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
if (ntfs_bitmap_clear_bit(mftbmp_na, bit))
|
||||
ntfs_error(vol->sb, "Failed to clear bit in mft bitmap.%s", es);
|
||||
errno = err;
|
||||
err_out:
|
||||
err = errno;
|
||||
free(buf);
|
||||
if (err)
|
||||
errno = err;
|
||||
else
|
||||
if (!errno)
|
||||
errno = EIO;
|
||||
return NULL;
|
||||
|
||||
|
@ -1386,7 +1434,6 @@ undo_data_init_err_out:
|
|||
|
||||
trunc_mft_rl_shrink_mftbmp_alloc_err_out:
|
||||
|
||||
undo_mftbmp_alloc_err_out:
|
||||
return NULL;
|
||||
|
||||
#if 0
|
||||
|
|
Loading…
Reference in New Issue