mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-28 23:29:45 +01:00
nanocoap_fs: add nanoCoAP as VFS backend
This commit is contained in:
parent
994211d955
commit
a87687c14e
@ -550,6 +550,12 @@ ifneq (,$(filter nanocoap_cache,$(USEMODULE)))
|
||||
USEMODULE += hashes
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nanocoap_fs,$(USEMODULE)))
|
||||
USEMODULE += nanocoap_sock
|
||||
USEMODULE += nanocoap_link_format
|
||||
USEMODULE += vfs
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nanocoap_link_format,$(USEMODULE)))
|
||||
USEMODULE += fmt
|
||||
endif
|
||||
|
67
sys/include/net/nanocoap/fs.h
Normal file
67
sys/include/net/nanocoap/fs.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2024 ML!PA Consulting GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup net_nanosock
|
||||
* @brief nanoCoAP virtual file system
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*/
|
||||
#ifndef NET_NANOCOAP_FS_H
|
||||
#define NET_NANOCOAP_FS_H
|
||||
|
||||
#include "mutex.h"
|
||||
#include "net/nanocoap_sock.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief nanoCoAP file system configuration
|
||||
*/
|
||||
typedef struct {
|
||||
const char *url; /**< base URL of the remote fs */
|
||||
nanocoap_sock_t sock; /**< connection to the remote server */
|
||||
mutex_t lock; /**< lock for common urlbuf */
|
||||
char urlbuf[CONFIG_SOCK_URLPATH_MAXLEN]; /**< shared url buffer */
|
||||
} nanocoap_fs_t;
|
||||
|
||||
/**
|
||||
* @brief nanoCoAP remote file struct
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t offset; /**< offset into the file */
|
||||
char urlbuf[CONFIG_SOCK_URLPATH_MAXLEN]; /**< full path to the file */
|
||||
} nanocoap_fs_file_t;
|
||||
|
||||
/**
|
||||
* @brief nanoCoAP remote dir struct
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t offset; /**< current directory element */
|
||||
char urlbuf[CONFIG_SOCK_URLPATH_MAXLEN]; /**< full path of the directory */
|
||||
} nanocoap_fs_dir_t;
|
||||
|
||||
/**
|
||||
* @brief nanoCoAP file system driver
|
||||
*
|
||||
* For use with vfs_mount
|
||||
*/
|
||||
extern const vfs_file_system_t nanocoap_fs_file_system;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* NET_NANOCOAP_FS_H */
|
||||
/** @} */
|
@ -61,7 +61,11 @@
|
||||
#include "sched.h"
|
||||
#include "clist.h"
|
||||
#include "iolist.h"
|
||||
#include "macros/utils.h"
|
||||
#include "mtd.h"
|
||||
#ifdef MODULE_NANOCOAP_FS
|
||||
#include "net/sock/config.h"
|
||||
#endif
|
||||
#include "xfa.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -75,21 +79,12 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MAX functions for internal use
|
||||
* @{
|
||||
* @brief MAX6 Function to get the largest of 6 values
|
||||
*/
|
||||
#ifndef _MAX
|
||||
#define _MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#ifndef MAX6
|
||||
#define MAX6(a, b, c, d, e, f) MAX(MAX(MAX(MAX((a), (b)), MAX((c), (d))), (e)), (f))
|
||||
#endif
|
||||
|
||||
#ifndef MAX5
|
||||
/**
|
||||
* @brief MAX5 Function to get the largest of 5 values
|
||||
*/
|
||||
#define MAX5(a, b, c, d, e) _MAX(_MAX(_MAX((a), (b)), _MAX((c),(d))), (e))
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief VFS parameters for FAT
|
||||
* @{
|
||||
@ -208,6 +203,21 @@ extern "C" {
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief VFS parameters for nanoCoAP FS
|
||||
* @{
|
||||
*/
|
||||
#if defined(MODULE_NANOCOAP_FS) || DOXYGEN
|
||||
# define NANOCOAP_FS_VFS_DIR_BUFFER_SIZE \
|
||||
(4 + CONFIG_SOCK_URLPATH_MAXLEN) /**< sizeof(nanocoap_fs_dir_t) */
|
||||
# define NANOCOAP_FS_VFS_FILE_BUFFER_SIZE \
|
||||
(4 + CONFIG_SOCK_URLPATH_MAXLEN) /**< sizeof(nanocoap_fs_file_t) */
|
||||
#else
|
||||
# define NANOCOAP_FS_VFS_DIR_BUFFER_SIZE (1)
|
||||
# define NANOCOAP_FS_VFS_FILE_BUFFER_SIZE (1)
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
#ifndef VFS_MAX_OPEN_FILES
|
||||
/**
|
||||
* @brief Maximum number of simultaneous open files
|
||||
@ -243,11 +253,12 @@ extern "C" {
|
||||
* @attention Put the check in the public header file (.h), do not put the check in the
|
||||
* implementation (.c) file.
|
||||
*/
|
||||
#define VFS_DIR_BUFFER_SIZE MAX5(FATFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LITTLEFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LITTLEFS2_VFS_DIR_BUFFER_SIZE, \
|
||||
SPIFFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LWEXT4_VFS_DIR_BUFFER_SIZE \
|
||||
#define VFS_DIR_BUFFER_SIZE MAX6(FATFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LITTLEFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LITTLEFS2_VFS_DIR_BUFFER_SIZE, \
|
||||
SPIFFS_VFS_DIR_BUFFER_SIZE, \
|
||||
LWEXT4_VFS_DIR_BUFFER_SIZE, \
|
||||
NANOCOAP_FS_VFS_DIR_BUFFER_SIZE \
|
||||
)
|
||||
#endif
|
||||
|
||||
@ -271,11 +282,12 @@ extern "C" {
|
||||
* @attention Put the check in the public header file (.h), do not put the check in the
|
||||
* implementation (.c) file.
|
||||
*/
|
||||
#define VFS_FILE_BUFFER_SIZE MAX5(FATFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LITTLEFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LITTLEFS2_VFS_FILE_BUFFER_SIZE,\
|
||||
SPIFFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LWEXT4_VFS_FILE_BUFFER_SIZE \
|
||||
#define VFS_FILE_BUFFER_SIZE MAX6(FATFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LITTLEFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LITTLEFS2_VFS_FILE_BUFFER_SIZE, \
|
||||
SPIFFS_VFS_FILE_BUFFER_SIZE, \
|
||||
LWEXT4_VFS_FILE_BUFFER_SIZE, \
|
||||
NANOCOAP_FS_VFS_FILE_BUFFER_SIZE \
|
||||
)
|
||||
#endif
|
||||
|
||||
|
322
sys/net/application_layer/nanocoap/fs.c
Normal file
322
sys/net/application_layer/nanocoap/fs.c
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (C) 2024 ML!PA Consulting GmbH
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup net_nanocoap
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief nanoCoAP VFS backend
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "net/nanocoap_sock.h"
|
||||
#include "net/nanocoap/fs.h"
|
||||
#include "net/nanocoap/link_format.h"
|
||||
#include "net/sock/util.h"
|
||||
#include "string_utils.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
static int nanocoap_fs_mount(vfs_mount_t *mountp)
|
||||
{
|
||||
nanocoap_fs_t *fs = mountp->private_data;
|
||||
|
||||
static_assert(VFS_FILE_BUFFER_SIZE >= sizeof(nanocoap_fs_file_t),
|
||||
"nanocoap_fs_file_t must fit into VFS_FILE_BUFFER_SIZE");
|
||||
static_assert(VFS_DIR_BUFFER_SIZE >= sizeof(nanocoap_fs_dir_t),
|
||||
"nanocoap_fs_dir_t must fit into VFS_DIR_BUFFER_SIZE");
|
||||
|
||||
return nanocoap_sock_url_connect(fs->url, &fs->sock);
|
||||
}
|
||||
|
||||
static int nanocoap_fs_umount(vfs_mount_t *mountp)
|
||||
{
|
||||
nanocoap_fs_t *fs = mountp->private_data;
|
||||
|
||||
nanocoap_sock_close(&fs->sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fill_urlbuf(nanocoap_fs_t *fs, char dst[CONFIG_SOCK_URLPATH_MAXLEN],
|
||||
const char *name, bool dir)
|
||||
{
|
||||
name += 1; /* skip leading '/' */
|
||||
|
||||
const char *extra = "";
|
||||
if (dir) {
|
||||
const char *end = name + strlen(name) - 1;
|
||||
if (*end != '/') {
|
||||
extra = "/";
|
||||
}
|
||||
}
|
||||
|
||||
if ((unsigned)snprintf(dst, CONFIG_SOCK_URLPATH_MAXLEN, "%s%s%s",
|
||||
sock_urlpath(fs->url), name, extra) > CONFIG_SOCK_URLPATH_MAXLEN) {
|
||||
DEBUG("nanocoap_fs: %s%s > %u bytes\n",
|
||||
sock_urlpath(fs->url), name, CONFIG_SOCK_URLPATH_MAXLEN);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nanocoap_fs_unlink(vfs_mount_t *mountp, const char *name)
|
||||
{
|
||||
nanocoap_fs_t *fs = mountp->private_data;
|
||||
int res;
|
||||
|
||||
mutex_lock(&fs->lock);
|
||||
|
||||
if ((res = _fill_urlbuf(fs, fs->urlbuf, name, true))) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
DEBUG("nanocoap_fs: delete %s\n", fs->urlbuf);
|
||||
res = nanocoap_sock_delete(&fs->sock, fs->urlbuf);
|
||||
|
||||
out:
|
||||
mutex_unlock(&fs->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int nanocoap_fs_open(vfs_file_t *filp, const char *name, int flags, mode_t mode)
|
||||
{
|
||||
nanocoap_fs_t *fs = filp->mp->private_data;
|
||||
nanocoap_fs_file_t *file = (void *)filp->private_data.buffer;
|
||||
int res;
|
||||
|
||||
(void)mode;
|
||||
|
||||
if (flags != O_RDONLY) {
|
||||
/* so far only read is implemented */
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if ((res = _fill_urlbuf(fs, file->urlbuf, name, false))) {
|
||||
return res;
|
||||
}
|
||||
|
||||
DEBUG("nanocoap_fs: open %s\n", file->urlbuf);
|
||||
|
||||
file->offset = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t nanocoap_fs_read(vfs_file_t *filp, void *dest, size_t nbytes)
|
||||
{
|
||||
nanocoap_fs_t *fs = filp->mp->private_data;
|
||||
nanocoap_fs_file_t *file = (void *)filp->private_data.buffer;
|
||||
int res;
|
||||
|
||||
DEBUG("nanocoap_fs: read %"PRIuSIZE" bytes\n", nbytes);
|
||||
|
||||
res = nanocoap_sock_get_slice(&fs->sock, file->urlbuf, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT,
|
||||
file->offset, dest, nbytes);
|
||||
if (res > 0) {
|
||||
file->offset += res;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static off_t nanocoap_fs_lseek(vfs_file_t *filp, off_t off, int whence)
|
||||
{
|
||||
nanocoap_fs_file_t *file = (void *)filp->private_data.buffer;
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
file->offset = off;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
file->offset += off;
|
||||
break;
|
||||
case SEEK_END:
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return file->offset;
|
||||
}
|
||||
|
||||
static int _block_cb(void *arg, coap_pkt_t *pkt)
|
||||
{
|
||||
struct stat *restrict buf = arg;
|
||||
|
||||
if (coap_get_code_class(pkt) != COAP_CLASS_SUCCESS) {
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
buf->st_mode = S_IFREG;
|
||||
|
||||
uint32_t size = 0;
|
||||
coap_opt_get_uint(pkt, COAP_OPT_SIZE2, &size);
|
||||
buf->st_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _query_server(nanocoap_sock_t *sock, const char *path,
|
||||
struct stat *restrict arg)
|
||||
{
|
||||
uint8_t _buf[CONFIG_NANOCOAP_BLOCK_HEADER_MAX];
|
||||
uint8_t *buf = _buf;
|
||||
|
||||
coap_pkt_t pkt = {
|
||||
.hdr = (void *)buf,
|
||||
};
|
||||
|
||||
uint16_t lastonum = 0;
|
||||
|
||||
buf += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET,
|
||||
nanocoap_sock_next_msg_id(sock));
|
||||
buf += coap_opt_put_uri_pathquery(buf, &lastonum, path);
|
||||
buf += coap_opt_put_uint(buf, lastonum, COAP_OPT_BLOCK2, 0);
|
||||
buf += coap_opt_put_uint(buf, COAP_OPT_BLOCK2, COAP_OPT_SIZE2, 0);
|
||||
|
||||
assert((uintptr_t)buf - (uintptr_t)pkt.hdr < sizeof(_buf));
|
||||
|
||||
pkt.payload = buf;
|
||||
pkt.payload_len = 0;
|
||||
|
||||
return nanocoap_sock_request_cb(sock, &pkt, _block_cb, arg);
|
||||
}
|
||||
|
||||
static int nanocoap_fs_stat(vfs_mount_t *mountp, const char *restrict path,
|
||||
struct stat *restrict buf)
|
||||
{
|
||||
nanocoap_fs_t *fs = mountp->private_data;
|
||||
int res = 0;
|
||||
|
||||
mutex_lock(&fs->lock);
|
||||
|
||||
if ((res = _fill_urlbuf(fs, fs->urlbuf, path, false))) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint8_t dummy[4];
|
||||
if (_query_server(&fs->sock, fs->urlbuf, buf) < 0) {
|
||||
if ((res = _fill_urlbuf(fs, fs->urlbuf, path, true))) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (nanocoap_sock_get_slice(&fs->sock, fs->urlbuf, COAP_BLOCKSIZE_16,
|
||||
0, dummy, sizeof(dummy)) >= 0) {
|
||||
buf->st_mode = S_IFDIR;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&fs->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int nanocoap_fs_opendir(vfs_DIR *dirp, const char *dirname)
|
||||
{
|
||||
nanocoap_fs_t *fs = dirp->mp->private_data;
|
||||
nanocoap_fs_dir_t *dir = (void *)dirp->private_data.buffer;
|
||||
int res;
|
||||
|
||||
if ((res = _fill_urlbuf(fs, dir->urlbuf, dirname, true))) {
|
||||
return res;
|
||||
}
|
||||
|
||||
DEBUG("nanocoap_fs: opendir(%s)\n", dir->urlbuf);
|
||||
|
||||
dir->offset = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct _dir_ctx {
|
||||
vfs_dirent_t *dirent;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
static int _dir_cb(char *entry, void *arg)
|
||||
{
|
||||
struct _dir_ctx *ctx = arg;
|
||||
|
||||
if (ctx->offset) {
|
||||
--ctx->offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *start = strchr(entry, '<');
|
||||
if (start) {
|
||||
char *end = strchr(entry, '>');
|
||||
*end = '\0';
|
||||
entry = start + 1;
|
||||
}
|
||||
|
||||
char *end = entry + strlen(entry) - 1;
|
||||
if (*end == '/') {
|
||||
*end = '\0';
|
||||
}
|
||||
|
||||
char *basename = strrchr(entry, '/');
|
||||
if (basename) {
|
||||
entry = basename + 1;
|
||||
}
|
||||
|
||||
strscpy(ctx->dirent->d_name, entry, sizeof(ctx->dirent->d_name));
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
static int nanocoap_fs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
|
||||
{
|
||||
nanocoap_fs_t *fs = dirp->mp->private_data;
|
||||
nanocoap_fs_dir_t *dir = (void *)dirp->private_data.buffer;
|
||||
int res;
|
||||
|
||||
entry->d_ino = dir->offset;
|
||||
|
||||
struct _dir_ctx ctx = {
|
||||
.dirent = entry,
|
||||
.offset = dir->offset++,
|
||||
};
|
||||
|
||||
res = nanocoap_link_format_get(&fs->sock, dir->urlbuf, _dir_cb, &ctx);
|
||||
if (res == -EINTR) {
|
||||
/* we use this to abort listing early */
|
||||
res = 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static const vfs_file_system_ops_t nanocoap_fs_ops = {
|
||||
.stat = nanocoap_fs_stat,
|
||||
.mount = nanocoap_fs_mount,
|
||||
.umount = nanocoap_fs_umount,
|
||||
.unlink = nanocoap_fs_unlink,
|
||||
};
|
||||
|
||||
static const vfs_file_ops_t nanocoap_fs_file_ops = {
|
||||
.lseek = nanocoap_fs_lseek,
|
||||
.open = nanocoap_fs_open,
|
||||
.read = nanocoap_fs_read,
|
||||
};
|
||||
|
||||
static const vfs_dir_ops_t nanocoap_fs_dir_ops = {
|
||||
.opendir = nanocoap_fs_opendir,
|
||||
.readdir = nanocoap_fs_readdir,
|
||||
};
|
||||
|
||||
const vfs_file_system_t nanocoap_fs_file_system = {
|
||||
.f_op = &nanocoap_fs_file_ops,
|
||||
.fs_op = &nanocoap_fs_ops,
|
||||
.d_op = &nanocoap_fs_dir_ops,
|
||||
};
|
Loading…
Reference in New Issue
Block a user