diff --git a/lfs.c b/lfs.c index aad2152..d4e6dc5 100644 --- a/lfs.c +++ b/lfs.c @@ -264,7 +264,7 @@ int lfs_fs_traverse(lfs_t *lfs, static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir); static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *parent, lfs_entry_t *entry); -static int lfs_moved(lfs_t *lfs, const lfs_block_t pair[2]); +static int lfs_moved(lfs_t *lfs, lfs_dir_t *fromdir, uint16_t fromid); static int lfs_relocate(lfs_t *lfs, const lfs_block_t oldpair[2], const lfs_block_t newpair[2]); int lfs_deorphan(lfs_t *lfs); @@ -473,40 +473,6 @@ struct lfs_commit { } filter; }; -static int lfs_commit_traverse(lfs_t *lfs, struct lfs_commit *commit, - int (*cb)(lfs_t *lfs, void *data, lfs_entry_t entry), void *data) { - // TODO duplicate this? move it to dir? - // iterate over dir block backwards (for faster lookups) - lfs_block_t block = commit->block; - lfs_off_t off = commit->off; - lfs_tag_t tag = commit->ptag; - - while (off != sizeof(uint32_t)) { - // TODO rm me - printf("tag r %#010x (%x:%x %03x %03x %03x)\n", tag, block, off-lfs_tag_size(tag), lfs_tag_type(tag), lfs_tag_id(tag), lfs_tag_size(tag)); - int err = cb(lfs, data, (lfs_entry_t){ - (0x80000000 | tag), - .u.d.block=block, - .u.d.off=off-lfs_tag_size(tag)}); - if (err) { - return err; - } - - LFS_ASSERT(off > sizeof(tag)+lfs_tag_size(tag)); - off -= sizeof(tag)+lfs_tag_size(tag); - - lfs_tag_t ntag; - err = lfs_bd_read(lfs, block, off, &ntag, sizeof(ntag)); - if (err) { - return err; - } - - tag ^= lfs_fromle32(ntag); - } - - return 0; -} - //static int lfs_commit_compactcheck(lfs_t *lfs, void *p, lfs_entry_t entry) { // struct lfs_commit *commit = p; // if (lfs_tag_id(entry.tag) != commit->compact.id) { @@ -527,7 +493,7 @@ static int lfs_commit_commit(lfs_t *lfs, return 0; } uint16_t id = lfs_tag_id(entry.tag) - commit->filter.begin; - entry.tag = (id << 12) | (entry.tag & 0xffe00fff); + entry.tag = lfs_mktag(0, id, 0) | (entry.tag & 0xffe00fff); // check if we fit lfs_size_t size = lfs_tag_size(entry.tag); @@ -537,7 +503,7 @@ static int lfs_commit_commit(lfs_t *lfs, // write out tag // TODO rm me - printf("tag w %#010x (%x:%x %03x %03x %03x)\n", entry.tag, commit->block, commit->off+sizeof(lfs_tag_t), lfs_tag_type(entry.tag), lfs_tag_id(entry.tag), lfs_tag_size(entry.tag)); + //printf("tag w %#010x (%x:%x %03x %03x %03x)\n", entry.tag, commit->block, commit->off+sizeof(lfs_tag_t), lfs_tag_type(entry.tag), lfs_tag_id(entry.tag), lfs_tag_size(entry.tag)); lfs_tag_t tag = lfs_tole32((entry.tag & 0x7fffffff) ^ commit->ptag); lfs_crc(&commit->crc, &tag, sizeof(tag)); int err = lfs_bd_prog(lfs, commit->block, commit->off, &tag, sizeof(tag)); @@ -572,7 +538,7 @@ static int lfs_commit_commit(lfs_t *lfs, } } commit->off += size; - commit->ptag = entry.tag; + commit->ptag = entry.tag & 0x7fffffff; // TODO do this once return 0; } @@ -595,7 +561,7 @@ static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) { noff - (commit->off+sizeof(uint32_t))); // write out crc - printf("tag w %#010x (%x:%x %03x %03x %03x)\n", tag, commit->block, commit->off+sizeof(tag), lfs_tag_type(tag), lfs_tag_id(tag), lfs_tag_size(tag)); + //printf("tag w %#010x (%x:%x %03x %03x %03x)\n", tag, commit->block, commit->off+sizeof(tag), lfs_tag_type(tag), lfs_tag_id(tag), lfs_tag_size(tag)); uint32_t footer[2]; footer[0] = lfs_tole32(tag ^ commit->ptag); lfs_crc(&commit->crc, &footer[0], sizeof(footer[0])); @@ -641,10 +607,6 @@ static int lfs_commit_regions(lfs_t *lfs, void *p, struct lfs_commit *commit) { return 0; } -// TODO redeclare -static int lfs_dir_traverse(lfs_t *lfs, lfs_dir_t *dir, - int (*cb)(lfs_t *lfs, void *data, lfs_entry_t entry), - void *data); // committer for moves struct lfs_commit_move { @@ -653,40 +615,31 @@ struct lfs_commit_move { uint16_t from; uint16_t to; } id; - uint16_t type; struct lfs_commit *commit; }; -static int lfs_commit_movecheck(lfs_t *lfs, void *p, lfs_entry_t entry) { +// TODO redeclare +static int lfs_dir_traverse(lfs_t *lfs, lfs_dir_t *dir, + int (*cb)(lfs_t *lfs, void *data, lfs_entry_t entry), + void *data); +static int lfs_dir_get(lfs_t *lfs, lfs_dir_t *dir, + uint32_t mask, lfs_entry_t *entry); + +static int lfs_commit_movescan(lfs_t *lfs, void *p, lfs_entry_t entry) { struct lfs_commit_move *move = p; - if (lfs_tag_id(entry.tag) != move->id.to - move->commit->filter.begin) { - return 1; + if (lfs_tag_type(entry.tag) == LFS_TYPE_DELETE && + lfs_tag_id(entry.tag) <= move->id.from) { + // something was deleted, we need to move around it + move->id.from += 1; + return 0; } - if (lfs_tag_type(entry.tag) & 0x100) { - if (lfs_tag_type(entry.tag) == move->type) { - return 2; - } - } else { - // TODO hmm - if (lfs_tag_subtype(entry.tag) == (move->type & 0x1f0)) { - return 2; - } - } - - return 0; -} - -static int lfs_commit_moveiter(lfs_t *lfs, void *p, lfs_entry_t entry) { - struct lfs_commit_move *move = p; - - if (lfs_tag_type(entry.tag) == LFS_TYPE_DELETE) { - if (lfs_tag_id(entry.tag) <= move->id.from) { - // something was deleted, we need to move around it - move->id.from += 1; - } + if (lfs_tag_type(entry.tag) == LFS_TYPE_MOVE) { + // TODO need this? + // ignore moves + return 0; } if (lfs_tag_id(entry.tag) != move->id.from) { @@ -694,28 +647,41 @@ static int lfs_commit_moveiter(lfs_t *lfs, void *p, lfs_entry_t entry) { return 0; } - move->type = lfs_tag_type(entry.tag); - int res = lfs_commit_traverse(lfs, move->commit, - lfs_commit_movecheck, move); - if (res < 0) { - return res; + // check if type has already been committed + int err = lfs_dir_get(lfs, + &(lfs_dir_t){ + .pair[0]=move->commit->block, + .off=move->commit->off, + .etag=move->commit->ptag, + .stop_at_commit=true}, + lfs_tag_type(entry.tag) & 0x100 ? 0x7ffff000 : 0x7c1ff000, + &(lfs_entry_t){ + lfs_mktag(lfs_tag_type(entry.tag), move->id.to, 0)}); + if (err && err != LFS_ERR_NOENT) { + return err; } - if (res == 2) { + if (err != LFS_ERR_NOENT) { // already committed return 0; } // update id and commit, as we are currently unique - entry.tag = (move->id.to << 12) | (entry.tag & 0xffe00fff); + entry.tag = lfs_mktag(0, move->id.to, 0) | (entry.tag & 0xffe00fff); return lfs_commit_commit(lfs, move->commit, entry); } +// TODO change this to be special RAM-side type in linked list of commits? static int lfs_commit_move(lfs_t *lfs, void *p, struct lfs_commit *commit) { struct lfs_commit_move *move = p; move->commit = commit; + if (move->id.to < commit->filter.begin || + move->id.to >= commit->filter.end) { + // skip if not in filter + return 0; + } - int err = lfs_dir_traverse(lfs, move->dir, lfs_commit_moveiter, move); + int err = lfs_dir_traverse(lfs, move->dir, lfs_commit_movescan, move); if (err < 0) { return err; } @@ -759,6 +725,7 @@ static int lfs_dir_fetchwith(lfs_t *lfs, int (*cb)(lfs_t *lfs, void *data, lfs_entry_t entry), void *data) { dir->pair[0] = pair[0]; dir->pair[1] = pair[1]; + dir->stop_at_commit = false; // find the block with the most recent revision uint32_t rev[2]; @@ -812,7 +779,7 @@ static int lfs_dir_fetchwith(lfs_t *lfs, break; } - printf("tag r %#010x (%x:%x %03x %03x %03x)\n", tag, dir->pair[0], off+sizeof(tag), lfs_tag_type(tag), lfs_tag_id(tag), lfs_tag_size(tag)); + //printf("tag r %#010x (%x:%x %03x %03x %03x)\n", tag, dir->pair[0], off+sizeof(tag), lfs_tag_type(tag), lfs_tag_id(tag), lfs_tag_size(tag)); if (lfs_tag_type(tag) == LFS_TYPE_CRC) { // check the crc entry uint32_t dcrc; @@ -862,6 +829,9 @@ static int lfs_dir_fetchwith(lfs_t *lfs, if (lfs_tag_type(tag) == LFS_TYPE_DELETE) { dir->count -= 1; + if (dir->moveid != -1) { + //printf("RENAME DEL %d (%d)\n", lfs_tag_id(tag), dir->moveid); + } if (lfs_tag_id(tag) == dir->moveid) { dir->moveid = -1; } else if (lfs_tag_id(tag) < dir->moveid) { @@ -901,9 +871,41 @@ static int lfs_dir_fetch(lfs_t *lfs, static int lfs_dir_traverse(lfs_t *lfs, lfs_dir_t *dir, int (*cb)(lfs_t *lfs, void *data, lfs_entry_t entry), void *data) { - return lfs_commit_traverse(lfs, &(struct lfs_commit){ - .block=dir->pair[0], .off=dir->off, .ptag=dir->etag}, - cb, data); + // iterate over dir block backwards (for faster lookups) + lfs_block_t block = dir->pair[0]; + lfs_off_t off = dir->off; + lfs_tag_t tag = dir->etag; + + while (off != sizeof(uint32_t)) { + // TODO rm me + //printf("tag r %#010x (%x:%x %03x %03x %03x)\n", tag, block, off-lfs_tag_size(tag), lfs_tag_type(tag), lfs_tag_id(tag), lfs_tag_size(tag)); + + // TODO hmm + if (dir->stop_at_commit && lfs_tag_type(tag) == LFS_TYPE_CRC) { + break; + } + + int err = cb(lfs, data, (lfs_entry_t){ + (0x80000000 | tag), + .u.d.block=block, + .u.d.off=off-lfs_tag_size(tag)}); + if (err) { + return err; + } + + LFS_ASSERT(off > sizeof(tag)+lfs_tag_size(tag)); + off -= sizeof(tag)+lfs_tag_size(tag); + + lfs_tag_t ntag; + err = lfs_bd_read(lfs, block, off, &ntag, sizeof(ntag)); + if (err) { + return err; + } + + tag ^= lfs_fromle32(ntag); + } + + return 0; } //struct lfs_dir_commitmove { @@ -1159,6 +1161,7 @@ static int lfs_dir_commitwith(lfs_t *lfs, lfs_dir_t *dir, int err = cb(lfs, data, &commit); if (err) { if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { + lfs->pcache.block = 0xffffffff; return lfs_dir_compact(lfs, dir, cb, data, dir, 0, dir->count); } return err; @@ -1167,6 +1170,7 @@ static int lfs_dir_commitwith(lfs_t *lfs, lfs_dir_t *dir, err = lfs_commit_crc(lfs, &commit); if (err) { if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { + lfs->pcache.block = 0xffffffff; return lfs_dir_compact(lfs, dir, cb, data, dir, 0, dir->count); } return err; @@ -1203,11 +1207,14 @@ struct lfs_dir_getter { static int lfs_dir_getter(lfs_t *lfs, void *p, lfs_entry_t entry) { struct lfs_dir_getter *get = p; + if ((entry.tag & get->mask) == (get->tag & get->mask)) { - if (get->entry) { - *get->entry = entry; - } + *get->entry = entry; return true; + } else if (lfs_tag_type(entry.tag) == LFS_TYPE_DELETE) { + if (lfs_tag_id(entry.tag) <= lfs_tag_id(get->tag)) { + get->tag += lfs_mktag(0, 1, 0); + } } return false; @@ -1215,6 +1222,7 @@ static int lfs_dir_getter(lfs_t *lfs, void *p, lfs_entry_t entry) { static int lfs_dir_get(lfs_t *lfs, lfs_dir_t *dir, uint32_t mask, lfs_entry_t *entry) { + uint16_t id = lfs_tag_id(entry->tag); int res = lfs_dir_traverse(lfs, dir, lfs_dir_getter, &(struct lfs_dir_getter){mask, entry->tag, entry}); if (res < 0) { @@ -1225,6 +1233,18 @@ static int lfs_dir_get(lfs_t *lfs, lfs_dir_t *dir, return LFS_ERR_NOENT; } + if (id == dir->moveid) { + int moved = lfs_moved(lfs, dir, dir->moveid); + if (moved < 0) { + return moved; + } + + if (moved) { + return LFS_ERR_NOENT; + } + } + + entry->tag = lfs_mktag(0, id, 0) | (entry->tag & 0xffe00fff); return 0; } @@ -1287,7 +1307,7 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_dir_t *dir, } err = lfs_dir_getbuffer(lfs, dir, 0x7ffff000, &(lfs_entry_t){ - lfs_mktag(LFS_TYPE_NAME, id, lfs->cfg->name_size+1), + lfs_mktag(LFS_TYPE_NAME, id, lfs->name_size+1), .u.buffer=info->name}); if (err) { return err; @@ -1401,24 +1421,24 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, break; } - if (lfs_pairisnull(dir->tail)) { + if (!dir->split) { return LFS_ERR_NOENT; } entry.u.pair[0] = dir->tail[0]; entry.u.pair[1] = dir->tail[1]; } - -// TODO handle moves -// // check that entry has not been moved -// if (entry->d.type & LFS_STRUCT_MOVED) { -// int moved = lfs_moved(lfs, &entry->d.u); -// if (moved < 0 || moved) { -// return (moved < 0) ? moved : LFS_ERR_NOENT; -// } -// -// entry->d.type &= ~LFS_STRUCT_MOVED; -// } + + if (find.id == dir->moveid) { + int moved = lfs_moved(lfs, dir, dir->moveid); + if (moved < 0) { + return moved; + } + + if (moved) { + return LFS_ERR_NOENT; + } + } *id = find.id; find.name += find.len; @@ -2319,11 +2339,15 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { return err; } + cwd.tail[0] = dir.pair[0]; + cwd.tail[1] = dir.pair[1]; err = lfs_dir_commit(lfs, &cwd, &(lfs_entrylist_t){ {lfs_mktag(LFS_TYPE_NAME, id, nlen), .u.buffer=(void*)path}, &(lfs_entrylist_t){ {lfs_mktag(LFS_TYPE_DIR | LFS_STRUCT_DIR, id, sizeof(dir.pair)), - .u.buffer=dir.pair}}}); + .u.buffer=dir.pair}, &(lfs_entrylist_t){ + {lfs_mktag(LFS_TYPE_SOFTTAIL, 0x1ff, sizeof(cwd.tail)), + .u.buffer=cwd.tail}}}}); // TODO need ack here? lfs_alloc_ack(lfs); @@ -3571,198 +3595,209 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { return lfs_dir_getinfo(lfs, &cwd, id, info); } -// -//int lfs_remove(lfs_t *lfs, const char *path) { -// // deorphan if we haven't yet, needed at most once after poweron -// if (!lfs->deorphaned) { -// int err = lfs_deorphan(lfs); -// if (err) { -// return err; -// } -// } -// -// lfs_dir_t cwd; -// int err = lfs_dir_fetch(lfs, &cwd, lfs->root); -// if (err) { -// return err; -// } -// -// lfs_entry_t entry; -// err = lfs_dir_find(lfs, &cwd, &entry, &path); -// if (err) { -// return err; -// } -// -// lfs_dir_t dir; -// if ((0xf & entry.d.type) == LFS_TYPE_DIR) { -// // must be empty before removal, checking size -// // without masking top bit checks for any case where -// // dir is not empty -// err = lfs_dir_fetch(lfs, &dir, entry.d.u.dir); -// if (err) { -// return err; -// } else if (dir.d.size != sizeof(dir.d)+4) { -// return LFS_ERR_NOTEMPTY; -// } -// } -// -// // remove the entry -// err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ -// {LFS_FROM_MEM, 0, entry.size, NULL, 0}}, 1); -// if (err) { -// return err; -// } -// -// // if we were a directory, find pred, replace tail -// if ((0xf & entry.d.type) == LFS_TYPE_DIR) { +int lfs_remove(lfs_t *lfs, const char *path) { + // deorphan if we haven't yet, needed at most once after poweron + if (!lfs->deorphaned) { + int err = lfs_deorphan(lfs); + if (err) { + return err; + } + } + + lfs_dir_t cwd; + int err = lfs_dir_fetch(lfs, &cwd, lfs->root); + if (err) { + return err; + } + + int16_t id; + err = lfs_dir_find(lfs, &cwd, &path, &id); + if (err) { + return err; + } + + // grab entry to see if we're dealing with a dir + lfs_entry_t entry; + err = lfs_dir_getentry(lfs, &cwd, 0x701ff000, + lfs_mktag(LFS_TYPE_REG, id, 0), &entry); + if (err) { + return err; + } + + if (lfs_tag_subtype(entry.tag) == LFS_TYPE_DIR) { + lfs_dir_t dir; + // must be empty before removal + err = lfs_dir_fetch(lfs, &dir, entry.u.pair); + if (err) { + return err; + } + + if (dir.count > 0 || dir.split) { + return LFS_ERR_NOTEMPTY; + } + } + + // delete the entry + err = lfs_dir_delete(lfs, &cwd, id); + if (err) { + return err; + } + + // if we were a directory, find pred, replace tail + // TODO can this just deorphan? + if (lfs_tag_subtype(entry.tag) == LFS_TYPE_DIR) { + err = lfs_deorphan(lfs); + if (err) { + return err; + } + } + +// if (lfs_tag_subtype(entry.tag) == LFS_TYPE_DIR) { // int res = lfs_pred(lfs, dir.pair, &cwd); // if (res < 0) { // return res; // } // // LFS_ASSERT(res); // must have pred -// cwd.d.tail[0] = dir.d.tail[0]; -// cwd.d.tail[1] = dir.d.tail[1]; +// cwd.tail[0] = dir.tail[0]; +// cwd.tail[1] = dir.tail[1]; // // err = lfs_dir_commit(lfs, &cwd, NULL, 0); // if (err) { // return err; // } // } -// -// return 0; -//} -// -//int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { -// // deorphan if we haven't yet, needed at most once after poweron -// if (!lfs->deorphaned) { -// int err = lfs_deorphan(lfs); -// if (err) { -// return err; -// } -// } -// -// // find old entry -// lfs_dir_t oldcwd; -// int err = lfs_dir_fetch(lfs, &oldcwd, lfs->root); -// if (err) { -// return err; -// } -// -// lfs_entry_t oldentry; -// err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath); -// if (err) { -// return err; -// } -// -// // allocate new entry -// lfs_dir_t newcwd; -// err = lfs_dir_fetch(lfs, &newcwd, lfs->root); -// if (err) { -// return err; -// } -// -// lfs_entry_t preventry; -// err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath); -// if (err && (err != LFS_ERR_NOENT || strchr(newpath, '/') != NULL)) { -// return err; -// } -// -// bool prevexists = (err != LFS_ERR_NOENT); -// bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0); -// -// // check that name fits -// lfs_size_t nlen = strlen(newpath); -// if (nlen > lfs->name_size) { -// return LFS_ERR_NAMETOOLONG; -// } -// -// if (oldentry.size - oldentry.d.nlen + nlen > lfs->cfg->block_size) { -// return LFS_ERR_NOSPC; -// } -// -// // must have same type -// if (prevexists && preventry.d.type != oldentry.d.type) { -// return LFS_ERR_ISDIR; -// } -// -// lfs_dir_t dir; -// if (prevexists && (0xf & preventry.d.type) == LFS_TYPE_DIR) { -// // must be empty before removal, checking size -// // without masking top bit checks for any case where -// // dir is not empty -// err = lfs_dir_fetch(lfs, &dir, preventry.d.u.dir); -// if (err) { -// return err; -// } else if (dir.d.size != sizeof(dir.d)+4) { -// return LFS_ERR_NOTEMPTY; -// } -// } -// -// // mark as moving -// oldentry.d.type |= LFS_STRUCT_MOVED; -// err = lfs_dir_set(lfs, &oldcwd, &oldentry, (struct lfs_region[]){ -// {LFS_FROM_MEM, 0, 1, &oldentry.d.type, 1}}, 1); -// oldentry.d.type &= ~LFS_STRUCT_MOVED; -// if (err) { -// return err; -// } -// -// // update pair if newcwd == oldcwd -// if (samepair) { -// newcwd = oldcwd; -// } -// -// // move to new location -// lfs_entry_t newentry = preventry; -// newentry.d = oldentry.d; -// newentry.d.type &= ~LFS_STRUCT_MOVED; -// newentry.d.nlen = nlen; -// newentry.size = prevexists ? preventry.size : 0; -// -// 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, prevexists ? preventry.size : 0, -// &(struct lfs_region_region){ -// oldcwd.pair[0], oldentry.off, (struct lfs_region[]){ -// {LFS_FROM_MEM, 0, 4, &newentry.d, 4}, -// {LFS_FROM_MEM, newsize-nlen, 0, newpath, nlen}}, 2}, -// newsize}}, 1); -// if (err) { -// return err; -// } -// -// // update pair if newcwd == oldcwd -// if (samepair) { -// oldcwd = newcwd; -// } -// -// // remove old entry -// err = lfs_dir_set(lfs, &oldcwd, &oldentry, (struct lfs_region[]){ -// {LFS_FROM_MEM, 0, oldentry.size, NULL, 0}}, 1); -// if (err) { -// return err; -// } -// -// // if we were a directory, find pred, replace tail -// if (prevexists && (0xf & preventry.d.type) == LFS_TYPE_DIR) { -// int res = lfs_pred(lfs, dir.pair, &newcwd); -// if (res < 0) { -// return res; -// } -// -// LFS_ASSERT(res); // must have pred -// newcwd.d.tail[0] = dir.d.tail[0]; -// newcwd.d.tail[1] = dir.d.tail[1]; -// -// err = lfs_dir_commit(lfs, &newcwd, NULL, 0); -// if (err) { -// return err; -// } -// } -// -// return 0; -//} + + return 0; +} + +int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { + // deorphan if we haven't yet, needed at most once after poweron + if (!lfs->deorphaned) { + int err = lfs_deorphan(lfs); + if (err) { + return err; + } + } + + // find old entry + lfs_dir_t oldcwd; + int16_t oldid; + int err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldid); + if (err) { + return err; + } + + lfs_entry_t oldentry; + err = lfs_dir_getentry(lfs, &oldcwd, 0x701ff000, + lfs_mktag(LFS_TYPE_REG, oldid, 0), &oldentry); + if (err) { + return err; + } + + // find new entry + lfs_dir_t newcwd; + int16_t newid; + err = lfs_dir_find(lfs, &newcwd, &newpath, &newid); + if (err && err != LFS_ERR_NOENT) { + return err; + } + + bool prevexists = (err != LFS_ERR_NOENT); + bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0); + lfs_entry_t preventry; + if (prevexists) { + // get prev entry, check that we have same type + err = lfs_dir_getentry(lfs, &newcwd, 0x701ff000, + lfs_mktag(LFS_TYPE_REG, newid, 0), &preventry); + if (err) { + return err; + } + + if (lfs_tag_subtype(preventry.tag) != lfs_tag_subtype(oldentry.tag)) { + return LFS_ERR_ISDIR; + } + + if (lfs_tag_subtype(preventry.tag) == LFS_TYPE_DIR) { + lfs_dir_t prevdir; + // must be empty before removal + err = lfs_dir_fetch(lfs, &prevdir, preventry.u.pair); + if (err) { + return err; + } + + if (prevdir.count > 0 || prevdir.split) { + return LFS_ERR_NOTEMPTY; + } + } + } else { + // check that name fits + lfs_size_t nlen = strlen(newpath); + if (nlen > lfs->name_size) { + return LFS_ERR_NAMETOOLONG; + } + + // get next id + err = lfs_dir_append(lfs, &newcwd, &newid); + if (err) { + return err; + } + } + + // mark as moving + //printf("RENAME MOVE %d %d %d\n", oldcwd.pair[0], oldcwd.pair[1], oldid); + err = lfs_dir_commit(lfs, &oldcwd, &(lfs_entrylist_t){ + {lfs_mktag(LFS_TYPE_MOVE, oldid, 0)}}); + if (err) { + return err; + } + + if (samepair) { + // update pair if newcwd == oldcwd + newcwd = oldcwd; + } + + // move to new location + // TODO NAME????? + // TODO HAH, move doesn't want to override things (due + // to its use in compaction), but that's _exactly_ what we want here + err = lfs_dir_commitwith(lfs, &newcwd, lfs_commit_move, + &(struct lfs_commit_move){.dir=&oldcwd, .id={oldid, newid}}); + if (err) { + return err; + } + // TODO NONONONONO + // TODO also don't call strlen twice (see prev name check) + err = lfs_dir_commit(lfs, &newcwd, &(lfs_entrylist_t){ + {lfs_mktag(LFS_TYPE_NAME, newid, strlen(newpath)), + .u.buffer=(void*)newpath}}); + if (err) { + return err; + } + + if (samepair) { + // update pair if newcwd == oldcwd + oldcwd = newcwd; + } + + // remove old entry + //printf("RENAME DELETE %d %d %d\n", oldcwd.pair[0], oldcwd.pair[1], oldid); + err = lfs_dir_delete(lfs, &oldcwd, oldid); + if (err) { + return err; + } + + // if we were a directory, find pred, replace tail + // TODO can this just deorphan? + if (prevexists && lfs_tag_subtype(preventry.tag) == LFS_TYPE_DIR) { + err = lfs_deorphan(lfs); + if (err) { + return err; + } + } + + return 0; +} //int lfs_getattrs(lfs_t *lfs, const char *path, // const struct lfs_attr *attrs, int count) { @@ -3940,9 +3975,9 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { .block_size = lfs->cfg->block_size, .block_count = lfs->cfg->block_count, - .inline_size = lfs->cfg->inline_size, - .attrs_size = lfs->cfg->attrs_size, - .name_size = lfs->cfg->name_size, + .inline_size = lfs->inline_size, + .attrs_size = lfs->attrs_size, + .name_size = lfs->name_size, }; dir.count += 1; @@ -4088,7 +4123,6 @@ int lfs_fs_traverse(lfs_t *lfs, return err; } } - } } @@ -4240,6 +4274,7 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t pair[2], return err; } + // TODO make this O(n) by using fetchwith to match the pointers for (uint16_t id = 0; id < parent->count; id++) { int err = lfs_dir_getentry(lfs, parent, 0x43dff000, lfs_mktag(LFS_STRUCT_DIR, id, 0), entry); @@ -4295,25 +4330,40 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], return false; } */ -static int lfs_moved(lfs_t *lfs, const lfs_block_t pair[2]) { +static int lfs_moved(lfs_t *lfs, lfs_dir_t *fromdir, uint16_t fromid) { + // grab entry pair we're looking for + fromdir->moveid = -1; + lfs_entry_t fromentry; + int err = lfs_dir_getentry(lfs, fromdir, 0x43dff000, + lfs_mktag(LFS_STRUCT_DIR, fromid, 0), &fromentry); + fromdir->moveid = fromid; + if (err) { + return err; + } + // skip superblock - lfs_dir_t dir; - int err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); + lfs_dir_t todir; + err = lfs_dir_fetch(lfs, &todir, (const lfs_block_t[2]){0, 1}); if (err) { return err; } // iterate over all directory directory entries - while (!lfs_pairisnull(dir.tail)) { - int err = lfs_dir_fetch(lfs, &dir, dir.tail); + while (!lfs_pairisnull(todir.tail)) { + int err = lfs_dir_fetch(lfs, &todir, todir.tail); if (err) { return err; } - for (int id = 0; id < dir.count; id++) { - lfs_entry_t entry; - int err = lfs_dir_getentry(lfs, &dir, 0x43dff000, - lfs_mktag(LFS_STRUCT_DIR, id, 0), &entry); + for (int toid = 0; toid < todir.count; toid++) { + if (lfs_paircmp(todir.pair, fromdir->pair) == 0 && + toid == fromid) { + continue; + } + + lfs_entry_t toentry; + int err = lfs_dir_getentry(lfs, &todir, 0x43dff000, + lfs_mktag(LFS_STRUCT_DIR, toid, 0), &toentry); if (err) { if (err == LFS_ERR_NOENT) { continue; @@ -4321,16 +4371,7 @@ static int lfs_moved(lfs_t *lfs, const lfs_block_t pair[2]) { return err; } - err = lfs_dir_get(lfs, &dir, 0x7ffff000, &(lfs_entry_t){ - lfs_mktag(LFS_TYPE_MOVE, id, 0)}); - if (err != LFS_ERR_NOENT) { - if (!err) { - continue; - } - return err; - } - - if (lfs_paircmp(entry.u.pair, pair) == 0) { + if (lfs_paircmp(toentry.u.pair, fromentry.u.pair) == 0) { return true; } } diff --git a/lfs.h b/lfs.h index a089125..5cb8377 100644 --- a/lfs.h +++ b/lfs.h @@ -114,6 +114,11 @@ enum lfs_type { LFS_STRUCT_INLINE = 0x000, LFS_STRUCT_CTZ = 0x004, LFS_STRUCT_DIR = 0x008, + + // internal sources + LFS_FROM_REGION = 0x000, + LFS_FROM_DISK = 0x001, + LFS_FROM_MOVE = 0x002, }; // File open flags @@ -313,6 +318,7 @@ typedef struct lfs_dir { uint16_t count; bool erased; bool split; + bool stop_at_commit; // TODO hmmm int16_t moveid; uint16_t id; diff --git a/tests/test_dirs.sh b/tests/test_dirs.sh index d5303cc..c9cc669 100755 --- a/tests/test_dirs.sh +++ b/tests/test_dirs.sh @@ -117,8 +117,6 @@ tests/test.py << TEST for (int i = 0; i < $LARGESIZE; i++) { sprintf((char*)buffer, "test%d", i); lfs_dir_read(&lfs, &dir[0], &info) => 1; - printf("nameee %s\n", info.name); - printf("expect %s\n", (char*)buffer); strcmp(info.name, (char*)buffer) => 0; info.type => LFS_TYPE_DIR; } @@ -154,6 +152,8 @@ tests/test.py << TEST strcmp(info.name, "..") => 0; info.type => LFS_TYPE_DIR; lfs_dir_read(&lfs, &dir[0], &info) => 1; + printf("nameee \"%s\"\n", info.name); + printf("expect \"%s\"\n", "burito"); strcmp(info.name, "burito") => 0; info.type => LFS_TYPE_REG; lfs_dir_read(&lfs, &dir[0], &info) => 1;