// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include #include "lfs_ioaccess.h" #include "parse_partitions.h" #include #include #include #include #include #include #include #include struct lfs_ioaccess_context { int file_des; loff_t part_offs; size_t last_offs; const void *empty_flash_mem; struct timeval last_sync; }; static int lfs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { struct lfs_ioaccess_context *ctx = c->context; if (!ctx) { return LFS_ERR_IO; } const off_t offrq = (off_t)block * c->block_size + (off_t)off + ctx->part_offs; if (offrq > (off_t)ctx->last_offs + size) { return LFS_ERR_IO; } off_t err = lseek(ctx->file_des, offrq, SEEK_SET); if (err < 0) { return -errno; } else if (err != offrq) { return LFS_ERR_IO; } char *rd_buf = buffer; do { ssize_t ret = read(ctx->file_des, rd_buf, (size_t)size); if (ret > 0) { size -= ret; rd_buf += ret; } else if (ret == 0) { break; } else { return ret; } } while (size > 0); return size > 0 ? LFS_ERR_IO : 0; } static int lfs_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { struct lfs_ioaccess_context *ctx = c->context; if (!ctx) { return LFS_ERR_IO; } const off_t offrq = (off_t)block * c->block_size + (off_t)off + ctx->part_offs; if (offrq > (off_t)ctx->last_offs + size) { return LFS_ERR_IO; } off_t err = lseek(ctx->file_des, offrq, SEEK_SET); if (err < 0) { return -errno; } else if (err != offrq) { return LFS_ERR_IO; } const char *wr_buf = buffer; do { ssize_t ret = write(ctx->file_des, wr_buf, (size_t)size); if (ret > 0) { size -= ret; wr_buf += ret; } else if (ret == 0) { break; } else { return ret; } } while (size > 0); return size > 0 ? LFS_ERR_IO : 0; } static int lfs_erase(const struct lfs_config *c, lfs_block_t block) { struct lfs_ioaccess_context *ctx = c->context; if (!ctx) { return LFS_ERR_IO; } const off_t offrq = (off_t)block * c->block_size + ctx->part_offs; if (offrq > (off_t)ctx->last_offs + c->block_size) { return LFS_ERR_IO; } off_t err = lseek(ctx->file_des, offrq, SEEK_SET); if (err < 0) { return -errno; } else if (err != offrq) { return LFS_ERR_IO; } const char *wr_buf = ctx->empty_flash_mem; size_t size; do { size = c->block_size; ssize_t ret = write(ctx->file_des, wr_buf, (size_t)size); if (ret > 0) { size -= ret; wr_buf += ret; } else if (ret == 0) { break; } else { return ret; } } while (size > 0); return size > 0 ? LFS_ERR_IO : 0; } static int lfs_sync(const struct lfs_config *c) { struct lfs_ioaccess_context *ctx = c->context; struct timeval curr_msync, result_msync; if (gettimeofday(&curr_msync, NULL) == -1) { return -1; } timersub(&curr_msync, &ctx->last_sync, &result_msync); int err = 0; if (result_msync.tv_sec >= 1) { err = fsync(ctx->file_des); ctx->last_sync = curr_msync; } return err; } #ifdef LFS_THREADSAFE //! Note single threaded application don't need locks static int lfs_lock_unlock(const struct lfs_config *cfg) { (void)cfg; return LFS_ERR_OK; } #endif struct lfs_ioaccess_context *lfs_ioaccess_open(struct lfs_config *cfg, const char *filename, const struct partition *partition) { struct lfs_ioaccess_context *ret = calloc(1, sizeof(struct lfs_ioaccess_context)); if (!ret) { return NULL; } { char *memm = malloc(cfg->block_size); if (!memm) { free(ret); return NULL; } memset(memm, 0xff, cfg->block_size); ret->empty_flash_mem = memm; } ret->file_des = open(filename, O_RDWR); if (ret->file_des < 0) { free((void *)ret->empty_flash_mem); free(ret); return NULL; } struct stat statbuf; int err = fstat(ret->file_des, &statbuf); if (err < 0) { close(ret->file_des); free((void *)ret->empty_flash_mem); free(ret); return NULL; } if (S_ISBLK(statbuf.st_mode)) { uint64_t blk_size; err = ioctl(ret->file_des, BLKGETSIZE64, &blk_size); if (err < 0) { close(ret->file_des); free((void *)ret->empty_flash_mem); free(ret); return NULL; } else { statbuf.st_size = blk_size; } } off_t start_pos = 0; ret->last_offs = statbuf.st_size; if (partition) { if (partition->end > statbuf.st_size) { close(ret->file_des); free((void *)ret->empty_flash_mem); free(ret); errno = E2BIG; return NULL; } else { start_pos = partition->start; ret->last_offs = partition->end; } } ret->part_offs = start_pos; // Mount the file system cfg->read = lfs_read; cfg->prog = lfs_prog; cfg->erase = lfs_erase; cfg->sync = lfs_sync; #ifdef LFS_THREADSAFE cfg->lock = lfs_lock_unlock; cfg->unlock = lfs_lock_unlock; #endif cfg->context = ret; return ret; } int lfs_ioaccess_close(struct lfs_ioaccess_context *ctx) { if (!ctx) { errno = EINVAL; return -1; } free((void *)ctx->empty_flash_mem); int ret = close(ctx->file_des); free(ctx); return ret; } int lfs_ioaccess_is_lfs_filesystem(struct lfs_ioaccess_context *ctx) { static const char lfs_id[] = "littlefs"; static const size_t lfs_offs = 8U; char buf[32]; if (!ctx) { errno = EINVAL; return -1; } off_t offs = lseek(ctx->file_des, ctx->part_offs + lfs_offs, SEEK_SET); if (offs < 0) { return -1; } else if (offs != ctx->part_offs + (off_t)lfs_offs) { errno = ERANGE; return -1; } size_t rd_req = sizeof buf; char *rd_buf = buf; do { ssize_t ret = read(ctx->file_des, rd_buf, rd_req); if (ret > 0) { rd_req -= ret; rd_buf += ret; } else if (ret == 0) { break; } else { return ret; } } while (rd_req > 0); if (rd_req) { errno = ERANGE; return -1; } return memcmp(buf, lfs_id, sizeof(lfs_id) - sizeof('\0')) == 0; }