mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-23 18:14:24 +08:00
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:
parent
ede1808ba6
commit
3c964b6af3
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user