2 Star 0 Fork 0

mirrors_ARMmbed/littlefs-fuse

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
lfs_fuse.c 18.01 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
/*
* FUSE wrapper for the littlefs
*
* Copyright (c) 2022, the littlefs authors.
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#define FUSE_USE_VERSION 26
#ifdef linux
// needed for a few things fuse depends on
#define _XOPEN_SOURCE 700
#endif
#include <fuse/fuse.h>
#include "lfs.h"
#include "lfs_util.h"
#include "lfs_fuse_bd.h"
#include <stdio.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
// littefs-fuse version
//
// Note this is different from the littlefs core version, and littlefs
// on-disk version
//
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_FUSE_VERSION 0x00020007
#define LFS_FUSE_VERSION_MAJOR (0xffff & (LFS_FUSE_VERSION >> 16))
#define LFS_FUSE_VERSION_MINOR (0xffff & (LFS_FUSE_VERSION >> 0))
// config and other state
static struct lfs_config config = {0};
static const char *device = NULL;
static bool stat_ = false;
static bool format = false;
static bool migrate = false;
static lfs_t lfs;
// actual fuse functions
void lfs_fuse_defaults(struct lfs_config *config) {
// default to 512 erase cycles, arbitrary value
if (!config->block_cycles) {
config->block_cycles = 512;
}
// defaults, ram is less of a concern here than what
// littlefs is used to, so these may end up a bit funny
if (!config->prog_size) {
config->prog_size = config->block_size;
}
if (!config->read_size) {
config->read_size = config->block_size;
}
if (!config->cache_size) {
config->cache_size = config->block_size;
}
// arbitrary, though we have a lot of RAM here
if (!config->lookahead_size) {
config->lookahead_size = 8192;
}
}
void *lfs_fuse_init(struct fuse_conn_info *conn) {
// set that we want to take care of O_TRUNC
conn->want |= FUSE_CAP_ATOMIC_O_TRUNC;
// we also support writes of any size
conn->want |= FUSE_CAP_BIG_WRITES;
return 0;
}
int lfs_fuse_stat(void) {
int err = lfs_fuse_bd_create(&config, device);
if (err) {
return err;
}
lfs_fuse_defaults(&config);
err = lfs_mount(&lfs, &config);
if (err) {
goto failed;
}
// get on-disk info
struct lfs_fsinfo fsinfo;
err = lfs_fs_stat(&lfs, &fsinfo);
if (err) {
goto failed;
}
// get block usage
lfs_ssize_t in_use = lfs_fs_size(&lfs);
if (in_use < 0) {
err = in_use;
goto failed;
}
// print to stdout
printf("disk_version: lfs%d.%d\n",
0xffff & (fsinfo.disk_version >> 16),
0xffff & (fsinfo.disk_version >> 0));
printf("block_size: %d\n", config.block_size);
printf("block_count: %d\n", config.block_count);
printf(" used: %d/%d (%.1f%%)\n",
in_use,
config.block_count,
100.0f * (float)in_use / (float)config.block_count);
printf(" free: %d/%d (%.1f%%)\n",
config.block_count-in_use,
config.block_count,
100.0f * (float)(config.block_count-in_use)
/ (float)config.block_count);
printf("name_max: %d\n", fsinfo.name_max);
printf("file_max: %d\n", fsinfo.file_max);
printf("attr_max: %d\n", fsinfo.attr_max);
err = lfs_unmount(&lfs);
failed:
lfs_fuse_bd_destroy(&config);
return err;
}
int lfs_fuse_format(void) {
int err = lfs_fuse_bd_create(&config, device);
if (err) {
return err;
}
lfs_fuse_defaults(&config);
err = lfs_format(&lfs, &config);
lfs_fuse_bd_destroy(&config);
return err;
}
int lfs_fuse_migrate(void) {
int err = lfs_fuse_bd_create(&config, device);
if (err) {
return err;
}
lfs_fuse_defaults(&config);
err = lfs_migrate(&lfs, &config);
lfs_fuse_bd_destroy(&config);
return err;
}
int lfs_fuse_mount(void) {
int err = lfs_fuse_bd_create(&config, device);
if (err) {
return err;
}
lfs_fuse_defaults(&config);
return lfs_mount(&lfs, &config);
}
void lfs_fuse_destroy(void *eh) {
lfs_unmount(&lfs);
lfs_fuse_bd_destroy(&config);
}
int lfs_fuse_statfs(const char *path, struct statvfs *s) {
memset(s, 0, sizeof(struct statvfs));
// get the on-disk name_max from littlefs
struct lfs_fsinfo fsinfo;
int err = lfs_fs_stat(&lfs, &fsinfo);
if (err) {
return err;
}
// get the filesystem block usage from littlefs
lfs_ssize_t in_use = lfs_fs_size(&lfs);
if (in_use < 0) {
return in_use;
}
s->f_bsize = config.block_size;
s->f_frsize = config.block_size;
s->f_blocks = config.block_count;
s->f_bfree = config.block_count - in_use;
s->f_bavail = config.block_count - in_use;
s->f_namemax = fsinfo.name_max;
return 0;
}
static void lfs_fuse_tostat(struct stat *s, struct lfs_info *info) {
memset(s, 0, sizeof(struct stat));
s->st_size = info->size;
s->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
switch (info->type) {
case LFS_TYPE_DIR: s->st_mode |= S_IFDIR; break;
case LFS_TYPE_REG: s->st_mode |= S_IFREG; break;
}
}
int lfs_fuse_getattr(const char *path, struct stat *s) {
struct lfs_info info;
int err = lfs_stat(&lfs, path, &info);
if (err) {
return err;
}
lfs_fuse_tostat(s, &info);
return 0;
}
int lfs_fuse_access(const char *path, int mask) {
struct lfs_info info;
return lfs_stat(&lfs, path, &info);
}
int lfs_fuse_mkdir(const char *path, mode_t mode) {
return lfs_mkdir(&lfs, path);
}
int lfs_fuse_opendir(const char *path, struct fuse_file_info *fi) {
lfs_dir_t *dir = malloc(sizeof(lfs_dir_t));
memset(dir, 0, sizeof(lfs_dir_t));
int err = lfs_dir_open(&lfs, dir, path);
if (err) {
free(dir);
return err;
}
fi->fh = (uint64_t)dir;
return 0;
}
int lfs_fuse_releasedir(const char *path, struct fuse_file_info *fi) {
lfs_dir_t *dir = (lfs_dir_t*)fi->fh;
int err = lfs_dir_close(&lfs, dir);
free(dir);
return err;
}
int lfs_fuse_readdir(const char *path, void *buf,
fuse_fill_dir_t filler, off_t offset,
struct fuse_file_info *fi) {
lfs_dir_t *dir = (lfs_dir_t*)fi->fh;
struct stat s;
struct lfs_info info;
while (true) {
int err = lfs_dir_read(&lfs, dir, &info);
if (err != 1) {
return err;
}
lfs_fuse_tostat(&s, &info);
filler(buf, info.name, &s, 0);
}
}
int lfs_fuse_rename(const char *from, const char *to) {
return lfs_rename(&lfs, from, to);
}
int lfs_fuse_unlink(const char *path) {
return lfs_remove(&lfs, path);
}
int lfs_fuse_open(const char *path, struct fuse_file_info *fi) {
lfs_file_t *file = malloc(sizeof(lfs_file_t));
memset(file, 0, sizeof(lfs_file_t));
int flags = 0;
if ((fi->flags & 3) == O_RDONLY) flags |= LFS_O_RDONLY;
if ((fi->flags & 3) == O_WRONLY) flags |= LFS_O_WRONLY;
if ((fi->flags & 3) == O_RDWR) flags |= LFS_O_RDWR;
if (fi->flags & O_CREAT) flags |= LFS_O_CREAT;
if (fi->flags & O_EXCL) flags |= LFS_O_EXCL;
if (fi->flags & O_TRUNC) flags |= LFS_O_TRUNC;
if (fi->flags & O_APPEND) flags |= LFS_O_APPEND;
int err = lfs_file_open(&lfs, file, path, flags);
if (err) {
free(file);
return err;
}
fi->fh = (uint64_t)file;
return 0;
}
int lfs_fuse_release(const char *path, struct fuse_file_info *fi) {
lfs_file_t *file = (lfs_file_t*)fi->fh;
int err = lfs_file_close(&lfs, file);
free(file);
return err;
}
int lfs_fuse_fgetattr(const char *path, struct stat *s,
struct fuse_file_info *fi) {
lfs_file_t *file = (lfs_file_t*)fi->fh;
lfs_fuse_tostat(s, &(struct lfs_info){
.size = lfs_file_size(&lfs, file),
.type = LFS_TYPE_REG,
});
return 0;
}
int lfs_fuse_read(const char *path, char *buf, size_t size,
off_t off, struct fuse_file_info *fi) {
lfs_file_t *file = (lfs_file_t*)fi->fh;
if (lfs_file_tell(&lfs, file) != off) {
lfs_soff_t soff = lfs_file_seek(&lfs, file, off, LFS_SEEK_SET);
if (soff < 0) {
return soff;
}
}
return lfs_file_read(&lfs, file, buf, size);
}
int lfs_fuse_write(const char *path, const char *buf, size_t size,
off_t off, struct fuse_file_info *fi) {
lfs_file_t *file = (lfs_file_t*)fi->fh;
if (lfs_file_tell(&lfs, file) != off) {
lfs_soff_t soff = lfs_file_seek(&lfs, file, off, LFS_SEEK_SET);
if (soff < 0) {
return soff;
}
}
return lfs_file_write(&lfs, file, buf, size);
}
int lfs_fuse_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi) {
lfs_file_t *file = (lfs_file_t*)fi->fh;
return lfs_file_sync(&lfs, file);
}
int lfs_fuse_flush(const char *path, struct fuse_file_info *fi) {
lfs_file_t *file = (lfs_file_t*)fi->fh;
return lfs_file_sync(&lfs, file);
}
int lfs_fuse_create(const char *path, mode_t mode, struct fuse_file_info *fi) {
int err = lfs_fuse_open(path, fi);
if (err) {
return err;
}
return lfs_fuse_fsync(path, 0, fi);
}
int lfs_fuse_ftruncate(const char *path, off_t size,
struct fuse_file_info *fi) {
lfs_file_t *file = (lfs_file_t*)fi->fh;
return lfs_file_truncate(&lfs, file, size);
}
int lfs_fuse_truncate(const char *path, off_t size) {
lfs_file_t file;
int err = lfs_file_open(&lfs, &file, path, LFS_O_WRONLY);
if (err) {
return err;
}
err = lfs_file_truncate(&lfs, &file, size);
if (err) {
return err;
}
return lfs_file_close(&lfs, &file);
}
// unsupported functions
int lfs_fuse_link(const char *from, const char *to) {
// not supported, fail
return -EPERM;
}
int lfs_fuse_mknod(const char *path, mode_t mode, dev_t dev) {
// not supported, fail
return -EPERM;
}
int lfs_fuse_chmod(const char *path, mode_t mode) {
// not supported, always succeed
return 0;
}
int lfs_fuse_chown(const char *path, uid_t uid, gid_t gid) {
// not supported, fail
return -EPERM;
}
int lfs_fuse_utimens(const char *path, const struct timespec ts[2]) {
// not supported, always succeed
return 0;
}
static struct fuse_operations lfs_fuse_ops = {
.init = lfs_fuse_init,
.destroy = lfs_fuse_destroy,
.statfs = lfs_fuse_statfs,
.getattr = lfs_fuse_getattr,
.access = lfs_fuse_access,
.mkdir = lfs_fuse_mkdir,
.rmdir = lfs_fuse_unlink,
.opendir = lfs_fuse_opendir,
.releasedir = lfs_fuse_releasedir,
.readdir = lfs_fuse_readdir,
.rename = lfs_fuse_rename,
.unlink = lfs_fuse_unlink,
.open = lfs_fuse_open,
.create = lfs_fuse_create,
.truncate = lfs_fuse_truncate,
.release = lfs_fuse_release,
.fgetattr = lfs_fuse_fgetattr,
.read = lfs_fuse_read,
.write = lfs_fuse_write,
.fsync = lfs_fuse_fsync,
.flush = lfs_fuse_flush,
.link = lfs_fuse_link,
.symlink = lfs_fuse_link,
.mknod = lfs_fuse_mknod,
.chmod = lfs_fuse_chmod,
.chown = lfs_fuse_chown,
.utimens = lfs_fuse_utimens,
};
// binding into fuse and general ui
enum lfs_fuse_keys {
KEY_HELP,
KEY_VERSION,
KEY_STAT,
KEY_FORMAT,
KEY_MIGRATE,
KEY_DISK_VERSION,
};
#define OPT(t, p) { t, offsetof(struct lfs_config, p), 0}
static struct fuse_opt lfs_fuse_opts[] = {
FUSE_OPT_KEY("--stat", KEY_STAT),
FUSE_OPT_KEY("--format", KEY_FORMAT),
FUSE_OPT_KEY("--migrate", KEY_MIGRATE),
{"-d=", -1U, KEY_DISK_VERSION},
{"--disk_version=", -1U, KEY_DISK_VERSION},
OPT("-b=%" SCNu32, block_size),
OPT("--block_size=%" SCNu32, block_size),
OPT("--block_count=%" SCNu32, block_count),
OPT("--block_cycles=%" SCNu32, block_cycles),
OPT("--read_size=%" SCNu32, read_size),
OPT("--prog_size=%" SCNu32, prog_size),
OPT("--cache_size=%" SCNu32, cache_size),
OPT("--lookahead_size=%" SCNu32, lookahead_size),
OPT("--name_max=%" SCNu32, name_max),
OPT("--file_max=%" SCNu32, file_max),
OPT("--attr_max=%" SCNu32, attr_max),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_END
};
static const char help_text[] =
"usage: %s [options] device mountpoint\n"
"\n"
"general options:\n"
" -o opt,[opt...] FUSE options\n"
" -h --help print help\n"
" -V --version print version\n"
"\n"
"littlefs options:\n"
" --stat print filesystem info instead of mounting\n"
" --format format instead of mounting\n"
" --migrate migrate previous version instead of mounting\n"
" -d --disk_version attempt to use this on-disk version of littlefs\n"
" -b --block_size logical block size, overrides the block device\n"
" --block_count block count, overrides the block device\n"
" --block_cycles number of erase cycles before eviction (512)\n"
" --read_size readable unit (block_size)\n"
" --prog_size programmable unit (block_size)\n"
" --cache_size size of caches (block_size)\n"
" --lookahead_size size of lookahead buffer (8192)\n"
" --name_max max size of file names (255)\n"
" --file_max max size of file contents (2147483647)\n"
" --attr_max max size of custom attributes (1022)\n"
"\n";
int lfs_fuse_opt_proc(void *data, const char *arg,
int key, struct fuse_args *args) {
// option parsing
switch (key) {
case FUSE_OPT_KEY_NONOPT:
if (!device) {
device = strdup(arg);
return 0;
}
break;
case KEY_STAT:
stat_ = true;
return 0;
case KEY_FORMAT:
format = true;
return 0;
case KEY_MIGRATE:
migrate = true;
return 0;
case KEY_HELP:
fprintf(stderr, help_text, args->argv[0]);
fuse_opt_add_arg(args, "-ho");
fuse_main(args->argc, args->argv, &lfs_fuse_ops, NULL);
exit(1);
case KEY_VERSION:
fprintf(stderr, "littlefs-fuse version: v%d.%d\n",
LFS_FUSE_VERSION_MAJOR, LFS_FUSE_VERSION_MINOR);
fprintf(stderr, "littlefs version: v%d.%d\n",
LFS_VERSION_MAJOR, LFS_VERSION_MINOR);
fprintf(stderr, "littlefs disk version: lfs%d.%d\n",
LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
fuse_opt_add_arg(args, "--version");
fuse_main(args->argc, args->argv, &lfs_fuse_ops, NULL);
exit(0);
case KEY_DISK_VERSION: {
// skip opt prefix
const char *arg_ = strchr(arg, '=');
if (arg_) {
arg = arg_ + 1;
}
// parse out the requested disk version
// supported formats:
// - no-prefix - 2.1
// - v-prefix - v2.1
// - lfs-prefix - lfs2.1
// - littlefs-prefix - littlefs2.1
const char *orig_arg = arg;
if (strlen(arg) >= strlen("v")
&& memcmp(arg, "v", strlen("v")) == 0) {
arg += strlen("v");
} else if (strlen(arg) >= strlen("lfs")
&& memcmp(arg, "lfs", strlen("lfs")) == 0) {
arg += strlen("lfs");
} else if (strlen(arg) >= strlen("littlefs")
&& memcmp(arg, "littlefs", strlen("littlefs")) == 0) {
arg += strlen("littlefs");
}
char *parsed;
uintmax_t major = strtoumax(arg, &parsed, 0);
if (parsed == arg) {
goto invalid_version;
}
arg = parsed;
if (arg[0] != '.') {
goto invalid_version;
}
arg += 1;
uintmax_t minor = strtoumax(arg, &parsed, 0);
if (parsed == arg) {
goto invalid_version;
}
arg = parsed;
if (arg[0] != '\0') {
goto invalid_version;
}
if (major > 0xffff || minor > 0xffff) {
goto invalid_version;
}
config.disk_version
= ((major & 0xffff) << 16)
| ((minor & 0xffff) << 0);
return 0;
invalid_version:
fprintf(stderr, "invalid disk version: \"%s\"\n", orig_arg);
exit(1);
}
}
return 1;
}
int main(int argc, char *argv[]) {
// parse custom options
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
fuse_opt_parse(&args, &config, lfs_fuse_opts, lfs_fuse_opt_proc);
if (!device) {
fprintf(stderr, "missing device parameter\n");
exit(1);
}
if (stat_) {
// stat time, no mount
int err = lfs_fuse_stat();
if (err) {
LFS_ERROR("%s", strerror(-err));
exit(-err);
}
exit(0);
}
if (format) {
// format time, no mount
int err = lfs_fuse_format();
if (err) {
LFS_ERROR("%s", strerror(-err));
exit(-err);
}
exit(0);
}
if (migrate) {
// migrate time, no mount
int err = lfs_fuse_migrate();
if (err) {
LFS_ERROR("%s", strerror(-err));
exit(-err);
}
exit(0);
}
// go ahead and mount so errors are reported before backgrounding
int err = lfs_fuse_mount();
if (err) {
LFS_ERROR("%s", strerror(-err));
exit(-err);
}
// always single-threaded
fuse_opt_add_arg(&args, "-s");
// enter fuse
err = fuse_main(args.argc, args.argv, &lfs_fuse_ops, NULL);
if (err) {
lfs_fuse_destroy(NULL);
}
return err;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors_ARMmbed/littlefs-fuse.git
git@gitee.com:mirrors_ARMmbed/littlefs-fuse.git
mirrors_ARMmbed
littlefs-fuse
littlefs-fuse
master

搜索帮助