~aleteoryx/muditaos

58cc0a1847c4eaf08be3ac25b19c37bb5c21aef1 — Lucjan Bryndza 5 years ago 9cf04ca
[EGD-4760] littlefs host tools (#1161)

* [EGD-4760] Remove littlefs sumbmodule

* [EGD-4760] littlefs submodule in new location

* [EGD-4760] Littlefs host tools inital build

* [EGD-4760] Find fdisk

* [EGD-4760] LFS Cmake file changed

* [EGD-4760] GenLittlefs work in progress

* [EGD-4760] Parse args in the tool

* [EGD-4760] Inital version of genlittlefs

* [EGD-4760] genlittle fs seams to be working

It is a first working release for genlittle fs

Signed-off-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>

* [EGD-4760] genlittle Speed improvement

Signed-off-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>

* [EGD-4760] genlittlefs verbose flag

Signed-off-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>

* [EGD-4760] genlittlefs fix file support

Signed-off-by: Lucjan Bryndza <lucjan.bryndza@mudita.com>

* [EGD-4760] Check if it is a littlefs

* [EGD-4760] Genlittle fs image

* [EGD-4760] Gen image fixed

* [EGD-4760] Final version of genlittlefs

* [EGD-4760] Rename littlefs rootdir.

* [EGD-4760] Code review round #1 fixes
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