mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Added internal lfs_dir_set, an umbrella to dir append/update/remove operations
This move was surprisingly complex, but offers the ultimate opportunity for code reuse in terms of resizable entries. Instead of needing to provide separate functions for adding and removing entries, adding and removing entries can just be viewed as changing an entry's size to-and-from zero. Unfortunately, it's not _quite_ that simple, since append and remove hide some relatively complex operations for when directory blocks overflow or need to be cleaned up. However, with enough shoehorning, and a new committer type that allows specifying recursive commit lists (is this now a push-down automata?), it does seem to be possible to shove all of the entry update logic into a single function. Sidenote, I switched back to an enum-based DSL, since the addition of a recursive region opcode breaks the consistency of what needs to be passed to the DSL callback functions. It's much simpler to handle each opcode explicitly inside a recursive lfs_commit_region function.
This commit is contained in:
		
							
								
								
									
										579
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										579
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -152,6 +152,7 @@ static int lfs_cache_prog(lfs_t *lfs, lfs_cache_t *pcache, | ||||
|         lfs_off_t off, const void *buffer, lfs_size_t size) { | ||||
|     const uint8_t *data = buffer; | ||||
|     LFS_ASSERT(block != 0xffffffff); | ||||
|     LFS_ASSERT(off + size <= lfs->cfg->block_size); | ||||
|  | ||||
|     while (size > 0) { | ||||
|         if (block == pcache->block && off >= pcache->off && | ||||
| @@ -483,83 +484,93 @@ static int lfs_dir_fetch(lfs_t *lfs, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| struct lfs_commit { | ||||
|     uint32_t crc; | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
| }; | ||||
|  | ||||
| static int lfs_commit(lfs_t *lfs, struct lfs_commit *c, | ||||
|         const void *data, lfs_size_t size) { | ||||
|     lfs_crc(&c->crc, data, size); | ||||
|     int err = lfs_bd_prog(lfs, c->block, c->off, data, size); | ||||
|     c->off += size; | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| struct lfs_region { | ||||
|     lfs_off_t off; | ||||
|     lfs_ssize_t diff; | ||||
|     enum { | ||||
|         LFS_FROM_DROP, | ||||
|         LFS_FROM_MEM, | ||||
|         LFS_FROM_REGION, | ||||
|     } type; | ||||
|  | ||||
|     int (*commit)(lfs_t *lfs, struct lfs_commit *c, | ||||
|             const void *data, lfs_size_t size); | ||||
|     const void *data; | ||||
|     lfs_size_t size; | ||||
|     struct lfs_region *next; | ||||
|     lfs_off_t off; | ||||
|     const void *buffer; | ||||
|     lfs_ssize_t size; | ||||
| }; | ||||
|  | ||||
| static int lfs_commit_mem(lfs_t *lfs, struct lfs_commit *c, | ||||
|         const void *data, lfs_size_t size) { | ||||
|     return lfs_commit(lfs, c, data, size); | ||||
| } | ||||
|  | ||||
| struct lfs_commit_disk { | ||||
| struct lfs_region_region { | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|     struct lfs_region *regions; | ||||
|     int count; | ||||
| }; | ||||
|  | ||||
| static int lfs_commit_disk(lfs_t *lfs, struct lfs_commit *c, | ||||
|         const void *p, lfs_size_t size) { | ||||
|     const struct lfs_commit_disk *d = p; | ||||
|  | ||||
|     struct lfs_region *r = d->regions; | ||||
|     lfs_off_t off = 0; | ||||
|     while (true) { | ||||
|         if (r && r->off == off) { | ||||
|             lfs_off_t orig = c->off; | ||||
|             int err = r->commit(lfs, c, r->data, r->size); | ||||
|             if (err) { | ||||
|                 return err; | ||||
| static int lfs_commit_region(lfs_t *lfs, | ||||
|         lfs_block_t oldblock, lfs_off_t oldoff, | ||||
|         lfs_block_t newblock, lfs_off_t newoff, | ||||
|         lfs_off_t regionoff, | ||||
|         const struct lfs_region *regions, int count, | ||||
|         lfs_size_t size, uint32_t *crc) { | ||||
|     int i = 0; | ||||
|     lfs_size_t end = newoff + size; | ||||
|     while (newoff < end) { | ||||
|         // commit from different types of regions | ||||
|         if (i < count && regions[i].off == oldoff - regionoff) { | ||||
|             switch (regions[i].type) { | ||||
|                 case LFS_FROM_DROP: { | ||||
|                     oldoff -= regions[i].size; | ||||
|                     break; | ||||
|                 } | ||||
|                 case LFS_FROM_MEM: { | ||||
|                     lfs_crc(crc, regions[i].buffer, regions[i].size); | ||||
|                     int err = lfs_bd_prog(lfs, newblock, newoff, | ||||
|                             regions[i].buffer, regions[i].size); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
|                     newoff += regions[i].size; | ||||
|                     break; | ||||
|                 } | ||||
|                 case LFS_FROM_REGION: { | ||||
|                     const struct lfs_region_region *disk = regions[i].buffer; | ||||
|                     int err = lfs_commit_region(lfs, | ||||
|                             disk->block, disk->off, | ||||
|                             newblock, newoff, | ||||
|                             disk->off, disk->regions, disk->count, | ||||
|                             regions[i].size, crc); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
|                     newoff += regions[i].size; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             off += (c->off - orig) - r->diff; | ||||
|             r = r->next; | ||||
|         } else if (off < size) { | ||||
|             uint8_t data; | ||||
|             int err = lfs_bd_read(lfs, d->block, d->off + off, &data, 1); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             err = lfs_commit(lfs, c, &data, 1); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             off += 1; | ||||
|             i += 1; | ||||
|         } else { | ||||
|             return 0; | ||||
|             // copy data from old block if not covered by region | ||||
|             uint8_t data; | ||||
|             int err = lfs_bd_read(lfs, oldblock, oldoff, &data, 1); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             lfs_crc(crc, &data, 1); | ||||
|             err = lfs_bd_prog(lfs, newblock, newoff, &data, 1); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             oldoff += 1; | ||||
|             newoff += 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| // TODO handle overflowing reads (zero?) | ||||
| static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
|         struct lfs_region *regions) { | ||||
| static int lfs_dif_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
|         const struct lfs_region *regions, int count) { | ||||
|     // state for copying over | ||||
|     const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; | ||||
|     lfs_size_t oldsize = (0x7fffffff & dir->d.size) - 4; | ||||
|     bool relocated = false; | ||||
|  | ||||
|     // increment revision count | ||||
| @@ -567,8 +578,8 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
|  | ||||
|     // keep pairs in order such that pair[0] is most recent | ||||
|     lfs_pairswap(dir->pair); | ||||
|     for (struct lfs_region *r = regions; r; r = r->next) { | ||||
|         dir->d.size += r->diff; | ||||
|     for (int i = 0; i < count; i++) { | ||||
|         dir->d.size += regions[i].size; | ||||
|     } | ||||
|  | ||||
|     while (true) { | ||||
| @@ -581,19 +592,11 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             struct lfs_commit c = { | ||||
|                 .crc = 0xffffffff, | ||||
|                 .block = dir->pair[0], | ||||
|                 .off = 0, | ||||
|             }; | ||||
|  | ||||
|             // commit header | ||||
|             uint32_t crc = 0xffffffff; | ||||
|             lfs_dir_tole32(&dir->d); | ||||
|             err = lfs_commit_disk(lfs, &c, &(struct lfs_commit_disk){ | ||||
|                     oldpair[1], 0, | ||||
|                     &(struct lfs_region){ | ||||
|                         0, 0, | ||||
|                         lfs_commit_mem, &dir->d, sizeof(dir->d), | ||||
|                         regions}}, oldsize); | ||||
|             lfs_crc(&crc, &dir->d, sizeof(dir->d)); | ||||
|             err = lfs_bd_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d)); | ||||
|             lfs_dir_fromle32(&dir->d); | ||||
|             if (err) { | ||||
|                 if (err == LFS_ERR_CORRUPT) { | ||||
| @@ -602,9 +605,25 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             c.crc = lfs_tole32(c.crc); | ||||
|             err = lfs_bd_prog(lfs, dir->pair[0], c.off, &c.crc, 4); | ||||
|             c.crc = lfs_fromle32(c.crc); | ||||
|             // commit region | ||||
|             err = lfs_commit_region(lfs, | ||||
|                     dir->pair[1], sizeof(dir->d), | ||||
|                     dir->pair[0], sizeof(dir->d), | ||||
|                     0, regions, count, | ||||
|                     (0x7fffffff & dir->d.size)-sizeof(dir->d)-4, | ||||
|                     &crc); | ||||
|             if (err) { | ||||
|                 if (err == LFS_ERR_CORRUPT) { | ||||
|                     goto relocate; | ||||
|                 } | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             // commit crc | ||||
|             crc = lfs_tole32(crc); | ||||
|             err = lfs_bd_prog(lfs, dir->pair[0], | ||||
|                     (0x7fffffff & dir->d.size)-4, &crc, 4); | ||||
|             crc = lfs_fromle32(crc); | ||||
|             if (err) { | ||||
|                 if (err == LFS_ERR_CORRUPT) { | ||||
|                     goto relocate; | ||||
| @@ -628,7 +647,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             if (ncrc != c.crc) { | ||||
|             if (ncrc != crc) { | ||||
|                 goto relocate; | ||||
|             } | ||||
|         } | ||||
| @@ -683,61 +702,106 @@ static int lfs_dir_get(lfs_t *lfs, const lfs_dir_t *dir, | ||||
|     return lfs_bd_read(lfs, dir->pair[0], off, buffer, size); | ||||
| } | ||||
|  | ||||
| static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, | ||||
|         lfs_entry_t *entry, struct lfs_region *regions) { | ||||
|     // check if we fit, if top bit is set we do not and move on | ||||
|     while (true) { | ||||
|         if ((0x7fffffff & dir->d.size) + entry->size <= lfs->cfg->block_size) { | ||||
|             entry->off = dir->d.size - 4; | ||||
|             for (struct lfs_region *r = regions; r; r = r->next) { | ||||
|                 r->off += entry->off; | ||||
|             } | ||||
| static int lfs_dir_set(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|         struct lfs_region *regions, int count) { | ||||
|     lfs_ssize_t diff = 0;  | ||||
|     for (int i = 0; i < count; i++) { | ||||
|         diff += regions[i].size; | ||||
|     } | ||||
|  | ||||
|             lfs_entry_tole32(&entry->d); | ||||
|             int err = lfs_dir_commit(lfs, dir, regions); | ||||
|             lfs_entry_fromle32(&entry->d); | ||||
|             return err; | ||||
|         } | ||||
|     lfs_size_t oldsize = entry->size; | ||||
|     if (entry->off == 0) { | ||||
|         entry->off = (0x7fffffff & dir->d.size) - 4; | ||||
|     } | ||||
|  | ||||
|         // we need to allocate a new dir block | ||||
|         if (!(0x80000000 & dir->d.size)) { | ||||
|             lfs_dir_t olddir = *dir; | ||||
|             int err = lfs_dir_alloc(lfs, dir); | ||||
|     if ((0x7fffffff & dir->d.size) + diff > lfs->cfg->block_size) { | ||||
|         lfs_dir_t olddir = *dir; | ||||
|         lfs_off_t oldoff = entry->off; | ||||
|  | ||||
|         if (oldsize) { | ||||
|             // mark as moving | ||||
|             uint8_t type; | ||||
|             int err = lfs_dir_get(lfs, &olddir, oldoff, &type, 1); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             dir->d.tail[0] = olddir.d.tail[0]; | ||||
|             dir->d.tail[1] = olddir.d.tail[1]; | ||||
|             entry->off = dir->d.size - 4; | ||||
|             for (struct lfs_region *r = regions; r; r = r->next) { | ||||
|                 r->off += entry->off; | ||||
|             } | ||||
|  | ||||
|             lfs_entry_tole32(&entry->d); | ||||
|             err = lfs_dir_commit(lfs, dir, regions); | ||||
|             lfs_entry_fromle32(&entry->d); | ||||
|             type |= LFS_STRUCT_MOVED; | ||||
|             err = lfs_dif_commit(lfs, &olddir, (struct lfs_region[]){ | ||||
|                         {LFS_FROM_MEM, oldoff, &type, 1}, | ||||
|                         {LFS_FROM_DROP, oldoff, NULL, -1}}, 2); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             olddir.d.size |= 0x80000000; | ||||
|             olddir.d.tail[0] = dir->pair[0]; | ||||
|             olddir.d.tail[1] = dir->pair[1]; | ||||
|             return lfs_dir_commit(lfs, &olddir, NULL); | ||||
|         } | ||||
|  | ||||
|         int err = lfs_dir_fetch(lfs, dir, dir->d.tail); | ||||
|         lfs_dir_t pdir = olddir; | ||||
|  | ||||
|         // find available block or create a new one | ||||
|         while ((0x7fffffff & dir->d.size) + oldsize + diff | ||||
|                 > lfs->cfg->block_size) { | ||||
|             // we need to allocate a new dir block | ||||
|             if (!(0x80000000 & dir->d.size)) { | ||||
|                 pdir = *dir; | ||||
|                 int err = lfs_dir_alloc(lfs, dir); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|                 dir->d.tail[0] = pdir.d.tail[0]; | ||||
|                 dir->d.tail[1] = pdir.d.tail[1]; | ||||
|  | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             int err = lfs_dir_fetch(lfs, dir, dir->d.tail); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // writing out new entry | ||||
|         entry->off = dir->d.size - 4; | ||||
|         entry->size += diff; | ||||
|         int err = lfs_dif_commit(lfs, dir, (struct lfs_region[]){ | ||||
|                 {LFS_FROM_REGION, entry->off, &(struct lfs_region_region){ | ||||
|                     olddir.pair[0], oldoff, | ||||
|                     regions, count}, entry->size}}, 1); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, | ||||
|         const lfs_entry_t *entry) { | ||||
|     // check if we should just drop the directory block | ||||
|     if ((dir->d.size & 0x7fffffff) == sizeof(dir->d)+4 + entry->size) { | ||||
|         // update pred dir, unless pred == old we can coalesce | ||||
|         if (!oldsize || lfs_paircmp(pdir.pair, olddir.pair) != 0) { | ||||
|             pdir.d.size |= 0x80000000; | ||||
|             pdir.d.tail[0] = dir->pair[0]; | ||||
|             pdir.d.tail[1] = dir->pair[1]; | ||||
|  | ||||
|             err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } else if (oldsize) { | ||||
|             olddir.d.size |= 0x80000000; | ||||
|             olddir.d.tail[0] = dir->pair[0]; | ||||
|             olddir.d.tail[1] = dir->pair[1]; | ||||
|         } | ||||
|  | ||||
|         // remove old entry | ||||
|         if (oldsize) { | ||||
|             lfs_entry_t oldentry; | ||||
|             oldentry.off = oldoff; | ||||
|             err = lfs_dir_set(lfs, &olddir, &oldentry, (struct lfs_region[]){ | ||||
|                     {LFS_FROM_DROP, 0, NULL, -oldsize}}, 1); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         goto shift; | ||||
|     } | ||||
|  | ||||
|     if ((0x7fffffff & dir->d.size) + diff == sizeof(dir->d)+4) { | ||||
|         lfs_dir_t pdir; | ||||
|         int res = lfs_pred(lfs, dir->pair, &pdir); | ||||
|         if (res < 0) { | ||||
| @@ -748,27 +812,34 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, | ||||
|             pdir.d.size &= dir->d.size | 0x7fffffff; | ||||
|             pdir.d.tail[0] = dir->d.tail[0]; | ||||
|             pdir.d.tail[1] = dir->d.tail[1]; | ||||
|             return lfs_dir_commit(lfs, &pdir, NULL); | ||||
|             int err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|             goto shift; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // shift out the entry | ||||
|     int err = lfs_dir_commit(lfs, dir, | ||||
|             &(struct lfs_region){ | ||||
|                 entry->off, -entry->size, | ||||
|                 lfs_commit_mem, NULL, 0}); | ||||
|     for (int i = 0; i < count; i++) { | ||||
|         regions[i].off += entry->off; | ||||
|     } | ||||
|  | ||||
|     int err = lfs_dif_commit(lfs, dir, regions, count); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     entry->size += diff; | ||||
|  | ||||
| shift: | ||||
|     // shift over any files/directories that are affected | ||||
|     for (lfs_file_t *f = lfs->files; f; f = f->next) { | ||||
|         if (lfs_paircmp(f->pair, dir->pair) == 0) { | ||||
|             if (f->poff == entry->off) { | ||||
|             if (f->poff == entry->off && entry->size == 0) { | ||||
|                 f->pair[0] = 0xffffffff; | ||||
|                 f->pair[1] = 0xffffffff; | ||||
|             } else if (f->poff > entry->off) { | ||||
|                 f->poff -= entry->size; | ||||
|                 f->poff += diff; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -776,8 +847,8 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, | ||||
|     for (lfs_dir_t *d = lfs->dirs; d; d = d->next) { | ||||
|         if (lfs_paircmp(d->pair, dir->pair) == 0) { | ||||
|             if (d->off > entry->off) { | ||||
|                 d->off -= entry->size; | ||||
|                 d->pos -= entry->size; | ||||
|                 d->off += diff; | ||||
|                 d->pos += diff; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -785,87 +856,6 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir, | ||||
|         lfs_entry_t *entry, struct lfs_region *regions) { | ||||
|     lfs_ssize_t diff = 0; | ||||
|     for (struct lfs_region *r = regions; r; r = r->next) { | ||||
|         diff += r->diff; | ||||
|     } | ||||
|  | ||||
|     // do we still fit? | ||||
|     if ((0x7fffffff & dir->d.size) + diff <= lfs->cfg->block_size) { | ||||
|         for (struct lfs_region *r = regions; r; r = r->next) { | ||||
|             r->off += entry->off; | ||||
|         } | ||||
|  | ||||
|         lfs_entry_tole32(&entry->d); | ||||
|         int err = lfs_dir_commit(lfs, dir, regions); | ||||
|         lfs_entry_fromle32(&entry->d); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // shift over any files/directories that are affected | ||||
|         for (lfs_file_t *f = lfs->files; f; f = f->next) { | ||||
|             if (lfs_paircmp(f->pair, dir->pair) == 0) { | ||||
|                 if (f->poff > entry->off) { | ||||
|                     f->poff += diff; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (lfs_dir_t *d = lfs->dirs; d; d = d->next) { | ||||
|             if (lfs_paircmp(d->pair, dir->pair) == 0) { | ||||
|                 if (d->off > entry->off) { | ||||
|                     d->off += diff; | ||||
|                     d->pos += diff; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         lfs_dir_t olddir = *dir; | ||||
|         lfs_entry_t oldentry = { | ||||
|             .off = entry->off, | ||||
|             .size = entry->size - diff, | ||||
|             .d.type = entry->d.type | LFS_STRUCT_MOVED, // TODO FIXME when changing type?? | ||||
|         }; | ||||
|  | ||||
|         // mark as moving | ||||
|         int err = lfs_dir_commit(lfs, &olddir, | ||||
|                 &(struct lfs_region){ | ||||
|                     oldentry.off, 0, | ||||
|                     lfs_commit_mem, &oldentry.d.type, 1}); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // append updated entry | ||||
|         lfs_entry_tole32(&entry->d); | ||||
|         err = lfs_dir_append(lfs, dir, entry, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, +entry->size, | ||||
|                     lfs_commit_disk, &(struct lfs_commit_disk){ | ||||
|                         olddir.pair[0], entry->off, regions}, oldentry.size}); | ||||
|         lfs_entry_fromle32(&entry->d); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // remove old entry | ||||
|         err = lfs_dir_fetch(lfs, &olddir, olddir.pair); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         err = lfs_dir_remove(lfs, &olddir, &oldentry); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | ||||
|     while (dir->off >= (0x7fffffff & dir->d.size)-4) { | ||||
|         if (!(0x80000000 & dir->d.size)) { | ||||
| @@ -1039,7 +1029,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
|     dir.d.tail[0] = cwd.d.tail[0]; | ||||
|     dir.d.tail[1] = cwd.d.tail[1]; | ||||
|  | ||||
|     err = lfs_dir_commit(lfs, &dir, NULL); | ||||
|     err = lfs_dif_commit(lfs, &dir, NULL, 0); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -1050,18 +1040,13 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
|     entry.d.nlen = strlen(path); | ||||
|     entry.d.u.dir[0] = dir.pair[0]; | ||||
|     entry.d.u.dir[1] = dir.pair[1]; | ||||
|     entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen; | ||||
|     entry.size = 0; | ||||
|  | ||||
|     cwd.d.tail[0] = dir.pair[0]; | ||||
|     cwd.d.tail[1] = dir.pair[1]; | ||||
|  | ||||
|     err = lfs_dir_append(lfs, &cwd, &entry, | ||||
|             &(struct lfs_region){ | ||||
|                 0, +sizeof(entry.d), | ||||
|                 lfs_commit_mem, &entry.d, sizeof(entry.d), | ||||
|             &(struct lfs_region){ | ||||
|                 0, +entry.d.nlen, | ||||
|                 lfs_commit_mem, path, entry.d.nlen}}); | ||||
|     err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_MEM, 0, &entry.d, sizeof(entry.d)}, | ||||
|             {LFS_FROM_MEM, 0, path, entry.d.nlen}}, 2); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -1447,15 +1432,11 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         entry.d.elen = 0; | ||||
|         entry.d.alen = 0; | ||||
|         entry.d.nlen = strlen(path); | ||||
|         entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen; | ||||
|         entry.size = 0; | ||||
|  | ||||
|         err = lfs_dir_append(lfs, &cwd, &entry, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, +4, | ||||
|                     lfs_commit_mem, &entry.d, 4, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, +entry.d.nlen, | ||||
|                     lfs_commit_mem, path, entry.d.nlen}}); | ||||
|         err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|                 {LFS_FROM_MEM, 0, &entry.d, 4}, | ||||
|                 {LFS_FROM_MEM, 0, path, entry.d.nlen}}, 2); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -1684,43 +1665,37 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | ||||
|  | ||||
|         // TODO entry read function? | ||||
|         lfs_entry_t entry = {.off = file->poff}; | ||||
|         err = lfs_dir_get(lfs, &cwd, | ||||
|                 entry.off, &entry.d, sizeof(entry.d)); | ||||
|         err = lfs_dir_get(lfs, &cwd, entry.off, &entry.d, sizeof(entry.d)); | ||||
|         lfs_entry_fromle32(&entry.d); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|         LFS_ASSERT((0xf & entry.d.type) == LFS_TYPE_REG); | ||||
|  | ||||
|         LFS_ASSERT((0xf & entry.d.type) == LFS_TYPE_REG); | ||||
|         lfs_size_t oldlen = entry.d.elen; | ||||
|         entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen; | ||||
|  | ||||
|         // either update the references or inline the whole file | ||||
|         if (!(file->flags & LFS_F_INLINE)) { | ||||
|             lfs_ssize_t diff = sizeof(entry.d)-4 - entry.d.elen; | ||||
|             entry.d.type = LFS_STRUCT_CTZ | LFS_TYPE_REG; | ||||
|             entry.d.elen = sizeof(entry.d)-4; | ||||
|             entry.d.u.file.head = file->head; | ||||
|             entry.d.u.file.size = file->size; | ||||
|             entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen; | ||||
|  | ||||
|             // TODO combine down? | ||||
|             err = lfs_dir_update(lfs, &cwd, &entry, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, diff, | ||||
|                     lfs_commit_mem, &entry.d, sizeof(entry.d)}); | ||||
|             err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|                     {LFS_FROM_MEM, 0, &entry.d, sizeof(entry.d)}, | ||||
|                     {LFS_FROM_DROP, 0, NULL, -oldlen-4}}, 2); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } else { | ||||
|             lfs_ssize_t diff = file->size - entry.d.elen; | ||||
|             entry.d.type = LFS_STRUCT_INLINE | LFS_TYPE_REG; | ||||
|             entry.d.elen = file->size; | ||||
|             entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen; | ||||
|  | ||||
|             err = lfs_dir_update(lfs, &cwd, &entry, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, 0, | ||||
|                     lfs_commit_mem, &entry.d, 4, | ||||
|                 &(struct lfs_region){ | ||||
|                     4, diff, | ||||
|                     lfs_commit_mem, file->cache.buffer, file->size}}); | ||||
|             err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|                     {LFS_FROM_MEM, 0, &entry.d, 4}, | ||||
|                     {LFS_FROM_MEM, 0, file->cache.buffer, file->size}, | ||||
|                     {LFS_FROM_DROP, 0, NULL, -oldlen-4}}, 3); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
| @@ -2098,7 +2073,8 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|     } | ||||
|  | ||||
|     // remove the entry | ||||
|     err = lfs_dir_remove(lfs, &cwd, &entry); | ||||
|     err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_DROP, 0, NULL, -entry.size}}, 1); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -2114,7 +2090,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|         cwd.d.tail[0] = dir.d.tail[0]; | ||||
|         cwd.d.tail[1] = dir.d.tail[1]; | ||||
|  | ||||
|         err = lfs_dir_commit(lfs, &cwd, NULL); | ||||
|         err = lfs_dif_commit(lfs, &cwd, NULL, 0); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -2181,10 +2157,9 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|  | ||||
|     // mark as moving | ||||
|     oldentry.d.type |= LFS_STRUCT_MOVED; | ||||
|     err = lfs_dir_update(lfs, &oldcwd, &oldentry, | ||||
|             &(struct lfs_region){ | ||||
|                 0, 0, | ||||
|                 lfs_commit_mem, &oldentry.d.type, 1}); | ||||
|     err = lfs_dir_set(lfs, &oldcwd, &oldentry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_MEM, 0, &oldentry.d.type, 1}, | ||||
|             {LFS_FROM_DROP, 0, NULL, -1}}, 2); | ||||
|     oldentry.d.type &= ~LFS_STRUCT_MOVED; | ||||
|     if (err) { | ||||
|         return err; | ||||
| @@ -2200,42 +2175,22 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|     newentry.d = oldentry.d; | ||||
|     newentry.d.type &= ~LFS_STRUCT_MOVED; | ||||
|     newentry.d.nlen = strlen(newpath); | ||||
|     newentry.size = 4 + newentry.d.elen + newentry.d.alen + newentry.d.nlen; | ||||
|     if (!prevexists) { | ||||
|         newentry.size = 0; | ||||
|     } | ||||
|  | ||||
|     if (prevexists) { | ||||
|         err = lfs_dir_update(lfs, &newcwd, &newentry, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, newentry.size - preventry.size, | ||||
|                     lfs_commit_disk, &(struct lfs_commit_disk){ | ||||
|                         oldcwd.pair[0], oldentry.off, | ||||
|                         &(struct lfs_region){ | ||||
|                             0, 0, | ||||
|                             lfs_commit_mem, &newentry.d, 4, | ||||
|                         &(struct lfs_region){ | ||||
|                             newentry.size - newentry.d.nlen, | ||||
|                             +newentry.d.nlen-oldentry.d.nlen, | ||||
|                             lfs_commit_mem, newpath, newentry.d.nlen}}}, | ||||
|                             oldentry.size}); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     } else { | ||||
|         err = lfs_dir_append(lfs, &newcwd, &newentry, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, +newentry.size, | ||||
|                     lfs_commit_disk, &(struct lfs_commit_disk){ | ||||
|                         oldcwd.pair[0], oldentry.off, | ||||
|                         &(struct lfs_region){ | ||||
|                             0, 0, | ||||
|                             lfs_commit_mem, &newentry.d, 4, | ||||
|                         &(struct lfs_region){ | ||||
|                             newentry.size - newentry.d.nlen, | ||||
|                             +newentry.d.nlen-oldentry.d.nlen, | ||||
|                             lfs_commit_mem, newpath, newentry.d.nlen}}}, | ||||
|                             oldentry.size}); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|     lfs_size_t newsize = oldentry.size - oldentry.d.nlen + newentry.d.nlen; | ||||
|     err = lfs_dir_set(lfs, &newcwd, &newentry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_REGION, 0, &(struct lfs_region_region){ | ||||
|                 oldcwd.pair[0], oldentry.off, (struct lfs_region[]){ | ||||
|                     {LFS_FROM_MEM, 0, &newentry.d, 4}, | ||||
|                     {LFS_FROM_DROP, 0, NULL, -4}, | ||||
|                     {LFS_FROM_MEM, newsize - newentry.d.nlen, | ||||
|                         newpath, newentry.d.nlen}}, 3}, | ||||
|                 newsize}, | ||||
|             {LFS_FROM_DROP, 0, NULL, -preventry.size}}, prevexists ? 2 : 1); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // update pair if newcwd == oldcwd | ||||
| @@ -2244,7 +2199,8 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|     } | ||||
|  | ||||
|     // remove old entry | ||||
|     err = lfs_dir_remove(lfs, &oldcwd, &oldentry); | ||||
|     err = lfs_dir_set(lfs, &oldcwd, &oldentry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_DROP, 0, NULL, -oldentry.size}}, 1); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -2260,7 +2216,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         newcwd.d.tail[0] = dir.d.tail[0]; | ||||
|         newcwd.d.tail[1] = dir.d.tail[1]; | ||||
|  | ||||
|         err = lfs_dir_commit(lfs, &newcwd, NULL); | ||||
|         err = lfs_dif_commit(lfs, &newcwd, NULL, 0); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -2370,7 +2326,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = lfs_dir_commit(lfs, &root, NULL); | ||||
|     err = lfs_dif_commit(lfs, &root, NULL, 0); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -2394,21 +2350,14 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     superentry.d.alen = 0; | ||||
|     superentry.d.nlen = strlen("littlefs"); | ||||
|     superentry.off = sizeof(superdir.d); | ||||
|     superentry.size = 4 + superentry.d.elen + | ||||
|             superentry.d.alen + superentry.d.nlen; | ||||
|     superentry.size = 0; | ||||
|  | ||||
|     lfs_entry_tole32(&superentry.d); | ||||
|     lfs_superblock_tole32(&superblock.d); | ||||
|     err = lfs_dir_append(lfs, &superdir, &superentry, | ||||
|             &(struct lfs_region){ | ||||
|                 0, +4, | ||||
|                 lfs_commit_mem, &superentry.d, 4, | ||||
|             &(struct lfs_region){ | ||||
|                 0, +sizeof(superblock.d), | ||||
|                 lfs_commit_mem, &superblock.d, sizeof(superblock.d), | ||||
|             &(struct lfs_region){ | ||||
|                 0, +superentry.d.nlen, | ||||
|                 lfs_commit_mem, "littlefs", superentry.d.nlen}}}); | ||||
|     err = lfs_dir_set(lfs, &superdir, &superentry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_MEM, 0, &superentry.d, 4}, | ||||
|             {LFS_FROM_MEM, 0, &superblock.d, sizeof(superblock.d)}, | ||||
|             {LFS_FROM_MEM, 0, "littlefs", superentry.d.nlen}}, 3); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -2452,8 +2401,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|         } | ||||
|  | ||||
|         err = lfs_dir_get(lfs, &dir, | ||||
|                 sizeof(dir.d) + 4 + sizeof(superblock.d), | ||||
|                 magic, sizeof(magic)); | ||||
|                 sizeof(dir.d)+4 + sizeof(superblock.d), magic, sizeof(magic)); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -2671,11 +2619,9 @@ static int lfs_relocate(lfs_t *lfs, | ||||
|         // update disk, this creates a desync | ||||
|         entry.d.u.dir[0] = newpair[0]; | ||||
|         entry.d.u.dir[1] = newpair[1]; | ||||
|  | ||||
|         int err = lfs_dir_update(lfs, &parent, &entry, | ||||
|                 &(struct lfs_region){ | ||||
|                     0, 0, | ||||
|                     lfs_commit_mem, &entry.d, sizeof(entry.d)}); | ||||
|         int err = lfs_dir_set(lfs, &parent, &entry, (struct lfs_region[]){ | ||||
|                 {LFS_FROM_MEM, 0, &entry.d, sizeof(entry.d)}, | ||||
|                 {LFS_FROM_DROP, 0, NULL, (lfs_ssize_t)-sizeof(entry.d)}}, 2); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -2702,7 +2648,7 @@ static int lfs_relocate(lfs_t *lfs, | ||||
|         parent.d.tail[0] = newpair[0]; | ||||
|         parent.d.tail[1] = newpair[1]; | ||||
|  | ||||
|         return lfs_dir_commit(lfs, &parent, NULL); | ||||
|         return lfs_dif_commit(lfs, &parent, NULL, 0); | ||||
|     } | ||||
|  | ||||
|     // couldn't find dir, must be new | ||||
| @@ -2744,7 +2690,7 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                 pdir.d.tail[0] = cwd.d.tail[0]; | ||||
|                 pdir.d.tail[1] = cwd.d.tail[1]; | ||||
|  | ||||
|                 err = lfs_dir_commit(lfs, &pdir, NULL); | ||||
|                 err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
| @@ -2760,7 +2706,7 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                 pdir.d.tail[0] = entry.d.u.dir[0]; | ||||
|                 pdir.d.tail[1] = entry.d.u.dir[1]; | ||||
|  | ||||
|                 err = lfs_dir_commit(lfs, &pdir, NULL); | ||||
|                 err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
| @@ -2791,7 +2737,8 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                 if (moved) { | ||||
|                     LFS_DEBUG("Found move %d %d", | ||||
|                             entry.d.u.dir[0], entry.d.u.dir[1]); | ||||
|                     err = lfs_dir_remove(lfs, &cwd, &entry); | ||||
|                     err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|                             {LFS_FROM_DROP, 0, NULL, -entry.size}}, 1); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
| @@ -2799,10 +2746,10 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                     LFS_DEBUG("Found partial move %d %d", | ||||
|                             entry.d.u.dir[0], entry.d.u.dir[1]); | ||||
|                     entry.d.type &= ~LFS_STRUCT_MOVED; | ||||
|                     err = lfs_dir_update(lfs, &cwd, &entry, | ||||
|                             &(struct lfs_region){ | ||||
|                                 0, 0, | ||||
|                                 lfs_commit_mem, &entry.d, sizeof(entry.d)}); | ||||
|                     err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|                             {LFS_FROM_MEM, 0, &entry.d, sizeof(entry.d)}, | ||||
|                             {LFS_FROM_DROP, 0, NULL, | ||||
|                                 (lfs_ssize_t)-sizeof(entry.d)}}, 2); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user