*
* 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;
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;
}
*
* 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
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;
static inline void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx,
ntfs_inode *ni, MFT_RECORD *mrec)
{
- ctx->mrec = mrec;
- /* Sanity checks are performed elsewhere. */
- ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
- ctx->is_first = TRUE;
- ctx->ntfs_ino = ni;
- ctx->al_entry = NULL;
- ctx->base_ntfs_ino = NULL;
- ctx->base_mrec = NULL;
- ctx->base_attr = NULL;
+ *ctx = (ntfs_attr_search_ctx) {
+ .mrec = mrec,
+ /* Sanity checks are performed elsewhere. */
+ .attr = (ATTR_RECORD*)((u8*)mrec +
+ le16_to_cpu(mrec->attrs_offset)),
+ .is_first = TRUE,
+ .ntfs_ino = ni,
+ };
}
/**
return;
}
+#ifdef NTFS_RW
+
/**
* ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
* @vol: ntfs volume to which the attribute belongs
* Check whether the attribute of @type on the ntfs volume @vol is allowed to
* be non-resident. This information is obtained from $AttrDef system file.
*
- * Return 0 if the attribute is allowed to be non-resident, -EPERM if not, or
+ * Return 0 if the attribute is allowed to be non-resident, -EPERM if not, and
* -ENOENT if the attribute is not listed in $AttrDef.
*/
int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
{
ATTR_DEF *ad;
- /*
- * $DATA and $EA are always allowed to be non-resident even if $AttrDef
- * does not specify this in the flags of the $DATA attribute definition
- * record.
- */
- if (type == AT_DATA || type == AT_EA)
- return 0;
/* Find the attribute definition record in $AttrDef. */
ad = ntfs_attr_find_in_attrdef(vol, type);
if (unlikely(!ad))
return -ENOENT;
/* Check the flags and return the result. */
- if (ad->flags & CAN_BE_NON_RESIDENT)
- return 0;
- return -EPERM;
+ if (ad->flags & ATTR_DEF_RESIDENT)
+ return -EPERM;
+ return 0;
}
/**
*/
int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
{
- if (type != AT_INDEX_ALLOCATION && type != AT_EA)
- return 0;
- return -EPERM;
+ if (type == AT_INDEX_ALLOCATION || type == AT_EA)
+ return -EPERM;
+ return 0;
}
/**
* -ENOSPC - Not enough disk space.
* -EINVAL - Attribute not defined on the volume.
* -EIO - I/o error or other error.
+ * Note that -ENOSPC is also returned in the case that there is not enough
+ * space in the mft record to do the conversion. This can happen when the mft
+ * record is already very full. The caller is responsible for trying to make
+ * space in the mft record and trying again. FIXME: Do we need a separate
+ * error return code for this kind of -ENOSPC or is it always worth trying
+ * again in case the attribute may then fit in a resident state so no need to
+ * make it non-resident at all? Ho-hum... (AIA)
*
* NOTE to self: No changes in the attribute list are required to move from
* a resident to a non-resident attribute.
new_size = (i_size_read(vi) + vol->cluster_size - 1) &
~(vol->cluster_size - 1);
if (new_size > 0) {
+ runlist_element *rl2;
+
/*
* Will need the page later and since the page lock nests
* outside all ntfs locks, we need to get the page now.
if (IS_ERR(rl)) {
err = PTR_ERR(rl);
ntfs_debug("Failed to allocate cluster%s, error code "
- "%i.\n", (new_size >>
+ "%i.", (new_size >>
vol->cluster_size_bits) > 1 ? "s" : "",
err);
goto page_err_out;
}
+ /* Change the runlist terminator to LCN_ENOENT. */
+ rl2 = rl;
+ while (rl2->length)
+ rl2++;
+ BUG_ON(rl2->lcn != LCN_RL_NOT_MAPPED);
+ rl2->lcn = LCN_ENOENT;
} else {
rl = NULL;
page = NULL;
}
/* Determine the size of the mapping pairs array. */
- mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0);
+ mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, -1);
if (unlikely(mp_size < 0)) {
err = mp_size;
ntfs_debug("Failed to get size for mapping pairs array, error "
cpu_to_sle64(attr_size);
/* Generate the mapping pairs array into the attribute record. */
err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs,
- arec_size - mp_ofs, rl, 0, NULL);
+ arec_size - mp_ofs, rl, 0, -1, NULL);
if (unlikely(err)) {
ntfs_debug("Failed to build mapping pairs, error code %i.",
err);
rl_err_out:
if (rl) {
if (ntfs_cluster_free_from_rl(vol, rl) < 0) {
- ntfs_free(rl);
ntfs_error(vol->sb, "Failed to release allocated "
"cluster(s) in error code path. Run "
"chkdsk to recover the lost "
"cluster(s).");
NVolSetErrors(vol);
}
+ ntfs_free(rl);
page_err_out:
unlock_page(page);
page_cache_release(page);
ntfs_debug("Done.");
return 0;
}
+
+#endif /* NTFS_RW */