diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 9dd0999c7d..0cd06ec26b 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -86,6 +86,7 @@ #include #ifdef RIOT_VERSION +#include "bitarithm.h" #include "bitfield.h" #include "byteorder.h" #include "iolist.h" @@ -988,6 +989,19 @@ static inline unsigned coap_szx2size(unsigned szx) { return (1 << (szx + 4)); } + +/** + * @brief Helper to encode byte size into next equal or smaller SZX value + * + * @param[in] len Size in bytes + * + * @returns closest SZX value that fits into a buffer of @p len + */ +static inline unsigned coap_size2szx(unsigned len) +{ + assert(len >= 16); + return bitarithm_msb(len >> 4); +} /**@}*/ /** diff --git a/sys/include/net/nanocoap_vfs.h b/sys/include/net/nanocoap_vfs.h index 2ba77fb931..c049c7720e 100644 --- a/sys/include/net/nanocoap_vfs.h +++ b/sys/include/net/nanocoap_vfs.h @@ -51,6 +51,37 @@ int nanocoap_vfs_get_url(const char *url, const char *dst); */ int nanocoap_vfs_get(nanocoap_sock_t *sock, const char *path, const char *dst); +/** + * @brief Uploads the @p file to @p url via blockwise PUT. + * + * @param[in] url URL to the resource + * @param[in] src Path to the source file + * @param[in] work_buf Buffer to read file blocks into + * @param[in] work_buf_len Size of the buffer. Should be 1 byte more + * than the desired CoAP blocksize. + * + * @returns 0 on success + * @returns <0 on error + */ +int nanocoap_vfs_put_url(const char *url, const char *src, + void *work_buf, size_t work_buf_len); + +/** + * @brief Uploads the @p file to @p path via blockwise PUT. + * + * @param[in] sock Connection to the server + * @param[in] path Remote query path to the resource + * @param[in] src Path to the source file + * @param[in] work_buf Buffer to read file blocks into + * @param[in] work_buf_len Size of the buffer. Should be 1 byte more + * than the desired CoAP blocksize. + * + * @returns 0 on success + * @returns <0 on error + */ +int nanocoap_vfs_put(nanocoap_sock_t *sock, const char *path, const char *src, + void *work_buf, size_t work_buf_len); + #ifdef __cplusplus } #endif diff --git a/sys/net/application_layer/nanocoap/vfs.c b/sys/net/application_layer/nanocoap/vfs.c index 3e1f8bfdea..9a1f058bc6 100644 --- a/sys/net/application_layer/nanocoap/vfs.c +++ b/sys/net/application_layer/nanocoap/vfs.c @@ -19,7 +19,7 @@ */ #include -#include "net/nanocoap_sock.h" +#include "net/nanocoap_vfs.h" #include "net/sock/util.h" #include "vfs.h" @@ -92,3 +92,68 @@ int nanocoap_vfs_get_url(const char *url, const char *dst) _2file, &fd); return _finalize_file(fd, res, dst, dst_tmp); } + +static int _vfs_put(coap_block_request_t *ctx, const char *file, void *buffer) +{ + int res, fd = vfs_open(file, O_RDONLY, 0644); + if (fd < 0) { + return fd; + } + + /* buffer is at least one larger than SZX value */ + int buffer_len = coap_szx2size(ctx->blksize) + 1; + + bool more = true; + while (more && (res = vfs_read(fd, buffer, buffer_len)) > 0) { + more = res == buffer_len; + res = nanocoap_sock_block_request(ctx, buffer, + res, more, NULL, NULL); + if (res < 0) { + break; + } + vfs_lseek(fd, -1, SEEK_CUR); + } + + nanocoap_block_request_done(ctx); + + vfs_close(fd); + return res; +} + +int nanocoap_vfs_put(nanocoap_sock_t *sock, const char *path, const char *src, + void *work_buf, size_t work_buf_len) +{ + DEBUG("nanocoap: uploading %s to %s\n", src, path); + + if (work_buf_len < coap_szx2size(0) + 1) { + return -ENOBUFS; + } + + coap_block_request_t ctx = { + .path = path, + .method = COAP_METHOD_PUT, + .blksize = coap_size2szx(work_buf_len - 1), + .sock = *sock, + }; + + return _vfs_put(&ctx, src, work_buf); +} + +int nanocoap_vfs_put_url(const char *url, const char *src, + void *work_buf, size_t work_buf_len) +{ + DEBUG("nanocoap: uploading %s to %s\n", src, url); + + if (work_buf_len < coap_szx2size(0) + 1) { + return -ENOBUFS; + } + + coap_block_request_t ctx; + int res = nanocoap_block_request_init_url(&ctx, url, COAP_METHOD_PUT, + coap_size2szx(work_buf_len - 1)); + if (res) { + return res; + } + + return _vfs_put(&ctx, src, work_buf); +} diff --git a/sys/shell/commands/sc_nanocoap_vfs.c b/sys/shell/commands/sc_nanocoap_vfs.c index 66b5bbcdad..e9fd5ba730 100644 --- a/sys/shell/commands/sc_nanocoap_vfs.c +++ b/sys/shell/commands/sc_nanocoap_vfs.c @@ -91,7 +91,7 @@ static int _print_dir(const char *url, char *buf, size_t len) static int _nanocoap_get_handler(int argc, char **argv) { int res; - char buffer[CONFIG_NANOCOAP_QS_MAX]; + char buffer[CONFIG_NANOCOAP_URI_MAX]; char *dst, *url = argv[1]; if (argc < 2) { @@ -139,4 +139,43 @@ static int _nanocoap_get_handler(int argc, char **argv) return res; } +static int _nanocoap_put_handler(int argc, char **argv) +{ + int res; + char *file, *url; + char buffer[CONFIG_NANOCOAP_URI_MAX]; + char work_buf[coap_szx2size(CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT) + 1]; + + if (argc < 3) { + printf("Usage: %s \n", argv[0]); + return -EINVAL; + } + + file = argv[1]; + url = argv[2]; + + if (_is_dir(url)) { + const char *basename = strrchr(file, '/'); + if (basename == NULL) { + return -EINVAL; + } + if (snprintf(buffer, sizeof(buffer), "%s%s", + url, basename + 1) >= (int)sizeof(buffer)) { + puts("Constructed URI too long"); + return -ENOBUFS; + } + url = buffer; + } + + res = nanocoap_vfs_put_url(url, file, work_buf, sizeof(work_buf)); + if (res < 0) { + printf("Upload failed: %s\n", strerror(-res)); + } + else { + printf("Saved to %s\n", url); + } + return res; +} + SHELL_COMMAND(ncget, "download a file from a CoAP server", _nanocoap_get_handler); +SHELL_COMMAND(ncput, "upload a file to a CoAP server", _nanocoap_put_handler); diff --git a/tests/nanocoap_cli/Makefile b/tests/nanocoap_cli/Makefile index e964e76531..f280ab7951 100644 --- a/tests/nanocoap_cli/Makefile +++ b/tests/nanocoap_cli/Makefile @@ -27,6 +27,9 @@ ifeq (,$(filter $(BOARD),$(LOW_MEMORY_BOARDS))) # USEMODULE += vfs_auto_format USEMODULE += shell_commands + # VFS operations require more stack on the main thread + CFLAGS += -DTHREAD_STACKSIZE_MAIN=THREAD_STACKSIZE_LARGE + # always enable auto-format for native ifeq ($(BOARD),native) USEMODULE += vfs_auto_format