Compare commits

...

6 Commits
v1.4 ... v1.5

Author SHA1 Message Date
Christopher Haster
84adead98b Merge pull request #80 from FreddieChopin/fix-memory-leaks
Fix memory leaks
2018-07-19 17:30:48 -05:00
Freddie Chopin
0422c55b81 Fix memory leaks in lfs_mount and lfs_format
Squashed:
- Change lfs_deinit() return to void to simplify error handling
- Move lfs_deinit() before lfs_init()
- Fix memory leaks in lfs_init()
- Fix memory leaks in lfs_format()
- Fix memory leaks in lfs_mount()
2018-07-19 16:54:38 -05:00
Christopher Haster
11ad3a2414 Merge pull request #76 from ARMmbed/fix-corrupt-read
Add handling for corrupt as initial state of blocks
2018-07-17 20:32:33 -05:00
Christopher Haster
16318d003f Merge pull request #58 from dpgeorge/file-open-no-malloc
Added possibility to open multiple files with LFS_NO_MALLOC enabled
2018-07-17 20:31:20 -05:00
Damien George
961fab70c3 Added file config structure and lfs_file_opencfg
The optional config structure options up the possibility of adding
file-level configuration in a backwards compatible manner.

Also adds possibility to open multiple files with LFS_NO_MALLOC
enabled thanks to dpgeorge

Also bumped minor version to v1.5
2018-07-17 18:32:18 -05:00
Christopher Haster
041e90a1ca Added handling for corrupt as initial state of blocks
Before this, littlefs incorrectly assumed corrupt blocks were only the result
of our own modification. This would be fine for most cases of freshly
erased storage, but for storage with block-level ECC this wasn't always
true.

Fortunately, it's quite easy for littlefs to handle this case correctly,
as long as corrupt storage always reports that it is corrupt, which for
most forms of ECC is the case unless we perform a write on the storage.

found by rojer
2018-07-16 15:33:52 -05:00
2 changed files with 98 additions and 43 deletions

107
lfs.c
View File

@@ -417,11 +417,14 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
// rather than clobbering one of the blocks we just pretend // rather than clobbering one of the blocks we just pretend
// the revision may be valid // the revision may be valid
int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4); int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4);
dir->d.rev = lfs_fromle32(dir->d.rev); if (err && err != LFS_ERR_CORRUPT) {
if (err) {
return err; return err;
} }
if (err != LFS_ERR_CORRUPT) {
dir->d.rev = lfs_fromle32(dir->d.rev);
}
// set defaults // set defaults
dir->d.rev += 1; dir->d.rev += 1;
dir->d.size = sizeof(dir->d)+4; dir->d.size = sizeof(dir->d)+4;
@@ -445,6 +448,9 @@ static int lfs_dir_fetch(lfs_t *lfs,
int err = lfs_bd_read(lfs, tpair[i], 0, &test, sizeof(test)); int err = lfs_bd_read(lfs, tpair[i], 0, &test, sizeof(test));
lfs_dir_fromle32(&test); lfs_dir_fromle32(&test);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) {
continue;
}
return err; return err;
} }
@@ -464,6 +470,9 @@ static int lfs_dir_fetch(lfs_t *lfs,
err = lfs_bd_crc(lfs, tpair[i], sizeof(test), err = lfs_bd_crc(lfs, tpair[i], sizeof(test),
(0x7fffffff & test.size) - sizeof(test), &crc); (0x7fffffff & test.size) - sizeof(test), &crc);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) {
continue;
}
return err; return err;
} }
@@ -1282,8 +1291,9 @@ static int lfs_ctz_traverse(lfs_t *lfs,
/// Top level file operations /// /// Top level file operations ///
int lfs_file_open(lfs_t *lfs, lfs_file_t *file, int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) { const char *path, int flags,
const struct lfs_file_config *cfg) {
// deorphan if we haven't yet, needed at most once after poweron // deorphan if we haven't yet, needed at most once after poweron
if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned) { if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned) {
int err = lfs_deorphan(lfs); int err = lfs_deorphan(lfs);
@@ -1323,6 +1333,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
} }
// setup file struct // setup file struct
file->cfg = cfg;
file->pair[0] = cwd.pair[0]; file->pair[0] = cwd.pair[0];
file->pair[1] = cwd.pair[1]; file->pair[1] = cwd.pair[1];
file->poff = entry.off; file->poff = entry.off;
@@ -1340,7 +1351,10 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
} }
// allocate buffer if needed // allocate buffer if needed
if (lfs->cfg->file_buffer) { file->cache.block = 0xffffffff;
if (file->cfg && file->cfg->buffer) {
file->cache.buffer = file->cfg->buffer;
} else if (lfs->cfg->file_buffer) {
if (lfs->files) { if (lfs->files) {
// already in use // already in use
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
@@ -1368,6 +1382,11 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
return 0; return 0;
} }
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) {
return lfs_file_opencfg(lfs, file, path, flags, NULL);
}
int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
int err = lfs_file_sync(lfs, file); int err = lfs_file_sync(lfs, file);
@@ -1380,7 +1399,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
} }
// clean up memory // clean up memory
if (!lfs->cfg->file_buffer) { if (!(file->cfg && file->cfg->buffer) && !lfs->cfg->file_buffer) {
lfs_free(file->cache.buffer); lfs_free(file->cache.buffer);
} }
@@ -1997,6 +2016,21 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
/// Filesystem operations /// /// Filesystem operations ///
static void lfs_deinit(lfs_t *lfs) {
// free allocated memory
if (!lfs->cfg->read_buffer) {
lfs_free(lfs->rcache.buffer);
}
if (!lfs->cfg->prog_buffer) {
lfs_free(lfs->pcache.buffer);
}
if (!lfs->cfg->lookahead_buffer) {
lfs_free(lfs->free.buffer);
}
}
static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->cfg = cfg; lfs->cfg = cfg;
@@ -2006,7 +2040,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
} else { } else {
lfs->rcache.buffer = lfs_malloc(lfs->cfg->read_size); lfs->rcache.buffer = lfs_malloc(lfs->cfg->read_size);
if (!lfs->rcache.buffer) { if (!lfs->rcache.buffer) {
return LFS_ERR_NOMEM; goto cleanup;
} }
} }
@@ -2016,7 +2050,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
} else { } else {
lfs->pcache.buffer = lfs_malloc(lfs->cfg->prog_size); lfs->pcache.buffer = lfs_malloc(lfs->cfg->prog_size);
if (!lfs->pcache.buffer) { if (!lfs->pcache.buffer) {
return LFS_ERR_NOMEM; goto cleanup;
} }
} }
@@ -2032,7 +2066,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
} else { } else {
lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead/8); lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead/8);
if (!lfs->free.buffer) { if (!lfs->free.buffer) {
return LFS_ERR_NOMEM; goto cleanup;
} }
} }
@@ -2052,23 +2086,10 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->deorphaned = false; lfs->deorphaned = false;
return 0; return 0;
}
static int lfs_deinit(lfs_t *lfs) { cleanup:
// free allocated memory lfs_deinit(lfs);
if (!lfs->cfg->read_buffer) { return LFS_ERR_NOMEM;
lfs_free(lfs->rcache.buffer);
}
if (!lfs->cfg->prog_buffer) {
lfs_free(lfs->pcache.buffer);
}
if (!lfs->cfg->lookahead_buffer) {
lfs_free(lfs->free.buffer);
}
return 0;
} }
int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
@@ -2088,19 +2109,19 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_dir_t superdir; lfs_dir_t superdir;
err = lfs_dir_alloc(lfs, &superdir); err = lfs_dir_alloc(lfs, &superdir);
if (err) { if (err) {
return err; goto cleanup;
} }
// write root directory // write root directory
lfs_dir_t root; lfs_dir_t root;
err = lfs_dir_alloc(lfs, &root); err = lfs_dir_alloc(lfs, &root);
if (err) { if (err) {
return err; goto cleanup;
} }
err = lfs_dir_commit(lfs, &root, NULL, 0); err = lfs_dir_commit(lfs, &root, NULL, 0);
if (err) { if (err) {
return err; goto cleanup;
} }
lfs->root[0] = root.pair[0]; lfs->root[0] = root.pair[0];
@@ -2131,24 +2152,28 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
&superblock.d, sizeof(superblock.d)} &superblock.d, sizeof(superblock.d)}
}, 1); }, 1);
if (err && err != LFS_ERR_CORRUPT) { if (err && err != LFS_ERR_CORRUPT) {
return err; goto cleanup;
} }
valid = valid || !err; valid = valid || !err;
} }
if (!valid) { if (!valid) {
return LFS_ERR_CORRUPT; err = LFS_ERR_CORRUPT;
goto cleanup;
} }
// sanity check that fetch works // sanity check that fetch works
err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1}); err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1});
if (err) { if (err) {
return err; goto cleanup;
} }
lfs_alloc_ack(lfs); lfs_alloc_ack(lfs);
return lfs_deinit(lfs);
cleanup:
lfs_deinit(lfs);
return err;
} }
int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
@@ -2168,7 +2193,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_superblock_t superblock; lfs_superblock_t superblock;
err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
if (err && err != LFS_ERR_CORRUPT) { if (err && err != LFS_ERR_CORRUPT) {
return err; goto cleanup;
} }
if (!err) { if (!err) {
@@ -2176,7 +2201,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
&superblock.d, sizeof(superblock.d)); &superblock.d, sizeof(superblock.d));
lfs_superblock_fromle32(&superblock.d); lfs_superblock_fromle32(&superblock.d);
if (err) { if (err) {
return err; goto cleanup;
} }
lfs->root[0] = superblock.d.root[0]; lfs->root[0] = superblock.d.root[0];
@@ -2185,7 +2210,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) {
LFS_ERROR("Invalid superblock at %d %d", 0, 1); LFS_ERROR("Invalid superblock at %d %d", 0, 1);
return LFS_ERR_CORRUPT; err = LFS_ERR_CORRUPT;
goto cleanup;
} }
uint16_t major_version = (0xffff & (superblock.d.version >> 16)); uint16_t major_version = (0xffff & (superblock.d.version >> 16));
@@ -2193,14 +2219,21 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
if ((major_version != LFS_DISK_VERSION_MAJOR || if ((major_version != LFS_DISK_VERSION_MAJOR ||
minor_version > LFS_DISK_VERSION_MINOR)) { minor_version > LFS_DISK_VERSION_MINOR)) {
LFS_ERROR("Invalid version %d.%d", major_version, minor_version); LFS_ERROR("Invalid version %d.%d", major_version, minor_version);
return LFS_ERR_INVAL; err = LFS_ERR_INVAL;
goto cleanup;
} }
return 0; return 0;
cleanup:
lfs_deinit(lfs);
return err;
} }
int lfs_unmount(lfs_t *lfs) { int lfs_unmount(lfs_t *lfs) {
return lfs_deinit(lfs); lfs_deinit(lfs);
return 0;
} }

34
lfs.h
View File

@@ -21,7 +21,7 @@ extern "C"
// Software library version // Software library version
// Major (top-nibble), incremented on backwards incompatible changes // Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions // Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00010004 #define LFS_VERSION 0x00010005
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
@@ -167,6 +167,12 @@ struct lfs_config {
void *file_buffer; void *file_buffer;
}; };
// Optional configuration provided during lfs_file_opencfg
struct lfs_file_config {
// Optional, statically allocated buffer for files. Must be program sized.
// If NULL, malloc will be used by default.
void *buffer;
};
// File info structure // File info structure
struct lfs_info { struct lfs_info {
@@ -214,6 +220,7 @@ typedef struct lfs_file {
lfs_block_t head; lfs_block_t head;
lfs_size_t size; lfs_size_t size;
const struct lfs_file_config *cfg;
uint32_t flags; uint32_t flags;
lfs_off_t pos; lfs_off_t pos;
lfs_block_t block; lfs_block_t block;
@@ -281,7 +288,8 @@ typedef struct lfs {
// Format a block device with the littlefs // Format a block device with the littlefs
// //
// Requires a littlefs object and config struct. This clobbers the littlefs // Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted. // object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
// //
// Returns a negative error code on failure. // Returns a negative error code on failure.
int lfs_format(lfs_t *lfs, const struct lfs_config *config); int lfs_format(lfs_t *lfs, const struct lfs_config *config);
@@ -290,7 +298,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config);
// //
// Requires a littlefs object and config struct. Multiple filesystems // Requires a littlefs object and config struct. Multiple filesystems
// may be mounted simultaneously with multiple littlefs objects. Both // may be mounted simultaneously with multiple littlefs objects. Both
// lfs and config must be allocated while mounted. // lfs and config must be allocated while mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
// //
// Returns a negative error code on failure. // Returns a negative error code on failure.
int lfs_mount(lfs_t *lfs, const struct lfs_config *config); int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
@@ -328,14 +337,27 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
// Open a file // Open a file
// //
// The mode that the file is opened in is determined // The mode that the file is opened in is determined by the flags, which
// by the flags, which are values from the enum lfs_open_flags // are values from the enum lfs_open_flags that are bitwise-ored together.
// that are bitwise-ored together.
// //
// Returns a negative error code on failure. // Returns a negative error code on failure.
int lfs_file_open(lfs_t *lfs, lfs_file_t *file, int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags); const char *path, int flags);
// Open a file with extra configuration
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs_open_flags that are bitwise-ored together.
//
// The config struct provides additional config options per file as described
// above. The config struct must be allocated while the file is open, and the
// config struct must be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags,
const struct lfs_file_config *config);
// Close a file // Close a file
// //
// Any pending writes are written out to storage as though // Any pending writes are written out to storage as though