kv_lfs

2 阅读2分钟

kv_lfs.h

#ifndef KV_LFS_H
#define KV_LFS_H

#include <stddef.h>
#include <stdbool.h>

int  kv_init(const char *mount_point);          /* 挂载点,如 /kv    */
int  kv_set(const char *key, const void *val, size_t len);
int  kv_get(const char *key, void *buf, size_t *len); /* 出:实际长度     */
int  kv_del(const char *key);
bool kv_exist(const char *key);
void kv_deinit(void);

#endif

kv_lfs.c

/*
 *  MIT License  (c) 2024
 *  KV over LittleFS  for embedded Linux
 */
#include "kv_lfs.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "lfs.h"          /* LittleFS 头 */

#define KV_MAX_KEY    31
#define KV_TMP_FILE   "_kv.tmp"
static char g_mount[64] = {0};

static int _make_path(char *dst, size_t dst_len, const char *key)
{
    /* key 中不能出现 '/' */
    if (strchr(key, '/')) return -1;
    int n = snprintf(dst, dst_len, "%s/%s", g_mount, key);
    return (n < (int)dst_len) ? 0 : -1;
}

int kv_init(const char *mount_point)
{
    strncpy(g_mount, mount_point, sizeof(g_mount) - 1);
    struct stat st;
    if (stat(g_mount, &st) != 0 && mkdir(g_mount, 0777) != 0) return -1;
    return 0;
}

int kv_set(const char *key, const void *val, size_t len)
{
    char path[128], tmp[128];
    if (_make_path(path, sizeof(path), key) < 0) return -1;
    if (snprintf(tmp, sizeof(tmp), "%s/%s", g_mount, KV_TMP_FILE) >= (int)sizeof(tmp)) return -1;

    FILE *f = fopen(tmp, "wb");
    if (!f) return -1;
    if (fwrite(val, 1, len, f) != len) { fclose(f); unlink(tmp); return -1; }
    if (fflush(f) != 0 || fclose(f) != 0) { unlink(tmp); return -1; }

    return rename(tmp, path) == 0 ? 0 : -1;
}

int kv_get(const char *key, void *buf, size_t *len)
{
    char path[128];
    if (_make_path(path, sizeof(path), key) < 0) return -1;
    struct stat st;
    if (stat(path, &st) != 0) return -1;

    FILE *f = fopen(path, "rb");
    if (!f) return -1;
    size_t need = *len;
    *len = fread(buf, 1, need, f);
    fclose(f);
    return 0;
}

int kv_del(const char *key)
{
    char path[128];
    if (_make_path(path, sizeof(path), key) < 0) return -1;
    return unlink(path) == 0 ? 0 : -1;
}

bool kv_exist(const char *key)
{
    char path[128];
    if (_make_path(path, sizeof(path), key) < 0) return false;
    return access(path, F_OK) == 0;
}

void kv_deinit(void) { /* LittleFS 卸载由应用调用 lfs_unmount */ }

test.c

kv_init("/lfs/kv");                 /* 启动时一次 */

uint32_t boot_cnt = 0;
size_t len = sizeof(boot_cnt);
if (kv_get("boot_cnt", &boot_cnt, &len) == 0)
    boot_cnt++;
else
    boot_cnt = 1;
kv_set("boot_cnt", &boot_cnt, sizeof(boot_cnt));

/* 传大文件 */
FILE *fp = fopen("/userdata/fw.bin", "rb");
char buf[4096];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
    kv_set("fw", buf, n);           /* 实际可改为流式追加,这里演示原子写 */
fclose(fp);