📄 mft.c
字号:
ntfs_error(vol->sb, "Failed to free allocated cluster.%s", es); NVolSetErrors(vol); } up_write(&vol->lcnbmp_lock); if (status.mp_rebuilt) { if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu( a->data.non_resident.mapping_pairs_offset), old_alen - le16_to_cpu( a->data.non_resident.mapping_pairs_offset), rl2, ll, -1, NULL)) { ntfs_error(vol->sb, "Failed to restore mapping pairs " "array.%s", es); NVolSetErrors(vol); } if (ntfs_attr_record_resize(ctx->mrec, a, old_alen)) { ntfs_error(vol->sb, "Failed to restore attribute " "record.%s", es); NVolSetErrors(vol); } flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); } if (ctx) ntfs_attr_put_search_ctx(ctx); if (!IS_ERR(mrec)) unmap_mft_record(mft_ni); up_write(&mftbmp_ni->runlist.lock); return ret;}/** * ntfs_mft_bitmap_extend_initialized_nolock - extend mftbmp initialized data * @vol: volume on which to extend the mft bitmap attribute * * Extend the initialized portion of the mft bitmap attribute on the ntfs * volume @vol by 8 bytes. * * Note: Only changes initialized_size and data_size, i.e. requires that * allocated_size is big enough to fit the new initialized_size. * * Return 0 on success and -error on error. * * Locking: Caller must hold vol->mftbmp_lock for writing. */static int ntfs_mft_bitmap_extend_initialized_nolock(ntfs_volume *vol){ s64 old_data_size, old_initialized_size; unsigned long flags; struct inode *mftbmp_vi; ntfs_inode *mft_ni, *mftbmp_ni; ntfs_attr_search_ctx *ctx; MFT_RECORD *mrec; ATTR_RECORD *a; int ret; ntfs_debug("Extending mft bitmap initiailized (and data) size."); mft_ni = NTFS_I(vol->mft_ino); mftbmp_vi = vol->mftbmp_ino; mftbmp_ni = NTFS_I(mftbmp_vi); /* Get the attribute record. */ mrec = map_mft_record(mft_ni); if (IS_ERR(mrec)) { ntfs_error(vol->sb, "Failed to map mft record."); return PTR_ERR(mrec); } ctx = ntfs_attr_get_search_ctx(mft_ni, mrec); if (unlikely(!ctx)) { ntfs_error(vol->sb, "Failed to get search context."); ret = -ENOMEM; goto unm_err_out; } ret = ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, mftbmp_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(ret)) { ntfs_error(vol->sb, "Failed to find first attribute extent of " "mft bitmap attribute."); if (ret == -ENOENT) ret = -EIO; goto put_err_out; } a = ctx->attr; write_lock_irqsave(&mftbmp_ni->size_lock, flags); old_data_size = i_size_read(mftbmp_vi); old_initialized_size = mftbmp_ni->initialized_size; /* * We can simply update the initialized_size before filling the space * with zeroes because the caller is holding the mft bitmap lock for * writing which ensures that no one else is trying to access the data. */ mftbmp_ni->initialized_size += 8; a->data.non_resident.initialized_size = cpu_to_sle64(mftbmp_ni->initialized_size); if (mftbmp_ni->initialized_size > old_data_size) { i_size_write(mftbmp_vi, mftbmp_ni->initialized_size); a->data.non_resident.data_size = cpu_to_sle64(mftbmp_ni->initialized_size); } write_unlock_irqrestore(&mftbmp_ni->size_lock, flags); /* Ensure the changes make it to disk. */ flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(mft_ni); /* Initialize the mft bitmap attribute value with zeroes. */ ret = ntfs_attr_set(mftbmp_ni, old_initialized_size, 8, 0); if (likely(!ret)) { ntfs_debug("Done. (Wrote eight initialized bytes to mft " "bitmap."); return 0; } ntfs_error(vol->sb, "Failed to write to mft bitmap."); /* Try to recover from the error. */ mrec = map_mft_record(mft_ni); if (IS_ERR(mrec)) { ntfs_error(vol->sb, "Failed to map mft record.%s", es); NVolSetErrors(vol); return ret; } ctx = ntfs_attr_get_search_ctx(mft_ni, mrec); if (unlikely(!ctx)) { ntfs_error(vol->sb, "Failed to get search context.%s", es); NVolSetErrors(vol); goto unm_err_out; } if (ntfs_attr_lookup(mftbmp_ni->type, mftbmp_ni->name, mftbmp_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_error(vol->sb, "Failed to find first attribute extent of " "mft bitmap attribute.%s", es); NVolSetErrors(vol);put_err_out: ntfs_attr_put_search_ctx(ctx);unm_err_out: unmap_mft_record(mft_ni); goto err_out; } a = ctx->attr; write_lock_irqsave(&mftbmp_ni->size_lock, flags); mftbmp_ni->initialized_size = old_initialized_size; a->data.non_resident.initialized_size = cpu_to_sle64(old_initialized_size); if (i_size_read(mftbmp_vi) != old_data_size) { i_size_write(mftbmp_vi, old_data_size); a->data.non_resident.data_size = cpu_to_sle64(old_data_size); } write_unlock_irqrestore(&mftbmp_ni->size_lock, flags); flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(mft_ni);#ifdef DEBUG read_lock_irqsave(&mftbmp_ni->size_lock, flags); ntfs_debug("Restored status of mftbmp: allocated_size 0x%llx, " "data_size 0x%llx, initialized_size 0x%llx.", (long long)mftbmp_ni->allocated_size, (long long)i_size_read(mftbmp_vi), (long long)mftbmp_ni->initialized_size); read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);#endif /* DEBUG */err_out: return ret;}/** * ntfs_mft_data_extend_allocation_nolock - extend mft data attribute * @vol: volume on which to extend the mft data attribute * * Extend the mft data attribute on the ntfs volume @vol by 16 mft records * worth of clusters or if not enough space for this by one mft record worth * of clusters. * * Note: Only changes allocated_size, i.e. does not touch initialized_size or * data_size. * * Return 0 on success and -errno on error. * * Locking: - Caller must hold vol->mftbmp_lock for writing. * - This function takes NTFS_I(vol->mft_ino)->runlist.lock for * writing and releases it before returning. * - This function calls functions which take vol->lcnbmp_lock for * writing and release it before returning. */static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol){ LCN lcn; VCN old_last_vcn; s64 min_nr, nr, ll; unsigned long flags; ntfs_inode *mft_ni; runlist_element *rl, *rl2; ntfs_attr_search_ctx *ctx = NULL; MFT_RECORD *mrec; ATTR_RECORD *a = NULL; int ret, mp_size; u32 old_alen = 0; bool mp_rebuilt = false; ntfs_debug("Extending mft data allocation."); mft_ni = NTFS_I(vol->mft_ino); /* * Determine the preferred allocation location, i.e. the last lcn of * the mft data attribute. The allocated size of the mft data * attribute cannot be zero so we are ok to do this. */ down_write(&mft_ni->runlist.lock); read_lock_irqsave(&mft_ni->size_lock, flags); ll = mft_ni->allocated_size; read_unlock_irqrestore(&mft_ni->size_lock, flags); rl = ntfs_attr_find_vcn_nolock(mft_ni, (ll - 1) >> vol->cluster_size_bits, NULL); if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) { up_write(&mft_ni->runlist.lock); ntfs_error(vol->sb, "Failed to determine last allocated " "cluster of mft data attribute."); if (!IS_ERR(rl)) ret = -EIO; else ret = PTR_ERR(rl); return ret; } lcn = rl->lcn + rl->length; ntfs_debug("Last lcn of mft data attribute is 0x%llx.", (long long)lcn); /* Minimum allocation is one mft record worth of clusters. */ min_nr = vol->mft_record_size >> vol->cluster_size_bits; if (!min_nr) min_nr = 1; /* Want to allocate 16 mft records worth of clusters. */ nr = vol->mft_record_size << 4 >> vol->cluster_size_bits; if (!nr) nr = min_nr; /* Ensure we do not go above 2^32-1 mft records. */ read_lock_irqsave(&mft_ni->size_lock, flags); ll = mft_ni->allocated_size; read_unlock_irqrestore(&mft_ni->size_lock, flags); if (unlikely((ll + (nr << vol->cluster_size_bits)) >> vol->mft_record_size_bits >= (1ll << 32))) { nr = min_nr; if (unlikely((ll + (nr << vol->cluster_size_bits)) >> vol->mft_record_size_bits >= (1ll << 32))) { ntfs_warning(vol->sb, "Cannot allocate mft record " "because the maximum number of inodes " "(2^32) has already been reached."); up_write(&mft_ni->runlist.lock); return -ENOSPC; } } ntfs_debug("Trying mft data allocation with %s cluster count %lli.", nr > min_nr ? "default" : "minimal", (long long)nr); old_last_vcn = rl[1].vcn; do { rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE, true); if (likely(!IS_ERR(rl2))) break; if (PTR_ERR(rl2) != -ENOSPC || nr == min_nr) { ntfs_error(vol->sb, "Failed to allocate the minimal " "number of clusters (%lli) for the " "mft data attribute.", (long long)nr); up_write(&mft_ni->runlist.lock); return PTR_ERR(rl2); } /* * There is not enough space to do the allocation, but there * might be enough space to do a minimal allocation so try that * before failing. */ nr = min_nr; ntfs_debug("Retrying mft data allocation with minimal cluster " "count %lli.", (long long)nr); } while (1); rl = ntfs_runlists_merge(mft_ni->runlist.rl, rl2); if (IS_ERR(rl)) { up_write(&mft_ni->runlist.lock); ntfs_error(vol->sb, "Failed to merge runlists for mft data " "attribute."); if (ntfs_cluster_free_from_rl(vol, rl2)) { ntfs_error(vol->sb, "Failed to dealocate clusters " "from the mft data attribute.%s", es); NVolSetErrors(vol); } ntfs_free(rl2); return PTR_ERR(rl); } mft_ni->runlist.rl = rl; ntfs_debug("Allocated %lli clusters.", (long long)nr); /* Find the last run in the new runlist. */ for (; rl[1].length; rl++) ; /* Update the attribute record as well. */ mrec = map_mft_record(mft_ni); if (IS_ERR(mrec)) { ntfs_error(vol->sb, "Failed to map mft record."); ret = PTR_ERR(mrec); goto undo_alloc; } ctx = ntfs_attr_get_search_ctx(mft_ni, mrec); if (unlikely(!ctx)) { ntfs_error(vol->sb, "Failed to get search context."); ret = -ENOMEM; goto undo_alloc; } ret = ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len, CASE_SENSITIVE, rl[1].vcn, NULL, 0, ctx); if (unlikely(ret)) { ntfs_error(vol->sb, "Failed to find last attribute extent of " "mft data attribute."); if (ret == -ENOENT) ret = -EIO; goto undo_alloc; } a = ctx->attr; ll = sle64_to_cpu(a->data.non_resident.lowest_vcn); /* Search back for the previous last allocated cluster of mft bitmap. */ for (rl2 = rl; rl2 > mft_ni->runlist.rl; rl2--) { if (ll >= rl2->vcn) break; } BUG_ON(ll < rl2->vcn); BUG_ON(ll >= rl2->vcn + rl2->length); /* Get the size for the new mapping pairs array for this extent. */ mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1); if (unlikely(mp_size <= 0)) { ntfs_error(vol->sb, "Get size for mapping pairs failed for " "mft data attribute extent."); ret = mp_size; if (!ret) ret = -EIO; goto undo_alloc; } /* Expand the attribute record if necessary. */ old_alen = le32_to_cpu(a->length); ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size + le16_to_cpu(a->data.non_resident.mapping_pairs_offset)); if (unlikely(ret)) { if (ret != -ENOSPC) { ntfs_error(vol->sb, "Failed to resize attribute " "record for mft data attribute."); goto undo_alloc; } // TODO: Deal with this by moving this extent to a new mft // record or by starting a new extent in a new mft record or by // moving other attributes out of this mft record. // Note: Use the special reserved mft records and ensure that // this extent is not required to find the mft record in // question. If no free special records left we would need to // move an existing record away, insert ours in its place, and // then place the moved record into the newly allocated space // and we would then need to update all references to this mft // record appropriately. This is rather complicated... ntfs_error(vol->sb, "Not enough space in this mft record to " "accomodate extended mft data attribute " "extent. Cannot handle this yet."); ret = -EOPNOTSUPP; goto undo_alloc; } mp_rebuilt = true; /* Generate the mapping pairs array directly into the attr record. */ ret = ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(a->data.non_resident.mapping_pairs_offset), mp_size, rl2, ll, -1, NULL); if (unlikely(ret)) { ntfs_error(vol->sb, "Failed to build mapping pairs array of " "mft data attribute."); goto undo_alloc; } /* Update the highest_vcn. */ a->data.non_resident.highest_vcn = cpu_to_sle64(rl[1].vcn - 1); /* * We now have extended the mft data allocated_size by nr clusters. * Reflect this in the ntfs_inode structure and the attribute record. * @rl is the last (non-terminator) runlist element of mft data * attribute. */ if (a->data.non_resident.lowest_vcn) { /* * We are not in the first attribute extent, switch to it, but * first ensure the changes will make it to disk later. */ flush_dcache_mft_record_page(ctx->ntfs_ino); mark_mft_record_dirty(ctx->ntfs_ino); ntfs_attr_reinit_search_ctx(ctx); ret = ntfs_attr_lookup(mft_ni->type, mft_ni->name, mft_ni->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -