// 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 "parse_args.h" #include #include #include #include #include #include #include #include #include #include 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 = 0; 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 = 0; 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 { // 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(int error, const char *format, ...) __attribute__((nonnull(2))); static void print_error(int error, const char *format, ...) { va_list arglist; va_start(arglist, format); vfprintf(stderr, format, arglist); va_end(arglist); if (error == -1) { char buf[1024]; fprintf(stderr, " system_error: %s\n", strerror_r(errno, buf, sizeof buf)); } else { fprintf(stderr, " lfs_error: %i\n", 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 = 0; 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) { fprintf(stderr, "Unable to open file: %s error %s\n", lopts.dst_image, strerror(errno)); free(parts); free(lopts.src_dirs); return EXIT_FAILURE; } free(parts); if (write_partition_bootunit(lopts.dst_image, lopts.partition_num, lopts.block_size)) { perror("Unable to write bootunit"); free(lopts.src_dirs); lfs_ioaccess_close(ioctx); return EXIT_FAILURE; } } 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"); close(fds); 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(err, "Unable to open file: %s", lopts.src_dirs[ndir]); lfs_ioaccess_close(ioctx); free(lopts.src_dirs); lfs_unmount(&lfs); return EXIT_FAILURE; } } const lfs_ssize_t used_blocks = lfs_fs_size(&lfs); 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: %i/%i.\n", prog_summary.directories_added, prog_summary.files_added, prog_summary.bytes_transferred / 1024UL, cfg.block_size, used_blocks, cfg.block_count); return EXIT_SUCCESS; }