@@ -77,6 +77,7 @@ static int lfs_bd_crc(lfs_t *lfs, lfs_block_t block,
77
77
78
78
// predeclared filesystem traversal
79
79
static int lfs_traverse (lfs_t * lfs , int (* cb )(void * , lfs_block_t ), void * data );
80
+ int lfs_deorphan (lfs_t * lfs );
80
81
81
82
static int lfs_alloc_lookahead (void * p , lfs_block_t block ) {
82
83
lfs_t * lfs = p ;
@@ -143,8 +144,24 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
143
144
int err = lfs_alloc_scan (lfs );
144
145
if (err ) {
145
146
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
+ }
148
165
}
149
166
}
150
167
@@ -1146,7 +1163,7 @@ static int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
1146
1163
// skip '.' and '..'
1147
1164
dir .off += 2 * sizeof (struct lfs_disk_entry ) + 3 ;
1148
1165
1149
- // TODO iterate over files
1166
+ // iterate over contents
1150
1167
while ((0x7fffffff & dir .d .size ) >= dir .off + sizeof (file .entry .d )) {
1151
1168
int err = lfs_bd_read (lfs , dir .pair [0 ], dir .off ,
1152
1169
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) {
1155
1172
}
1156
1173
1157
1174
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 ) {
1166
1176
if (file .entry .d .u .file .size < lfs -> block_size ) {
1167
1177
int err = cb (data , file .entry .d .u .file .head );
1168
1178
if (err ) {
@@ -1189,6 +1199,85 @@ static int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
1189
1199
}
1190
1200
}
1191
1201
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
+
1192
1281
int lfs_remove (lfs_t * lfs , const char * path ) {
1193
1282
lfs_dir_t cwd ;
1194
1283
int err = lfs_dir_fetch (lfs , & cwd , lfs -> cwd );
@@ -1202,18 +1291,61 @@ int lfs_remove(lfs_t *lfs, const char *path) {
1202
1291
return err ;
1203
1292
}
1204
1293
1294
+ lfs_dir_t dir ;
1205
1295
if (entry .d .type == LFS_TYPE_DIR ) {
1206
1296
// must be empty before removal
1207
- lfs_dir_t dir ;
1208
1297
int err = lfs_dir_fetch (lfs , & dir , entry .d .u .dir );
1209
1298
if (err ) {
1210
1299
return err ;
1211
1300
} else if (dir .d .size != sizeof (dir .d ) +
1212
1301
2 * sizeof (struct lfs_disk_entry ) + 3 ) {
1213
1302
return LFS_ERROR_INVALID ;
1214
1303
}
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
+ }
1215
1316
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 ) {
1216
1347
// remove ourselves from the dir list
1348
+ // this may create an orphan, which must be deorphaned
1217
1349
lfs_dir_t pdir ;
1218
1350
memcpy (& pdir , & cwd , sizeof (pdir ));
1219
1351
@@ -1241,15 +1373,6 @@ int lfs_remove(lfs_t *lfs, const char *path) {
1241
1373
}
1242
1374
}
1243
1375
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 ;
1254
1377
}
1255
1378
0 commit comments