Fixed issue with creating files named "littlefs"

A rather humorous issue, we accidentally ended up mixing our file
namespace with our superblocks. This meant if we created a file named
"littlefs" it would reference the superblock and all sorts of things
would break.

Fixing this also highlighted another issue, the fact that the superblock
always needs to come before any file entries in the directory. I didn't
account for this in the initial B-tree design, but we need a higher
ordering for superblocks + children + files than just name. To fix this
I added ordering information in the 2 bits currently unused in the tag
type. Though note that the size of these fields are flexible.

9-bit type field:
[---      9      ---]
[1|- 3 -|- 2 -|- 3 -]
 ^   ^     ^     ^- type-specific info
 |   |     \------- ordering info
 |   \------------- subtype
 \----------------- user bit
This commit is contained in:
Christopher Haster
2018-10-04 16:25:19 -05:00
parent aeca7667b3
commit 97a7191814
4 changed files with 50 additions and 40 deletions

62
lfs.c
View File

@@ -287,7 +287,7 @@ static inline uint16_t lfs_tag_type(lfs_tag_t tag) {
}
static inline uint16_t lfs_tag_subtype(lfs_tag_t tag) {
return ((tag & 0x7c000000) >> 26) << 4;
return ((tag & 0x78000000) >> 26) << 4;
}
static inline uint16_t lfs_tag_id(lfs_tag_t tag) {
@@ -531,7 +531,7 @@ static int lfs_dir_traverse(lfs_t *lfs,
}
}
if (lfs_tag_subtype(tag) == LFS_TYPE_NAME) {
if (lfs_tag_subtype(tag) == LFS_TYPE_CREATE) {
// found where something was created
if (lfs_tag_id(tag) == lfs_tag_id(matchtag - matchdiff)) {
break;
@@ -673,7 +673,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
}
// check for special tags
if (lfs_tag_subtype(tag) == LFS_TYPE_NAME) {
if (lfs_tag_subtype(tag) == LFS_TYPE_CREATE) {
temp.count += 1;
if (tempfoundtag &&
@@ -822,7 +822,7 @@ static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
static int lfs_dir_getglobals(lfs_t *lfs, const lfs_mdir_t *dir,
struct lfs_globals *globals) {
struct lfs_globals locals;
lfs_stag_t res = lfs_dir_get(lfs, dir, 0x7c000000,
lfs_stag_t res = lfs_dir_get(lfs, dir, 0x78000000,
LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals);
if (res < 0 && res != LFS_ERR_NOENT) {
return res;
@@ -848,7 +848,7 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
}
lfs_stag_t tag = lfs_dir_get(lfs, dir, 0x7c3fe000,
LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name);
LFS_MKTAG(LFS_TYPE_DIR, id, lfs->name_max+1), info->name);
if (tag < 0) {
return tag;
}
@@ -856,7 +856,7 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
info->type = lfs_tag_type(tag);
struct lfs_ctz ctz;
tag = lfs_dir_get(lfs, dir, 0x7c3fe000,
tag = lfs_dir_get(lfs, dir, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz);
if (tag < 0) {
return tag;
@@ -893,13 +893,12 @@ static int lfs_dir_find_match(void *data,
}
// found match?
if (res == 0 && (name->size == lfs_tag_size(tag) ||
lfs_tag_type(tag) == LFS_TYPE_SUPERBLOCK)) {
if (res == 0 && name->size == lfs_tag_size(tag)) {
return tag;
}
// a greater name found, exit early
if (res == 2 && lfs_tag_type(tag) != LFS_TYPE_SUPERBLOCK) {
if (res > 1 && lfs_tag_type(tag) != LFS_TYPE_SUPERBLOCK) {
return tag | 0x1fff;
}
@@ -969,7 +968,7 @@ nextname:
// grab the entry data
if (lfs_tag_id(tag) != 0x1ff) {
lfs_stag_t res = lfs_dir_get(lfs, dir, 0x7c3fe000,
lfs_stag_t res = lfs_dir_get(lfs, dir, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail);
if (res < 0) {
return res;
@@ -980,7 +979,7 @@ nextname:
// find entry matching name
while (true) {
tag = lfs_dir_fetchmatch(lfs, dir, dir->tail,
0x7c000000, LFS_MKTAG(LFS_TYPE_NAME, 0, namelen),
0x7c000000, LFS_MKTAG(LFS_TYPE_DIR, 0, namelen),
lfs_dir_find_match, &(struct lfs_dir_find_match){
lfs, name, namelen});
if (tag < 0) {
@@ -1059,7 +1058,7 @@ static int lfs_commit_move_match(void *data,
struct lfs_commit *commit = move->commit;
if (move->pass == 0) {
if (lfs_tag_subtype(tag) != LFS_TYPE_NAME) {
if (lfs_tag_subtype(tag) != LFS_TYPE_CREATE) {
return 0;
}
} else {
@@ -1068,7 +1067,7 @@ static int lfs_commit_move_match(void *data,
.pair[0] = commit->block,
.off = commit->off,
.etag = commit->ptag}, NULL,
lfs_tag_isuser(tag) ? 0x7fffe000 : 0x7c3fe000, tag, 0,
lfs_tag_isuser(tag) ? 0x7fffe000 : 0x783fe000, tag, 0,
lfs_dir_get_match, &(struct lfs_dir_get_match){
lfs, NULL, 0, true});
if (res < 0 && res != LFS_ERR_NOENT) {
@@ -1578,7 +1577,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
lfs_tag_t createtag = 0xffffffff;
int attrcount = 0;
for (const struct lfs_mattr *a = attrs; a; a = a->next) {
if (lfs_tag_subtype(a->tag) == LFS_TYPE_NAME) {
if (lfs_tag_subtype(a->tag) == LFS_TYPE_CREATE) {
dir->count += 1;
createtag = a->tag;
} else if (lfs_tag_subtype(a->tag) == LFS_TYPE_DELETE) {
@@ -1795,7 +1794,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
pair[1] = lfs->root[1];
} else {
// get dir pair from parent
lfs_stag_t res = lfs_dir_get(lfs, &dir->m, 0x7c3fe000,
lfs_stag_t res = lfs_dir_get(lfs, &dir->m, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
if (res < 0) {
return res;
@@ -2190,7 +2189,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
file->flags |= LFS_F_DIRTY;
} else {
// try to load what's on disk, if it's inlined we'll fix it later
tag = lfs_dir_get(lfs, &file->m, 0x7c3fe000,
tag = lfs_dir_get(lfs, &file->m, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz);
if (tag < 0) {
err = tag;
@@ -2245,7 +2244,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
// don't always read (may be new/trunc file)
if (file->ctz.size > 0) {
lfs_stag_t res = lfs_dir_get(lfs, &file->m, 0x7c3fe000,
lfs_stag_t res = lfs_dir_get(lfs, &file->m, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, file->id, file->ctz.size),
file->cache.buffer);
if (res < 0) {
@@ -2800,7 +2799,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
if (lfs_tag_type(tag) == LFS_TYPE_DIR) {
// must be empty before removal
lfs_block_t pair[2];
lfs_stag_t res = lfs_dir_get(lfs, &cwd, 0x7c3fe000,
lfs_stag_t res = lfs_dir_get(lfs, &cwd, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
if (res < 0) {
return res;
@@ -2880,7 +2879,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} else if (lfs_tag_type(prevtag) == LFS_TYPE_DIR) {
// must be empty before removal
lfs_block_t prevpair[2];
lfs_stag_t res = lfs_dir_get(lfs, &newcwd, 0x7c3fe000,
lfs_stag_t res = lfs_dir_get(lfs, &newcwd, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair);
if (res < 0) {
return res;
@@ -3139,9 +3138,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
// write one superblock
lfs_superblock_t superblock = {
.magic = {"littlefs"},
.version = LFS_DISK_VERSION,
.version = LFS_DISK_VERSION,
.block_size = lfs->cfg->block_size,
.block_count = lfs->cfg->block_count,
.name_max = lfs->name_max,
@@ -3151,8 +3148,10 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_superblock_tole32(&superblock);
err = lfs_dir_commit(lfs, &root,
LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, &superblock, sizeof(superblock),
NULL));
LFS_MKATTR(LFS_TYPE_INLINESTRUCT, 0,
&superblock, sizeof(superblock),
LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, "littlefs", 8,
NULL)));
if (err) {
goto cleanup;
}
@@ -3178,7 +3177,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_mdir_t dir = {.tail = {0, 1}};
while (!lfs_pair_isnull(dir.tail)) {
// fetch next block in tail list
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, 0x7fc00000,
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, 0x7fffe000,
LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8),
lfs_dir_find_match, &(struct lfs_dir_find_match){
lfs, "littlefs", 8});
@@ -3195,8 +3194,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
// grab superblock
lfs_superblock_t superblock;
tag = lfs_dir_get(lfs, &dir, 0x7f800000,
LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, sizeof(superblock)),
tag = lfs_dir_get(lfs, &dir, 0x7fffe000,
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
&superblock);
if (tag < 0) {
err = tag;
@@ -3248,7 +3247,6 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->attr_max = superblock.attr_max;
}
}
// has globals?
@@ -3258,6 +3256,12 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
}
}
// found superblock?
if (lfs_pair_isnull(lfs->root)) {
err = LFS_ERR_INVAL;
goto cleanup;
}
// update littlefs with globals
lfs_global_fromle32(&lfs->locals);
lfs_global_xor(&lfs->globals, &lfs->locals);
@@ -3306,7 +3310,7 @@ int lfs_fs_traverse(lfs_t *lfs,
for (uint16_t id = 0; id < dir.count; id++) {
struct lfs_ctz ctz;
lfs_stag_t tag = lfs_dir_get(lfs, &dir, 0x7c3fe000,
lfs_stag_t tag = lfs_dir_get(lfs, &dir, 0x783fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz);
if (tag < 0) {
if (tag == LFS_ERR_NOENT) {

12
lfs.h
View File

@@ -88,12 +88,12 @@ enum lfs_error {
// File types
enum lfs_type {
// file types
LFS_TYPE_REG = 0x002,
LFS_TYPE_DIR = 0x003,
LFS_TYPE_REG = 0x011,
LFS_TYPE_DIR = 0x010,
// internally used types
LFS_TYPE_USERATTR = 0x100,
LFS_TYPE_NAME = 0x000,
LFS_TYPE_CREATE = 0x000,
LFS_TYPE_DELETE = 0x020,
LFS_TYPE_STRUCT = 0x040,
LFS_TYPE_TAIL = 0x080,
@@ -110,8 +110,8 @@ enum lfs_type {
// internal chip sources
LFS_FROM_MEM = 0x000,
LFS_FROM_DISK = 0x200,
LFS_FROM_MOVE = 0x0c1,
LFS_FROM_USERATTRS = 0x0c2,
LFS_FROM_MOVE = 0x061,
LFS_FROM_USERATTRS = 0x062,
};
// File open flags
@@ -339,9 +339,7 @@ typedef struct lfs_file {
} lfs_file_t;
typedef struct lfs_superblock {
char magic[8];
uint32_t version;
lfs_size_t block_size;
lfs_size_t block_count;

View File

@@ -4,8 +4,8 @@ import struct
import binascii
TYPES = {
(0x1ff, 0x002): 'reg',
(0x1ff, 0x003): 'dir',
(0x1ff, 0x011): 'create reg',
(0x1ff, 0x010): 'create dir',
(0x1ff, 0x001): 'superblock',
(0x1ff, 0x020): 'delete',
(0x1f0, 0x0e0): 'globals',
@@ -23,10 +23,10 @@ def typeof(type):
mask = 0x1ff & ~((1 << prefix)-1)
if (mask, type & mask) in TYPES:
return TYPES[mask, type & mask] + (
' [%0*x]' % (prefix/4, type & ((1 << prefix)-1))
' %0*x' % (prefix/4, type & ((1 << prefix)-1))
if prefix else '')
else:
return '[%02x]' % type
return '%02x' % type
def main(*blocks):
# find most recent block

View File

@@ -139,6 +139,14 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
echo "--- Superblock conflict test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "littlefs") => 0;
lfs_remove(&lfs, "littlefs") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Max path test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;