mirror of
https://github.com/eledio-devices/thirdparty-littlefs.git
synced 2025-11-01 00:38:29 +01:00
Renamed all prefixes to include the major version
This is needed to allow compilation of multiple versions in the same binary. Also note that the FUSE testing was removed because of related name issues. ./scripts/prefix.py lfs2
This commit is contained in:
324
emubd/lfs2_emubd.c
Normal file
324
emubd/lfs2_emubd.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Block device emulated on standard files
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include "emubd/lfs2_emubd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
// Emulated block device utils
|
||||
static inline void lfs2_emubd_tole32(lfs2_emubd_t *emu) {
|
||||
emu->cfg.read_size = lfs2_tole32(emu->cfg.read_size);
|
||||
emu->cfg.prog_size = lfs2_tole32(emu->cfg.prog_size);
|
||||
emu->cfg.block_size = lfs2_tole32(emu->cfg.block_size);
|
||||
emu->cfg.block_count = lfs2_tole32(emu->cfg.block_count);
|
||||
|
||||
emu->stats.read_count = lfs2_tole32(emu->stats.read_count);
|
||||
emu->stats.prog_count = lfs2_tole32(emu->stats.prog_count);
|
||||
emu->stats.erase_count = lfs2_tole32(emu->stats.erase_count);
|
||||
|
||||
for (unsigned i = 0; i < sizeof(emu->history.blocks) /
|
||||
sizeof(emu->history.blocks[0]); i++) {
|
||||
emu->history.blocks[i] = lfs2_tole32(emu->history.blocks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void lfs2_emubd_fromle32(lfs2_emubd_t *emu) {
|
||||
emu->cfg.read_size = lfs2_fromle32(emu->cfg.read_size);
|
||||
emu->cfg.prog_size = lfs2_fromle32(emu->cfg.prog_size);
|
||||
emu->cfg.block_size = lfs2_fromle32(emu->cfg.block_size);
|
||||
emu->cfg.block_count = lfs2_fromle32(emu->cfg.block_count);
|
||||
|
||||
emu->stats.read_count = lfs2_fromle32(emu->stats.read_count);
|
||||
emu->stats.prog_count = lfs2_fromle32(emu->stats.prog_count);
|
||||
emu->stats.erase_count = lfs2_fromle32(emu->stats.erase_count);
|
||||
|
||||
for (unsigned i = 0; i < sizeof(emu->history.blocks) /
|
||||
sizeof(emu->history.blocks[0]); i++) {
|
||||
emu->history.blocks[i] = lfs2_fromle32(emu->history.blocks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Block device emulated on existing filesystem
|
||||
int lfs2_emubd_create(const struct lfs2_config *cfg, const char *path) {
|
||||
lfs2_emubd_t *emu = cfg->context;
|
||||
emu->cfg.read_size = cfg->read_size;
|
||||
emu->cfg.prog_size = cfg->prog_size;
|
||||
emu->cfg.block_size = cfg->block_size;
|
||||
emu->cfg.block_count = cfg->block_count;
|
||||
|
||||
// Allocate buffer for creating children files
|
||||
size_t pathlen = strlen(path);
|
||||
emu->path = malloc(pathlen + 1 + LFS2_NAME_MAX + 1);
|
||||
if (!emu->path) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strcpy(emu->path, path);
|
||||
emu->path[pathlen] = '/';
|
||||
emu->child = &emu->path[pathlen+1];
|
||||
memset(emu->child, '\0', LFS2_NAME_MAX+1);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
int err = mkdir(path, 0777);
|
||||
if (err && errno != EEXIST) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// Load stats to continue incrementing
|
||||
snprintf(emu->child, LFS2_NAME_MAX, ".stats");
|
||||
FILE *f = fopen(emu->path, "r");
|
||||
if (!f) {
|
||||
memset(&emu->stats, 0, sizeof(emu->stats));
|
||||
} else {
|
||||
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
|
||||
lfs2_emubd_fromle32(emu);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
// Load history
|
||||
snprintf(emu->child, LFS2_NAME_MAX, ".history");
|
||||
f = fopen(emu->path, "r");
|
||||
if (!f) {
|
||||
memset(&emu->history, 0, sizeof(emu->history));
|
||||
} else {
|
||||
size_t res = fread(&emu->history, sizeof(emu->history), 1, f);
|
||||
lfs2_emubd_fromle32(emu);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lfs2_emubd_destroy(const struct lfs2_config *cfg) {
|
||||
lfs2_emubd_sync(cfg);
|
||||
|
||||
lfs2_emubd_t *emu = cfg->context;
|
||||
free(emu->path);
|
||||
}
|
||||
|
||||
int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block,
|
||||
lfs2_off_t off, void *buffer, lfs2_size_t size) {
|
||||
lfs2_emubd_t *emu = cfg->context;
|
||||
uint8_t *data = buffer;
|
||||
|
||||
// Check if read is valid
|
||||
assert(off % cfg->read_size == 0);
|
||||
assert(size % cfg->read_size == 0);
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Zero out buffer for debugging
|
||||
memset(data, 0, size);
|
||||
|
||||
// Read data
|
||||
snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block);
|
||||
|
||||
FILE *f = fopen(emu->path, "rb");
|
||||
if (!f && errno != ENOENT) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (f) {
|
||||
int err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size_t res = fread(data, 1, size, f);
|
||||
if (res < size && !feof(f)) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
emu->stats.read_count += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
|
||||
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
|
||||
lfs2_emubd_t *emu = cfg->context;
|
||||
const uint8_t *data = buffer;
|
||||
|
||||
// Check if write is valid
|
||||
assert(off % cfg->prog_size == 0);
|
||||
assert(size % cfg->prog_size == 0);
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Program data
|
||||
snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block);
|
||||
|
||||
FILE *f = fopen(emu->path, "r+b");
|
||||
if (!f) {
|
||||
return (errno == EACCES) ? 0 : -errno;
|
||||
}
|
||||
|
||||
// Check that file was erased
|
||||
assert(f);
|
||||
|
||||
int err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size_t res = fwrite(data, 1, size, f);
|
||||
if (res < size) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
uint8_t dat;
|
||||
res = fread(&dat, 1, 1, f);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// update history and stats
|
||||
if (block != emu->history.blocks[0]) {
|
||||
memcpy(&emu->history.blocks[1], &emu->history.blocks[0],
|
||||
sizeof(emu->history) - sizeof(emu->history.blocks[0]));
|
||||
emu->history.blocks[0] = block;
|
||||
}
|
||||
|
||||
emu->stats.prog_count += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
|
||||
lfs2_emubd_t *emu = cfg->context;
|
||||
|
||||
// Check if erase is valid
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Erase the block
|
||||
snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block);
|
||||
struct stat st;
|
||||
int err = stat(emu->path, &st);
|
||||
if (err && errno != ENOENT) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) {
|
||||
err = unlink(emu->path);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (err || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) {
|
||||
FILE *f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
emu->stats.erase_count += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs2_emubd_sync(const struct lfs2_config *cfg) {
|
||||
lfs2_emubd_t *emu = cfg->context;
|
||||
|
||||
// Just write out info/stats for later lookup
|
||||
snprintf(emu->child, LFS2_NAME_MAX, ".config");
|
||||
FILE *f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
lfs2_emubd_tole32(emu);
|
||||
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
|
||||
lfs2_emubd_fromle32(emu);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
int err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
snprintf(emu->child, LFS2_NAME_MAX, ".stats");
|
||||
f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
lfs2_emubd_tole32(emu);
|
||||
res = fwrite(&emu->stats, sizeof(emu->stats), 1, f);
|
||||
lfs2_emubd_fromle32(emu);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
snprintf(emu->child, LFS2_NAME_MAX, ".history");
|
||||
f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
lfs2_emubd_tole32(emu);
|
||||
res = fwrite(&emu->history, sizeof(emu->history), 1, f);
|
||||
lfs2_emubd_fromle32(emu);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user