@@ -39,6 +39,9 @@ static int lfs_bd_read(lfs_t *lfs,
3939 void * buffer , lfs_size_t size ) {
4040 uint8_t * data = buffer ;
4141 LFS_ASSERT (block != 0xffffffff );
42+ if (off + size > lfs -> cfg -> block_size ) {
43+ return LFS_ERR_CORRUPT ;
44+ }
4245
4346 while (size > 0 ) {
4447 if (pcache && block == pcache -> block &&
@@ -452,6 +455,7 @@ struct lfs_commit {
452455
453456 lfs_off_t begin ;
454457 lfs_off_t end ;
458+ lfs_off_t ack ;
455459};
456460
457461struct lfs_diskoff {
@@ -503,6 +507,24 @@ static int32_t lfs_commit_get(lfs_t *lfs, lfs_block_t block, lfs_off_t off,
503507 return LFS_ERR_NOENT ;
504508}
505509
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+
506528static int lfs_commit_attrs (lfs_t * lfs , struct lfs_commit * commit ,
507529 uint16_t id , const struct lfs_attr * attrs );
508530
@@ -532,21 +554,14 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
532554
533555 // write out tag
534556 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 ));
539558 if (err ) {
540559 return err ;
541560 }
542- commit -> off += sizeof (ntag );
543561
544562 if (!(tag & 0x80000000 )) {
545563 // 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 );
550565 if (err ) {
551566 return err ;
552567 }
@@ -563,17 +578,13 @@ static int lfs_commit_attr(lfs_t *lfs, struct lfs_commit *commit,
563578 return err ;
564579 }
565580
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 );
570582 if (err ) {
571583 return err ;
572584 }
573585 }
574586 }
575587
576- commit -> off += size ;
577588 commit -> ptag = tag & 0x7fffffff ;
578589 return 0 ;
579590}
@@ -677,13 +688,11 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
677688
678689 // read erased state from next program unit
679690 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 ;
687696 }
688697
689698 // build crc tag
@@ -697,7 +706,7 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
697706 footer [0 ] = lfs_tole32 (tag ^ commit -> ptag );
698707 commit -> crc = lfs_crc32 (commit -> crc , & footer [0 ], sizeof (footer [0 ]));
699708 footer [1 ] = lfs_tole32 (commit -> crc );
700- int err = lfs_bd_prog (lfs ,
709+ err = lfs_bd_prog (lfs ,
701710 & lfs -> pcache , & lfs -> rcache , false,
702711 commit -> block , commit -> off , & footer , sizeof (footer ));
703712 if (err ) {
@@ -824,12 +833,6 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs,
824833 lfs_global_zero (& templocals );
825834
826835 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-
833836 // extract next tag
834837 uint32_t tag ;
835838 int err = lfs_bd_read (lfs ,
@@ -1076,79 +1079,88 @@ static int lfs_dir_compact(lfs_t *lfs,
10761079 lfs_global_zero (& dir -> locals );
10771080
10781081 while (true) {
1079- // last complete id
1080- dir -> count = end - begin ;
1081- int16_t ack = -1 ;
1082+ // setup compaction
1083+ bool splitted = false;
10821084 bool exhausted = false;
10831085
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+ }
10931122
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
10971132 exhausted = true;
1098- goto split ;
1133+ goto relocate ;
10991134 }
1100- } else {
1101- // we're writing too much, time to relocate
1102- exhausted = true;
1103- goto relocate ;
11041135 }
1105- }
11061136
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 ;
11121144 }
1113- return err ;
11141145 }
11151146
11161147 // write out header
1117- uint32_t crc = 0xffffffff ;
11181148 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 ));
11231150 if (err ) {
11241151 if (err == LFS_ERR_CORRUPT ) {
11251152 goto relocate ;
11261153 }
11271154 return err ;
11281155 }
11291156
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-
11451157 // 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 ++ ) {
11471159 err = lfs_commit_move (lfs , & commit ,
11481160 0x003ff000 , LFS_MKTAG (0 , id , 0 ),
11491161 0x003ff000 , LFS_MKTAG (0 , id - begin , 0 ),
11501162 source , attrs );
1151- if (err ) {
1163+ if (err && !( splitted && err == LFS_ERR_NOSPC ) ) {
11521164 if (err == LFS_ERR_NOSPC ) {
11531165 goto split ;
11541166 } else if (err == LFS_ERR_CORRUPT ) {
@@ -1157,12 +1169,25 @@ static int lfs_dir_compact(lfs_t *lfs,
11571169 return err ;
11581170 }
11591171
1160- ack = id ;
1172+ ackid = id ;
11611173 }
11621174
11631175 // reopen reserved space at the end
11641176 commit .end = lfs -> cfg -> block_size - 8 ;
11651177
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+
11661191 if (lfs_pair_cmp (dir -> pair , (const lfs_block_t [2 ]){0 , 1 }) == 0 ) {
11671192 // move over (duplicate) superblock if we are root
11681193 err = lfs_commit_move (lfs , & commit ,
@@ -1178,8 +1203,8 @@ static int lfs_dir_compact(lfs_t *lfs,
11781203 }
11791204
11801205 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
11831208 err = lfs_commit_globals (lfs , & commit , & dir -> locals );
11841209 if (err ) {
11851210 if (err == LFS_ERR_CORRUPT ) {
@@ -1222,8 +1247,13 @@ static int lfs_dir_compact(lfs_t *lfs,
12221247split :
12231248 // commit no longer fits, need to split dir,
12241249 // 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 ) {
12271257 // If we can't fit in this block, we won't fit in next block
12281258 return LFS_ERR_NOSPC ;
12291259 }
@@ -1234,25 +1264,26 @@ static int lfs_dir_compact(lfs_t *lfs,
12341264 return err ;
12351265 }
12361266
1237- if (exhausted ) {
1238- lfs -> root [0 ] = tail .pair [0 ];
1239- lfs -> root [1 ] = tail .pair [1 ];
1240- }
1241-
12421267 tail .split = dir -> split ;
12431268 tail .tail [0 ] = dir -> tail [0 ];
12441269 tail .tail [1 ] = dir -> tail [1 ];
12451270
1246- err = lfs_dir_compact (lfs , & tail , attrs , source , ack + 1 , end );
1271+ err = lfs_dir_compact (lfs , & tail , attrs , source , ackid + 1 , end );
12471272 if (err ) {
12481273 return err ;
12491274 }
12501275
1251- end = ack + 1 ;
1276+ end = ackid + 1 ;
12521277 dir -> tail [0 ] = tail .pair [0 ];
12531278 dir -> tail [1 ] = tail .pair [1 ];
12541279 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 ;
12561287
12571288relocate :
12581289 // 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,
13631394
13641395 .begin = dir -> off ,
13651396 .end = lfs -> cfg -> block_size - 8 ,
1397+ .ack = 0 ,
13661398 };
13671399
13681400 for (const lfs_mattr_t * a = attrs ; a ; a = a -> next ) {
0 commit comments