@@ -39,6 +39,9 @@ static int lfs_bd_read(lfs_t *lfs,
39
39
void * buffer , lfs_size_t size ) {
40
40
uint8_t * data = buffer ;
41
41
LFS_ASSERT (block != 0xffffffff );
42
+ if (off + size > lfs -> cfg -> block_size ) {
43
+ return LFS_ERR_CORRUPT ;
44
+ }
42
45
43
46
while (size > 0 ) {
44
47
if (pcache && block == pcache -> block &&
@@ -452,6 +455,7 @@ struct lfs_commit {
452
455
453
456
lfs_off_t begin ;
454
457
lfs_off_t end ;
458
+ lfs_off_t ack ;
455
459
};
456
460
457
461
struct lfs_diskoff {
@@ -503,6 +507,24 @@ static int32_t lfs_commit_get(lfs_t *lfs, lfs_block_t block, lfs_off_t off,
503
507
return LFS_ERR_NOENT ;
504
508
}
505
509
510
+ static int lfs_commit_prog (lfs_t * lfs , struct lfs_commit * commit ,
511
+ const void * buffer , lfs_size_t size ) {
512
+ lfs_off_t skip = lfs_min (lfs_max (commit -> ack , commit -> off )
513
+ - commit -> off , size );
514
+ int err = lfs_bd_prog (lfs ,
515
+ & lfs -> pcache , & lfs -> rcache , false,
516
+ commit -> block , commit -> off + skip ,
517
+ (const uint8_t * )buffer + skip , size - skip );
518
+ if (err ) {
519
+ return err ;
520
+ }
521
+
522
+ commit -> crc = lfs_crc32 (commit -> crc , buffer , size );
523
+ commit -> off += size ;
524
+ commit -> ack = lfs_max (commit -> off , commit -> ack );
525
+ return 0 ;
526
+ }
527
+
506
528
static int lfs_commit_attrs (lfs_t * lfs , struct lfs_commit * commit ,
507
529
uint16_t id , const struct lfs_attr * attrs );
508
530
@@ -532,21 +554,14 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
532
554
533
555
// write out tag
534
556
uint32_t ntag = lfs_tole32 ((tag & 0x7fffffff ) ^ commit -> ptag );
535
- commit -> crc = lfs_crc32 (commit -> crc , & ntag , sizeof (ntag ));
536
- int err = lfs_bd_prog (lfs ,
537
- & lfs -> pcache , & lfs -> rcache , false,
538
- commit -> block , commit -> off , & ntag , sizeof (ntag ));
557
+ int err = lfs_commit_prog (lfs , commit , & ntag , sizeof (ntag ));
539
558
if (err ) {
540
559
return err ;
541
560
}
542
- commit -> off += sizeof (ntag );
543
561
544
562
if (!(tag & 0x80000000 )) {
545
563
// from memory
546
- commit -> crc = lfs_crc32 (commit -> crc , buffer , size );
547
- err = lfs_bd_prog (lfs ,
548
- & lfs -> pcache , & lfs -> rcache , false,
549
- commit -> block , commit -> off , buffer , size );
564
+ err = lfs_commit_prog (lfs , commit , buffer , size );
550
565
if (err ) {
551
566
return err ;
552
567
}
@@ -563,17 +578,13 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
563
578
return err ;
564
579
}
565
580
566
- commit -> crc = lfs_crc32 (commit -> crc , & dat , 1 );
567
- err = lfs_bd_prog (lfs ,
568
- & lfs -> pcache , & lfs -> rcache , false,
569
- commit -> block , commit -> off + i , & dat , 1 );
581
+ err = lfs_commit_prog (lfs , commit , & dat , 1 );
570
582
if (err ) {
571
583
return err ;
572
584
}
573
585
}
574
586
}
575
587
576
- commit -> off += size ;
577
588
commit -> ptag = tag & 0x7fffffff ;
578
589
return 0 ;
579
590
}
@@ -677,13 +688,11 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
677
688
678
689
// read erased state from next program unit
679
690
uint32_t tag = 0 ;
680
- if (off < lfs -> cfg -> block_size ) {
681
- int err = lfs_bd_read (lfs ,
682
- & lfs -> pcache , & lfs -> rcache , lfs -> cfg -> block_size ,
683
- commit -> block , off , & tag , sizeof (tag ));
684
- if (err ) {
685
- return err ;
686
- }
691
+ int err = lfs_bd_read (lfs ,
692
+ & lfs -> pcache , & lfs -> rcache , sizeof (tag ),
693
+ commit -> block , off , & tag , sizeof (tag ));
694
+ if (err && err != LFS_ERR_CORRUPT ) {
695
+ return err ;
687
696
}
688
697
689
698
// build crc tag
@@ -697,7 +706,7 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
697
706
footer [0 ] = lfs_tole32 (tag ^ commit -> ptag );
698
707
commit -> crc = lfs_crc32 (commit -> crc , & footer [0 ], sizeof (footer [0 ]));
699
708
footer [1 ] = lfs_tole32 (commit -> crc );
700
- int err = lfs_bd_prog (lfs ,
709
+ err = lfs_bd_prog (lfs ,
701
710
& lfs -> pcache , & lfs -> rcache , false,
702
711
commit -> block , commit -> off , & footer , sizeof (footer ));
703
712
if (err ) {
@@ -824,12 +833,6 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs,
824
833
lfs_global_zero (& templocals );
825
834
826
835
while (true) {
827
- // reached end of block
828
- if (off + sizeof (uint32_t ) >= lfs -> cfg -> block_size ) {
829
- dir -> erased = false;
830
- break ;
831
- }
832
-
833
836
// extract next tag
834
837
uint32_t tag ;
835
838
int err = lfs_bd_read (lfs ,
@@ -1076,79 +1079,88 @@ static int lfs_dir_compact(lfs_t *lfs,
1076
1079
lfs_global_zero (& dir -> locals );
1077
1080
1078
1081
while (true) {
1079
- // last complete id
1080
- dir -> count = end - begin ;
1081
- int16_t ack = -1 ;
1082
+ // setup compaction
1083
+ bool splitted = false;
1082
1084
bool exhausted = false;
1083
1085
1084
- // increment revision count
1085
- dir -> rev += 1 ;
1086
- if (lfs -> cfg -> block_cycles && dir -> rev % lfs -> cfg -> block_cycles == 0 ) {
1087
- if (lfs_pair_cmp (dir -> pair , (const lfs_block_t [2 ]){0 , 1 }) == 0 ) {
1088
- // we're writing too much to the superblock, should we expand?
1089
- lfs_ssize_t res = lfs_fs_size (lfs );
1090
- if (res < 0 ) {
1091
- return res ;
1092
- }
1086
+ struct lfs_commit commit ;
1087
+ commit .block = dir -> pair [1 ];
1088
+ commit .ack = 0 ;
1089
+
1090
+ commit :
1091
+ // setup erase state
1092
+ exhausted = false;
1093
+ dir -> count = end - begin ;
1094
+ int16_t ackid = -1 ;
1095
+
1096
+ // setup commit state
1097
+ commit .off = 0 ;
1098
+ commit .crc = 0xffffffff ;
1099
+ commit .ptag = 0 ;
1100
+
1101
+ // space is complicated, we need room for tail, crc, globals,
1102
+ // cleanup delete, and we cap at half a block to give room
1103
+ // for metadata updates
1104
+ commit .begin = 0 ;
1105
+ commit .end = lfs_min (
1106
+ lfs_alignup (lfs -> cfg -> block_size /2 , lfs -> cfg -> prog_size ),
1107
+ lfs -> cfg -> block_size - 38 );
1108
+
1109
+ if (!splitted ) {
1110
+ // increment revision count
1111
+ dir -> rev += 1 ;
1112
+ if (lfs -> cfg -> block_cycles &&
1113
+ dir -> rev % lfs -> cfg -> block_cycles == 0 ) {
1114
+ if (lfs_pair_cmp (dir -> pair ,
1115
+ (const lfs_block_t [2 ]){0 , 1 }) == 0 ) {
1116
+ // we're writing too much to the superblock,
1117
+ // should we expand?
1118
+ lfs_ssize_t res = lfs_fs_size (lfs );
1119
+ if (res < 0 ) {
1120
+ return res ;
1121
+ }
1093
1122
1094
- // do we have enough space to expand?
1095
- if (res < lfs -> cfg -> block_count /2 ) {
1096
- LFS_DEBUG ("Expanding superblock at rev %" PRIu32 , dir -> rev );
1123
+ // do we have enough space to expand?
1124
+ if (res < lfs -> cfg -> block_count /2 ) {
1125
+ LFS_DEBUG ("Expanding superblock at rev %" PRIu32 ,
1126
+ dir -> rev );
1127
+ exhausted = true;
1128
+ goto split ;
1129
+ }
1130
+ } else {
1131
+ // we're writing too much, time to relocate
1097
1132
exhausted = true;
1098
- goto split ;
1133
+ goto relocate ;
1099
1134
}
1100
- } else {
1101
- // we're writing too much, time to relocate
1102
- exhausted = true;
1103
- goto relocate ;
1104
1135
}
1105
- }
1106
1136
1107
- // erase block to write to
1108
- int err = lfs_bd_erase (lfs , dir -> pair [1 ]);
1109
- if (err ) {
1110
- if (err == LFS_ERR_CORRUPT ) {
1111
- goto relocate ;
1137
+ // erase block to write to
1138
+ int err = lfs_bd_erase (lfs , dir -> pair [1 ]);
1139
+ if (err ) {
1140
+ if (err == LFS_ERR_CORRUPT ) {
1141
+ goto relocate ;
1142
+ }
1143
+ return err ;
1112
1144
}
1113
- return err ;
1114
1145
}
1115
1146
1116
1147
// write out header
1117
- uint32_t crc = 0xffffffff ;
1118
1148
uint32_t rev = lfs_tole32 (dir -> rev );
1119
- crc = lfs_crc32 (crc , & rev , sizeof (rev ));
1120
- err = lfs_bd_prog (lfs ,
1121
- & lfs -> pcache , & lfs -> rcache , false,
1122
- dir -> pair [1 ], 0 , & rev , sizeof (rev ));
1149
+ int err = lfs_commit_prog (lfs , & commit , & rev , sizeof (rev ));
1123
1150
if (err ) {
1124
1151
if (err == LFS_ERR_CORRUPT ) {
1125
1152
goto relocate ;
1126
1153
}
1127
1154
return err ;
1128
1155
}
1129
1156
1130
- // setup compaction
1131
- struct lfs_commit commit = {
1132
- .block = dir -> pair [1 ],
1133
- .off = sizeof (dir -> rev ),
1134
- .crc = crc ,
1135
- .ptag = 0 ,
1136
-
1137
- // space is complicated, we need room for tail, crc, globals,
1138
- // and we cap at half a block to give room for metadata updates
1139
- .begin = 0 ,
1140
- .end = lfs_min (
1141
- lfs_alignup (lfs -> cfg -> block_size /2 , lfs -> cfg -> prog_size ),
1142
- lfs -> cfg -> block_size - 34 ),
1143
- };
1144
-
1145
1157
// commit with a move
1146
- for (uint16_t id = begin ; id < end ; id ++ ) {
1158
+ for (uint16_t id = begin ; id < end || commit . off < commit . ack ; id ++ ) {
1147
1159
err = lfs_commit_move (lfs , & commit ,
1148
1160
0x003ff000 , LFS_MKTAG (0 , id , 0 ),
1149
1161
0x003ff000 , LFS_MKTAG (0 , id - begin , 0 ),
1150
1162
source , attrs );
1151
- if (err ) {
1163
+ if (err && !( splitted && err == LFS_ERR_NOSPC ) ) {
1152
1164
if (err == LFS_ERR_NOSPC ) {
1153
1165
goto split ;
1154
1166
} else if (err == LFS_ERR_CORRUPT ) {
@@ -1157,12 +1169,25 @@ static int lfs_dir_compact(lfs_t *lfs,
1157
1169
return err ;
1158
1170
}
1159
1171
1160
- ack = id ;
1172
+ ackid = id ;
1161
1173
}
1162
1174
1163
1175
// reopen reserved space at the end
1164
1176
commit .end = lfs -> cfg -> block_size - 8 ;
1165
1177
1178
+ if (ackid >= end ) {
1179
+ // extra garbage attributes were written out during split,
1180
+ // need to clean up
1181
+ err = lfs_commit_attr (lfs , & commit ,
1182
+ LFS_MKTAG (LFS_TYPE_DELETE , ackid , 0 ), NULL );
1183
+ if (err ) {
1184
+ if (err == LFS_ERR_CORRUPT ) {
1185
+ goto relocate ;
1186
+ }
1187
+ return err ;
1188
+ }
1189
+ }
1190
+
1166
1191
if (lfs_pair_cmp (dir -> pair , (const lfs_block_t [2 ]){0 , 1 }) == 0 ) {
1167
1192
// move over (duplicate) superblock if we are root
1168
1193
err = lfs_commit_move (lfs , & commit ,
@@ -1178,8 +1203,8 @@ static int lfs_dir_compact(lfs_t *lfs,
1178
1203
}
1179
1204
1180
1205
if (!relocated ) {
1181
- // commit any globals, unless we're relocating, in which case our
1182
- // parent will steal our globals
1206
+ // commit any globals, unless we're relocating,
1207
+ // in which case our parent will steal our globals
1183
1208
err = lfs_commit_globals (lfs , & commit , & dir -> locals );
1184
1209
if (err ) {
1185
1210
if (err == LFS_ERR_CORRUPT ) {
@@ -1222,8 +1247,13 @@ static int lfs_dir_compact(lfs_t *lfs,
1222
1247
split :
1223
1248
// commit no longer fits, need to split dir,
1224
1249
// drop caches and create tail
1225
- lfs_cache_drop (lfs , & lfs -> pcache );
1226
- if (!exhausted && ack < 0 ) {
1250
+ splitted = !exhausted ;
1251
+ if (lfs -> pcache .block != 0xffffffff ) {
1252
+ commit .ack -= lfs -> pcache .size ;
1253
+ lfs_cache_drop (lfs , & lfs -> pcache );
1254
+ }
1255
+
1256
+ if (!exhausted && ackid < 0 ) {
1227
1257
// If we can't fit in this block, we won't fit in next block
1228
1258
return LFS_ERR_NOSPC ;
1229
1259
}
@@ -1234,25 +1264,26 @@ static int lfs_dir_compact(lfs_t *lfs,
1234
1264
return err ;
1235
1265
}
1236
1266
1237
- if (exhausted ) {
1238
- lfs -> root [0 ] = tail .pair [0 ];
1239
- lfs -> root [1 ] = tail .pair [1 ];
1240
- }
1241
-
1242
1267
tail .split = dir -> split ;
1243
1268
tail .tail [0 ] = dir -> tail [0 ];
1244
1269
tail .tail [1 ] = dir -> tail [1 ];
1245
1270
1246
- err = lfs_dir_compact (lfs , & tail , attrs , source , ack + 1 , end );
1271
+ err = lfs_dir_compact (lfs , & tail , attrs , source , ackid + 1 , end );
1247
1272
if (err ) {
1248
1273
return err ;
1249
1274
}
1250
1275
1251
- end = ack + 1 ;
1276
+ end = ackid + 1 ;
1252
1277
dir -> tail [0 ] = tail .pair [0 ];
1253
1278
dir -> tail [1 ] = tail .pair [1 ];
1254
1279
dir -> split = true;
1255
- continue ;
1280
+
1281
+ if (exhausted ) {
1282
+ lfs -> root [0 ] = tail .pair [0 ];
1283
+ lfs -> root [1 ] = tail .pair [1 ];
1284
+ }
1285
+
1286
+ goto commit ;
1256
1287
1257
1288
relocate :
1258
1289
// commit was corrupted, drop caches and prepare to relocate block
@@ -1363,6 +1394,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
1363
1394
1364
1395
.begin = dir -> off ,
1365
1396
.end = lfs -> cfg -> block_size - 8 ,
1397
+ .ack = 0 ,
1366
1398
};
1367
1399
1368
1400
for (const lfs_mattr_t * a = attrs ; a ; a = a -> next ) {
0 commit comments