M .gitmodules => .gitmodules +5 -2
@@ 65,6 65,9 @@
[submodule "module-vfs/thirdparty/fatfs"]
path = module-vfs/thirdparty/fatfs
url = ../fatfs.git
-[submodule "module-vfs/thirdparty/littlefs"]
- path = module-vfs/thirdparty/littlefs
+[submodule "host-tools/littlefs-fuse/lfsfuse"]
+ path = host-tools/littlefs-fuse/lfsfuse
+ url = https://github.com/littlefs-project/littlefs-fuse.git
+[submodule "module-vfs/thirdparty/lfsfs/littlefs"]
+ path = module-vfs/thirdparty/lfsfs/littlefs
url = https://github.com/littlefs-project/littlefs.git
M CMakeLists.txt => CMakeLists.txt +2 -0
@@ 33,6 33,7 @@ if (${ENABLE_TESTS})
add_subdirectory(test)
endif ()
+add_subdirectory(host-tools)
add_custom_target(assets)
# setting build flags
@@ 220,6 221,7 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_Linux")
install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION "./")
endif()
+
if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
set(HEX_FILE ${CMAKE_PROJECT_NAME}.hex)
set(BIN_FILE boot.bin)
A host-tools/CMakeLists.txt => host-tools/CMakeLists.txt +18 -0
@@ 0,0 1,18 @@
+if (CMAKE_CROSSCOMPILING)
+ # Littlefs fuse is needed in rt1051 and Linux for manipulate images
+ # genlittlefs is needed only on the Linux image for generate emulator target image
+ add_custom_target(
+ lfsfuse ALL
+ COMMAND ${CMAKE_COMMAND}
+ -DCMAKE_BUILD_TYPE:STRING="Release"
+ -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE:PATH="${CMAKE_BINARY_DIR}"
+ -B"build"
+ -H"${CMAKE_SOURCE_DIR}/host-tools/littlefs-fuse"
+ COMMAND ${CMAKE_COMMAND} --build build --config Release
+ )
+else()
+ set(_genlittlefs "${CMAKE_BINARY_DIR}/genlittlefs${CMAKE_EXECUTABLE_SUFFIX}")
+ add_subdirectory(genlittlefs)
+ add_subdirectory(littlefs-fuse)
+endif()
+
A host-tools/genlittlefs/CMake/FindBLKID.cmake => host-tools/genlittlefs/CMake/FindBLKID.cmake +35 -0
@@ 0,0 1,35 @@
+# Find the BLKID includes and library
+#
+# BLKID_INCLUDE_DIR - where to find fuse.h, etc.
+# BLKID_LIBRARIES - List of libraries when using FDISK.
+# BLKID_FOUND - True if FDISK lib is found.
+
+# check if already in cache, be silent
+IF (BLKID_INCLUDE_DIR)
+ SET (BLKID_FIND_QUIETLY TRUE)
+ENDIF (BLKID_INCLUDE_DIR)
+
+# find includes
+FIND_PATH (BLKID_INCLUDE_DIR blkid.h
+ /usr/local/include/blkid
+ /usr/local/include
+ /usr/include
+ /usr/include/blkid
+)
+
+# find lib
+if (APPLE)
+ SET(BLKID_NAMES libblkid.dylib blkid)
+else (APPLE)
+ SET(BLKID_NAMES blkid)
+endif (APPLE)
+FIND_LIBRARY(BLKID_LIBRARIES
+ NAMES ${BLKID_NAMES}
+ PATHS /lib64 /lib /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib /usr/lib/x86_64-linux-gnu
+ )
+
+include ("FindPackageHandleStandardArgs")
+find_package_handle_standard_args ("BLKID" DEFAULT_MSG
+ BLKID_INCLUDE_DIR BLKID_LIBRARIES)
+
+mark_as_advanced (BLKID_INCLUDE_DIR FDISK_LIBRARIES)
A host-tools/genlittlefs/CMakeLists.txt => host-tools/genlittlefs/CMakeLists.txt +21 -0
@@ 0,0 1,21 @@
+cmake_minimum_required(VERSION 3.14)
+
+project(genlittlefs LANGUAGES C)
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
+
+find_package(BLKID REQUIRED)
+
+set(GENLITTLEFS_SRCS
+ mklfs.c
+ parse_partitions.c
+ parse_args.c
+ lfs_ioaccess.c
+)
+
+add_executable(${PROJECT_NAME} ${GENLITTLEFS_SRCS})
+target_compile_options(${PROJECT_NAME} PRIVATE -Wall -pedantic -Werror -Wextra )
+target_compile_definitions(${PROJECT_NAME} PRIVATE _GNU_SOURCE )
+
+target_link_libraries(${PROJECT_NAME} PRIVATE littlefs ${BLKID_LIBRARIES})
+target_include_directories(${PROJECT_NAME} PRIVATE ${BLKID_INCLUDE_DIR})
+
A host-tools/genlittlefs/lfs_ioaccess.c => host-tools/genlittlefs/lfs_ioaccess.c +246 -0
@@ 0,0 1,246 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "lfs.h"
+#include "lfs_ioaccess.h"
+#include "parse_partitions.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+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;
+}
+
+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(ret);
+ return NULL;
+ }
+ struct stat statbuf;
+ int err = fstat(ret->file_des, &statbuf);
+ if (err < 0) {
+ close(ret->file_des);
+ free(ret);
+ return NULL;
+ }
+ off_t start_pos = 0;
+ ret->last_offs = statbuf.st_size;
+ if (partition) {
+ if (partition->end > statbuf.st_size) {
+ close(ret->file_des);
+ 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;
+ 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;
+}
A host-tools/genlittlefs/lfs_ioaccess.h => host-tools/genlittlefs/lfs_ioaccess.h +17 -0
@@ 0,0 1,17 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+struct lfs_config;
+struct partition;
+
+struct lfs_ioaccess_context;
+
+struct lfs_ioaccess_context *lfs_ioaccess_open(struct lfs_config *cfg,
+ const char *filename,
+ const struct partition *partition);
+
+int lfs_ioaccess_close(struct lfs_ioaccess_context *ctx);
+
+int lfs_ioaccess_is_lfs_filesystem(struct lfs_ioaccess_context *ctx);
A host-tools/genlittlefs/mklfs.c => host-tools/genlittlefs/mklfs.c +373 -0
@@ 0,0 1,373 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "lfs.h"
+#include "lfs_ioaccess.h"
+#include "parse_partitions.h"
+#include "parse_args.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+struct lfs_info_summary
+{
+ size_t files_added;
+ size_t directories_added;
+ size_t bytes_transferred;
+};
+
+static int create_dir_in_lfs(lfs_t *lfs, const char *lfs_path, bool verbose)
+{
+ int ret;
+ if (verbose)
+ fprintf(stdout, "[%s]\n", lfs_path);
+ if ((ret = lfs_mkdir(lfs, lfs_path)) < 0) {
+ fprintf(stderr, "can't create directory %s: error=%d\n", lfs_path, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int create_file_in_lfs(lfs_t *lfs, const char *host_file, const char *lfs_file, bool verbose)
+{
+ int ret;
+ if (verbose)
+ fprintf(stdout, "%s\n", lfs_file);
+ // Open source file
+ int srcfd = open(host_file, O_RDONLY);
+ if (srcfd < 0) {
+ fprintf(stderr, "can't open source file %s: errno=%d (%s)\n", host_file, errno, strerror(errno));
+ return -1;
+ }
+
+ // Open destination file
+ lfs_file_t dstf;
+ if ((ret = lfs_file_open(lfs, &dstf, lfs_file, LFS_O_WRONLY | LFS_O_CREAT)) < 0) {
+ fprintf(stderr, "can't open destination file %s: error=%d\n", lfs_file, ret);
+ close(srcfd);
+ return ret;
+ }
+ do {
+ char copy_buffer[16384];
+ ret = read(srcfd, copy_buffer, sizeof copy_buffer);
+ if (ret < 0) {
+ close(srcfd);
+ lfs_file_close(lfs, &dstf);
+ return ret;
+ }
+ else if (ret == 0) {
+ break;
+ }
+ char *lfs_wptr = copy_buffer;
+ int lfs_wrleft = ret;
+ do {
+ int retlfs = lfs_file_write(lfs, &dstf, lfs_wptr, lfs_wrleft);
+ if (retlfs <= 0) {
+ close(srcfd);
+ lfs_file_close(lfs, &dstf);
+ fprintf(stderr, "can't write to destination file %s: error=%d\n", lfs_file, retlfs);
+ return retlfs;
+ }
+ else {
+ lfs_wrleft -= retlfs;
+ lfs_wptr += retlfs;
+ }
+ } while (lfs_wrleft > 0);
+
+ } while (ret > 0);
+
+ // Close destination file
+ ret = lfs_file_close(lfs, &dstf);
+ if (ret < 0) {
+ fprintf(stderr, "can't close destination file %s: error=%d\n", lfs_file, ret);
+ close(srcfd);
+ return ret;
+ }
+
+ // Close source file
+ close(srcfd);
+ return ret;
+}
+
+static int add_directory_to_lfs(
+ lfs_t *lfs, const char *host_path, const char *lfs_path, struct lfs_info_summary *summary, bool verbose)
+{
+ DIR *dir;
+ struct dirent *ent;
+ char lfs_curr_path[PATH_MAX];
+ char host_curr_path[PATH_MAX];
+ int err = -1;
+ dir = opendir(host_path);
+ if (dir) {
+ while ((ent = readdir(dir))) {
+ // Skip . and .. directories
+ if ((strcmp(ent->d_name, ".") != 0) && (strcmp(ent->d_name, "..") != 0)) {
+ // Update the current path
+ strcpy(lfs_curr_path, lfs_path);
+ strcat(lfs_curr_path, "/");
+ strcat(lfs_curr_path, ent->d_name);
+ // Update native current path
+ strcpy(host_curr_path, host_path);
+ strcat(host_curr_path, "/");
+ strcat(host_curr_path, ent->d_name);
+
+ if (ent->d_type == DT_DIR) {
+ err = create_dir_in_lfs(lfs, lfs_curr_path, verbose);
+ if (err) {
+ closedir(dir);
+ return err;
+ }
+ else {
+ summary->directories_added++;
+ }
+ err = add_directory_to_lfs(lfs, host_curr_path, lfs_curr_path, summary, verbose);
+ if (err) {
+ closedir(dir);
+ return err;
+ }
+ }
+ else if (ent->d_type == DT_REG) {
+ err = create_file_in_lfs(lfs, host_curr_path, lfs_curr_path, verbose);
+ if (err) {
+ closedir(dir);
+ return err;
+ }
+ else {
+ summary->files_added++;
+ struct stat statbuf;
+ if (stat(host_curr_path, &statbuf) == 0) {
+ summary->bytes_transferred += statbuf.st_size;
+ }
+ }
+ }
+ }
+ }
+ closedir(dir);
+ }
+ return err;
+}
+
+static int add_to_lfs(lfs_t *lfs, const char *dir, struct lfs_info_summary *summary, bool verbose)
+{
+ char *host_dir = canonicalize_file_name(dir);
+ bool is_dir, is_file;
+ off_t fsize;
+ {
+ struct stat stbuf;
+ int err = stat(host_dir, &stbuf);
+ if (err < 0) {
+ free(host_dir);
+ return -1;
+ }
+ fsize = stbuf.st_size;
+ is_dir = stbuf.st_mode & S_IFDIR;
+ is_file = stbuf.st_mode & S_IFREG;
+ }
+ if (!is_dir && !is_file) {
+ free(host_dir);
+ errno = ENOTDIR;
+ return -1;
+ }
+ char *sep_ptr = strrchr(host_dir, '/');
+ char *tgt_dir = malloc(strlen(sep_ptr + 1) + sizeof('\0') + sizeof('/'));
+ if (!tgt_dir) {
+ free(host_dir);
+ errno = ENOMEM;
+ return -1;
+ }
+ else {
+ tgt_dir[0] = '/';
+ strcpy(tgt_dir + 1, sep_ptr + 1);
+ }
+ int err;
+ if (is_dir) {
+ err = create_dir_in_lfs(lfs, tgt_dir, verbose);
+ if (err) {
+ free(host_dir);
+ free(tgt_dir);
+ return err;
+ }
+ err = add_directory_to_lfs(lfs, host_dir, tgt_dir, summary, verbose);
+ if (!err) {
+ summary->directories_added++;
+ }
+ }
+ else if (is_file) {
+ err = create_file_in_lfs(lfs, host_dir, tgt_dir, verbose);
+ if (!err) {
+ summary->files_added++;
+ summary->bytes_transferred += fsize;
+ }
+ }
+ free(host_dir);
+ free(tgt_dir);
+ return err;
+}
+
+static void print_error(const char *str, int error) __attribute__((nonnull(1)));
+static void print_error(const char *str, int error)
+{
+ if (error == -1) {
+ char buf[1024];
+ fprintf(stderr, "system_error %s %s\n", str, strerror_r(errno, buf, sizeof buf));
+ }
+ else {
+ fprintf(stderr, "lfs_error %s %i\n", str, error);
+ }
+}
+
+static void configure_lfs_params(struct lfs_config *lfsc, const struct littlefs_opts *opts)
+ __attribute__((nonnull(1, 2)));
+
+static void configure_lfs_params(struct lfs_config *lfsc, const struct littlefs_opts *opts)
+{
+ memset(lfsc, 0, sizeof *lfsc);
+ lfsc->block_size = opts->block_size;
+ lfsc->read_size = opts->read_size;
+ lfsc->prog_size = opts->prog_size;
+ lfsc->lookahead_size = opts->lockahead_size;
+ lfsc->cache_size = opts->cache_size;
+ lfsc->block_cycles = opts->block_cycles;
+}
+
+int main(int argc, char **argv)
+{
+
+ int err;
+ struct littlefs_opts lopts;
+ struct lfs_config cfg;
+ struct lfs_info_summary prog_summary;
+ struct lfs_ioaccess_context *ioctx = NULL;
+ lfs_t lfs;
+ err = parse_program_args(argc, argv, &lopts);
+ if (err < 0) {
+ return err;
+ }
+ if (lopts.mode == littlefs_opts_listparts) {
+ size_t elems;
+ struct partition *parts = find_partitions(lopts.dst_image, scan_all_partitions, &elems);
+ print_partitions(parts, elems);
+ free(parts);
+ free(lopts.src_dirs);
+ return EXIT_SUCCESS;
+ }
+
+ configure_lfs_params(&cfg, &lopts);
+ memset(&prog_summary, 0, sizeof(prog_summary));
+ if (lopts.mode == littlefs_opts_parts) {
+ size_t elems;
+ struct partition *parts = find_partitions(lopts.dst_image, scan_all_partitions, &elems);
+ if (!parts) {
+ perror("Unable to list partitions:");
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+ if (lopts.partition_num - 1 > (int)elems) {
+ fprintf(stderr, "Invalid partition selected. Max partition_num is: %lu\n", elems);
+ free(parts);
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+ const struct partition *curr_part = &parts[lopts.partition_num - 1];
+ cfg.block_count = (curr_part->end - curr_part->start) / lopts.block_size;
+ ioctx = lfs_ioaccess_open(&cfg, lopts.dst_image, curr_part);
+ if (!ioctx) {
+ perror("Unable to open file:");
+ free(parts);
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+ free(parts);
+ }
+ else if (lopts.mode == littlefs_opts_file) {
+ int fds = open(lopts.dst_image, O_CREAT | O_WRONLY, 0644);
+ if (fds < 0) {
+ perror("Unable to create file");
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+ err = ftruncate(fds, lopts.filesystem_size);
+ if (err) {
+ perror("Unable to truncate file");
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+ close(fds);
+ fds = -1;
+ cfg.block_count = lopts.filesystem_size / lopts.block_size;
+ ioctx = lfs_ioaccess_open(&cfg, lopts.dst_image, NULL);
+ if (!ioctx) {
+ perror("Unable to open file:");
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+ }
+ else {
+ fprintf(stderr, "Unknown option\n");
+ free(lopts.src_dirs);
+ lfs_ioaccess_close(ioctx);
+ return EXIT_FAILURE;
+ }
+ if (lopts.verbose) {
+ print_config_options(&lopts);
+ }
+
+ if (!lopts.overwrite_existing && lfs_ioaccess_is_lfs_filesystem(ioctx) > 0) {
+ fprintf(stderr, "LFS filesystem already exists. If you want to overwrite add --overwrite flag\n");
+ free(lopts.src_dirs);
+ lfs_ioaccess_close(ioctx);
+ return EXIT_FAILURE;
+ }
+
+ err = lfs_format(&lfs, &cfg);
+ if (err < 0) {
+ fprintf(stderr, "lfs format error: error=%d\n", err);
+ lfs_ioaccess_close(ioctx);
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+
+ err = lfs_mount(&lfs, &cfg);
+ if (err < 0) {
+ fprintf(stderr, "lfs mount error: error=%d\n", err);
+ lfs_ioaccess_close(ioctx);
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+
+ for (size_t ndir = 0; ndir < lopts.src_dirs_siz; ++ndir) {
+ err = add_to_lfs(&lfs, lopts.src_dirs[ndir], &prog_summary, lopts.verbose);
+ if (err) {
+ print_error("Unable to open file:", err);
+ lfs_ioaccess_close(ioctx);
+ free(lopts.src_dirs);
+ lfs_unmount(&lfs);
+ return EXIT_FAILURE;
+ }
+ }
+ err = lfs_unmount(&lfs);
+ if (err < 0) {
+ fprintf(stderr, "lfs umount error: error=%d\n", err);
+ lfs_ioaccess_close(ioctx);
+ free(lopts.src_dirs);
+ return EXIT_FAILURE;
+ }
+ free(lopts.src_dirs);
+ lfs_ioaccess_close(ioctx);
+ printf("Littlefs summary:\n"
+ " Directories created: %lu, Files added: %lu, Transferred %lu kbytes.\n"
+ " Littlefs block size: %i blocks count: %i.\n",
+ prog_summary.directories_added,
+ prog_summary.files_added,
+ prog_summary.bytes_transferred / 1024UL,
+ cfg.block_size,
+ cfg.block_count);
+ return EXIT_SUCCESS;
+}
A host-tools/genlittlefs/parse_args.c => host-tools/genlittlefs/parse_args.c +331 -0
@@ 0,0 1,331 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "parse_args.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+static int is_number(const char *s)
+{
+ const char *c = s;
+
+ while (*c) {
+ if ((*c < '0') || (*c > '9')) {
+ return 0;
+ }
+ c++;
+ }
+
+ return 1;
+}
+
+static int is_hex(const char *s)
+{
+ const char *c = s;
+
+ if (*c++ != '0') {
+ return 0;
+ }
+
+ if (*c++ != 'x') {
+ return 0;
+ }
+
+ while (*c) {
+ if (((*c < '0') || (*c > '9')) && ((*c < 'A') || (*c > 'F')) && ((*c < 'a') || (*c > 'f'))) {
+ return 0;
+ }
+ c++;
+ }
+
+ return 1;
+}
+
+static int to_int(const char *s)
+{
+ if (!s) {
+ return -1;
+ }
+ if (is_number(s)) {
+ return atoi(s);
+ }
+ else if (is_hex(s)) {
+ return (int)strtol(s, NULL, 16);
+ }
+
+ return -1;
+}
+
+static long long to_longlong(const char *s)
+{
+ if (!s) {
+ return -1;
+ }
+ if (is_number(s)) {
+ return atoll(s);
+ }
+ else if (is_hex(s)) {
+ return (long long)strtoll(s, NULL, 16);
+ }
+ return -1;
+}
+
+static void usage(const char *name) __attribute__((nonnull(1)));
+static void usage(const char *name)
+{
+ fprintf(stderr, "usage: %s [options] -- src_dir1 ... [src_dirN]\n", name);
+}
+
+static void help(const char *name) __attribute__((nonnull(1)));
+static void help(const char *name)
+{
+ static const char help_text[] = "usage: %s [options] -- src_dir1 ... [src_dirN]\n"
+ "\n"
+ "general options:\n"
+ " -h --help print help\n"
+ "\n"
+ "genlilttlefs options:\n"
+ " -i --image partition file image\n"
+ " -b --block_size logical block size, overrides the block device\n"
+ " -s --filesystem_size filesystem size when creating the new file\n"
+ " -p --partition_num partition number when use partitions (1-n)\n"
+ " -l --list_partitions list existing parition and numbers\n"
+ " --cache_size size of caches (block_size)\n"
+ " --read_size readable unit (block_size)\n"
+ " --prog_size programmable unit (block_size)\n"
+ " --lookahead_size size of lookahead buffer (8192)\n"
+ " --block_cycles number of erase cycles before eviction (512)\n"
+ " --overwrite reformat existing partition\n"
+ " --verbose verbose mode\n"
+ "\n"
+ "positional arguments:\n"
+ " src_dir1 first source directory\n"
+ " src_dirN N-th source directory\n";
+ fprintf(stderr, help_text, name);
+}
+
+int parse_program_args(int argc, char **argv, struct littlefs_opts *opts)
+{
+ int c; // Current option
+ int option_index = 0;
+ bool is_help = false;
+ bool is_listpart = false;
+ bool is_unknown = false;
+ static struct option long_options[] = {
+ {.name = "image", .has_arg = required_argument, .flag = 0, .val = 'i'},
+ {.name = "block_size", .has_arg = required_argument, .flag = 0, .val = 'b'},
+ {.name = "filesystem_size", .has_arg = required_argument, .flag = 0, .val = 's'},
+ {.name = "partition_num", .has_arg = required_argument, .flag = 0, .val = 'p'},
+ {.name = "list_partitions", .has_arg = no_argument, .flag = 0, .val = 'l'},
+ {.name = "help", .has_arg = no_argument, .flag = 0, .val = 'h'},
+ {.name = "read_size", .has_arg = required_argument, .flag = 0, .val = 0},
+ {.name = "prog_size", .has_arg = required_argument, .flag = 0, .val = 0},
+ {.name = "cache_size", .has_arg = required_argument, .flag = 0, .val = 0},
+ {.name = "lockahead_size", .has_arg = required_argument, .flag = 0, .val = 0},
+ {.name = "block_cycles", .has_arg = required_argument, .flag = 0, .val = 0},
+ {.name = "overwrite", .has_arg = no_argument, .flag = 0, .val = 0},
+ {.name = "verbose", .has_arg = no_argument, .flag = 0, .val = 0},
+ {.name = 0, .has_arg = 0, .flag = 0, .val = 0}};
+ memset(opts, 0, sizeof(*opts));
+ while ((c = getopt_long(argc, argv, "i:b:s:p:lh", long_options, &option_index)) != -1) {
+ switch (c) {
+ case 0:
+ if (long_options[option_index].flag != 0) {
+ break;
+ }
+ const char *optname = long_options[option_index].name;
+ if (!strcmp(optname, "overwrite")) {
+ opts->overwrite_existing = true;
+ }
+ else if (!strcmp(optname, "verbose")) {
+ opts->verbose = true;
+ }
+ else if (!strcmp(optname, "block_cycles")) {
+ opts->block_cycles = to_int(optarg);
+ }
+ else if (!strcmp(optname, "lockahead_size")) {
+ opts->lockahead_size = to_int(optarg);
+ }
+ else if (!strcmp(optname, "cache_size")) {
+ opts->cache_size = to_int(optarg);
+ }
+ else if (!strcmp(optname, "prog_size")) {
+ opts->prog_size = to_int(optarg);
+ }
+ else if (!strcmp(optname, "read_size")) {
+ opts->read_size = to_int(optarg);
+ }
+ break;
+ case 'i':
+ opts->dst_image = optarg;
+ break;
+ case 'b':
+ opts->block_size = to_int(optarg);
+ break;
+ case 's':
+ opts->filesystem_size = to_longlong(optarg);
+ break;
+ case 'p':
+ opts->partition_num = to_int(optarg);
+ break;
+ case 'l':
+ is_listpart = true;
+ break;
+ case 'h':
+ is_help = true;
+ break;
+ default:
+ is_unknown = true;
+ break;
+ }
+ }
+ if (argc == 1) {
+ usage(argv[0]);
+ return -1;
+ }
+ else if (is_help) {
+ help(argv[0]);
+ return -1;
+ }
+ else if (is_unknown) {
+ fprintf(stderr, "Unknown option\n");
+ return -1;
+ }
+ else if (is_listpart) {
+ opts->mode = littlefs_opts_listparts;
+ if (!opts->dst_image) {
+ fprintf(stderr, "--image <file> is not specified\n");
+ return -1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ if (opts->block_cycles < 0) {
+ fprintf(stderr, "Warning: disable wear leveling\n");
+ opts->block_cycles = -1;
+ }
+ else if (opts->block_cycles > 1000) {
+ fprintf(stderr, "Error: Maximum block cycles is 1000\n");
+ return -1;
+ }
+ else if (opts->block_cycles == 0) {
+ opts->block_cycles = 512;
+ }
+ if (opts->block_size == 0) {
+ fprintf(stderr, "Missing --block_size <n> argument\n");
+ return -1;
+ }
+ else if (opts->block_size < 0) {
+ fprintf(stderr, "argument --block_size <n> need to be > 0\n");
+ return -1;
+ }
+ if (opts->read_size == 0) {
+ opts->read_size = opts->block_size;
+ }
+ else if (opts->read_size < 0) {
+ fprintf(stderr, "argument --read_size <n> needs to be >0\n");
+ return -1;
+ }
+ if (opts->prog_size == 0) {
+ opts->prog_size = opts->block_size;
+ }
+ else if (opts->prog_size < 0) {
+ fprintf(stderr, "argument --prog_size <n> needs to be >0\n");
+ return -1;
+ }
+ if (opts->cache_size == 0) {
+ opts->cache_size = opts->block_size;
+ }
+ else if (opts->cache_size < 0) {
+ fprintf(stderr, "argument --cache_size <n> needs to be >0\n");
+ return -1;
+ }
+ if (opts->lockahead_size == 0) {
+ opts->lockahead_size = 8192;
+ }
+ else if (opts->cache_size < 0) {
+ fprintf(stderr, "argument --lockahead_size <n> needs to be >0\n");
+ return -1;
+ }
+ if (!opts->dst_image) {
+ fprintf(stderr, "--image <filename> is not specified\n");
+ return -1;
+ }
+ if (opts->filesystem_size < 0) {
+ fprintf(stderr, "argument --filesystem_size <n> needs to be >0\n");
+ return -1;
+ }
+ else if (opts->partition_num < 0) {
+ fprintf(stderr, "argument --partition_num <n> needs to be >0\n");
+ return -1;
+ }
+ else if (opts->filesystem_size == 0 && opts->partition_num == 0) {
+ fprintf(stderr, "Missing --filesystem_size <size> or --partition_num <num>\n");
+ return -1;
+ }
+ else if (opts->filesystem_size > 0 && opts->partition_num > 0) {
+ fprintf(stderr, "Only --filesystem_size or --partition_num is allowed in same time");
+ return -1;
+ }
+ else if (opts->filesystem_size > 0) {
+ if (opts->filesystem_size % opts->block_size) {
+ fprintf(stderr, "--filesystem_size <size> should be multiply of block size\n");
+ return -1;
+ }
+ opts->mode = littlefs_opts_file;
+ }
+ else if (opts->partition_num > 0) {
+ opts->mode = littlefs_opts_parts;
+ }
+ if (optind < argc) {
+ opts->src_dirs_siz = argc - optind;
+ opts->src_dirs = calloc(opts->src_dirs_siz, sizeof(char *));
+ for (int i = 0; optind < argc; ++i) {
+ char *arg = argv[optind++];
+ const size_t arg_sz = strlen(arg);
+ if (arg[arg_sz - 1] == '/')
+ arg[arg_sz - 1] = '\0';
+ opts->src_dirs[i] = arg;
+ }
+ }
+ else {
+ fprintf(stderr, "source directories not specified\n");
+ return -1;
+ }
+ if (!opts->dst_image) {
+ fprintf(stderr, "--image <file> is not specified\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void print_config_options(const struct littlefs_opts *opts)
+{
+ static const char struct_info[] = "genlittlefs configuration:\n"
+ " LFS read size %i\n"
+ " LFS block size: %i\n"
+ " LFS prog size: %i\n"
+ " LFS cache size: %i\n"
+ " LFS lookahead size: %i\n"
+ " LFS block cycles: %i\n"
+ " Filesystem size: %lli\n"
+ " Partition number: %i\n"
+ " Overwrite existing fs: %i\n";
+ printf(struct_info,
+ opts->read_size,
+ opts->block_size,
+ opts->prog_size,
+ opts->cache_size,
+ opts->lockahead_size,
+ opts->block_cycles,
+ opts->filesystem_size,
+ opts->partition_num,
+ opts->overwrite_existing);
+}
A host-tools/genlittlefs/parse_args.h => host-tools/genlittlefs/parse_args.h +36 -0
@@ 0,0 1,36 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+#include <stdbool.h>
+#include <stddef.h>
+
+enum littlefs_opts_mode
+{
+ littlefs_opts_listparts,
+ littlefs_opts_parts,
+ littlefs_opts_file
+};
+struct littlefs_opts
+{
+ //! LFS config options
+ int read_size; //! Read size
+ int block_size; //! block size
+ int prog_size; //! Eraseable area size
+ int cache_size; //! Cache size
+ int lockahead_size; //! Lock ahead size
+ int block_cycles; //! Progam counter cycles
+ //! Other ars
+ enum littlefs_opts_mode mode; //! Software mode
+ long long filesystem_size; //! Filesystem size
+ int partition_num; //! Parition number
+ size_t src_dirs_siz; //! SOurce dirs size
+ char **src_dirs; //! Source directories
+ char *dst_image; //! Destination image
+ bool overwrite_existing; //! Overwrite existing format
+ bool verbose; //! Verbose mode
+};
+
+int parse_program_args(int argc, char **argv, struct littlefs_opts *opts) __attribute__((nonnull(3)));
+
+void print_config_options(const struct littlefs_opts *opts) __attribute__((nonnull(1)));
A host-tools/genlittlefs/parse_partitions.c => host-tools/genlittlefs/parse_partitions.c +63 -0
@@ 0,0 1,63 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+#include <blkid.h>
+#include "parse_partitions.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const size_t sector_size = 512;
+
+struct partition *find_partitions(const char *filename, part_type_t ptype, size_t *nelems)
+{
+ struct partition *pret = NULL;
+ blkid_probe pr;
+ pr = blkid_new_probe_from_filename(filename);
+ if (!pr) {
+ return pret;
+ }
+ blkid_probe_enable_partitions(pr, true);
+ blkid_do_fullprobe(pr);
+
+ blkid_partlist ls = blkid_probe_get_partitions(pr);
+ if (!ls) {
+ *nelems = 0;
+ return pret;
+ }
+ const int nparts = blkid_partlist_numof_partitions(ls);
+ if (nparts < 1) {
+ *nelems = 0;
+ return pret;
+ }
+ else {
+ pret = calloc(nparts, sizeof(struct partition));
+ }
+ size_t ipart = 0;
+ for (int i = 0; i < nparts; ++i) {
+ blkid_partition par = blkid_partlist_get_partition(ls, i);
+ if (ptype == scan_all_partitions || blkid_partition_get_type(par) == ptype) {
+ pret[ipart].start = (blkid_partition_get_start(par)) * sector_size;
+ pret[ipart].end = (blkid_partition_get_start(par) + blkid_partition_get_size(par) - 1LLU) * sector_size;
+ pret[ipart].type = blkid_partition_get_type(par);
+ ++ipart;
+ }
+ *nelems = ipart;
+ }
+ blkid_free_probe(pr);
+ return pret;
+}
+
+void print_partitions(const struct partition *part, size_t nparts)
+{
+ printf("List of partitions [%lu]:\n", nparts);
+ if (!part) {
+ return;
+ }
+ for (size_t s = 0; s < nparts; ++s) {
+ printf(" Number: [%lu] Type: [%02x] Start: [%luk] End: [%luk]\n",
+ s + 1,
+ (int)part->type,
+ part[s].start / 1024,
+ part[s].end / 1024);
+ }
+}
A host-tools/genlittlefs/parse_partitions.h => host-tools/genlittlefs/parse_partitions.h +24 -0
@@ 0,0 1,24 @@
+// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+#include <stddef.h>
+
+typedef int part_type_t;
+
+enum part_type_options
+{
+ scan_all_partitions = -1
+};
+
+struct partition
+{
+ off_t start;
+ off_t end;
+ part_type_t type;
+};
+
+struct partition *find_partitions(const char *filename, part_type_t ptype, size_t *nelems)
+ __attribute__((nonnull(1, 3)));
+
+void print_partitions(const struct partition *part, size_t nparts);
A host-tools/littlefs-fuse/CMake/FindFUSE.cmake => host-tools/littlefs-fuse/CMake/FindFUSE.cmake +34 -0
@@ 0,0 1,34 @@
+# Find the FUSE includes and library
+#
+# FUSE_INCLUDE_DIR - where to find fuse.h, etc.
+# FUSE_LIBRARIES - List of libraries when using FUSE.
+# FUSE_FOUND - True if FUSE lib is found.
+
+# check if already in cache, be silent
+IF (FUSE_INCLUDE_DIR)
+ SET (FUSE_FIND_QUIETLY TRUE)
+ENDIF (FUSE_INCLUDE_DIR)
+
+# find includes
+FIND_PATH (FUSE_INCLUDE_DIR fuse.h
+ /usr/local/include/osxfuse
+ /usr/local/include
+ /usr/include
+ )
+
+# find lib
+if (APPLE)
+ SET(FUSE_NAMES libosxfuse.dylib fuse)
+else (APPLE)
+ SET(FUSE_NAMES fuse)
+endif (APPLE)
+FIND_LIBRARY(FUSE_LIBRARIES
+ NAMES ${FUSE_NAMES}
+ PATHS /lib64 /lib /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib /usr/lib/x86_64-linux-gnu
+ )
+
+include ("FindPackageHandleStandardArgs")
+find_package_handle_standard_args ("FUSE" DEFAULT_MSG
+ FUSE_INCLUDE_DIR FUSE_LIBRARIES)
+
+mark_as_advanced (FUSE_INCLUDE_DIR FUSE_LIBRARIES)
A host-tools/littlefs-fuse/CMakeLists.txt => host-tools/littlefs-fuse/CMakeLists.txt +26 -0
@@ 0,0 1,26 @@
+cmake_minimum_required(VERSION 3.14)
+
+project(lfsfuse LANGUAGES C)
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
+
+find_package(FUSE REQUIRED)
+
+file(GLOB_RECURSE LFSFUSE_SRCS *.c)
+add_executable(${PROJECT_NAME} ${LFSFUSE_SRCS})
+
+
+target_compile_options(${PROJECT_NAME} PRIVATE -Wall -pedantic)
+target_compile_definitions( ${PROJECT_NAME}
+ PRIVATE
+ _FILE_OFFSET_BITS=64
+ _XOPEN_SOURCE=700
+ LFS_MIGRATE
+)
+
+target_include_directories( ${PROJECT_NAME}
+ PRIVATE
+ lfsfuse/littlefs
+ ${FUSE_INCLUDE_DIR}
+)
+
+target_link_libraries( ${PROJECT_NAME} ${FUSE_LIBRARIES} )
A host-tools/littlefs-fuse/lfsfuse => host-tools/littlefs-fuse/lfsfuse +1 -0
@@ 0,0 1,1 @@
+Subproject commit ccb30b35edf32cea35bf09cfbfc6b9f6e5f1d930
M module-vfs/CMakeLists.txt => module-vfs/CMakeLists.txt +3 -1
@@ 1,10 1,12 @@
cmake_minimum_required(VERSION 3.12)
include(thirdparty)
-include(thirdparty/littlefs.cmake)
+
project(module-vfs VERSION 1.0
DESCRIPTION "VFS module library")
+add_subdirectory(thirdparty/lfsfs)
+
set(FREERTOS_FAT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/board/freeRTOS_FAT/ff_crc.c
${CMAKE_CURRENT_SOURCE_DIR}/board/freeRTOS_FAT/ff_dir.c
R module-vfs/thirdparty/littlefs.cmake => module-vfs/thirdparty/lfsfs/CMakeLists.txt +14 -6
@@ 1,9 1,11 @@
-include(thirdparty)
+cmake_minimum_required(VERSION 3.14)
-set(LIBLITTLEFS_TARGET littlefs)
+project(littlefs LANGUAGES C)
+include(thirdparty OPTIONAL)
-set(LIBLITTLEFS_SRCDIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/littlefs)
+set(LIBLITTLEFS_SRCDIR littlefs)
+set(LIBLITTLEFS_TARGET ${PROJECT_NAME})
set(LIBLITTLEFS_SOURCES
${LIBLITTLEFS_SRCDIR}/lfs.c
@@ 18,7 20,12 @@ set( LIBLITTLEFS_PRIVATE_INCLUDES ${LIBLITTLEFS_SRCDIR}/bd)
add_library( ${LIBLITTLEFS_TARGET} STATIC ${LIBLITTLEFS_SOURCES} )
-third_party_target_setup( ${LIBLITTLEFS_TARGET} )
+
+#target_compile_definitions(${LIBLITTLEFS_TARGET} PRIVATE LFS_YES_TRACE )
+
+if( COMMAND third_party_target_setup )
+ third_party_target_setup( ${LIBLITTLEFS_TARGET} )
+endif()
target_include_directories( ${LIBLITTLEFS_TARGET}
PUBLIC
@@ 26,5 33,6 @@ target_include_directories( ${LIBLITTLEFS_TARGET}
PRIVATE
${LIBLITTLEFS_PRIVATE_INCLUDES}
)
-
-third_party_source_optimization( ${LIBLITTLEFS_SOURCES} )
+if( COMMAND third_party_source_optimization )
+ third_party_source_optimization( ${LIBLITTLEFS_SOURCES} )
+endif()
A module-vfs/thirdparty/lfsfs/littlefs => module-vfs/thirdparty/lfsfs/littlefs +1 -0
@@ 0,0 1,1 @@
+Subproject commit 1a59954ec64ca168828a15242cc6de94ac75f9d1
D module-vfs/thirdparty/littlefs => module-vfs/thirdparty/littlefs +0 -1
@@ 1,1 0,0 @@
-Subproject commit 4c9146ea539f72749d6cc3ea076372a81b12cb11