@@ -77,6 +77,7 @@ static int lfs_bd_crc(lfs_t *lfs, lfs_block_t block,
7777
7878// predeclared filesystem traversal
7979static int lfs_traverse (lfs_t * lfs , int (* cb )(void * , lfs_block_t ), void * data );
80+ int lfs_deorphan (lfs_t * lfs );
8081
8182static int lfs_alloc_lookahead (void * p , lfs_block_t block ) {
8283 lfs_t * lfs = p ;
@@ -143,8 +144,24 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
143144 int err = lfs_alloc_scan (lfs );
144145 if (err ) {
145146 return err ;
146- } else if (lfs -> free .begin == lfs -> free .end ) {
147- return LFS_ERROR_NO_SPACE ;
147+ }
148+
149+ if (lfs -> free .begin == lfs -> free .end ) {
150+ // Still can't allocate a block? check for orphans
151+ int err = lfs_deorphan (lfs );
152+ if (err ) {
153+ return err ;
154+ }
155+
156+ err = lfs_alloc_scan (lfs );
157+ if (err ) {
158+ return err ;
159+ }
160+
161+ if (lfs -> free .begin == lfs -> free .end ) {
162+ // Ok, it's true, we're out of space
163+ return LFS_ERROR_NO_SPACE ;
164+ }
148165 }
149166 }
150167
@@ -1146,7 +1163,7 @@ static int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
11461163 // skip '.' and '..'
11471164 dir .off += 2 * sizeof (struct lfs_disk_entry ) + 3 ;
11481165
1149- // TODO iterate over files
1166+ // iterate over contents
11501167 while ((0x7fffffff & dir .d .size ) >= dir .off + sizeof (file .entry .d )) {
11511168 int err = lfs_bd_read (lfs , dir .pair [0 ], dir .off ,
11521169 sizeof (file .entry .d ), & file .entry .d );
@@ -1155,14 +1172,7 @@ static int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
11551172 }
11561173
11571174 dir .off += file .entry .d .len ;
1158- if ((0xf & file .entry .d .type ) == LFS_TYPE_DIR ) {
1159- for (int i = 0 ; i < 2 ; i ++ ) {
1160- int err = cb (data , file .entry .d .u .dir [i ]);
1161- if (err ) {
1162- return err ;
1163- }
1164- }
1165- } else if ((0xf & file .entry .d .type ) == LFS_TYPE_REG ) {
1175+ if ((0xf & file .entry .d .type ) == LFS_TYPE_REG ) {
11661176 if (file .entry .d .u .file .size < lfs -> block_size ) {
11671177 int err = cb (data , file .entry .d .u .file .head );
11681178 if (err ) {
@@ -1189,6 +1199,85 @@ static int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
11891199 }
11901200}
11911201
1202+ int lfs_deorphan (lfs_t * lfs ) {
1203+ // iterate over all directories
1204+ lfs_block_t pred [2 ] = {0 , 1 };
1205+ lfs_block_t cwd [2 ] = {lfs -> root [0 ], lfs -> root [1 ]};
1206+
1207+ while (true) {
1208+ lfs_dir_t child ;
1209+ int err = lfs_dir_fetch (lfs , & child , cwd );
1210+ if (err ) {
1211+ return err ;
1212+ }
1213+
1214+ // orphans can only be empty dirs
1215+ // there still might be a dir block with this size that isn't
1216+ // the head of a directory, so we still have to check for '..'
1217+ if (child .d .size == sizeof (child .d ) +
1218+ 2 * sizeof (struct lfs_disk_entry ) + 3 ) {
1219+ lfs_entry_t entry ;
1220+ err = lfs_dir_find (lfs , & child , & (const char * ){".." }, & entry );
1221+ if (err && err != LFS_ERROR_NO_ENTRY ) {
1222+ return err ;
1223+ }
1224+
1225+ // only the head of directories can be orphans
1226+ if (err != LFS_ERROR_NO_ENTRY ) {
1227+ lfs_dir_t dir ;
1228+ int err = lfs_dir_fetch (lfs , & dir , entry .d .u .dir );
1229+ if (err ) {
1230+ return err ;
1231+ }
1232+
1233+ // check if we are any of our parents children
1234+ while (true) {
1235+ int err = lfs_dir_next (lfs , & dir , & entry );
1236+ if (err && err != LFS_ERROR_NO_ENTRY ) {
1237+ return err ;
1238+ }
1239+
1240+ if (err == LFS_ERROR_NO_ENTRY ) {
1241+ // we are an orphan
1242+ LFS_INFO ("Found orphan %d %d" , cwd [0 ], cwd [1 ]);
1243+ int err = lfs_dir_fetch (lfs , & dir , pred );
1244+ if (err ) {
1245+ return err ;
1246+ }
1247+
1248+ dir .d .tail [0 ] = child .d .tail [0 ];
1249+ dir .d .tail [1 ] = child .d .tail [1 ];
1250+ dir .d .rev += 1 ;
1251+
1252+ err = lfs_pair_commit (lfs , dir .pair ,
1253+ 1 , (struct lfs_commit_region []) {
1254+ {0 , sizeof (dir .d ), & dir .d },
1255+ });
1256+ if (err ) {
1257+ return err ;
1258+ }
1259+
1260+ break ;
1261+ } else if (lfs_paircmp (entry .d .u .dir , cwd ) == 0 ) {
1262+ // has parent
1263+ break ;
1264+ }
1265+ }
1266+ }
1267+ }
1268+
1269+ // to next directory
1270+ pred [0 ] = cwd [0 ];
1271+ pred [1 ] = cwd [1 ];
1272+ cwd [0 ] = child .d .tail [0 ];
1273+ cwd [1 ] = child .d .tail [1 ];
1274+
1275+ if (!cwd [0 ]) {
1276+ return 0 ;
1277+ }
1278+ }
1279+ }
1280+
11921281int lfs_remove (lfs_t * lfs , const char * path ) {
11931282 lfs_dir_t cwd ;
11941283 int err = lfs_dir_fetch (lfs , & cwd , lfs -> cwd );
@@ -1202,18 +1291,61 @@ int lfs_remove(lfs_t *lfs, const char *path) {
12021291 return err ;
12031292 }
12041293
1294+ lfs_dir_t dir ;
12051295 if (entry .d .type == LFS_TYPE_DIR ) {
12061296 // must be empty before removal
1207- lfs_dir_t dir ;
12081297 int err = lfs_dir_fetch (lfs , & dir , entry .d .u .dir );
12091298 if (err ) {
12101299 return err ;
12111300 } else if (dir .d .size != sizeof (dir .d ) +
12121301 2 * sizeof (struct lfs_disk_entry ) + 3 ) {
12131302 return LFS_ERROR_INVALID ;
12141303 }
1304+ }
1305+
1306+ cwd .d .rev += 1 ;
1307+ cwd .d .size -= entry .d .len ;
1308+
1309+ // either shift out the one entry or remove the whole dir block
1310+ if (cwd .d .size == sizeof (dir .d )) {
1311+ lfs_dir_t pdir ;
1312+ int err = lfs_dir_fetch (lfs , & pdir , lfs -> cwd );
1313+ if (err ) {
1314+ return err ;
1315+ }
12151316
1317+ while (lfs_paircmp (pdir .d .tail , cwd .pair ) != 0 ) {
1318+ int err = lfs_dir_fetch (lfs , & pdir , pdir .d .tail );
1319+ if (err ) {
1320+ return err ;
1321+ }
1322+ }
1323+
1324+ pdir .d .tail [0 ] = cwd .d .tail [0 ];
1325+ pdir .d .tail [1 ] = cwd .d .tail [1 ];
1326+ pdir .d .rev += 1 ;
1327+
1328+ err = lfs_pair_commit (lfs , pdir .pair ,
1329+ 1 , (struct lfs_commit_region []) {
1330+ {0 , sizeof (pdir .d ), & pdir .d },
1331+ });
1332+ if (err ) {
1333+ return err ;
1334+ }
1335+ } else {
1336+ int err = lfs_pair_shift (lfs , entry .dir ,
1337+ 1 , (struct lfs_commit_region []) {
1338+ {0 , sizeof (cwd .d ), & cwd .d },
1339+ },
1340+ entry .off , entry .d .len );
1341+ if (err ) {
1342+ return err ;
1343+ }
1344+ }
1345+
1346+ if (entry .d .type == LFS_TYPE_DIR ) {
12161347 // remove ourselves from the dir list
1348+ // this may create an orphan, which must be deorphaned
12171349 lfs_dir_t pdir ;
12181350 memcpy (& pdir , & cwd , sizeof (pdir ));
12191351
@@ -1241,15 +1373,6 @@ int lfs_remove(lfs_t *lfs, const char *path) {
12411373 }
12421374 }
12431375
1244- cwd .d .rev += 1 ;
1245- cwd .d .size -= entry .d .len ;
1246- // TODO remove dir block?
1247-
1248- // Drop contents on the floor
1249- return lfs_pair_shift (lfs , entry .dir ,
1250- 1 , (struct lfs_commit_region []) {
1251- {0 , sizeof (cwd .d ), & cwd .d },
1252- },
1253- entry .off , entry .d .len );
1376+ return 0 ;
12541377}
12551378
0 commit comments