mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 13:14:02 +02:00
ntfs: protect runlist updates with the runlist lock
ntfs_non_resident_attr_shrink() calls runlist helpers that require the
runlist write lock, but did not hold it while freeing clusters and
truncating the runlist. Serialize those operations and the resident
conversion with the runlist lock.
ntfs_attr_map_cluster() can merge a newly allocated run before updating
mapping pairs. If the update fails, free the clusters and restore both
the in-memory runlist and on-disk mapping pairs from a saved runlist.
Mark the volume in error if either rollback step fails.
Fixes: 495e90fa33 ("ntfs: update attrib operations")
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
631946431d
commit
91709ba5d6
205
fs/ntfs/attrib.c
205
fs/ntfs/attrib.c
|
|
@ -3574,7 +3574,8 @@ int ntfs_attr_record_move_away(struct ntfs_attr_search_ctx *ctx, int extra)
|
|||
* update allocated and compressed size.
|
||||
*/
|
||||
static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
||||
struct mft_record *m, struct ntfs_attr_search_ctx *ctx)
|
||||
struct mft_record *m, struct ntfs_attr_search_ctx *ctx,
|
||||
struct ntfs_inode *locked_ni, bool defer_attrlist)
|
||||
{
|
||||
int sparse, err = 0;
|
||||
struct ntfs_inode *base_ni;
|
||||
|
|
@ -3610,6 +3611,8 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
|||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset) == 8) &&
|
||||
!(le32_to_cpu(m->bytes_allocated) - le32_to_cpu(m->bytes_in_use))) {
|
||||
|
||||
if (defer_attrlist)
|
||||
return -ENOSPC;
|
||||
if (!NInoAttrList(base_ni)) {
|
||||
err = ntfs_inode_add_attrlist(base_ni);
|
||||
if (err)
|
||||
|
|
@ -3623,7 +3626,7 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = ntfs_attrlist_update(base_ni);
|
||||
err = ntfs_attrlist_update_locked(base_ni, locked_ni);
|
||||
if (err)
|
||||
goto out;
|
||||
err = -EAGAIN;
|
||||
|
|
@ -3703,6 +3706,8 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
|||
* ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute
|
||||
* @ni: non-resident ntfs inode for which we need update
|
||||
* @from_vcn: update runlist starting this VCN
|
||||
* @locked_ni: inode whose runlist write lock is already held
|
||||
* @defer_attrlist: return -ENOSPC instead of updating an attribute list
|
||||
*
|
||||
* Build mapping pairs from @na->rl and write them to the disk. Also, this
|
||||
* function updates sparse bit, allocated and compressed size (allocates/frees
|
||||
|
|
@ -3712,7 +3717,10 @@ static int ntfs_attr_update_meta(struct attr_record *a, struct ntfs_inode *ni,
|
|||
* call to this function. Vice-versa @na->compressed_size will be calculated and
|
||||
* set to correct value during this function.
|
||||
*/
|
||||
int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||
static int __ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni,
|
||||
s64 from_vcn,
|
||||
struct ntfs_inode *locked_ni,
|
||||
bool defer_attrlist)
|
||||
{
|
||||
struct ntfs_attr_search_ctx *ctx;
|
||||
struct ntfs_inode *base_ni;
|
||||
|
|
@ -3804,7 +3812,8 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
continue;
|
||||
}
|
||||
|
||||
err = ntfs_attr_update_meta(a, ni, m, ctx);
|
||||
err = ntfs_attr_update_meta(a, ni, m, ctx, locked_ni,
|
||||
defer_attrlist);
|
||||
if (err < 0) {
|
||||
if (err == -EAGAIN) {
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
|
@ -3844,11 +3853,17 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
*/
|
||||
if (ni->type == AT_ATTRIBUTE_LIST) {
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
ctx = NULL;
|
||||
if (locked_ni == ni || defer_attrlist) {
|
||||
err = -ENOSPC;
|
||||
goto put_err_out;
|
||||
}
|
||||
err = ntfs_inode_free_space(base_ni, mp_size -
|
||||
cur_max_mp_size);
|
||||
if (err)
|
||||
return err;
|
||||
err = ntfs_attrlist_update(base_ni);
|
||||
err = ntfs_attrlist_update_locked(
|
||||
base_ni, locked_ni);
|
||||
if (err)
|
||||
return err;
|
||||
goto retry;
|
||||
|
|
@ -3856,6 +3871,10 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
|
||||
/* Add attribute list if it isn't present, and retry. */
|
||||
if (!NInoAttrList(base_ni)) {
|
||||
if (defer_attrlist) {
|
||||
err = -ENOSPC;
|
||||
goto put_err_out;
|
||||
}
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
if (ntfs_inode_add_attrlist(base_ni)) {
|
||||
ntfs_error(sb, "Can not add attrlist");
|
||||
|
|
@ -3883,13 +3902,21 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
}
|
||||
}
|
||||
|
||||
if (defer_attrlist &&
|
||||
(ctx->ntfs_ino->nr_extents == -1 ||
|
||||
NInoAttrList(ctx->ntfs_ino)) &&
|
||||
ctx->attr->type != AT_ATTRIBUTE_LIST) {
|
||||
err = -ENOSPC;
|
||||
goto put_err_out;
|
||||
}
|
||||
|
||||
/* Update lowest vcn. */
|
||||
a->data.non_resident.lowest_vcn = cpu_to_le64(stop_vcn);
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
if ((ctx->ntfs_ino->nr_extents == -1 || NInoAttrList(ctx->ntfs_ino)) &&
|
||||
ctx->attr->type != AT_ATTRIBUTE_LIST) {
|
||||
ctx->al_entry->lowest_vcn = cpu_to_le64(stop_vcn);
|
||||
err = ntfs_attrlist_update(base_ni);
|
||||
err = ntfs_attrlist_update_locked(base_ni, locked_ni);
|
||||
if (err)
|
||||
goto put_err_out;
|
||||
}
|
||||
|
|
@ -4064,6 +4091,19 @@ int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
|||
return err;
|
||||
}
|
||||
|
||||
int ntfs_attr_update_mapping_pairs_locked(struct ntfs_inode *ni,
|
||||
s64 from_vcn,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
return __ntfs_attr_update_mapping_pairs(ni, from_vcn, locked_ni,
|
||||
false);
|
||||
}
|
||||
|
||||
int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn)
|
||||
{
|
||||
return ntfs_attr_update_mapping_pairs_locked(ni, from_vcn, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_attr_make_resident - convert a non-resident to a resident attribute
|
||||
* @ni: open ntfs attribute to make resident
|
||||
|
|
@ -4197,7 +4237,9 @@ static int ntfs_attr_make_resident(struct ntfs_inode *ni, struct ntfs_attr_searc
|
|||
*
|
||||
* Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes.
|
||||
*/
|
||||
static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsize)
|
||||
static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni,
|
||||
const s64 newsize,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
struct ntfs_volume *vol;
|
||||
struct ntfs_attr_search_ctx *ctx;
|
||||
|
|
@ -4205,6 +4247,7 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
s64 nr_freed_clusters;
|
||||
int err;
|
||||
struct ntfs_inode *base_ni;
|
||||
bool runlist_locked = locked_ni == ni;
|
||||
|
||||
ntfs_debug("Inode 0x%llx attr 0x%x new size %lld\n",
|
||||
(unsigned long long)ni->mft_no, ni->type, (long long)newsize);
|
||||
|
|
@ -4250,18 +4293,24 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
* clusters if there is a change.
|
||||
*/
|
||||
if (ntfs_bytes_to_cluster(vol, ni->allocated_size) != first_free_vcn) {
|
||||
struct ntfs_attr_search_ctx *ctx;
|
||||
/*
|
||||
* ntfs_cluster_free() and ntfs_rl_truncate_nolock()
|
||||
* both require this lock.
|
||||
*/
|
||||
if (!runlist_locked)
|
||||
down_write(&ni->runlist.lock);
|
||||
|
||||
err = ntfs_attr_map_whole_runlist(ni);
|
||||
if (err) {
|
||||
ntfs_debug("Eeek! ntfs_attr_map_whole_runlist failed.\n");
|
||||
return err;
|
||||
goto unlock_runlist;
|
||||
}
|
||||
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_error(vol->sb, "%s: Failed to get search context", __func__);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto unlock_runlist;
|
||||
}
|
||||
|
||||
/* Deallocate all clusters starting with the first free one. */
|
||||
|
|
@ -4269,7 +4318,8 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
if (nr_freed_clusters < 0) {
|
||||
ntfs_debug("Eeek! Freeing of clusters failed. Aborting...\n");
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return (int)nr_freed_clusters;
|
||||
err = (int)nr_freed_clusters;
|
||||
goto unlock_runlist;
|
||||
}
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
||||
|
|
@ -4282,7 +4332,8 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
kvfree(ni->runlist.rl);
|
||||
ni->runlist.rl = NULL;
|
||||
ntfs_error(vol->sb, "Eeek! Run list truncation failed.\n");
|
||||
return -EIO;
|
||||
err = -EIO;
|
||||
goto unlock_runlist;
|
||||
}
|
||||
|
||||
/* Prepare to mapping pairs update. */
|
||||
|
|
@ -4298,11 +4349,13 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
VFS_I(base_ni)->i_blocks = ni->allocated_size >> 9;
|
||||
|
||||
/* Write mapping pairs for new runlist. */
|
||||
err = ntfs_attr_update_mapping_pairs(ni, 0 /*first_free_vcn*/);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
if (err) {
|
||||
ntfs_debug("Eeek! Mapping pairs update failed. Leaving inconstant metadata. Run chkdsk.\n");
|
||||
return err;
|
||||
goto unlock_runlist;
|
||||
}
|
||||
if (!runlist_locked)
|
||||
up_write(&ni->runlist.lock);
|
||||
}
|
||||
|
||||
/* Get the first attribute record. */
|
||||
|
|
@ -4344,7 +4397,11 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
|
||||
/* If the attribute now has zero size, make it resident. */
|
||||
if (!newsize && !NInoEncrypted(ni) && !NInoCompressed(ni)) {
|
||||
if (!runlist_locked)
|
||||
down_write(&ni->runlist.lock);
|
||||
err = ntfs_attr_make_resident(ni, ctx);
|
||||
if (!runlist_locked)
|
||||
up_write(&ni->runlist.lock);
|
||||
if (err) {
|
||||
/* If couldn't make resident, just continue. */
|
||||
if (err != -EPERM)
|
||||
|
|
@ -4361,6 +4418,11 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
put_err_out:
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return err;
|
||||
|
||||
unlock_runlist:
|
||||
if (!runlist_locked)
|
||||
up_write(&ni->runlist.lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -4369,13 +4431,14 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
|
|||
* @prealloc_size: preallocation size (in bytes) to which to expand the attribute
|
||||
* @newsize: new size (in bytes) to which to expand the attribute
|
||||
* @holes: how to create a hole if expanding
|
||||
* @need_lock: whether mrec lock is needed or not
|
||||
* @locked_ni: inode whose runlist lock is already held
|
||||
*
|
||||
* Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes,
|
||||
* by allocating new clusters.
|
||||
*/
|
||||
static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsize,
|
||||
const s64 prealloc_size, unsigned int holes, bool need_lock)
|
||||
const s64 prealloc_size, unsigned int holes,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
s64 lcn_seek_from;
|
||||
s64 first_free_vcn;
|
||||
|
|
@ -4573,7 +4636,8 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
|||
|
||||
/* Prepare to mapping pairs update. */
|
||||
ni->allocated_size = ntfs_cluster_to_bytes(vol, first_free_vcn);
|
||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(
|
||||
ni, 0, locked_ni);
|
||||
if (err) {
|
||||
ntfs_debug("Mapping pairs update failed");
|
||||
goto rollback;
|
||||
|
|
@ -4617,11 +4681,11 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
|||
ntfs_debug("Leaking clusters");
|
||||
|
||||
/* Now, truncate the runlist itself. */
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
down_write(&ni->runlist.lock);
|
||||
err2 = ntfs_rl_truncate_nolock(vol, &ni->runlist,
|
||||
ntfs_bytes_to_cluster(vol, org_alloc_size));
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
up_write(&ni->runlist.lock);
|
||||
if (err2) {
|
||||
/*
|
||||
|
|
@ -4635,11 +4699,11 @@ static int ntfs_non_resident_attr_expand(struct ntfs_inode *ni, const s64 newsiz
|
|||
/* Prepare to mapping pairs update. */
|
||||
ni->allocated_size = org_alloc_size;
|
||||
/* Restore mapping pairs. */
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
down_read(&ni->runlist.lock);
|
||||
if (ntfs_attr_update_mapping_pairs(ni, 0))
|
||||
if (__ntfs_attr_update_mapping_pairs(ni, 0, locked_ni, true))
|
||||
ntfs_error(sb, "Failed to restore old mapping pairs");
|
||||
if (need_lock)
|
||||
if (ni != locked_ni)
|
||||
up_read(&ni->runlist.lock);
|
||||
|
||||
if (NInoSparse(ni) || NInoCompressed(ni)) {
|
||||
|
|
@ -4744,7 +4808,8 @@ static int ntfs_resident_attr_resize(struct ntfs_inode *attr_ni, const s64 newsi
|
|||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
/* Resize non-resident attribute */
|
||||
return ntfs_non_resident_attr_expand(attr_ni, newsize, prealloc_size, holes, true);
|
||||
return ntfs_non_resident_attr_expand(
|
||||
attr_ni, newsize, prealloc_size, holes, NULL);
|
||||
} else if (err != -ENOSPC && err != -EPERM) {
|
||||
ntfs_error(sb, "Failed to make attribute non-resident");
|
||||
goto put_err_out;
|
||||
|
|
@ -4919,13 +4984,14 @@ int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
|||
if (NInoNonResident(ni)) {
|
||||
if (newsize > i_size) {
|
||||
down_write(&ni->runlist.lock);
|
||||
err = ntfs_non_resident_attr_expand(ni, newsize, 0,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK,
|
||||
false);
|
||||
err = ntfs_non_resident_attr_expand(
|
||||
ni, newsize, 0,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK, ni);
|
||||
up_write(&ni->runlist.lock);
|
||||
} else
|
||||
err = ntfs_non_resident_attr_shrink(ni, newsize);
|
||||
err = ntfs_non_resident_attr_shrink(
|
||||
ni, newsize, NULL);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, 0,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
|
|
@ -4934,7 +5000,9 @@ int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
|||
return err;
|
||||
}
|
||||
|
||||
int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 prealloc_size)
|
||||
int ntfs_attr_expand_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||
const s64 prealloc_size,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
|
|
@ -4964,9 +5032,10 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
|||
|
||||
if (NInoNonResident(ni)) {
|
||||
if (newsize > ni->data_size || prealloc_size > ni->allocated_size)
|
||||
err = ntfs_non_resident_attr_expand(ni, newsize, prealloc_size,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK, true);
|
||||
err = ntfs_non_resident_attr_expand(
|
||||
ni, newsize, prealloc_size,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
HOLES_NO : HOLES_OK, locked_ni);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, prealloc_size,
|
||||
NVolDisableSparse(ni->vol) ?
|
||||
|
|
@ -4977,6 +5046,12 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
|||
return err;
|
||||
}
|
||||
|
||||
int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize,
|
||||
const s64 prealloc_size)
|
||||
{
|
||||
return ntfs_attr_expand_locked(ni, newsize, prealloc_size, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_attr_truncate_i - resize an ntfs attribute
|
||||
* @ni: open ntfs inode to resize
|
||||
|
|
@ -4989,7 +5064,9 @@ int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 preallo
|
|||
* newly allocated space is marked as not initialised and no real allocation
|
||||
* on disk is performed.
|
||||
*/
|
||||
int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int holes)
|
||||
int ntfs_attr_truncate_i_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||
unsigned int holes,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
|
@ -5023,15 +5100,23 @@ int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int
|
|||
|
||||
if (NInoNonResident(ni)) {
|
||||
if (newsize > ni->data_size)
|
||||
err = ntfs_non_resident_attr_expand(ni, newsize, 0, holes, true);
|
||||
err = ntfs_non_resident_attr_expand(
|
||||
ni, newsize, 0, holes, locked_ni);
|
||||
else
|
||||
err = ntfs_non_resident_attr_shrink(ni, newsize);
|
||||
err = ntfs_non_resident_attr_shrink(
|
||||
ni, newsize, locked_ni);
|
||||
} else
|
||||
err = ntfs_resident_attr_resize(ni, newsize, 0, holes);
|
||||
ntfs_debug("Return status %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize,
|
||||
unsigned int holes)
|
||||
{
|
||||
return ntfs_attr_truncate_i_locked(ni, newsize, holes, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize an attribute, creating a hole if relevant
|
||||
*/
|
||||
|
|
@ -5049,10 +5134,11 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
|||
struct ntfs_volume *vol = ni->vol;
|
||||
struct ntfs_attr_search_ctx *ctx;
|
||||
struct runlist_element *rl, *rlc;
|
||||
struct runlist_element *old_rl = NULL;
|
||||
s64 vcn = vcn_start, lcn, clu_count;
|
||||
s64 lcn_seek_from = -1;
|
||||
int err = 0;
|
||||
size_t new_rl_count;
|
||||
size_t new_rl_count, old_rl_count;
|
||||
|
||||
err = ntfs_attr_map_whole_runlist(ni);
|
||||
if (err)
|
||||
|
|
@ -5145,6 +5231,19 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
|||
WARN_ON(rlc->vcn != vcn);
|
||||
lcn = rlc->lcn;
|
||||
clu_count = rlc->length;
|
||||
old_rl_count = ni->runlist.count;
|
||||
old_rl = kmemdup(ni->runlist.rl,
|
||||
old_rl_count * sizeof(*old_rl), GFP_NOFS);
|
||||
if (!old_rl) {
|
||||
err = -ENOMEM;
|
||||
if (ntfs_cluster_free_from_rl(vol, rlc)) {
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to free cluster allocation after runlist backup failure.");
|
||||
NVolSetErrors(vol);
|
||||
}
|
||||
kvfree(rlc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, &new_rl_count);
|
||||
if (IS_ERR(rl)) {
|
||||
|
|
@ -5168,15 +5267,32 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
|||
|
||||
if (update_mp) {
|
||||
ntfs_attr_reinit_search_ctx(ctx);
|
||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
if (err) {
|
||||
int err2;
|
||||
|
||||
err2 = ntfs_cluster_free(ni, vcn, clu_count, ctx);
|
||||
if (err2 < 0)
|
||||
if (err2 < 0 || err2 != clu_count) {
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to free cluster allocation. Leaving inconstant metadata.\n");
|
||||
goto out;
|
||||
"Failed to free cluster allocation. Leaving inconsistent metadata.\n");
|
||||
NVolSetErrors(vol);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the runlist before repairing the on-disk
|
||||
* mapping pairs.
|
||||
*/
|
||||
kvfree(ni->runlist.rl);
|
||||
ni->runlist.rl = old_rl;
|
||||
ni->runlist.count = old_rl_count;
|
||||
old_rl = NULL;
|
||||
if (ntfs_attr_update_mapping_pairs_locked(
|
||||
ni, 0, ni)) {
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to restore mapping pairs after allocation rollback.\n");
|
||||
NVolSetErrors(vol);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VFS_I(ni)->i_blocks += clu_count << (vol->cluster_size_bits - 9);
|
||||
|
|
@ -5188,6 +5304,7 @@ int ntfs_attr_map_cluster(struct ntfs_inode *ni, s64 vcn_start, s64 *lcn_start,
|
|||
*lcn_count = clu_count;
|
||||
*balloc = true;
|
||||
out:
|
||||
kvfree(old_rl);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -5431,7 +5548,7 @@ int ntfs_non_resident_attr_insert_range(struct ntfs_inode *ni, s64 start_vcn, s6
|
|||
ni->data_size += ntfs_cluster_to_bytes(vol, len);
|
||||
if (ntfs_cluster_to_bytes(vol, start_vcn) < ni->initialized_size)
|
||||
ni->initialized_size += ntfs_cluster_to_bytes(vol, len);
|
||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
up_write(&ni->runlist.lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -5516,7 +5633,7 @@ int ntfs_non_resident_attr_collapse_range(struct ntfs_inode *ni, s64 start_vcn,
|
|||
}
|
||||
|
||||
if (ni->allocated_size > 0) {
|
||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
if (ret) {
|
||||
up_write(&ni->runlist.lock);
|
||||
goto out_rl;
|
||||
|
|
@ -5594,7 +5711,7 @@ int ntfs_non_resident_attr_punch_hole(struct ntfs_inode *ni, s64 start_vcn, s64
|
|||
ni->runlist.rl = rl;
|
||||
ni->runlist.count = new_rl_count;
|
||||
|
||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
up_write(&ni->runlist.lock);
|
||||
if (ret) {
|
||||
kvfree(punch_rl);
|
||||
|
|
@ -5770,7 +5887,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
|
|||
if (NInoRunlistDirty(ni)) {
|
||||
mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
|
||||
down_write(&ni->runlist.lock);
|
||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
if (err)
|
||||
ntfs_error(ni->vol->sb, "Updating mapping pairs failed");
|
||||
else
|
||||
|
|
|
|||
|
|
@ -112,7 +112,13 @@ int ntfs_non_resident_attr_punch_hole(struct ntfs_inode *ni, s64 start_vcn, s64
|
|||
int __ntfs_attr_truncate_vfs(struct ntfs_inode *ni, const s64 newsize,
|
||||
const s64 i_size);
|
||||
int ntfs_attr_expand(struct ntfs_inode *ni, const s64 newsize, const s64 prealloc_size);
|
||||
int ntfs_attr_expand_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||
const s64 prealloc_size,
|
||||
struct ntfs_inode *locked_ni);
|
||||
int ntfs_attr_truncate_i(struct ntfs_inode *ni, const s64 newsize, unsigned int holes);
|
||||
int ntfs_attr_truncate_i_locked(struct ntfs_inode *ni, const s64 newsize,
|
||||
unsigned int holes,
|
||||
struct ntfs_inode *locked_ni);
|
||||
int ntfs_attr_truncate(struct ntfs_inode *ni, const s64 newsize);
|
||||
int ntfs_attr_rm(struct ntfs_inode *ni);
|
||||
int ntfs_attr_exist(struct ntfs_inode *ni, const __le32 type, __le16 *name,
|
||||
|
|
@ -133,6 +139,9 @@ int ntfs_resident_attr_record_add(struct ntfs_inode *ni, __le32 type,
|
|||
__le16 *name, u8 name_len, u8 *val, u32 size,
|
||||
__le16 flags);
|
||||
int ntfs_attr_update_mapping_pairs(struct ntfs_inode *ni, s64 from_vcn);
|
||||
int ntfs_attr_update_mapping_pairs_locked(struct ntfs_inode *ni,
|
||||
s64 from_vcn,
|
||||
struct ntfs_inode *locked_ni);
|
||||
struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64 *lcn);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ int ntfs_attrlist_need(struct ntfs_inode *ni)
|
|||
* legal list size as a reserve.
|
||||
*/
|
||||
static int ntfs_attrlist_repack(struct inode *attr_vi,
|
||||
struct ntfs_inode *attr_ni, s64 min_alloc_size)
|
||||
struct ntfs_inode *attr_ni, s64 min_alloc_size,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
struct ntfs_volume *vol = attr_ni->vol;
|
||||
struct runlist_element *old_rl, *new_rl;
|
||||
|
|
@ -78,10 +79,12 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
|||
size_t old_rl_count, new_rl_count;
|
||||
unsigned long flags;
|
||||
int err, restore_err;
|
||||
|
||||
if (attr_ni->mft_no != FILE_MFT || !NInoNonResident(attr_ni) ||
|
||||
min_alloc_size < 0)
|
||||
return -EINVAL;
|
||||
/* The buffered I/O below can reacquire the attribute runlist lock. */
|
||||
if (attr_ni == locked_ni)
|
||||
return -ENOSPC;
|
||||
|
||||
err = ntfs_attr_map_whole_runlist(attr_ni);
|
||||
if (err)
|
||||
|
|
@ -131,7 +134,6 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
|||
err = -ENOSPC;
|
||||
goto out_free_data;
|
||||
}
|
||||
|
||||
old_rl = attr_ni->runlist.rl;
|
||||
old_rl_count = attr_ni->runlist.count;
|
||||
down_write(&attr_ni->runlist.lock);
|
||||
|
|
@ -152,7 +154,7 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
|||
}
|
||||
}
|
||||
|
||||
err = ntfs_attr_update_mapping_pairs(attr_ni, 0);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(attr_ni, 0, locked_ni);
|
||||
if (err)
|
||||
goto restore_old_runlist;
|
||||
|
||||
|
|
@ -177,7 +179,8 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
|||
attr_ni->allocated_size = old_alloc_size;
|
||||
write_unlock_irqrestore(&attr_ni->size_lock, flags);
|
||||
|
||||
restore_err = ntfs_attr_update_mapping_pairs(attr_ni, 0);
|
||||
restore_err = ntfs_attr_update_mapping_pairs_locked(
|
||||
attr_ni, 0, locked_ni);
|
||||
if (restore_err) {
|
||||
ntfs_error(vol->sb, "Failed to restore ATTRIBUTE_LIST mapping pairs (%d)",
|
||||
restore_err);
|
||||
|
|
@ -193,7 +196,8 @@ static int ntfs_attrlist_repack(struct inode *attr_vi,
|
|||
return err;
|
||||
}
|
||||
|
||||
int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
||||
int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
struct inode *attr_vi;
|
||||
struct ntfs_inode *attr_ni;
|
||||
|
|
@ -215,20 +219,27 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
|||
return err;
|
||||
}
|
||||
attr_ni = NTFS_I(attr_vi);
|
||||
/* Truncation and page-cache writes can reacquire this runlist lock. */
|
||||
if (attr_ni == locked_ni) {
|
||||
iput(attr_vi);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
err = ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size, HOLES_NO);
|
||||
err = ntfs_attr_truncate_i_locked(
|
||||
attr_ni, base_ni->attr_list_size, HOLES_NO, locked_ni);
|
||||
if (err == -ENOSPC && attr_ni->mft_no == FILE_MFT &&
|
||||
NInoNonResident(attr_ni)) {
|
||||
retry_err = ntfs_attrlist_repack(attr_vi, attr_ni,
|
||||
base_ni->attr_list_size);
|
||||
base_ni->attr_list_size, locked_ni);
|
||||
if (retry_err) {
|
||||
ntfs_error(base_ni->vol->sb, "Failed to repack attribute list");
|
||||
iput(attr_vi);
|
||||
return retry_err;
|
||||
}
|
||||
|
||||
retry_err = ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size,
|
||||
HOLES_NO);
|
||||
retry_err = ntfs_attr_truncate_i_locked(
|
||||
attr_ni, base_ni->attr_list_size,
|
||||
HOLES_NO, locked_ni);
|
||||
if (retry_err) {
|
||||
ntfs_error(base_ni->vol->sb,
|
||||
"Failed to resize attribute list after repack");
|
||||
|
|
@ -252,11 +263,13 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
|||
*/
|
||||
if (base_ni->mft_no == FILE_MFT && NInoNonResident(attr_ni) &&
|
||||
attr_ni->allocated_size < NTFS_MAX_ATTR_LIST_SIZE) {
|
||||
retry_err = ntfs_attr_expand(attr_ni, base_ni->attr_list_size,
|
||||
NTFS_MAX_ATTR_LIST_SIZE);
|
||||
retry_err = ntfs_attr_expand_locked(
|
||||
attr_ni, base_ni->attr_list_size,
|
||||
NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
|
||||
if (retry_err == -ENOSPC) {
|
||||
retry_err = ntfs_attrlist_repack(attr_vi, attr_ni,
|
||||
NTFS_MAX_ATTR_LIST_SIZE);
|
||||
retry_err = ntfs_attrlist_repack(
|
||||
attr_vi, attr_ni,
|
||||
NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
|
||||
if (retry_err == -ENOSPC)
|
||||
retry_err = 0;
|
||||
}
|
||||
|
|
@ -289,6 +302,11 @@ int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ntfs_attrlist_update(struct ntfs_inode *base_ni)
|
||||
{
|
||||
return ntfs_attrlist_update_locked(base_ni, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
||||
* @ni: opened ntfs inode, which contains that attribute
|
||||
|
|
|
|||
|
|
@ -16,5 +16,7 @@ int ntfs_attrlist_need(struct ntfs_inode *ni);
|
|||
int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr);
|
||||
int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx);
|
||||
int ntfs_attrlist_update(struct ntfs_inode *base_ni);
|
||||
int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
|
||||
struct ntfs_inode *locked_ni);
|
||||
|
||||
#endif /* defined _NTFS_ATTRLIST_H */
|
||||
|
|
|
|||
|
|
@ -1450,7 +1450,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
|
|||
ni->runlist.rl = rl;
|
||||
rlc = NULL;
|
||||
|
||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
up_write(&ni->runlist.lock);
|
||||
if (err)
|
||||
err = -EIO;
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ static int ntfs_trim_prealloc(struct inode *vi)
|
|||
ntfs_error(vol->sb, "Preallocated block rollback failed");
|
||||
} else {
|
||||
ni->allocated_size = ntfs_cluster_to_bytes(vol, vcn_tr);
|
||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(
|
||||
ni, 0, ni);
|
||||
if (err)
|
||||
ntfs_error(vol->sb,
|
||||
"Failed to rollback mapping pairs for prealloc");
|
||||
|
|
|
|||
|
|
@ -2772,7 +2772,7 @@ int __ntfs_write_inode(struct inode *vi, int sync)
|
|||
|
||||
if (NInoNonResident(ni) && NInoRunlistDirty(ni)) {
|
||||
down_write(&ni->runlist.lock);
|
||||
err = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
err = ntfs_attr_update_mapping_pairs_locked(ni, 0, ni);
|
||||
if (!err)
|
||||
NInoClearRunlistDirty(ni);
|
||||
up_write(&ni->runlist.lock);
|
||||
|
|
|
|||
|
|
@ -1131,7 +1131,8 @@ static s64 mft_bitmap_alloc_free_rec(struct ntfs_volume *vol,
|
|||
return ll;
|
||||
}
|
||||
|
||||
static int ntfs_mft_attr_extend(struct ntfs_inode *ni)
|
||||
static int ntfs_mft_attr_extend(struct ntfs_inode *ni,
|
||||
struct ntfs_inode *locked_ni)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ntfs_inode *base_ni;
|
||||
|
|
@ -1152,7 +1153,7 @@ static int ntfs_mft_attr_extend(struct ntfs_inode *ni)
|
|||
}
|
||||
}
|
||||
|
||||
ret = ntfs_attr_update_mapping_pairs(ni, 0);
|
||||
ret = ntfs_attr_update_mapping_pairs_locked(ni, 0, locked_ni);
|
||||
if (ret)
|
||||
pr_err("MP update failed\n");
|
||||
|
||||
|
|
@ -1340,7 +1341,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(struct ntfs_volume *vol)
|
|||
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
||||
if (unlikely(ret)) {
|
||||
ret = ntfs_mft_attr_extend(mftbmp_ni);
|
||||
ret = ntfs_mft_attr_extend(mftbmp_ni, mftbmp_ni);
|
||||
if (!ret)
|
||||
goto extended_ok;
|
||||
if (ret != -EAGAIN)
|
||||
|
|
@ -1451,7 +1452,9 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(struct ntfs_volume *vol)
|
|||
NVolSetErrors(vol);
|
||||
}
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
} else if (status.mp_extended && ntfs_attr_update_mapping_pairs(mftbmp_ni, 0)) {
|
||||
} else if (status.mp_extended &&
|
||||
ntfs_attr_update_mapping_pairs_locked(mftbmp_ni, 0,
|
||||
mftbmp_ni)) {
|
||||
ntfs_error(vol->sb, "Failed to restore mapping pairs.%s", es);
|
||||
NVolSetErrors(vol);
|
||||
}
|
||||
|
|
@ -1776,7 +1779,7 @@ static int ntfs_mft_data_extend_allocation_nolock(struct ntfs_volume *vol)
|
|||
ret = ntfs_attr_record_resize(ctx->mrec, a, mp_size +
|
||||
le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
|
||||
if (unlikely(ret)) {
|
||||
ret = ntfs_mft_attr_extend(mft_ni);
|
||||
ret = ntfs_mft_attr_extend(mft_ni, NULL);
|
||||
if (!ret)
|
||||
goto extended_ok;
|
||||
if (ret != -EAGAIN)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user