fs/ntfs3: Refactoring attr_set_size to restore after errors

Added comments to code
Added two undo labels for restoring after errors

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
Konstantin Komarov 2022-07-13 18:00:03 +03:00
parent c12df45ee6
commit 0e5b044cbf
No known key found for this signature in database
GPG Key ID: A9B0331F832407B6
1 changed files with 129 additions and 57 deletions

View File

@ -173,7 +173,6 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
{
int err;
CLST flen, vcn0 = vcn, pre = pre_alloc ? *pre_alloc : 0;
struct wnd_bitmap *wnd = &sbi->used.bitmap;
size_t cnt = run->count;
for (;;) {
@ -196,9 +195,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
/* Add new fragment into run storage. */
if (!run_add_entry(run, vcn, lcn, flen, opt == ALLOCATE_MFT)) {
/* Undo last 'ntfs_look_for_free_space' */
down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
wnd_set_free(wnd, lcn, flen);
up_write(&wnd->rw_lock);
mark_as_free_ex(sbi, lcn, len, false);
err = -ENOMEM;
goto out;
}
@ -419,40 +416,44 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
struct mft_inode *mi, *mi_b;
CLST alen, vcn, lcn, new_alen, old_alen, svcn, evcn;
CLST next_svcn, pre_alloc = -1, done = 0;
bool is_ext;
bool is_ext, is_bad = false;
u32 align;
struct MFT_REC *rec;
again:
alen = 0;
le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, type, name, name_len, NULL,
&mi_b);
if (!attr_b) {
err = -ENOENT;
goto out;
goto bad_inode;
}
if (!attr_b->non_res) {
err = attr_set_size_res(ni, attr_b, le_b, mi_b, new_size, run,
&attr_b);
if (err || !attr_b->non_res)
goto out;
if (err)
return err;
/* Return if file is still resident. */
if (!attr_b->non_res)
goto ok1;
/* Layout of records may be changed, so do a full search. */
goto again;
}
is_ext = is_attr_ext(attr_b);
again_1:
align = sbi->cluster_size;
if (is_ext)
align <<= attr_b->nres.c_unit;
old_valid = le64_to_cpu(attr_b->nres.valid_size);
old_size = le64_to_cpu(attr_b->nres.data_size);
old_alloc = le64_to_cpu(attr_b->nres.alloc_size);
again_1:
old_alen = old_alloc >> cluster_bits;
new_alloc = (new_size + align - 1) & ~(u64)(align - 1);
@ -475,24 +476,27 @@ again_1:
mi = mi_b;
} else if (!le_b) {
err = -EINVAL;
goto out;
goto bad_inode;
} else {
le = le_b;
attr = ni_find_attr(ni, attr_b, &le, type, name, name_len, &vcn,
&mi);
if (!attr) {
err = -EINVAL;
goto out;
goto bad_inode;
}
next_le_1:
svcn = le64_to_cpu(attr->nres.svcn);
evcn = le64_to_cpu(attr->nres.evcn);
}
/*
* Here we have:
* attr,mi,le - last attribute segment (containing 'vcn').
* attr_b,mi_b,le_b - base (primary) attribute segment.
*/
next_le:
rec = mi->mrec;
err = attr_load_runs(attr, ni, run, NULL);
if (err)
goto out;
@ -507,6 +511,13 @@ next_le:
goto ok;
}
/*
* Add clusters. In simple case we have to:
* - allocate space (vcn, lcn, len)
* - update packed run in 'mi'
* - update attr->nres.evcn
* - update attr_b->nres.data_size/attr_b->nres.alloc_size
*/
to_allocate = new_alen - old_alen;
add_alloc_in_same_attr_seg:
lcn = 0;
@ -520,9 +531,11 @@ add_alloc_in_same_attr_seg:
pre_alloc = 0;
if (type == ATTR_DATA && !name_len &&
sbi->options->prealloc) {
CLST new_alen2 = bytes_to_cluster(
sbi, get_pre_allocated(new_size));
pre_alloc = new_alen2 - new_alen;
pre_alloc =
bytes_to_cluster(
sbi,
get_pre_allocated(new_size)) -
new_alen;
}
/* Get the last LCN to allocate from. */
@ -580,7 +593,7 @@ add_alloc_in_same_attr_seg:
pack_runs:
err = mi_pack_runs(mi, attr, run, vcn - svcn);
if (err)
goto out;
goto undo_1;
next_svcn = le64_to_cpu(attr->nres.evcn) + 1;
new_alloc_tmp = (u64)next_svcn << cluster_bits;
@ -614,7 +627,7 @@ pack_runs:
if (type == ATTR_LIST) {
err = ni_expand_list(ni);
if (err)
goto out;
goto undo_2;
if (next_svcn < vcn)
goto pack_runs;
@ -624,8 +637,9 @@ pack_runs:
if (!ni->attr_list.size) {
err = ni_create_attr_list(ni);
/* In case of error layout of records is not changed. */
if (err)
goto out;
goto undo_2;
/* Layout of records is changed. */
}
@ -638,8 +652,24 @@ pack_runs:
err = ni_insert_nonresident(ni, type, name, name_len, run,
next_svcn, vcn - next_svcn,
attr_b->flags, &attr, &mi, NULL);
if (err)
goto out;
/*
* Layout of records maybe changed.
* Find base attribute to update.
*/
le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, type, name, name_len,
NULL, &mi_b);
if (!attr_b) {
err = -EINVAL;
goto bad_inode;
}
if (err) {
/* ni_insert_nonresident failed. */
attr = NULL;
goto undo_2;
}
if (!is_mft)
run_truncate_head(run, evcn + 1);
@ -647,38 +677,31 @@ pack_runs:
svcn = le64_to_cpu(attr->nres.svcn);
evcn = le64_to_cpu(attr->nres.evcn);
le_b = NULL;
/*
* Layout of records maybe changed.
* Find base attribute to update.
* Attribute is in consistency state.
* Save this point to restore to if next steps fail.
*/
attr_b = ni_find_attr(ni, NULL, &le_b, type, name, name_len,
NULL, &mi_b);
if (!attr_b) {
err = -ENOENT;
goto out;
}
attr_b->nres.alloc_size = cpu_to_le64((u64)vcn << cluster_bits);
attr_b->nres.data_size = attr_b->nres.alloc_size;
attr_b->nres.valid_size = attr_b->nres.alloc_size;
old_valid = old_size = old_alloc = (u64)vcn << cluster_bits;
attr_b->nres.valid_size = attr_b->nres.data_size =
attr_b->nres.alloc_size = cpu_to_le64(old_size);
mi_b->dirty = true;
goto again_1;
}
if (new_size != old_size ||
(new_alloc != old_alloc && !keep_prealloc)) {
/*
* Truncate clusters. In simple case we have to:
* - update packed run in 'mi'
* - update attr->nres.evcn
* - update attr_b->nres.data_size/attr_b->nres.alloc_size
* - mark and trim clusters as free (vcn, lcn, len)
*/
CLST dlen = 0;
vcn = max(svcn, new_alen);
new_alloc_tmp = (u64)vcn << cluster_bits;
alen = 0;
err = run_deallocate_ex(sbi, run, vcn, evcn - vcn + 1, &alen,
true);
if (err)
goto out;
run_truncate(run, vcn);
if (vcn > svcn) {
err = mi_pack_runs(mi, attr, run, vcn - svcn);
if (err)
@ -697,7 +720,7 @@ pack_runs:
if (!al_remove_le(ni, le)) {
err = -EINVAL;
goto out;
goto bad_inode;
}
le = (struct ATTR_LIST_ENTRY *)((u8 *)le - le_sz);
@ -723,13 +746,21 @@ pack_runs:
attr_b->nres.valid_size =
attr_b->nres.alloc_size;
}
if (is_ext)
le64_sub_cpu(&attr_b->nres.total_size,
((u64)alen << cluster_bits));
mi_b->dirty = true;
err = run_deallocate_ex(sbi, run, vcn, evcn - vcn + 1, &dlen,
true);
if (err)
goto out;
if (is_ext) {
/* dlen - really deallocated clusters. */
le64_sub_cpu(&attr_b->nres.total_size,
((u64)dlen << cluster_bits));
}
run_truncate(run, vcn);
if (new_alloc_tmp <= new_alloc)
goto ok;
@ -747,7 +778,7 @@ pack_runs:
if (le->type != type || le->name_len != name_len ||
memcmp(le_name(le), name, name_len * sizeof(short))) {
err = -EINVAL;
goto out;
goto bad_inode;
}
err = ni_load_mi(ni, le, &mi);
@ -757,7 +788,7 @@ pack_runs:
attr = mi_find_attr(mi, NULL, type, name, name_len, &le->id);
if (!attr) {
err = -EINVAL;
goto out;
goto bad_inode;
}
goto next_le_1;
}
@ -772,13 +803,13 @@ ok:
}
}
out:
if (!err && attr_b && ret)
ok1:
if (ret)
*ret = attr_b;
/* Update inode_set_bytes. */
if (!err && ((type == ATTR_DATA && !name_len) ||
(type == ATTR_ALLOC && name == I30_NAME))) {
if (((type == ATTR_DATA && !name_len) ||
(type == ATTR_ALLOC && name == I30_NAME))) {
bool dirty = false;
if (ni->vfs_inode.i_size != new_size) {
@ -786,7 +817,7 @@ out:
dirty = true;
}
if (attr_b && attr_b->non_res) {
if (attr_b->non_res) {
new_alloc = le64_to_cpu(attr_b->nres.alloc_size);
if (inode_get_bytes(&ni->vfs_inode) != new_alloc) {
inode_set_bytes(&ni->vfs_inode, new_alloc);
@ -800,6 +831,47 @@ out:
}
}
return 0;
undo_2:
vcn -= alen;
attr_b->nres.data_size = cpu_to_le64(old_size);
attr_b->nres.valid_size = cpu_to_le64(old_valid);
attr_b->nres.alloc_size = cpu_to_le64(old_alloc);
/* Restore 'attr' and 'mi'. */
if (attr)
goto restore_run;
if (le64_to_cpu(attr_b->nres.svcn) <= svcn &&
svcn <= le64_to_cpu(attr_b->nres.evcn)) {
attr = attr_b;
le = le_b;
mi = mi_b;
} else if (!le_b) {
err = -EINVAL;
goto bad_inode;
} else {
le = le_b;
attr = ni_find_attr(ni, attr_b, &le, type, name, name_len,
&svcn, &mi);
if (!attr)
goto bad_inode;
}
restore_run:
if (mi_pack_runs(mi, attr, run, evcn - svcn + 1))
is_bad = true;
undo_1:
run_deallocate_ex(sbi, run, vcn, alen, NULL, false);
run_truncate(run, vcn);
out:
if (is_bad) {
bad_inode:
_ntfs_bad_inode(&ni->vfs_inode);
}
return err;
}