Skip to content

Commit 910013f

Browse files
fs/ntfs3: Restore correct state after ENOSPC in attr_data_get_block
Added new function ntfs_check_for_free_space. Added undo mechanism in attr_data_get_block. Fixes xfstest generic/083 Signed-off-by: Konstantin Komarov <[email protected]>
1 parent 0ad9dfc commit 910013f

File tree

3 files changed

+125
-50
lines changed

3 files changed

+125
-50
lines changed

fs/ntfs3/attrib.c

Lines changed: 91 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -891,8 +891,10 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
891891
struct ATTR_LIST_ENTRY *le, *le_b;
892892
struct mft_inode *mi, *mi_b;
893893
CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0, alen;
894+
CLST alloc, evcn;
894895
unsigned fr;
895-
u64 total_size;
896+
u64 total_size, total_size0;
897+
int step = 0;
896898

897899
if (new)
898900
*new = false;
@@ -932,7 +934,12 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
932934

933935
asize = le64_to_cpu(attr_b->nres.alloc_size) >> cluster_bits;
934936
if (vcn >= asize) {
935-
err = -EINVAL;
937+
if (new) {
938+
err = -EINVAL;
939+
} else {
940+
*len = 1;
941+
*lcn = SPARSE_LCN;
942+
}
936943
goto out;
937944
}
938945

@@ -1036,10 +1043,12 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
10361043
if (err)
10371044
goto out;
10381045
*new = true;
1046+
step = 1;
10391047

10401048
end = vcn + alen;
1041-
total_size = le64_to_cpu(attr_b->nres.total_size) +
1042-
((u64)alen << cluster_bits);
1049+
/* Save 'total_size0' to restore if error. */
1050+
total_size0 = le64_to_cpu(attr_b->nres.total_size);
1051+
total_size = total_size0 + ((u64)alen << cluster_bits);
10431052

10441053
if (vcn != vcn0) {
10451054
if (!run_lookup_entry(run, vcn0, lcn, len, NULL)) {
@@ -1081,7 +1090,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
10811090
if (!ni->attr_list.size) {
10821091
err = ni_create_attr_list(ni);
10831092
if (err)
1084-
goto out;
1093+
goto undo1;
10851094
/* Layout of records is changed. */
10861095
le_b = NULL;
10871096
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL,
@@ -1098,67 +1107,83 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
10981107
}
10991108
}
11001109

1110+
/*
1111+
* The code below may require additional cluster (to extend attribute list)
1112+
* and / or one MFT record
1113+
* It is too complex to undo operations if -ENOSPC occurs deep inside
1114+
* in 'ni_insert_nonresident'.
1115+
* Return in advance -ENOSPC here if there are no free cluster and no free MFT.
1116+
*/
1117+
if (!ntfs_check_for_free_space(sbi, 1, 1)) {
1118+
/* Undo step 1. */
1119+
err = -ENOSPC;
1120+
goto undo1;
1121+
}
1122+
1123+
step = 2;
11011124
svcn = evcn1;
11021125

11031126
/* Estimate next attribute. */
11041127
attr = ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi);
11051128

1106-
if (attr) {
1107-
CLST alloc = bytes_to_cluster(
1108-
sbi, le64_to_cpu(attr_b->nres.alloc_size));
1109-
CLST evcn = le64_to_cpu(attr->nres.evcn);
1110-
1111-
if (end < next_svcn)
1112-
end = next_svcn;
1113-
while (end > evcn) {
1114-
/* Remove segment [svcn : evcn). */
1115-
mi_remove_attr(NULL, mi, attr);
1116-
1117-
if (!al_remove_le(ni, le)) {
1118-
err = -EINVAL;
1119-
goto out;
1120-
}
1129+
if (!attr) {
1130+
/* Insert new attribute segment. */
1131+
goto ins_ext;
1132+
}
11211133

1122-
if (evcn + 1 >= alloc) {
1123-
/* Last attribute segment. */
1124-
evcn1 = evcn + 1;
1125-
goto ins_ext;
1126-
}
1134+
/* Try to update existed attribute segment. */
1135+
alloc = bytes_to_cluster(sbi, le64_to_cpu(attr_b->nres.alloc_size));
1136+
evcn = le64_to_cpu(attr->nres.evcn);
11271137

1128-
if (ni_load_mi(ni, le, &mi)) {
1129-
attr = NULL;
1130-
goto out;
1131-
}
1138+
if (end < next_svcn)
1139+
end = next_svcn;
1140+
while (end > evcn) {
1141+
/* Remove segment [svcn : evcn). */
1142+
mi_remove_attr(NULL, mi, attr);
11321143

1133-
attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0,
1134-
&le->id);
1135-
if (!attr) {
1136-
err = -EINVAL;
1137-
goto out;
1138-
}
1139-
svcn = le64_to_cpu(attr->nres.svcn);
1140-
evcn = le64_to_cpu(attr->nres.evcn);
1144+
if (!al_remove_le(ni, le)) {
1145+
err = -EINVAL;
1146+
goto out;
11411147
}
11421148

1143-
if (end < svcn)
1144-
end = svcn;
1149+
if (evcn + 1 >= alloc) {
1150+
/* Last attribute segment. */
1151+
evcn1 = evcn + 1;
1152+
goto ins_ext;
1153+
}
11451154

1146-
err = attr_load_runs(attr, ni, run, &end);
1147-
if (err)
1155+
if (ni_load_mi(ni, le, &mi)) {
1156+
attr = NULL;
11481157
goto out;
1158+
}
11491159

1150-
evcn1 = evcn + 1;
1151-
attr->nres.svcn = cpu_to_le64(next_svcn);
1152-
err = mi_pack_runs(mi, attr, run, evcn1 - next_svcn);
1153-
if (err)
1160+
attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, &le->id);
1161+
if (!attr) {
1162+
err = -EINVAL;
11541163
goto out;
1164+
}
1165+
svcn = le64_to_cpu(attr->nres.svcn);
1166+
evcn = le64_to_cpu(attr->nres.evcn);
1167+
}
11551168

1156-
le->vcn = cpu_to_le64(next_svcn);
1157-
ni->attr_list.dirty = true;
1158-
mi->dirty = true;
1169+
if (end < svcn)
1170+
end = svcn;
1171+
1172+
err = attr_load_runs(attr, ni, run, &end);
1173+
if (err)
1174+
goto out;
1175+
1176+
evcn1 = evcn + 1;
1177+
attr->nres.svcn = cpu_to_le64(next_svcn);
1178+
err = mi_pack_runs(mi, attr, run, evcn1 - next_svcn);
1179+
if (err)
1180+
goto out;
1181+
1182+
le->vcn = cpu_to_le64(next_svcn);
1183+
ni->attr_list.dirty = true;
1184+
mi->dirty = true;
1185+
next_svcn = le64_to_cpu(attr->nres.evcn) + 1;
11591186

1160-
next_svcn = le64_to_cpu(attr->nres.evcn) + 1;
1161-
}
11621187
ins_ext:
11631188
if (evcn1 > next_svcn) {
11641189
err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run,
@@ -1170,10 +1195,26 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
11701195
ok:
11711196
run_truncate_around(run, vcn);
11721197
out:
1198+
if (err && step > 1) {
1199+
/* Too complex to restore. */
1200+
_ntfs_bad_inode(&ni->vfs_inode);
1201+
}
11731202
up_write(&ni->file.run_lock);
11741203
ni_unlock(ni);
11751204

11761205
return err;
1206+
1207+
undo1:
1208+
/* Undo step1. */
1209+
attr_b->nres.total_size = cpu_to_le64(total_size0);
1210+
inode_set_bytes(&ni->vfs_inode, total_size0);
1211+
1212+
if (run_deallocate_ex(sbi, run, vcn, alen, NULL, false) ||
1213+
!run_add_entry(run, vcn, SPARSE_LCN, alen, false) ||
1214+
mi_pack_runs(mi, attr, run, max(end, evcn1) - svcn)) {
1215+
_ntfs_bad_inode(&ni->vfs_inode);
1216+
}
1217+
goto out;
11771218
}
11781219

11791220
int attr_data_read_resident(struct ntfs_inode *ni, struct page *page)

fs/ntfs3/fsntfs.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,39 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
443443
return err;
444444
}
445445

446+
/*
447+
* ntfs_check_for_free_space
448+
*
449+
* Check if it is possible to allocate 'clen' clusters and 'mlen' Mft records
450+
*/
451+
bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen)
452+
{
453+
size_t free, zlen, avail;
454+
struct wnd_bitmap *wnd;
455+
456+
wnd = &sbi->used.bitmap;
457+
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
458+
free = wnd_zeroes(wnd);
459+
zlen = wnd_zone_len(wnd);
460+
up_read(&wnd->rw_lock);
461+
462+
if (free < zlen + clen)
463+
return false;
464+
465+
avail = free - (zlen + clen);
466+
467+
wnd = &sbi->mft.bitmap;
468+
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT);
469+
free = wnd_zeroes(wnd);
470+
zlen = wnd_zone_len(wnd);
471+
up_read(&wnd->rw_lock);
472+
473+
if (free >= zlen + mlen)
474+
return true;
475+
476+
return avail >= bytes_to_cluster(sbi, mlen << sbi->record_bits);
477+
}
478+
446479
/*
447480
* ntfs_extend_mft - Allocate additional MFT records.
448481
*

fs/ntfs3/ntfs_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi);
588588
int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
589589
CLST *new_lcn, CLST *new_len,
590590
enum ALLOCATE_OPT opt);
591+
bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen);
591592
int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft,
592593
struct ntfs_inode *ni, struct mft_inode **mi);
593594
void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno, bool is_mft);

0 commit comments

Comments
 (0)