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,41 +939,46 @@ mpa_err:
|
||||
"attribute.\n");
|
||||
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_bits) - 1;
|
||||
vol->cluster_size_bits);
|
||||
|
||||
if (num_clusters > vcn) {
|
||||
/*
|
||||
* A highest_vcn of zero means this is a single extent
|
||||
* attribute so simply terminate the runlist with LCN_ENOENT).
|
||||
* The runlist doesn't cover all the clusters, so there
|
||||
* must be more extents.
|
||||
*/
|
||||
if (deltaxcn) {
|
||||
/*
|
||||
* If there is a difference between the highest_vcn and
|
||||
* the highest cluster, the runlist is either corrupt
|
||||
* or, more likely, there are more extents following
|
||||
* this one.
|
||||
*/
|
||||
if (deltaxcn < max_cluster) {
|
||||
ntfs_log_debug("More extents to follow; deltaxcn = "
|
||||
"0x%llx, max_cluster = 0x%llx\n",
|
||||
(long long)deltaxcn,
|
||||
(long long)max_cluster);
|
||||
ntfs_log_debug("More extents to follow; vcn = 0x%llx, "
|
||||
"num_clusters = 0x%llx\n",
|
||||
(long long)vcn,
|
||||
(long long)num_clusters);
|
||||
rl[rlpos].vcn = vcn;
|
||||
vcn += rl[rlpos].length = max_cluster - deltaxcn;
|
||||
vcn += rl[rlpos].length = num_clusters - vcn;
|
||||
rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED;
|
||||
rlpos++;
|
||||
} else if (deltaxcn > max_cluster) {
|
||||
ntfs_log_debug("Corrupt attribute. deltaxcn = "
|
||||
"0x%llx, max_cluster = 0x%llx\n",
|
||||
(long long)deltaxcn,
|
||||
(long long)max_cluster);
|
||||
} else if (vcn > num_clusters) {
|
||||
/*
|
||||
* There are more VCNs in the runlist than expected, so
|
||||
* the runlist is corrupt.
|
||||
*/
|
||||
ntfs_log_error("Corrupt attribute. vcn = 0x%llx, "
|
||||
"num_clusters = 0x%llx\n",
|
||||
(long long)vcn,
|
||||
(long long)num_clusters);
|
||||
goto mpa_err;
|
||||
}
|
||||
}
|
||||
rl[rlpos].lcn = (LCN)LCN_ENOENT;
|
||||
} else /* Not the base extent. There may be more extents to follow. */
|
||||
rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED;
|
||||
|
Loading…
Reference in New Issue
Block a user