lfs.c 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452
  1. /*
  2. * The little filesystem
  3. *
  4. * Copyright (c) 2017 Christopher Haster
  5. * Distributed under the MIT license
  6. */
  7. #include "lfs.h"
  8. #include "lfs_util.h"
  9. #include <string.h>
  10. #include <stdbool.h>
  11. /// Block device operations ///
  12. static int lfs_bd_info(lfs_t *lfs, struct lfs_bd_info *info) {
  13. return lfs->bd_ops->info(lfs->bd, info);
  14. }
  15. static int lfs_bd_read(lfs_t *lfs, lfs_block_t block,
  16. lfs_off_t off, lfs_size_t size, void *buffer) {
  17. return lfs->bd_ops->read(lfs->bd, block, off, size, buffer);
  18. }
  19. static int lfs_bd_prog(lfs_t *lfs, lfs_block_t block,
  20. lfs_off_t off, lfs_size_t size, const void *buffer) {
  21. return lfs->bd_ops->prog(lfs->bd, block, off, size, buffer);
  22. }
  23. static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block,
  24. lfs_off_t off, lfs_size_t size) {
  25. return lfs->bd_ops->erase(lfs->bd, block, off, size);
  26. }
  27. static int lfs_bd_sync(lfs_t *lfs) {
  28. return lfs->bd_ops->sync(lfs->bd);
  29. }
  30. static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
  31. lfs_off_t off, lfs_size_t size, const void *buffer) {
  32. const uint8_t *data = buffer;
  33. for (lfs_off_t i = 0; i < size; i++) {
  34. uint8_t c;
  35. int err = lfs_bd_read(lfs, block, off+i, 1, &c);
  36. if (err) {
  37. return err;
  38. }
  39. if (c != *data) {
  40. return false;
  41. }
  42. data += 1;
  43. }
  44. return true;
  45. }
  46. static int lfs_bd_crc(lfs_t *lfs, lfs_block_t block,
  47. lfs_off_t off, lfs_size_t size, uint32_t *crc) {
  48. while (off < size) {
  49. uint8_t c;
  50. int err = lfs_bd_read(lfs, block, off, 1, &c);
  51. if (err) {
  52. return err;
  53. }
  54. *crc = lfs_crc(&c, 1, *crc);
  55. off += 1;
  56. }
  57. return 0;
  58. }
  59. /// Block allocator ///
  60. static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
  61. lfs_t *lfs = p;
  62. lfs_block_t off = block - lfs->free.begin;
  63. if (off < LFS_CFG_LOOKAHEAD) {
  64. lfs->lookahead[off / 32] |= 1U << (off % 32);
  65. }
  66. return 0;
  67. }
  68. static int lfs_alloc_stride(void *p, lfs_block_t block) {
  69. lfs_t *lfs = p;
  70. lfs_block_t noff = block - lfs->free.begin;
  71. lfs_block_t off = lfs->free.end - lfs->free.begin;
  72. if (noff < off) {
  73. lfs->free.end = noff + lfs->free.begin;
  74. }
  75. return 0;
  76. }
  77. static int lfs_alloc_scan(lfs_t *lfs) {
  78. lfs_block_t start = lfs->free.begin;
  79. while (true) {
  80. // mask out blocks in lookahead region
  81. memset(lfs->lookahead, 0, sizeof(lfs->lookahead));
  82. int err = lfs_traverse(lfs, lfs_alloc_lookahead, lfs);
  83. if (err) {
  84. return err;
  85. }
  86. // check if we've found a free block
  87. for (uint32_t off = 0; off < LFS_CFG_LOOKAHEAD; off++) {
  88. if (lfs->lookahead[off / 32] & (1U << (off % 32))) {
  89. continue;
  90. }
  91. // found free block, now find stride of free blocks
  92. // since this is relatively cheap (stress on relatively)
  93. lfs->free.begin += off;
  94. lfs->free.end = lfs->block_count; // before superblock
  95. // find maximum stride in tree
  96. return lfs_traverse(lfs, lfs_alloc_stride, lfs);
  97. }
  98. // continue to next lookahead unless we've searched the whole device
  99. if (start-1 - lfs->free.begin < LFS_CFG_LOOKAHEAD) {
  100. return 0;
  101. }
  102. // continue to next lookahead region
  103. lfs->free.begin += LFS_CFG_LOOKAHEAD;
  104. }
  105. }
  106. static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
  107. // If we don't remember any free blocks we will need to start searching
  108. if (lfs->free.begin == lfs->free.end) {
  109. int err = lfs_alloc_scan(lfs);
  110. if (err) {
  111. return err;
  112. }
  113. if (lfs->free.begin == lfs->free.end) {
  114. // Still can't allocate a block? check for orphans
  115. int err = lfs_deorphan(lfs);
  116. if (err) {
  117. return err;
  118. }
  119. err = lfs_alloc_scan(lfs);
  120. if (err) {
  121. return err;
  122. }
  123. if (lfs->free.begin == lfs->free.end) {
  124. // Ok, it's true, we're out of space
  125. return LFS_ERROR_NO_SPACE;
  126. }
  127. }
  128. }
  129. // Take first available block
  130. *block = lfs->free.begin;
  131. lfs->free.begin += 1;
  132. return 0;
  133. }
  134. static int lfs_alloc_erased(lfs_t *lfs, lfs_block_t *block) {
  135. int err = lfs_alloc(lfs, block);
  136. if (err) {
  137. return err;
  138. }
  139. return lfs_bd_erase(lfs, *block, 0, lfs->block_size);
  140. }
  141. /// Index list operations ///
  142. // Next index offset
  143. static lfs_off_t lfs_indexnext(lfs_t *lfs, lfs_off_t ioff) {
  144. ioff += 1;
  145. while (ioff % lfs->words == 0) {
  146. ioff += lfs_min(lfs_ctz(ioff/lfs->words + 1), lfs->words-1) + 1;
  147. }
  148. return ioff;
  149. }
  150. static lfs_off_t lfs_indexfrom(lfs_t *lfs, lfs_off_t off) {
  151. lfs_off_t i = 0;
  152. while (off > lfs->block_size) {
  153. i = lfs_indexnext(lfs, i);
  154. off -= lfs->block_size;
  155. }
  156. return i;
  157. }
  158. // Find index in index chain given its index offset
  159. static int lfs_index_find(lfs_t *lfs, lfs_block_t head,
  160. lfs_size_t icount, lfs_off_t ioff, lfs_block_t *block) {
  161. lfs_off_t iitarget = ioff / lfs->words;
  162. lfs_off_t iicurrent = (icount-1) / lfs->words;
  163. while (iitarget != iicurrent) {
  164. lfs_size_t skip = lfs_min(
  165. lfs_min(lfs_ctz(iicurrent+1), lfs->words-1),
  166. lfs_npw2((iitarget ^ iicurrent)+1)-1);
  167. int err = lfs_bd_read(lfs, head, 4*skip, 4, &head);
  168. if (err) {
  169. return err;
  170. }
  171. iicurrent -= 1 << skip;
  172. }
  173. return lfs_bd_read(lfs, head, 4*(ioff % lfs->words), 4, block);
  174. }
  175. // Append index to index chain, updates head and icount
  176. static int lfs_index_append(lfs_t *lfs, lfs_block_t *headp,
  177. lfs_size_t *icountp, lfs_block_t block) {
  178. lfs_block_t head = *headp;
  179. lfs_size_t ioff = *icountp - 1;
  180. ioff += 1;
  181. while (ioff % lfs->words == 0) {
  182. lfs_block_t nhead;
  183. int err = lfs_alloc_erased(lfs, &nhead);
  184. if (err) {
  185. return err;
  186. }
  187. lfs_off_t skips = lfs_min(
  188. lfs_ctz(ioff/lfs->words + 1), lfs->words-2) + 1;
  189. for (lfs_off_t i = 0; i < skips; i++) {
  190. err = lfs_bd_prog(lfs, nhead, 4*i, 4, &head);
  191. if (err) {
  192. return err;
  193. }
  194. if (head && i != skips-1) {
  195. err = lfs_bd_read(lfs, head, 4*i, 4, &head);
  196. if (err) {
  197. return err;
  198. }
  199. }
  200. }
  201. ioff += skips;
  202. head = nhead;
  203. }
  204. int err = lfs_bd_prog(lfs, head, 4*(ioff % lfs->words), 4, &block);
  205. if (err) {
  206. return err;
  207. }
  208. *headp = head;
  209. *icountp = ioff + 1;
  210. return 0;
  211. }
  212. static int lfs_index_traverse(lfs_t *lfs, lfs_block_t head,
  213. lfs_size_t icount, int (*cb)(void*, lfs_block_t), void *data) {
  214. lfs_off_t iicurrent = (icount-1) / lfs->words;
  215. while (iicurrent > 0) {
  216. int err = cb(data, head);
  217. if (err) {
  218. return err;
  219. }
  220. lfs_size_t skip = lfs_min(lfs_ctz(iicurrent+1), lfs->words-1);
  221. for (lfs_off_t i = skip; i < lfs->words; i++) {
  222. lfs_block_t block;
  223. int err = lfs_bd_read(lfs, head, 4*i, 4, &block);
  224. if (err) {
  225. return err;
  226. }
  227. err = cb(data, block);
  228. if (err) {
  229. return err;
  230. }
  231. }
  232. err = lfs_bd_read(lfs, head, 0, 4, &head);
  233. if (err) {
  234. return err;
  235. }
  236. iicurrent -= 1;
  237. }
  238. int err = cb(data, head);
  239. if (err) {
  240. return err;
  241. }
  242. for (lfs_off_t i = 0; i < lfs->words; i++) {
  243. lfs_block_t block;
  244. int err = lfs_bd_read(lfs, head, 4*i, 4, &block);
  245. if (err) {
  246. return err;
  247. }
  248. err = cb(data, block);
  249. if (err) {
  250. return err;
  251. }
  252. }
  253. return 0;
  254. }
  255. /// Metadata pair operations ///
  256. static inline void lfs_pairswap(lfs_block_t pair[2]) {
  257. lfs_block_t t = pair[0];
  258. pair[0] = pair[1];
  259. pair[1] = t;
  260. }
  261. static inline int lfs_paircmp(
  262. const lfs_block_t paira[2],
  263. const lfs_block_t pairb[2]) {
  264. return !((paira[0] == pairb[0] && paira[1] == pairb[1]) ||
  265. (paira[0] == pairb[1] && paira[1] == pairb[0]));
  266. }
  267. struct lfs_fetch_region {
  268. lfs_off_t off;
  269. lfs_size_t size;
  270. void *data;
  271. };
  272. static int lfs_pair_fetch(lfs_t *lfs, lfs_block_t pair[2],
  273. int count, const struct lfs_fetch_region *regions) {
  274. int checked = 0;
  275. int rev = 0;
  276. for (int i = 0; i < 2; i++) {
  277. uint32_t nrev;
  278. int err = lfs_bd_read(lfs, pair[1], 0, 4, &nrev);
  279. if (err) {
  280. return err;
  281. }
  282. if (checked > 0 && lfs_scmp(nrev, rev) < 0) {
  283. continue;
  284. }
  285. uint32_t crc = 0xffffffff;
  286. err = lfs_bd_crc(lfs, pair[1], 0, lfs->block_size, &crc);
  287. if (err) {
  288. return err;
  289. }
  290. if (crc != 0) {
  291. lfs_pairswap(pair);
  292. }
  293. checked += 1;
  294. rev = nrev;
  295. lfs_pairswap(pair);
  296. }
  297. if (checked == 0) {
  298. LFS_ERROR("Corrupted metadata pair at %d %d", pair[0], pair[1]);
  299. return LFS_ERROR_CORRUPT;
  300. }
  301. for (int i = 0; i < count; i++) {
  302. int err = lfs_bd_read(lfs, pair[0],
  303. regions[i].off, regions[i].size, regions[i].data);
  304. if (err) {
  305. return err;
  306. }
  307. }
  308. return 0;
  309. }
  310. struct lfs_commit_region {
  311. lfs_off_t off;
  312. lfs_size_t size;
  313. const void *data;
  314. };
  315. static int lfs_pair_commit(lfs_t *lfs, lfs_block_t pair[2],
  316. int count, const struct lfs_commit_region *regions) {
  317. uint32_t crc = 0xffffffff;
  318. int err = lfs_bd_erase(lfs, pair[1], 0, lfs->block_size);
  319. if (err) {
  320. return err;
  321. }
  322. lfs_off_t off = 0;
  323. while (off < lfs->block_size - 4) {
  324. if (count > 0 && regions[0].off == off) {
  325. crc = lfs_crc(regions[0].data, regions[0].size, crc);
  326. int err = lfs_bd_prog(lfs, pair[1],
  327. off, regions[0].size, regions[0].data);
  328. if (err) {
  329. return err;
  330. }
  331. off += regions[0].size;
  332. count -= 1;
  333. regions += 1;
  334. } else {
  335. // TODO faster strides?
  336. // TODO should we start crcing what's already
  337. // programmed after dir size?
  338. uint8_t data;
  339. int err = lfs_bd_read(lfs, pair[0], off, 1, &data);
  340. if (err) {
  341. return err;
  342. }
  343. crc = lfs_crc((void*)&data, 1, crc);
  344. err = lfs_bd_prog(lfs, pair[1], off, 1, &data);
  345. if (err) {
  346. return err;
  347. }
  348. off += 1;
  349. }
  350. }
  351. err = lfs_bd_prog(lfs, pair[1], lfs->block_size-4, 4, &crc);
  352. if (err) {
  353. return err;
  354. }
  355. err = lfs_bd_sync(lfs);
  356. if (err) {
  357. return err;
  358. }
  359. lfs_pairswap(pair);
  360. return 0;
  361. }
  362. // TODO maybe there is a better abstraction for this?
  363. static int lfs_pair_shift(lfs_t *lfs, lfs_block_t pair[2],
  364. int count, const struct lfs_commit_region *regions,
  365. lfs_off_t blank_start, lfs_size_t blank_size) {
  366. uint32_t crc = 0xffffffff;
  367. int err = lfs_bd_erase(lfs, pair[1], 0, lfs->block_size);
  368. if (err) {
  369. return err;
  370. }
  371. lfs_off_t woff = 0;
  372. lfs_off_t roff = 0;
  373. while (woff < lfs->block_size - 4) {
  374. if (count > 0 && regions[0].off == woff) {
  375. crc = lfs_crc(regions[0].data, regions[0].size, crc);
  376. int err = lfs_bd_prog(lfs, pair[1],
  377. woff, regions[0].size, regions[0].data);
  378. if (err) {
  379. return err;
  380. }
  381. woff += regions[0].size;
  382. roff += regions[0].size;
  383. count -= 1;
  384. regions += 1;
  385. } else if (roff == blank_start) {
  386. roff += blank_size;
  387. } else if (roff < lfs->block_size - 4) {
  388. // TODO faster strides?
  389. // TODO should we start crcing what's already
  390. // programmed after dir size?
  391. uint8_t data;
  392. int err = lfs_bd_read(lfs, pair[0], roff, 1, &data);
  393. if (err) {
  394. return err;
  395. }
  396. crc = lfs_crc((void*)&data, 1, crc);
  397. err = lfs_bd_prog(lfs, pair[1], woff, 1, &data);
  398. if (err) {
  399. return err;
  400. }
  401. woff += 1;
  402. roff += 1;
  403. } else {
  404. uint8_t data = 0;
  405. crc = lfs_crc((void*)&data, 1, crc);
  406. err = lfs_bd_prog(lfs, pair[1], woff, 1, &data);
  407. if (err) {
  408. return err;
  409. }
  410. woff += 1;
  411. }
  412. }
  413. err = lfs_bd_prog(lfs, pair[1], lfs->block_size-4, 4, &crc);
  414. if (err) {
  415. return err;
  416. }
  417. err = lfs_bd_sync(lfs);
  418. if (err) {
  419. return err;
  420. }
  421. lfs_pairswap(pair);
  422. return 0;
  423. }
  424. /// Directory operations ///
  425. static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir,
  426. lfs_block_t parent[2], lfs_block_t tail[2]) {
  427. // Allocate pair of dir blocks
  428. for (int i = 0; i < 2; i++) {
  429. int err = lfs_alloc(lfs, &dir->pair[i]);
  430. if (err) {
  431. return err;
  432. }
  433. }
  434. // Rather than clobbering one of the blocks we just pretend
  435. // the revision may be valid
  436. int err = lfs_bd_read(lfs, dir->pair[0], 0, 4, &dir->d.rev);
  437. if (err) {
  438. return err;
  439. }
  440. dir->d.rev += 1;
  441. // Calculate total size
  442. dir->d.size = sizeof(dir->d);
  443. dir->off = sizeof(dir->d);
  444. // Other defaults
  445. dir->d.tail[0] = tail[0];
  446. dir->d.tail[1] = tail[1];
  447. // Write out to memory
  448. if (!parent) {
  449. return lfs_pair_commit(lfs, dir->pair,
  450. 1, (struct lfs_commit_region[]){
  451. {0, sizeof(dir->d), &dir->d}
  452. });
  453. } else {
  454. dir->d.size += 2*sizeof(struct lfs_disk_entry) + 3;
  455. return lfs_pair_commit(lfs, dir->pair,
  456. 5, (struct lfs_commit_region[]){
  457. {0, sizeof(dir->d), &dir->d},
  458. {sizeof(dir->d), sizeof(struct lfs_disk_entry),
  459. &(struct lfs_disk_entry){
  460. .type = LFS_TYPE_DIR,
  461. .len = sizeof(struct lfs_disk_entry)+1,
  462. .u.dir[0] = dir->pair[0],
  463. .u.dir[1] = dir->pair[1],
  464. }},
  465. {sizeof(dir->d)+sizeof(struct lfs_disk_entry), 1, "."},
  466. {sizeof(dir->d)+sizeof(struct lfs_disk_entry)+1,
  467. sizeof(struct lfs_disk_entry),
  468. &(struct lfs_disk_entry){
  469. .type = LFS_TYPE_DIR,
  470. .len = sizeof(struct lfs_disk_entry)+2,
  471. .u.dir[0] = parent[0] ? parent[0] : dir->pair[0],
  472. .u.dir[1] = parent[1] ? parent[1] : dir->pair[1],
  473. }},
  474. {sizeof(dir->d)+2*sizeof(struct lfs_disk_entry)+1, 2, ".."},
  475. });
  476. }
  477. }
  478. static int lfs_dir_fetch(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t pair[2]) {
  479. dir->pair[0] = pair[0];
  480. dir->pair[1] = pair[1];
  481. dir->off = sizeof(dir->d);
  482. return lfs_pair_fetch(lfs, dir->pair,
  483. 1, (struct lfs_fetch_region[1]) {
  484. {0, sizeof(dir->d), &dir->d}
  485. });
  486. }
  487. static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
  488. while (true) {
  489. if ((0x7fffffff & dir->d.size) - dir->off < sizeof(entry->d)) {
  490. if (!(dir->d.size >> 31)) {
  491. entry->dir[0] = dir->pair[0];
  492. entry->dir[1] = dir->pair[1];
  493. entry->off = dir->off;
  494. return LFS_ERROR_NO_ENTRY;
  495. }
  496. int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
  497. if (err) {
  498. return err;
  499. }
  500. dir->off = sizeof(dir->d);
  501. }
  502. int err = lfs_bd_read(lfs, dir->pair[0], dir->off,
  503. sizeof(entry->d), &entry->d);
  504. if (err) {
  505. return err;
  506. }
  507. dir->off += entry->d.len;
  508. if (entry->d.type == LFS_TYPE_REG || entry->d.type == LFS_TYPE_DIR) {
  509. entry->dir[0] = dir->pair[0];
  510. entry->dir[1] = dir->pair[1];
  511. entry->off = dir->off - entry->d.len;
  512. return 0;
  513. }
  514. }
  515. }
  516. static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
  517. const char **path, lfs_entry_t *entry) {
  518. const char *pathname = *path;
  519. size_t pathlen;
  520. while (true) {
  521. nextname:
  522. // skip slashes
  523. pathname += strspn(pathname, "/");
  524. pathlen = strcspn(pathname, "/");
  525. // skip '.' and root '..'
  526. if ((pathlen == 1 && memcmp(pathname, ".", 1) == 0) ||
  527. (pathlen == 2 && memcmp(pathname, "..", 2) == 0)) {
  528. pathname += pathlen;
  529. goto nextname;
  530. }
  531. // skip if matched by '..' in name
  532. const char *suffix = pathname + pathlen;
  533. size_t sufflen;
  534. int depth = 1;
  535. while (true) {
  536. suffix += strspn(suffix, "/");
  537. sufflen = strcspn(suffix, "/");
  538. if (sufflen == 0) {
  539. break;
  540. }
  541. if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
  542. depth -= 1;
  543. if (depth == 0) {
  544. pathname = suffix + sufflen;
  545. goto nextname;
  546. }
  547. } else {
  548. depth += 1;
  549. }
  550. suffix += sufflen;
  551. }
  552. // find path
  553. while (true) {
  554. int err = lfs_dir_next(lfs, dir, entry);
  555. if (err) {
  556. return err;
  557. }
  558. if (entry->d.len - sizeof(entry->d) != pathlen) {
  559. continue;
  560. }
  561. int ret = lfs_bd_cmp(lfs, entry->dir[0],
  562. entry->off + sizeof(entry->d), pathlen, pathname);
  563. if (ret < 0) {
  564. return ret;
  565. }
  566. // Found match
  567. if (ret == true) {
  568. break;
  569. }
  570. }
  571. pathname += pathlen;
  572. pathname += strspn(pathname, "/");
  573. if (pathname[0] == '\0') {
  574. return 0;
  575. }
  576. // continue on if we hit a directory
  577. if (entry->d.type != LFS_TYPE_DIR) {
  578. return LFS_ERROR_NOT_DIR;
  579. }
  580. int err = lfs_dir_fetch(lfs, dir, entry->d.u.dir);
  581. if (err) {
  582. return err;
  583. }
  584. *path = pathname;
  585. }
  586. return 0;
  587. }
  588. static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
  589. const char **path, lfs_entry_t *entry) {
  590. int err = lfs_dir_find(lfs, dir, path, entry);
  591. if (err != LFS_ERROR_NO_ENTRY) {
  592. return err ? err : LFS_ERROR_EXISTS;
  593. }
  594. // Check if we fit
  595. if ((0x7fffffff & dir->d.size) + sizeof(entry->d) + strlen(*path)
  596. > lfs->block_size - 4) {
  597. lfs_dir_t olddir;
  598. memcpy(&olddir, dir, sizeof(olddir));
  599. int err = lfs_dir_alloc(lfs, dir, 0, olddir.d.tail);
  600. if (err) {
  601. return err;
  602. }
  603. entry->dir[0] = dir->pair[0];
  604. entry->dir[1] = dir->pair[1];
  605. entry->off = dir->off;
  606. olddir.d.rev += 1;
  607. olddir.d.size |= 1 << 31;
  608. olddir.d.tail[0] = dir->pair[0];
  609. olddir.d.tail[1] = dir->pair[1];
  610. return lfs_pair_commit(lfs, olddir.pair,
  611. 1, (struct lfs_commit_region[]){
  612. {0, sizeof(olddir.d), &olddir.d}
  613. });
  614. }
  615. return 0;
  616. }
  617. int lfs_mkdir(lfs_t *lfs, const char *path) {
  618. // Allocate entry for directory
  619. lfs_dir_t cwd;
  620. int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
  621. if (err) {
  622. return err;
  623. }
  624. lfs_entry_t entry;
  625. err = lfs_dir_append(lfs, &cwd, &path, &entry);
  626. if (err) {
  627. return err;
  628. }
  629. // Build up new directory
  630. lfs_dir_t dir;
  631. err = lfs_dir_alloc(lfs, &dir, cwd.pair, cwd.d.tail);
  632. if (err) {
  633. return err;
  634. }
  635. entry.d.type = LFS_TYPE_DIR;
  636. entry.d.len = sizeof(entry.d) + strlen(path);
  637. entry.d.u.dir[0] = dir.pair[0];
  638. entry.d.u.dir[1] = dir.pair[1];
  639. cwd.d.rev += 1;
  640. cwd.d.size += entry.d.len;
  641. cwd.d.tail[0] = dir.pair[0];
  642. cwd.d.tail[1] = dir.pair[1];
  643. return lfs_pair_commit(lfs, entry.dir,
  644. 3, (struct lfs_commit_region[3]) {
  645. {0, sizeof(cwd.d), &cwd.d},
  646. {entry.off, sizeof(entry.d), &entry.d},
  647. {entry.off+sizeof(entry.d), entry.d.len - sizeof(entry.d), path}
  648. });
  649. }
  650. int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
  651. if (path[0] == '/') {
  652. dir->pair[0] = lfs->root[0];
  653. dir->pair[1] = lfs->root[1];
  654. } else {
  655. dir->pair[0] = lfs->cwd[0];
  656. dir->pair[1] = lfs->cwd[1];
  657. }
  658. int err = lfs_dir_fetch(lfs, dir, dir->pair);
  659. if (err) {
  660. return err;
  661. } else if (strcmp(path, "/") == 0) {
  662. return 0;
  663. }
  664. lfs_entry_t entry;
  665. err = lfs_dir_find(lfs, dir, &path, &entry);
  666. if (err) {
  667. return err;
  668. } else if (entry.d.type != LFS_TYPE_DIR) {
  669. return LFS_ERROR_NOT_DIR;
  670. }
  671. return lfs_dir_fetch(lfs, dir, entry.d.u.dir);
  672. }
  673. int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
  674. // Do nothing, dir is always synchronized
  675. return 0;
  676. }
  677. int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
  678. memset(info, 0, sizeof(*info));
  679. lfs_entry_t entry;
  680. int err = lfs_dir_next(lfs, dir, &entry);
  681. if (err) {
  682. return (err == LFS_ERROR_NO_ENTRY) ? 0 : err;
  683. }
  684. info->type = entry.d.type & 0xff;
  685. if (info->type == LFS_TYPE_REG) {
  686. info->size = entry.d.u.file.size;
  687. }
  688. err = lfs_bd_read(lfs, entry.dir[0], entry.off + sizeof(entry.d),
  689. entry.d.len - sizeof(entry.d), info->name);
  690. if (err) {
  691. return err;
  692. }
  693. return 1;
  694. }
  695. /// File operations ///
  696. int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
  697. const char *path, int flags) {
  698. // Allocate entry for file if it doesn't exist
  699. // TODO check open files
  700. lfs_dir_t cwd;
  701. int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
  702. if (err) {
  703. return err;
  704. }
  705. if (flags & LFS_O_CREAT) {
  706. err = lfs_dir_append(lfs, &cwd, &path, &file->entry);
  707. if (err && err != LFS_ERROR_EXISTS) {
  708. return err;
  709. }
  710. } else {
  711. err = lfs_dir_find(lfs, &cwd, &path, &file->entry);
  712. if (err) {
  713. return err;
  714. }
  715. }
  716. if ((flags & LFS_O_CREAT) && err != LFS_ERROR_EXISTS) {
  717. // Store file
  718. file->head = 0;
  719. file->size = 0;
  720. file->wblock = 0;
  721. file->windex = 0;
  722. file->rblock = 0;
  723. file->rindex = 0;
  724. file->roff = 0;
  725. file->entry.d.type = 1;
  726. file->entry.d.len = sizeof(file->entry.d) + strlen(path);
  727. file->entry.d.u.file.head = file->head;
  728. file->entry.d.u.file.size = file->size;
  729. cwd.d.rev += 1;
  730. cwd.d.size += file->entry.d.len;
  731. return lfs_pair_commit(lfs, file->entry.dir,
  732. 3, (struct lfs_commit_region[3]) {
  733. {0, sizeof(cwd.d), &cwd.d},
  734. {file->entry.off,
  735. sizeof(file->entry.d),
  736. &file->entry.d},
  737. {file->entry.off+sizeof(file->entry.d),
  738. file->entry.d.len-sizeof(file->entry.d),
  739. path}
  740. });
  741. } else if (file->entry.d.type == LFS_TYPE_DIR) {
  742. return LFS_ERROR_IS_DIR;
  743. } else {
  744. file->head = file->entry.d.u.file.head;
  745. file->size = file->entry.d.u.file.size;
  746. file->windex = lfs_indexfrom(lfs, file->size);
  747. file->rblock = 0;
  748. file->rindex = 0;
  749. file->roff = 0;
  750. // TODO do this lazily in write?
  751. // TODO cow the head i/d block
  752. if (file->size < lfs->block_size) {
  753. file->wblock = file->head;
  754. } else {
  755. int err = lfs_index_find(lfs, file->head, file->windex,
  756. file->windex, &file->wblock);
  757. if (err) {
  758. return err;
  759. }
  760. }
  761. return 0;
  762. }
  763. }
  764. int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
  765. // Store file
  766. lfs_dir_t cwd;
  767. int err = lfs_dir_fetch(lfs, &cwd, file->entry.dir);
  768. if (err) {
  769. return err;
  770. }
  771. file->entry.d.u.file.head = file->head;
  772. file->entry.d.u.file.size = file->size;
  773. cwd.d.rev += 1;
  774. return lfs_pair_commit(lfs, file->entry.dir,
  775. 3, (struct lfs_commit_region[3]) {
  776. {0, sizeof(cwd.d), &cwd.d},
  777. {file->entry.off, sizeof(file->entry.d), &file->entry.d},
  778. });
  779. }
  780. lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
  781. const void *buffer, lfs_size_t size) {
  782. const uint8_t *data = buffer;
  783. lfs_size_t nsize = size;
  784. while (nsize > 0) {
  785. lfs_off_t woff = file->size % lfs->block_size;
  786. if (file->size == 0) {
  787. int err = lfs_alloc_erased(lfs, &file->wblock);
  788. if (err) {
  789. return err;
  790. }
  791. file->head = file->wblock;
  792. file->windex = 0;
  793. } else if (woff == 0) {
  794. int err = lfs_alloc_erased(lfs, &file->wblock);
  795. if (err) {
  796. return err;
  797. }
  798. err = lfs_index_append(lfs, &file->head,
  799. &file->windex, file->wblock);
  800. if (err) {
  801. return err;
  802. }
  803. }
  804. lfs_size_t diff = lfs_min(nsize, lfs->block_size - woff);
  805. int err = lfs_bd_prog(lfs, file->wblock, woff, diff, data);
  806. if (err) {
  807. return err;
  808. }
  809. file->size += diff;
  810. data += diff;
  811. nsize -= diff;
  812. }
  813. return size;
  814. }
  815. lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
  816. void *buffer, lfs_size_t size) {
  817. uint8_t *data = buffer;
  818. lfs_size_t nsize = size;
  819. while (nsize > 0 && file->roff < file->size) {
  820. lfs_off_t roff = file->roff % lfs->block_size;
  821. // TODO cache index blocks
  822. if (file->size < lfs->block_size) {
  823. file->rblock = file->head;
  824. } else if (roff == 0) {
  825. int err = lfs_index_find(lfs, file->head, file->windex,
  826. file->rindex, &file->rblock);
  827. if (err) {
  828. return err;
  829. }
  830. file->rindex = lfs_indexnext(lfs, file->rindex);
  831. }
  832. lfs_size_t diff = lfs_min(
  833. lfs_min(nsize, file->size-file->roff),
  834. lfs->block_size - roff);
  835. int err = lfs_bd_read(lfs, file->rblock, roff, diff, data);
  836. if (err) {
  837. return err;
  838. }
  839. file->roff += diff;
  840. data += diff;
  841. nsize -= diff;
  842. }
  843. return size - nsize;
  844. }
  845. /// Generic filesystem operations ///
  846. static int lfs_configure(lfs_t *lfs, const struct lfs_config *config) {
  847. lfs->bd = config->bd;
  848. lfs->bd_ops = config->bd_ops;
  849. struct lfs_bd_info info;
  850. int err = lfs_bd_info(lfs, &info);
  851. if (err) {
  852. return err;
  853. }
  854. if (config->read_size) {
  855. if (config->read_size < info.read_size ||
  856. config->read_size % info.read_size != 0) {
  857. LFS_ERROR("Invalid read size %u, device has %u\n",
  858. config->read_size, info.read_size);
  859. return LFS_ERROR_INVALID;
  860. }
  861. lfs->read_size = config->read_size;
  862. } else {
  863. lfs->read_size = info.read_size;
  864. }
  865. if (config->prog_size) {
  866. if (config->prog_size < info.prog_size ||
  867. config->prog_size % info.prog_size != 0) {
  868. LFS_ERROR("Invalid prog size %u, device has %u\n",
  869. config->prog_size, info.prog_size);
  870. return LFS_ERROR_INVALID;
  871. }
  872. lfs->prog_size = config->prog_size;
  873. } else {
  874. lfs->prog_size = info.prog_size;
  875. }
  876. if (config->block_size) {
  877. if (config->block_size < info.erase_size ||
  878. config->block_size % info.erase_size != 0) {
  879. LFS_ERROR("Invalid block size %u, device has %u\n",
  880. config->prog_size, info.prog_size);
  881. return LFS_ERROR_INVALID;
  882. }
  883. lfs->block_size = config->block_size;
  884. } else {
  885. lfs->block_size = lfs_min(512, info.erase_size);
  886. }
  887. if (config->block_count) {
  888. if (config->block_count > info.total_size/info.erase_size) {
  889. LFS_ERROR("Invalid block size %u, device has %u\n",
  890. config->block_size,
  891. (uint32_t)(info.total_size/info.erase_size));
  892. return LFS_ERROR_INVALID;
  893. }
  894. lfs->block_count = config->block_count;
  895. } else {
  896. lfs->block_count = info.total_size / info.erase_size;
  897. }
  898. lfs->words = lfs->block_size / sizeof(uint32_t);
  899. return 0;
  900. }
  901. int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
  902. int err = lfs_configure(lfs, config);
  903. if (err) {
  904. return err;
  905. }
  906. // Create free list
  907. lfs->free.begin = 2;
  908. lfs->free.end = lfs->block_count-1;
  909. // Write root directory
  910. lfs_dir_t root;
  911. err = lfs_dir_alloc(lfs, &root,
  912. (lfs_block_t[2]){0, 0}, (lfs_block_t[2]){0, 0});
  913. if (err) {
  914. return err;
  915. }
  916. lfs->root[0] = root.pair[0];
  917. lfs->root[1] = root.pair[1];
  918. lfs->cwd[0] = root.pair[0];
  919. lfs->cwd[1] = root.pair[1];
  920. // Write superblocks
  921. lfs_superblock_t superblock = {
  922. .pair = {0, 1},
  923. .d.rev = 1,
  924. .d.size = sizeof(superblock),
  925. .d.root = {lfs->cwd[0], lfs->cwd[1]},
  926. .d.magic = {"littlefs"},
  927. .d.block_size = lfs->block_size,
  928. .d.block_count = lfs->block_count,
  929. };
  930. for (int i = 0; i < 2; i++) {
  931. int err = lfs_pair_commit(lfs, superblock.pair,
  932. 1, (struct lfs_commit_region[]){
  933. {0, sizeof(superblock.d), &superblock.d}
  934. });
  935. if (err) {
  936. LFS_ERROR("Failed to write superblock at %d", superblock.pair[1]);
  937. return err;
  938. }
  939. uint32_t crc = 0xffffffff;
  940. err = lfs_bd_crc(lfs, superblock.pair[0], 0, lfs->block_size, &crc);
  941. if (err || crc != 0) {
  942. LFS_ERROR("Failed to write superblock at %d", superblock.pair[0]);
  943. return err ? err : LFS_ERROR_CORRUPT;
  944. }
  945. }
  946. return 0;
  947. }
  948. int lfs_mount(lfs_t *lfs, const struct lfs_config *config) {
  949. int err = lfs_configure(lfs, config);
  950. if (err) {
  951. return err;
  952. }
  953. lfs_superblock_t superblock = {
  954. .pair = {0, 1},
  955. };
  956. err = lfs_pair_fetch(lfs, superblock.pair,
  957. 1, (struct lfs_fetch_region[]){
  958. {0, sizeof(superblock.d), &superblock.d}
  959. });
  960. if ((err == LFS_ERROR_CORRUPT ||
  961. memcmp(superblock.d.magic, "littlefs", 8) != 0)) {
  962. LFS_ERROR("Invalid superblock at %d %d",
  963. superblock.pair[0], superblock.pair[1]);
  964. return LFS_ERROR_CORRUPT;
  965. }
  966. lfs->root[0] = superblock.d.root[0];
  967. lfs->root[1] = superblock.d.root[1];
  968. lfs->cwd[0] = superblock.d.root[0];
  969. lfs->cwd[1] = superblock.d.root[1];
  970. return err;
  971. }
  972. int lfs_unmount(lfs_t *lfs) {
  973. // Do nothing for now
  974. return 0;
  975. }
  976. int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
  977. // iterate over metadata pairs
  978. lfs_dir_t dir;
  979. lfs_file_t file;
  980. lfs_block_t cwd[2] = {0, 1};
  981. while (true) {
  982. for (int i = 0; i < 2; i++) {
  983. int err = cb(data, cwd[i]);
  984. if (err) {
  985. return err;
  986. }
  987. }
  988. int err = lfs_dir_fetch(lfs, &dir, cwd);
  989. if (err) {
  990. return err;
  991. }
  992. // skip '.' and '..'
  993. dir.off += 2*sizeof(struct lfs_disk_entry) + 3;
  994. // iterate over contents
  995. while ((0x7fffffff & dir.d.size) >= dir.off + sizeof(file.entry.d)) {
  996. int err = lfs_bd_read(lfs, dir.pair[0], dir.off,
  997. sizeof(file.entry.d), &file.entry.d);
  998. if (err) {
  999. return err;
  1000. }
  1001. dir.off += file.entry.d.len;
  1002. if ((0xf & file.entry.d.type) == LFS_TYPE_REG) {
  1003. if (file.entry.d.u.file.size < lfs->block_size) {
  1004. int err = cb(data, file.entry.d.u.file.head);
  1005. if (err) {
  1006. return err;
  1007. }
  1008. } else {
  1009. int err = lfs_index_traverse(lfs,
  1010. file.entry.d.u.file.head,
  1011. lfs_indexfrom(lfs, file.entry.d.u.file.size),
  1012. cb, data);
  1013. if (err) {
  1014. return err;
  1015. }
  1016. }
  1017. }
  1018. }
  1019. cwd[0] = dir.d.tail[0];
  1020. cwd[1] = dir.d.tail[1];
  1021. if (!cwd[0]) {
  1022. return 0;
  1023. }
  1024. }
  1025. }
  1026. static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) {
  1027. // iterate over all directory directory entries
  1028. lfs_dir_t parent = {
  1029. .d.tail[0] = lfs->root[0],
  1030. .d.tail[1] = lfs->root[1],
  1031. };
  1032. while (parent.d.tail[0]) {
  1033. lfs_entry_t entry;
  1034. int err = lfs_dir_fetch(lfs, &parent, parent.d.tail);
  1035. if (err) {
  1036. return err;
  1037. }
  1038. // skip .. and . entries
  1039. for (int i = 0; i < 2; i++) {
  1040. int err = lfs_dir_next(lfs, &parent, &entry);
  1041. if (err) {
  1042. return err;
  1043. }
  1044. }
  1045. while (true) {
  1046. int err = lfs_dir_next(lfs, &parent, &entry);
  1047. if (err && err != LFS_ERROR_NO_ENTRY) {
  1048. return err;
  1049. }
  1050. if (err == LFS_ERROR_NO_ENTRY) {
  1051. break;
  1052. }
  1053. if ((0xf & entry.d.type) == LFS_TYPE_DIR &&
  1054. lfs_paircmp(entry.d.u.dir, dir) == 0) {
  1055. return true;
  1056. }
  1057. }
  1058. }
  1059. return false;
  1060. }
  1061. int lfs_deorphan(lfs_t *lfs) {
  1062. // iterate over all directories
  1063. lfs_dir_t pdir;
  1064. lfs_dir_t cdir;
  1065. // skip root
  1066. int err = lfs_dir_fetch(lfs, &pdir, lfs->root);
  1067. if (err) {
  1068. return err;
  1069. }
  1070. while (pdir.d.tail[0]) {
  1071. int err = lfs_dir_fetch(lfs, &cdir, pdir.d.tail);
  1072. if (err) {
  1073. return err;
  1074. }
  1075. // check if we have a parent
  1076. int parent = lfs_parent(lfs, pdir.d.tail);
  1077. if (parent < 0) {
  1078. return parent;
  1079. }
  1080. if (!parent) {
  1081. // we are an orphan
  1082. LFS_INFO("Found orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]);
  1083. pdir.d.tail[0] = cdir.d.tail[0];
  1084. pdir.d.tail[1] = cdir.d.tail[1];
  1085. pdir.d.rev += 1;
  1086. err = lfs_pair_commit(lfs, pdir.pair,
  1087. 1, (struct lfs_commit_region[]) {
  1088. {0, sizeof(pdir.d), &pdir.d},
  1089. });
  1090. if (err) {
  1091. return err;
  1092. }
  1093. break;
  1094. }
  1095. memcpy(&pdir, &cdir, sizeof(pdir));
  1096. }
  1097. return 0;
  1098. }
  1099. int lfs_remove(lfs_t *lfs, const char *path) {
  1100. lfs_dir_t cwd;
  1101. int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
  1102. if (err) {
  1103. return err;
  1104. }
  1105. lfs_entry_t entry;
  1106. err = lfs_dir_find(lfs, &cwd, &path, &entry);
  1107. if (err) {
  1108. return err;
  1109. }
  1110. lfs_dir_t dir;
  1111. if (entry.d.type == LFS_TYPE_DIR) {
  1112. // must be empty before removal
  1113. int err = lfs_dir_fetch(lfs, &dir, entry.d.u.dir);
  1114. if (err) {
  1115. return err;
  1116. } else if (dir.d.size != sizeof(dir.d) +
  1117. 2*sizeof(struct lfs_disk_entry) + 3) {
  1118. return LFS_ERROR_INVALID;
  1119. }
  1120. }
  1121. cwd.d.rev += 1;
  1122. cwd.d.size -= entry.d.len;
  1123. // either shift out the one entry or remove the whole dir block
  1124. if (cwd.d.size == sizeof(dir.d)) {
  1125. lfs_dir_t pdir;
  1126. int err = lfs_dir_fetch(lfs, &pdir, lfs->cwd);
  1127. if (err) {
  1128. return err;
  1129. }
  1130. while (lfs_paircmp(pdir.d.tail, cwd.pair) != 0) {
  1131. int err = lfs_dir_fetch(lfs, &pdir, pdir.d.tail);
  1132. if (err) {
  1133. return err;
  1134. }
  1135. }
  1136. pdir.d.tail[0] = cwd.d.tail[0];
  1137. pdir.d.tail[1] = cwd.d.tail[1];
  1138. pdir.d.rev += 1;
  1139. err = lfs_pair_commit(lfs, pdir.pair,
  1140. 1, (struct lfs_commit_region[]) {
  1141. {0, sizeof(pdir.d), &pdir.d},
  1142. });
  1143. if (err) {
  1144. return err;
  1145. }
  1146. } else {
  1147. int err = lfs_pair_shift(lfs, entry.dir,
  1148. 1, (struct lfs_commit_region[]) {
  1149. {0, sizeof(cwd.d), &cwd.d},
  1150. },
  1151. entry.off, entry.d.len);
  1152. if (err) {
  1153. return err;
  1154. }
  1155. }
  1156. if (entry.d.type == LFS_TYPE_DIR) {
  1157. // remove ourselves from the dir list
  1158. // this may create an orphan, which must be deorphaned
  1159. lfs_dir_t pdir;
  1160. memcpy(&pdir, &cwd, sizeof(pdir));
  1161. while (pdir.d.tail[0]) {
  1162. if (lfs_paircmp(pdir.d.tail, entry.d.u.dir) == 0) {
  1163. pdir.d.tail[0] = dir.d.tail[0];
  1164. pdir.d.tail[1] = dir.d.tail[1];
  1165. pdir.d.rev += 1;
  1166. int err = lfs_pair_commit(lfs, pdir.pair,
  1167. 1, (struct lfs_commit_region[]) {
  1168. {0, sizeof(pdir.d), &pdir.d},
  1169. });
  1170. if (err) {
  1171. return err;
  1172. }
  1173. break;
  1174. }
  1175. int err = lfs_dir_fetch(lfs, &pdir, pdir.d.tail);
  1176. if (err) {
  1177. return err;
  1178. }
  1179. }
  1180. }
  1181. return 0;
  1182. }
  1183. int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
  1184. lfs_dir_t cwd;
  1185. int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
  1186. if (err) {
  1187. return err;
  1188. }
  1189. lfs_entry_t entry;
  1190. err = lfs_dir_find(lfs, &cwd, &path, &entry);
  1191. if (err) {
  1192. return err;
  1193. }
  1194. // TODO abstract out info assignment
  1195. memset(info, 0, sizeof(*info));
  1196. info->type = entry.d.type & 0xff;
  1197. if (info->type == LFS_TYPE_REG) {
  1198. info->size = entry.d.u.file.size;
  1199. }
  1200. err = lfs_bd_read(lfs, entry.dir[0], entry.off + sizeof(entry.d),
  1201. entry.d.len - sizeof(entry.d), info->name);
  1202. if (err) {
  1203. return err;
  1204. }
  1205. return 0;
  1206. }