diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index 67994c9c248fc7ac1a579126fc6974c0e13b1f32..a6d2b943a148a2bb6a32536e8b23466306aec65b 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog @@ -139,6 +139,12 @@ ToDo/Notes: and ntfs_mapping_pairs_build() to allow the runlist encoding to be partial which is desirable when filling holes in sparse attributes. Update all callers. + - Change ntfs_map_runlist_nolock() to only decompress the mapping pairs + if the requested vcn is inside it. Otherwise we get into problems + when we try to map an out of bounds vcn because we then try to map + the already mapped runlist fragment which causes + ntfs_mapping_pairs_decompress() to fail and return error. Update + ntfs_attr_find_vcn_nolock() accordingly. 2.1.22 - Many bug and race fixes and error handling improvements. diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index c6b2bb64d65149686d5311f07fff936698bff4df..543d47fa5fc9f01ca9f7c02b66b8780ab5855518 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -39,15 +39,19 @@ * * Map the part of a runlist containing the @vcn of the ntfs inode @ni. * - * Return 0 on success and -errno on error. + * Return 0 on success and -errno on error. There is one special error code + * which is not an error as such. This is -ENOENT. It means that @vcn is out + * of bounds of the runlist. * * Locking: - The runlist must be locked for writing. * - This function modifies the runlist. */ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn) { + VCN end_vcn; ntfs_inode *base_ni; - MFT_RECORD *mrec; + MFT_RECORD *m; + ATTR_RECORD *a; ntfs_attr_search_ctx *ctx; runlist_element *rl; int err = 0; @@ -58,26 +62,43 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn) base_ni = ni; else base_ni = ni->ext.base_ntfs_ino; - mrec = map_mft_record(base_ni); - if (IS_ERR(mrec)) - return PTR_ERR(mrec); - ctx = ntfs_attr_get_search_ctx(base_ni, mrec); + m = map_mft_record(base_ni); + if (IS_ERR(m)) + return PTR_ERR(m); + ctx = ntfs_attr_get_search_ctx(base_ni, m); if (unlikely(!ctx)) { err = -ENOMEM; goto err_out; } err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, vcn, NULL, 0, ctx); - if (likely(!err)) { - rl = ntfs_mapping_pairs_decompress(ni->vol, ctx->attr, - ni->runlist.rl); - if (IS_ERR(rl)) - err = PTR_ERR(rl); - else - ni->runlist.rl = rl; + if (unlikely(err)) { + if (err == -ENOENT) + err = -EIO; + goto err_out; } - ntfs_attr_put_search_ctx(ctx); + a = ctx->attr; + /* + * Only decompress the mapping pairs if @vcn is inside it. Otherwise + * we get into problems when we try to map an out of bounds vcn because + * we then try to map the already mapped runlist fragment and + * ntfs_mapping_pairs_decompress() fails. + */ + end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1; + if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1)) + end_vcn = ni->allocated_size >> ni->vol->cluster_size_bits; + if (unlikely(vcn >= end_vcn)) { + err = -ENOENT; + goto err_out; + } + rl = ntfs_mapping_pairs_decompress(ni->vol, a, ni->runlist.rl); + if (IS_ERR(rl)) + err = PTR_ERR(rl); + else + ni->runlist.rl = rl; err_out: + if (likely(ctx)) + ntfs_attr_put_search_ctx(ctx); unmap_mft_record(base_ni); return err; } @@ -89,7 +110,9 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn) * * Map the part of a runlist containing the @vcn of the ntfs inode @ni. * - * Return 0 on success and -errno on error. + * Return 0 on success and -errno on error. There is one special error code + * which is not an error as such. This is -ENOENT. It means that @vcn is out + * of bounds of the runlist. * * Locking: - The runlist must be unlocked on entry and is unlocked on return. * - This function takes the runlist lock for writing and modifies the @@ -287,11 +310,11 @@ runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn, goto retry_remap; } /* - * -EINVAL and -ENOENT coming from a failed mapping attempt are - * equivalent to i/o errors for us as they should not happen in - * our code paths. + * -EINVAL coming from a failed mapping attempt is equivalent + * to i/o error for us as it should not happen in our code + * paths. */ - if (err == -EINVAL || err == -ENOENT) + if (err == -EINVAL) err = -EIO; } else if (!err) err = -EIO;