Fixed truncating an extended bad cluster list in ntfsresize

This fixes the case where the original bad cluster list requires extents.
The list is processed globally, no relocation is done, and the list is
truncated, possibly fitting into fewer extents.
This commit is contained in:
Jean-Pierre André 2015-11-20 16:04:47 +01:00
parent ede1808ba6
commit 3c964b6af3

View File

@ -2112,10 +2112,17 @@ static int handle_mftdata(ntfs_resize_t *resize, int do_mftdata)
static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata)
{
int ret;
leMFT_REF lemref;
MFT_REF base_mref;
if (!(resize->ctx = attr_get_search_ctx(NULL, resize->mrec)))
exit(1);
lemref = resize->mrec->base_mft_record;
if (lemref)
base_mref = MREF(le64_to_cpu(lemref));
else
base_mref = resize->mref;
while (!ntfs_attrs_walk(resize->ctx)) {
if (resize->ctx->attr->type == AT_END)
break;
@ -2133,6 +2140,11 @@ static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata)
resize->ctx->attr->type == AT_DATA)
continue;
/* Do not relocate bad clusters */
if ((base_mref == FILE_BadClus)
&& (resize->ctx->attr->type == AT_DATA))
continue;
relocate_attribute(resize);
}
@ -2310,60 +2322,6 @@ static void advise_on_resize(ntfs_resize_t *resize)
print_advise(vol, resize->last_unsupp);
}
static void rl_expand(runlist **rl, const VCN last_vcn)
{
int len;
runlist *p = *rl;
len = rl_items(p) - 1;
if (len <= 0)
err_exit("rl_expand: bad runlist length: %d\n", len);
if (p[len].vcn > last_vcn)
err_exit("rl_expand: length is already more than requested "
"(%lld > %lld)\n",
(long long)p[len].vcn, (long long)last_vcn);
if (p[len - 1].lcn == LCN_HOLE) {
p[len - 1].length += last_vcn - p[len].vcn;
p[len].vcn = last_vcn;
} else if (p[len - 1].lcn >= 0) {
p = realloc(*rl, (++len + 1) * sizeof(runlist_element));
if (!p)
perr_exit("rl_expand: realloc");
p[len - 1].lcn = LCN_HOLE;
p[len - 1].length = last_vcn - p[len - 1].vcn;
rl_set(p + len, last_vcn, LCN_ENOENT, 0LL);
*rl = p;
} else
err_exit("rl_expand: bad LCN: %lld\n",
(long long)p[len - 1].lcn);
}
static void rl_truncate(runlist **rl, const VCN last_vcn)
{
int len;
VCN vcn;
len = rl_items(*rl) - 1;
if (len <= 0)
err_exit("rl_truncate: bad runlist length: %d\n", len);
vcn = (*rl)[len].vcn;
if (vcn < last_vcn)
rl_expand(rl, last_vcn);
else if (vcn > last_vcn)
if (ntfs_rl_truncate(rl, last_vcn) == -1)
perr_exit("ntfs_rl_truncate");
}
/**
* bitmap_file_data_fixup
*
@ -2376,6 +2334,37 @@ static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm)
ntfs_bit_set(bm->bm, (u64)cluster, 1);
}
/*
* Open the attribute $BadClust:$Bad and get its runlist
*/
static ntfs_attr *open_badclust_bad_attr(ntfs_attr_search_ctx *ctx)
{
ntfs_inode *base_ni;
ntfs_attr *na;
static ntfschar Bad[4] = {
const_cpu_to_le16('$'), const_cpu_to_le16('B'),
const_cpu_to_le16('a'), const_cpu_to_le16('d')
} ;
base_ni = ctx->base_ntfs_ino;
if (!base_ni)
base_ni = ctx->ntfs_ino;
na = ntfs_attr_open(base_ni, AT_DATA, Bad, 4);
if (!na) {
err_printf("Could not access the bad sector list\n");
} else {
if (ntfs_attr_map_whole_runlist(na) || !na->rl) {
err_printf("Could not decode the bad sector list\n");
ntfs_attr_close(na);
ntfs_inode_close(base_ni);
na = (ntfs_attr*)NULL;
}
}
return (na);
}
/**
* truncate_badclust_bad_attr
*
@ -2386,27 +2375,26 @@ static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm)
*/
static void truncate_badclust_bad_attr(ntfs_resize_t *resize)
{
ATTR_RECORD *a;
runlist *rl_bad;
ntfs_inode *base_ni;
ntfs_attr *na;
s64 nr_clusters = resize->new_volume_size;
ntfs_volume *vol = resize->vol;
a = resize->ctx->attr;
if (!a->non_resident)
/* FIXME: handle resident attribute value */
err_exit("Resident attribute in $BadClust isn't supported!\n");
na = open_badclust_bad_attr(resize->ctx);
if (!na) {
err_printf("Could not access the bad sector list\n");
exit(1);
}
base_ni = na->ni;
if (ntfs_attr_truncate(na,nr_clusters << vol->cluster_size_bits)) {
err_printf("Could not adjust the bad sector list\n");
exit(1);
}
na->ni->flags |= FILE_ATTR_SPARSE_FILE;
NInoFileNameSetDirty(na->ni);
if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, a, NULL)))
perr_exit("ntfs_mapping_pairs_decompress");
rl_truncate(&rl_bad, nr_clusters);
a->highest_vcn = cpu_to_sle64(nr_clusters - 1LL);
a->allocated_size = cpu_to_sle64(nr_clusters * vol->cluster_size);
a->data_size = cpu_to_sle64(nr_clusters * vol->cluster_size);
if (!replace_attribute_runlist(resize, rl_bad))
free(rl_bad);
ntfs_attr_close(na);
ntfs_inode_mark_dirty(base_ni);
}
/**
@ -2568,32 +2556,19 @@ static void close_inode_and_context(ntfs_attr_search_ctx *ctx)
static int check_bad_sectors(ntfs_volume *vol)
{
ntfs_attr_search_ctx *ctx;
ntfs_inode *base_ni;
ntfs_attr *na;
runlist *rl;
s64 i, badclusters = 0;
static le16 Bad[4] = {
const_cpu_to_le16('$'), const_cpu_to_le16('B'),
const_cpu_to_le16('a'), const_cpu_to_le16('d')
} ;
ntfs_log_verbose("Checking for bad sectors ...\n");
lookup_data_attr(vol, FILE_BadClus, "$Bad", &ctx);
base_ni = ctx->base_ntfs_ino;
if (!base_ni)
base_ni = ctx->ntfs_ino;
na = ntfs_attr_open(base_ni, AT_DATA, Bad, 4);
na = open_badclust_bad_attr(ctx);
if (!na) {
err_printf("Could not access the bad sector list\n");
exit(1);
}
if (ntfs_attr_map_whole_runlist(na) || !na->rl) {
err_printf("Could not decode the bad sector list\n");
exit(1);
}
rl = na->rl;
for (i = 0; rl[i].length; i++) {
/* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */
@ -2644,10 +2619,6 @@ static void truncate_badclust_file(ntfs_resize_t *resize)
resize->mref = FILE_BadClus;
truncate_badclust_bad_attr(resize);
if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no,
resize->ctx->mrec))
perr_exit("Couldn't update $BadClust");
#if CLEAN_EXIT
close_inode_and_context(resize->ctx);
#else