mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-23 10:04:00 +08:00
Fixed special case of decompressing a runlist
When the unreadable directory has an ATTRIBUTE_LIST attribute and an INDEX_ALLOCATION attribute occupying split over several extents, the first of which defines a single cluster, the first INDEX_ALLOCATION extent has lowest_vcn=0 and highest_vcn=0, and the second one has lowest_vcn=1. This unusual case, which can be created by the combination of a small volume and near-full MFT records, triggers some special-case behavior in ntfs_mapping_pairs_decompress_i(). That behavior is incorrect if the attribute's first extent only contains a single cluster, since in that case highest_vcn=0 as well. This configuration has been tested on Windows and it *is* able to successfully read the directory. This supports the hypothesis that the volume is valid and NTFS-3g has a bug on the read side. This bug could, in theory, occur with any non-resident attribute, not just INDEX_ALLOCATION attributes. (Contributed by Eric Biggers)
This commit is contained in:
parent
f85b82c8e1
commit
aeb1d7fb74
@ -939,40 +939,45 @@ mpa_err:
|
|||||||
"attribute.\n");
|
"attribute.\n");
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
/* Setup not mapped runlist element if this is the base extent. */
|
|
||||||
if (!attr->lowest_vcn) {
|
|
||||||
VCN max_cluster;
|
|
||||||
|
|
||||||
max_cluster = ((sle64_to_cpu(attr->allocated_size) +
|
/*
|
||||||
|
* If this is the base of runlist (if 'lowest_vcn' is 0), then
|
||||||
|
* 'allocated_size' is valid, and we can use it to compute the total
|
||||||
|
* number of clusters across all extents. If the runlist covers all
|
||||||
|
* clusters, then it fits into a single extent and we can terminate
|
||||||
|
* the runlist with LCN_NOENT. Otherwise, we must terminate the runlist
|
||||||
|
* with LCN_RL_NOT_MAPPED and let the caller look for more extents.
|
||||||
|
*/
|
||||||
|
if (!attr->lowest_vcn) {
|
||||||
|
VCN num_clusters;
|
||||||
|
|
||||||
|
num_clusters = ((sle64_to_cpu(attr->allocated_size) +
|
||||||
vol->cluster_size - 1) >>
|
vol->cluster_size - 1) >>
|
||||||
vol->cluster_size_bits) - 1;
|
vol->cluster_size_bits);
|
||||||
/*
|
|
||||||
* A highest_vcn of zero means this is a single extent
|
if (num_clusters > vcn) {
|
||||||
* attribute so simply terminate the runlist with LCN_ENOENT).
|
|
||||||
*/
|
|
||||||
if (deltaxcn) {
|
|
||||||
/*
|
/*
|
||||||
* If there is a difference between the highest_vcn and
|
* The runlist doesn't cover all the clusters, so there
|
||||||
* the highest cluster, the runlist is either corrupt
|
* must be more extents.
|
||||||
* or, more likely, there are more extents following
|
|
||||||
* this one.
|
|
||||||
*/
|
*/
|
||||||
if (deltaxcn < max_cluster) {
|
ntfs_log_debug("More extents to follow; vcn = 0x%llx, "
|
||||||
ntfs_log_debug("More extents to follow; deltaxcn = "
|
"num_clusters = 0x%llx\n",
|
||||||
"0x%llx, max_cluster = 0x%llx\n",
|
(long long)vcn,
|
||||||
(long long)deltaxcn,
|
(long long)num_clusters);
|
||||||
(long long)max_cluster);
|
rl[rlpos].vcn = vcn;
|
||||||
rl[rlpos].vcn = vcn;
|
vcn += rl[rlpos].length = num_clusters - vcn;
|
||||||
vcn += rl[rlpos].length = max_cluster - deltaxcn;
|
rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED;
|
||||||
rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED;
|
rlpos++;
|
||||||
rlpos++;
|
} else if (vcn > num_clusters) {
|
||||||
} else if (deltaxcn > max_cluster) {
|
/*
|
||||||
ntfs_log_debug("Corrupt attribute. deltaxcn = "
|
* There are more VCNs in the runlist than expected, so
|
||||||
"0x%llx, max_cluster = 0x%llx\n",
|
* the runlist is corrupt.
|
||||||
(long long)deltaxcn,
|
*/
|
||||||
(long long)max_cluster);
|
ntfs_log_error("Corrupt attribute. vcn = 0x%llx, "
|
||||||
goto mpa_err;
|
"num_clusters = 0x%llx\n",
|
||||||
}
|
(long long)vcn,
|
||||||
|
(long long)num_clusters);
|
||||||
|
goto mpa_err;
|
||||||
}
|
}
|
||||||
rl[rlpos].lcn = (LCN)LCN_ENOENT;
|
rl[rlpos].lcn = (LCN)LCN_ENOENT;
|
||||||
} else /* Not the base extent. There may be more extents to follow. */
|
} else /* Not the base extent. There may be more extents to follow. */
|
||||||
|
Loading…
Reference in New Issue
Block a user