From 688acff5bc877e7a258da19b039aa0d444c0538a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Tue, 17 May 2022 09:20:54 +0200 Subject: [PATCH 1/7] gcoap/fileserver: implement PUT/POST/DELETE functionality --- dist/tools/doccheck/exclude_patterns | 2 + examples/gcoap_fileserver/main.c | 3 +- sys/include/net/coap.h | 2 + sys/net/application_layer/gcoap/fileserver.c | 515 ++++++++++++++----- 4 files changed, 385 insertions(+), 137 deletions(-) diff --git a/dist/tools/doccheck/exclude_patterns b/dist/tools/doccheck/exclude_patterns index 19855e3185..8843c85594 100644 --- a/dist/tools/doccheck/exclude_patterns +++ b/dist/tools/doccheck/exclude_patterns @@ -14281,6 +14281,8 @@ sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_BLOCK1 \(macro definiti sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_BLOCK2 \(macro definition\) of group net_coap is not documented\. sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_CONTENT_FORMAT \(macro definition\) of group net_coap is not documented\. sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_ETAG \(macro definition\) of group net_coap is not documented\. +sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_IF_MATCH \(macro definition\) of group net_coap is not documented\. +sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_IF_NONE_MATCH \(macro definition\) of group net_coap is not documented\. sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_MAX_AGE \(macro definition\) of group net_coap is not documented\. sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_LOCATION_PATH \(macro definition\) of group net_coap is not documented\. sys/include/net/coap\.h:[0-9]+: warning: Member COAP_OPT_LOCATION_QUERY \(macro definition\) of group net_coap is not documented\. diff --git a/examples/gcoap_fileserver/main.c b/examples/gcoap_fileserver/main.c index c909ab9da7..59f7d3d567 100644 --- a/examples/gcoap_fileserver/main.c +++ b/examples/gcoap_fileserver/main.c @@ -27,7 +27,8 @@ static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; /* CoAP resources. Must be sorted by path (ASCII order). */ static const coap_resource_t _resources[] = { - { "/vfs", COAP_GET | COAP_MATCH_SUBTREE, gcoap_fileserver_handler, VFS_DEFAULT_DATA }, + { "/vfs", COAP_GET | COAP_PUT | COAP_DELETE | COAP_MATCH_SUBTREE, + gcoap_fileserver_handler, VFS_DEFAULT_DATA }, }; static gcoap_listener_t _listener = { diff --git a/sys/include/net/coap.h b/sys/include/net/coap.h index 692414fc8a..fd094ba891 100644 --- a/sys/include/net/coap.h +++ b/sys/include/net/coap.h @@ -35,8 +35,10 @@ extern "C" { * @name CoAP option numbers * @{ */ +#define COAP_OPT_IF_MATCH (1) #define COAP_OPT_URI_HOST (3) #define COAP_OPT_ETAG (4) +#define COAP_OPT_IF_NONE_MATCH (5) #define COAP_OPT_OBSERVE (6) #define COAP_OPT_LOCATION_PATH (8) #define COAP_OPT_URI_PATH (11) diff --git a/sys/net/application_layer/gcoap/fileserver.c b/sys/net/application_layer/gcoap/fileserver.c index 4b8edbecb1..7a46206265 100644 --- a/sys/net/application_layer/gcoap/fileserver.c +++ b/sys/net/application_layer/gcoap/fileserver.c @@ -32,14 +32,27 @@ /** Maximum length of an expressible path, including the trailing 0 character. */ #define COAPFILESERVER_PATH_MAX (64) +/** + * @brief Structure holding information about present options in a request + */ +struct requestoptions { + uint32_t etag; /**< Etag value in an Etag option */ + uint32_t if_match; /**< Etag value in an If-Match option */ + char if_match_len; /**< Length of the Etag in an If-Match option */ + struct { + bool if_match :1; /**< Request carries an If-Match option */ + bool if_none_match :1; /**< Request carries an If-None-Match option */ + bool etag :1; /**< Request carries an Etag option */ + bool block2 :1; /**< Request carries a Block2 option */ + bool block1 :1; /**< Request carries a Block1 option */ + } exists; /**< Structure holding flags of present request options */ +}; + /** Data extracted from a request on a file */ struct requestdata { /** 0-terminated expanded file name in the VFS */ char namebuf[COAPFILESERVER_PATH_MAX]; - uint32_t blocknum2; - uint32_t etag; - bool etag_sent; - uint8_t szx2; + struct requestoptions options; }; /** @@ -84,69 +97,75 @@ static unsigned _count_char(const char *s, char c) /** Build an ETag based on the given file's VFS stat. If the stat fails, * returns the error and leaves etag in any state; otherwise there's an etag * in the stattag's field */ -static int stat_etag(const char *filename, uint32_t *etag) +static void stat_etag(struct stat *stat, uint32_t *etag) { - struct stat stat; - int err = vfs_stat(filename, &stat); - if (err < 0) { - return err; - } - /* Normalizing fields whose value can change without affecting the ETag */ - stat.st_nlink = 0; + stat->st_nlink = 0; #if defined(CPU_ESP32) || defined(CPU_ESP8266) || defined(CPU_MIPS_PIC32MX) || defined(CPU_MIPS_PIC32MZ) - memset(&stat.st_atime, 0, sizeof(stat.st_atime)); + memset(&stat->st_atime, 0, sizeof(stat->st_atime)); #else - memset(&stat.st_atim, 0, sizeof(stat.st_atim)); + memset(&stat->st_atim, 0, sizeof(stat->st_atim)); #endif - - *etag = fletcher32((void *)&stat, sizeof(stat) / 2); - - return 0; + *etag = fletcher32((void *)stat, sizeof(*stat) / 2); } /** Create a CoAP response for a given errno (eg. EACCESS -> 4.03 Forbidden - * etc., defaulting to 5.03 Internal Server Error) */ -static size_t gcoap_fileserver_errno_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, int err) + * etc., defaulting to 5.03 Internal Server Error), or interpret a positive + * value for err as a CoAP response code */ +static size_t gcoap_fileserver_error_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, int err) { - uint8_t code; - switch (err) { - case -EACCES: - code = COAP_CODE_FORBIDDEN; - break; - case -ENOENT: - code = COAP_CODE_PATH_NOT_FOUND; - break; - default: - code = COAP_CODE_INTERNAL_SERVER_ERROR; - }; - DEBUG("gcoap_fileserver: Rejecting error %d (%s) as %d.%02d\n", err, strerror(err), - code >> 5, code & 0x1f); + uint8_t code = err; + if (err < 0) { + switch (err) { + case -EACCES: + code = COAP_CODE_FORBIDDEN; + break; + case -ENOENT: + code = COAP_CODE_PATH_NOT_FOUND; + break; + default: + code = COAP_CODE_INTERNAL_SERVER_ERROR; + }; + DEBUG("gcoap_fileserver: Rejecting error %d (%s) as %d.%02d\n", err, strerror(err), + code >> 5, code & 0x1f); + } return gcoap_response(pdu, buf, len, code); } -static void _calc_szx2(coap_pkt_t *pdu, size_t reserve, struct requestdata *request) +static void _calc_szx2(coap_pkt_t *pdu, size_t reserve, coap_block1_t *block2) { assert(pdu->payload_len > reserve); size_t remaining_length = pdu->payload_len - reserve; /* > 0: To not wrap around; if that still won't fit that's later caught in * an assertion */ - while ((coap_szx2size(request->szx2) > remaining_length) && (request->szx2 > 0)) { - request->szx2--; - request->blocknum2 <<= 1; + while ((coap_szx2size(block2->szx) > remaining_length) && (block2->szx > 0)) { + block2->szx--; + block2->blknum <<= 1; } } -static ssize_t gcoap_fileserver_file_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, - struct requestdata *request) +static ssize_t _get_file(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request) { + int err; uint32_t etag; - int err = stat_etag(request->namebuf, &etag); - if (err < 0) { - return gcoap_fileserver_errno_handler(pdu, buf, len, err); + coap_block1_t block2 = { .szx = CONFIG_NANOCOAP_BLOCK_SIZE_EXP_MAX }; + { + struct stat stat; + if ((err = vfs_stat(request->namebuf, &stat)) < 0) { + return gcoap_fileserver_error_handler(pdu, buf, len, err); + } + stat_etag(&stat, &etag); } - - if (request->etag_sent && etag == request->etag) { + if (request->options.exists.block2 && !coap_get_block2(pdu, &block2)) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_BAD_OPTION); + } + if (request->options.exists.if_match && + memcmp(&etag, &request->options.if_match, request->options.if_match_len)) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); + } + if (request->options.exists.etag && + !memcmp(&etag, &request->options.etag, sizeof(etag))) { gcoap_resp_init(pdu, buf, len, COAP_CODE_VALID); coap_opt_add_opaque(pdu, COAP_OPT_ETAG, &etag, sizeof(etag)); return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); @@ -154,7 +173,7 @@ static ssize_t gcoap_fileserver_file_handler(coap_pkt_t *pdu, uint8_t *buf, size int fd = vfs_open(request->namebuf, O_RDONLY, 0); if (fd < 0) { - return gcoap_fileserver_errno_handler(pdu, buf, len, fd); + return gcoap_fileserver_error_handler(pdu, buf, len, fd); } gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); @@ -162,8 +181,8 @@ static ssize_t gcoap_fileserver_file_handler(coap_pkt_t *pdu, uint8_t *buf, size coap_block_slicer_t slicer; _calc_szx2(pdu, 5 + 1 + 1 /* reserve BLOCK2 size + payload marker + more */, - request); - coap_block_slicer_init(&slicer, request->blocknum2, coap_szx2size(request->szx2)); + &block2); + coap_block_slicer_init(&slicer, block2.blknum, coap_szx2size(block2.szx)); coap_opt_add_block2(pdu, &slicer, true); size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); @@ -205,16 +224,162 @@ late_err: return coap_get_total_hdr_len(pdu); } -static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, - struct requestdata *request, - const char *root, const char* resource_dir) +static ssize_t _put_file(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request) { + int ret, fd; + uint32_t etag; + struct stat stat; + coap_block1_t block1 = {0}; + bool create = (vfs_stat(request->namebuf, &stat) == -ENOENT); + if (create) { + /* While a file 'f' is initially being created, + we save the partial content in '.f' and rename it afterwards */ + if (!(ret = strlen(request->namebuf)) || (unsigned)ret >= sizeof(request->namebuf) - 1) { + /* need one more char '.' */ + return gcoap_fileserver_error_handler(pdu, buf, len, -ENOBUFS); + } + char *file = strrchr(request->namebuf, '/'); + memmove(file + 2, file + 1, strlen(file + 1)); + *(file + 1) = '.'; + } + if (request->options.exists.block1 && !coap_get_block1(pdu, &block1)) { + ret = COAP_CODE_BAD_OPTION; + goto unlink_on_error; + } + if (block1.blknum == 0) { + /* "If-None-Match only works correctly on Block1 requests with (NUM=0) + and MUST NOT be used on Block1 requests with NUM != 0." */ + if (request->options.exists.if_none_match && !create) { + ret = COAP_CODE_PRECONDITION_FAILED; + goto unlink_on_error; + } + } + if (request->options.exists.if_match) { + stat_etag(&stat, &etag); /* Etag before write */ + if (create || memcmp(&etag, &request->options.if_match, request->options.if_match_len)) { + ret = COAP_CODE_PRECONDITION_FAILED; + goto unlink_on_error; + } + } + ret = O_WRONLY; + ret |= (create ? O_CREAT | O_APPEND : 0); + if ((fd = vfs_open(request->namebuf, ret, 0)) < 0) { + ret = fd; + goto unlink_on_error; + } + if (create) { + if ((ret = vfs_lseek(fd, 0, SEEK_END)) < 0) { + goto close_on_error; + } + if (block1.offset != (unsigned)ret) { + /* expect block to be in the correct order during initial creation */ + ret = COAP_CODE_REQUEST_ENTITY_INCOMPLETE; + goto close_on_error; + } + } + else { + if (block1.offset > (unsigned)stat.st_size) { + /* after initial file creation, random access is fine, + as long as it does not result in holes in the file */ + ret = COAP_CODE_REQUEST_ENTITY_INCOMPLETE; + goto close_on_error; + } + if ((ret = vfs_lseek(fd, block1.offset, SEEK_SET)) < 0) { + goto close_on_error; + } + } + if ((ret = vfs_write(fd, pdu->payload, pdu->payload_len)) < 0 || + (unsigned)ret != pdu->payload_len) { + goto close_on_error; + } + vfs_close(fd); + if (!block1.more) { + if ((ret = vfs_stat(request->namebuf, &stat)) < 0) { + goto unlink_on_error; + } + if (create) { + char path[COAPFILESERVER_PATH_MAX]; + strcpy(path, request->namebuf); + char *file = strrchr(path, '/'); + memmove(file + 1, file + 2, strlen(file + 2) + 1); + if ((ret = vfs_rename(request->namebuf, path)) < 0) { + goto unlink_on_error; + } + } + stat_etag(&stat, &etag); /* Etag after write */ + gcoap_resp_init(pdu, buf, len, create ? COAP_CODE_CREATED : COAP_CODE_CHANGED); + coap_opt_add_opaque(pdu, COAP_OPT_ETAG, &etag, sizeof(etag)); + } + else { + gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTINUE); + block1.more = true; /* resource is created atomically */ + coap_opt_add_block1_control(pdu, &block1); + } + return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); + +close_on_error: + vfs_close(fd); +unlink_on_error: + if (create) { + vfs_unlink(request->namebuf); + } + return gcoap_fileserver_error_handler(pdu, buf, len, ret); +} + +static ssize_t _delete_file(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request) +{ + int ret; + uint32_t etag; + struct stat stat; + if ((ret = vfs_stat(request->namebuf, &stat)) < 0) { + return gcoap_fileserver_error_handler(pdu, buf, len, ret); + } + if (request->options.exists.if_match && request->options.if_match_len) { + stat_etag(&stat, &etag); + if (memcmp(&etag, &request->options.if_match, request->options.if_match_len)) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); + } + } + if ((ret = vfs_unlink(request->namebuf)) < 0) { + return gcoap_fileserver_error_handler(pdu, buf, len, ret); + } + gcoap_resp_init(pdu, buf, len, COAP_CODE_DELETED); + return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); +} + +static ssize_t gcoap_fileserver_file_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request) +{ + switch (coap_get_code(pdu)) { + case COAP_METHOD_GET: + return _get_file(pdu, buf, len, request); + case COAP_METHOD_PUT: + return _put_file(pdu, buf, len, request); + case COAP_METHOD_DELETE: + return _delete_file(pdu, buf, len, request); + default: + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED); + } +} + +static ssize_t _get_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request, + const char *root, const char* resource_dir) +{ + int err; vfs_DIR dir; coap_block_slicer_t slicer; - - int err = vfs_opendir(&dir, request->namebuf); - if (err != 0) { - return gcoap_fileserver_errno_handler(pdu, buf, len, err); + coap_block1_t block2 = { .szx = CONFIG_NANOCOAP_BLOCK_SIZE_EXP_MAX }; + if (request->options.exists.block2 && !coap_get_block2(pdu, &block2)) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_OPT_FINISH_NONE); + } + if ((err = vfs_opendir(&dir, request->namebuf)) < 0) { + return gcoap_fileserver_error_handler(pdu, buf, len, err); + } + if (request->options.exists.if_match && request->options.if_match_len) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); } DEBUG("gcoap_fileserver: Serving directory listing\n"); @@ -222,8 +387,8 @@ static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, coap_opt_add_format(pdu, COAP_FORMAT_LINK); _calc_szx2(pdu, 5 + 1 /* reserve BLOCK2 size + payload marker */, - request); - coap_block_slicer_init(&slicer, request->blocknum2, coap_szx2size(request->szx2)); + &block2); + coap_block_slicer_init(&slicer, block2.blknum, coap_szx2size(block2.szx)); coap_opt_add_block2(pdu, &slicer, true); buf += coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); @@ -236,8 +401,8 @@ static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, while (vfs_readdir(&dir, &entry) > 0) { const char *entry_name = entry.d_name; size_t entry_len = strlen(entry_name); - if (entry_len <= 2 && memcmp(entry_name, "..", entry_len) == 0) { - /* Up pointers don't work the same way in URI semantics */ + if (*entry_name == '.') { + /* Exclude everything that starts with '.' */ continue; } bool is_dir = entry_is_dir(request->namebuf, entry_name); @@ -246,7 +411,6 @@ static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, buf += coap_blockwise_put_char(&slicer, buf, ','); } buf += coap_blockwise_put_char(&slicer, buf, '<'); - buf += coap_blockwise_put_bytes(&slicer, buf, resource_dir, resource_dir_len); buf += coap_blockwise_put_bytes(&slicer, buf, root_dir, root_dir_len); buf += coap_blockwise_put_char(&slicer, buf, '/'); @@ -263,15 +427,65 @@ static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, return (uintptr_t)buf - (uintptr_t)pdu->hdr; } +static ssize_t _put_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request) +{ + int err; + vfs_DIR dir; + if ((err = vfs_opendir(&dir, request->namebuf)) == 0) { + vfs_closedir(&dir); + if (request->options.exists.if_match && request->options.if_match_len) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); + } + gcoap_resp_init(pdu, buf, len, COAP_CODE_CHANGED); + } + else { + if (request->options.exists.if_match) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); + } + if ((err = vfs_mkdir(request->namebuf, 0)) < 0) { + return gcoap_fileserver_error_handler(pdu, buf, len, err); + } + gcoap_resp_init(pdu, buf, len, COAP_CODE_CREATED); + } + return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); +} + +static ssize_t _delete_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request) +{ + int err; + if (request->options.exists.if_match && request->options.if_match_len) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); + } + if ((err = vfs_rmdir(request->namebuf)) < 0) { + return gcoap_fileserver_error_handler(pdu, buf, len, err); + } + gcoap_resp_init(pdu, buf, len, COAP_CODE_DELETED); + return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); +} + +static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, + struct requestdata *request, + const char *root, const char* resource_dir) +{ + switch (coap_get_code(pdu)) { + case COAP_METHOD_GET: + return _get_directory(pdu, buf, len, request, root, resource_dir); + case COAP_METHOD_PUT: + return _put_directory(pdu, buf, len, request); + case COAP_METHOD_DELETE: + return _delete_directory(pdu, buf, len, request); + default: + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED); + } +} + ssize_t gcoap_fileserver_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx) { const char *root = coap_request_ctx_get_context(ctx); const char *resource = coap_request_ctx_get_path(ctx); - struct requestdata request = { - .etag_sent = false, - .blocknum2 = 0, - .szx2 = CONFIG_NANOCOAP_BLOCK_SIZE_EXP_MAX, - }; + struct requestdata request = {0}; /** Index in request.namebuf. Must not point at the last entry as that will be * zeroed to get a 0-terminated string. */ @@ -284,75 +498,108 @@ ssize_t gcoap_fileserver_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, strncpy(request.namebuf, root, sizeof(request.namebuf)); namelength = strlen(root); } - bool is_directory = true; /* either no path component at all or trailing '/' */ - coap_optpos_t opt = { - .offset = coap_get_total_hdr_len(pdu), - }; - uint8_t *value; - ssize_t optlen; - while ((optlen = coap_opt_get_next(pdu, &opt, &value, 0)) != -ENOENT) { - - if (optlen < 0) { - errorcode = COAP_CODE_BAD_REQUEST; + { + coap_optpos_t opt = { .offset = coap_get_total_hdr_len(pdu) }; + uint8_t *value; + ssize_t optlen; + while ((optlen = coap_opt_get_next(pdu, &opt, &value, 0)) != -ENOENT) { + if (optlen < 0) { + errorcode = COAP_CODE_BAD_REQUEST; + goto error; + } + switch (opt.opt_num) { + case COAP_OPT_IF_MATCH: + /* support only one if-match option, although it is repeatable */ + if (request.options.exists.if_match) { + errorcode = COAP_CODE_NOT_IMPLEMENTED; + goto error; + } + if (optlen > 8) { + errorcode = COAP_CODE_BAD_OPTION; + goto error; + } + if (optlen && optlen != sizeof(request.options.if_match)) { + errorcode = COAP_CODE_PRECONDITION_FAILED; + goto error; + } + memcpy(&request.options.if_match, value, + request.options.if_match_len = optlen); + request.options.exists.if_match = true; + break; + case COAP_OPT_IF_NONE_MATCH: + if (optlen || request.options.exists.if_none_match) { + errorcode = COAP_CODE_BAD_OPTION; + goto error; + } + request.options.exists.if_none_match = true; + break; + case COAP_OPT_URI_PATH: + if (strip_remaining != 0) { + strip_remaining -= 1; + continue; + } + if ((is_directory = (optlen == 0))) { /* '/' */ + continue; + } + if (memchr(value, '\0', optlen) != NULL || + memchr(value, '/', optlen) != NULL) { + /* Path can not be expressed in the file system */ + errorcode = COAP_CODE_PATH_NOT_FOUND; + goto error; + } + size_t newlength = namelength + 1 + optlen; + if (newlength > sizeof(request.namebuf) - 1) { + /* Path too long, therefore can't exist in this mapping */ + errorcode = COAP_CODE_PATH_NOT_FOUND; + goto error; + } + request.namebuf[namelength] = '/'; + memcpy(&request.namebuf[namelength] + 1, value, optlen); + namelength = newlength; + break; + case COAP_OPT_ETAG: + if (optlen != sizeof(request.options.etag)) { + /* Can't be a matching tag, no use in carrying that */ + continue; + } + if (request.options.exists.etag) { + /* We can reasonably only check for a limited sized set, + * and it size is 1 here (sending multiple ETags is + * possible but rare) */ + continue; + } + request.options.exists.etag = true; + memcpy(&request.options.etag, value, sizeof(request.options.etag)); + break; + case COAP_OPT_BLOCK2: + if (optlen > 4 || request.options.exists.block2) { + errorcode = COAP_CODE_BAD_OPTION; + goto error; + } + request.options.exists.block2 = true; + break; + case COAP_OPT_BLOCK1: + if (optlen > 4 || request.options.exists.block1) { + errorcode = COAP_CODE_BAD_OPTION; + goto error; + } + request.options.exists.block1 = true; + break; + default: + if (opt.opt_num & 1) { + errorcode = COAP_CODE_BAD_OPTION; + goto error; + } + break; + } + } + if (request.options.exists.if_match && + request.options.exists.if_none_match) { + errorcode = COAP_CODE_PRECONDITION_FAILED; goto error; } - - switch (opt.opt_num) { - case COAP_OPT_URI_PATH: - if (strip_remaining != 0) { - strip_remaining -= 1; - continue; - } - if ((is_directory = (optlen == 0))) { /* '/' */ - continue; - } - if (memchr(value, '\0', optlen) != NULL || - memchr(value, '/', optlen) != NULL) { - /* Path can not be expressed in the file system */ - errorcode = COAP_CODE_PATH_NOT_FOUND; - goto error; - } - size_t newlength = namelength + 1 + optlen; - if (newlength > sizeof(request.namebuf) - 1) { - /* Path too long, therefore can't exist in this mapping */ - errorcode = COAP_CODE_PATH_NOT_FOUND; - goto error; - } - request.namebuf[namelength] = '/'; - memcpy(&request.namebuf[namelength] + 1, value, optlen); - namelength = newlength; - break; - case COAP_OPT_ETAG: - if (optlen != sizeof(request.etag)) { - /* Can't be a matching tag, no use in carrying that */ - continue; - } - if (request.etag_sent) { - /* We can reasonably only check for a limited sized set, - * and it size is 1 here (sending multiple ETags is - * possible but rare) */ - continue; - } - request.etag_sent = true; - memcpy(&request.etag, value, sizeof(request.etag)); - break; - case COAP_OPT_BLOCK2: - /* Could be more efficient now that we already know where it - * is, but meh */ - coap_get_blockopt(pdu, COAP_OPT_BLOCK2, &request.blocknum2, &request.szx2); - break; - default: - if (opt.opt_num & 1) { - errorcode = COAP_CODE_BAD_REQUEST; - goto error; - } - else { - /* Ignoring elective option */ - } - } } - request.namebuf[namelength] = '\0'; DEBUG("request: '%s'\n", request.namebuf); @@ -360,13 +607,9 @@ ssize_t gcoap_fileserver_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, /* Note to self: As we parse more options than just Uri-Path, we'll likely * pass a struct pointer later. So far, those could even be hooked into the * resource list, but that'll go away once we parse more options */ - if (is_directory) { - return gcoap_fileserver_directory_handler(pdu, buf, len, &request, root, resource); - } - else { - return gcoap_fileserver_file_handler(pdu, buf, len, &request); - } - + return is_directory + ? gcoap_fileserver_directory_handler(pdu, buf, len, &request, root, resource) + : gcoap_fileserver_file_handler(pdu, buf, len, &request); error: return gcoap_response(pdu, buf, len, errorcode); } From 2327bef57f8b3cc0393dd8dfbd42b16709886c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Fri, 20 May 2022 23:49:10 +0200 Subject: [PATCH 2/7] gcoap/fileserver: update documentation --- sys/include/net/gcoap/fileserver.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sys/include/net/gcoap/fileserver.h b/sys/include/net/gcoap/fileserver.h index eb1475a85d..5a874f9785 100644 --- a/sys/include/net/gcoap/fileserver.h +++ b/sys/include/net/gcoap/fileserver.h @@ -16,13 +16,18 @@ * This maps files in the local file system onto a resources in CoAP. In that, * it is what is called a static web server in the unconstrained web. * - * As usual, GET operations are used to read files. + * that files are not updated atomically, although files are created atomically. + * The Content-Format option is not checked in the current implementation. + * Conditional file modification and deletion is supported using the If-Match + * option. The If-Match option carries a previously received Etag or in case of + * zero length, requires a request to be processed only if the resource exists. + * In opposite, the If-None-Match option requires a request to be processed, + * only if the resource does not yet exist, and is most useful for file creation. * - * Directories are expressed to URIs with trailing slashes. + * Directories are expressed to URIs with trailing slashes and can be DELETEd + * when empty. * * @note The file server uses ETag for cache validation. The ETags are built * from the file system stat values. As clients rely on the ETag to differ when @@ -55,8 +60,8 @@ * The path argument specifies under which path the folder is served via CoAP while * the context argument contains the path on the local filesystem that will be served. * - * The allowed methods dictate whether it's read-only (``COAP_GET``) or (in the - * future) read-write (``COAP_GET | COAP_PUT | COAP_DELETE``). + * The allowed methods dictate whether it's read-only (``COAP_GET``) or + * read-write (``COAP_GET | COAP_PUT | COAP_DELETE``). * * @{ * From 2e587973eb105ee2d0eaa5cf9c980364af94c330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Fri, 22 Jul 2022 09:23:50 +0200 Subject: [PATCH 3/7] sys/vfs_util: add recursive unlink --- sys/include/vfs_util.h | 27 ++++++++ sys/vfs_util/vfs_util.c | 137 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/sys/include/vfs_util.h b/sys/include/vfs_util.h index a6ae1ec911..f2e11b95d0 100644 --- a/sys/include/vfs_util.h +++ b/sys/include/vfs_util.h @@ -101,6 +101,33 @@ int vfs_file_sha256(const char* file, void *digest, void *work_buf, size_t work_buf_len); #endif +/** + * @brief Checks if @p path is a file or a directory. + * + * This function uses @ref vfs_stat(), so if you need @ref vfs_stat() anyway, + * you should not do double work and check it yourself. + * + * @param[in] path Path to check + * + * @return < 0 on FS error + * @return 0 if @p path is a file + * @return > 0 if @p path is a directory + */ +int vfs_is_dir(const char *path); + +/** + * @brief Behaves like `rm -r @p root`. + * + * @param[in] root FS root directory to be deleted + * @param[in] path_buf Buffer that must be able to store the longest path + * of the file or directory being deleted + * @param[in] max_size Size of @p path_buf + * + * @return < 0 on error + * @return 0 + */ +int vfs_unlink_recursive(const char *root, char *path_buf, size_t max_size); + #ifdef __cplusplus } #endif diff --git a/sys/vfs_util/vfs_util.c b/sys/vfs_util/vfs_util.c index adf7e7f13f..be92d0d66e 100644 --- a/sys/vfs_util/vfs_util.c +++ b/sys/vfs_util/vfs_util.c @@ -154,3 +154,140 @@ int vfs_file_sha256(const char* file, void *digest, } #endif /* MODULE_HASHES */ + +int vfs_is_dir(const char *path) +{ + assert(path); + + int err; + struct stat stat; + if (*path != '/') { + /* only accept absolute paths */ + return -EINVAL; + } + if ((err = vfs_stat(path, &stat)) < 0) { + return err; + } + return ((stat.st_mode & S_IFMT) == S_IFDIR); +} + +/** + * @brief Removes additional "/" slashes from @p path + * + * @param[in] path Path to be prepared + */ +static void _vfs_prepare_path(char *path) +{ + assert(path); + assert(*path == '/'); + + int path_len = strlen(path); + char *p_write = path; /* end of so far constructed path */ + int len = 0; + const char *p_read = p_write; /* segment to be appended to the path */ + while (p_read < path + path_len) { + len = 0; + while (*p_read && *p_read == '/') { + p_read++; /* skip slashes */ + } + while (p_read[len] && p_read[len] != '/') { + len++; /* length of segment to be copied */ + } + if (*p_read && p_write + len + 1 <= path + path_len) { + memmove(p_write + 1, p_read, len); + p_write = p_write + len + 1; /* advance write pointer by segment length + 1 */ + *p_write = p_read[len]; /* either '\0' or '/' */ + } + p_read += len; /* advance read pointer by segment length */ + } + if (*p_write) { + *++p_write = '\0'; + } +} + +int vfs_unlink_recursive(const char *root, char *path_buf, size_t max_size) +{ + assert(root); + assert(path_buf); + + /* This function works like a Depth-first search (DFS). + First, we go as deep as we can into a directory and delete contained files. + Then we delete the now empty directory and go to the parent directory + and repeat the process. */ + int err; + if (*root != '/' || !strcmp(root, "/")) { + /* only accept absolute paths and not the FS root */ + return -EINVAL; + } + if (strlen(root) >= max_size) { + return -ENOBUFS; + } + strcpy(path_buf, root); + _vfs_prepare_path(path_buf); + if (path_buf[strlen(path_buf) - 1] != '/') { + if ((err = vfs_is_dir(path_buf)) < 0) { + return err; /* early unexpected error */ + } + else if (!err) { + /* just a file */ + return vfs_unlink(path_buf); + } + strcat(path_buf, "/"); + } + vfs_DIR dir; + vfs_dirent_t entry; + char seg[VFS_NAME_MAX + 1] = {0}; /* + 1 to append a '/' */ + size_t seg_len, root_len, fin = strlen(path_buf); + while ((root_len = strlen(path_buf)) >= fin) { + strcat(path_buf, seg); + *seg = '\0'; + if ((err = vfs_opendir(&dir, path_buf)) < 0) { /* this works with a trailing '/' */ + return err; + } + while (vfs_readdir(&dir, &entry) > 0) { + if (!strcmp(entry.d_name, "..")) { + continue; + } + seg_len = strlen(entry.d_name); + root_len = strlen(path_buf); + if (root_len + seg_len >= max_size) { + vfs_closedir(&dir); + return -ENOBUFS; + } + strcat(path_buf, entry.d_name); + if ((err = vfs_is_dir(path_buf)) < 0) { + /* error */ + vfs_closedir(&dir); + return err; + } + else if (err) { + /* is dir */ + if (*seg == '\0') { + strcat(seg, entry.d_name); + strcat(seg, "/"); + } + } + else { + /* is file */ + if ((err = vfs_unlink(path_buf)) < 0) { + vfs_closedir(&dir); + return err; + } + } + path_buf[root_len] = '\0'; + } + vfs_closedir(&dir); + if (*seg == '\0') { + /* no files and no subdirectory */ + if ((err = vfs_rmdir(path_buf)) < 0) { + return err; + } + /* go one segment up */ + char *end = &path_buf[strlen(path_buf) - 1]; + assert(*end == '/'); + while (*--end != '/') { } + *++end = '\0'; + } + } + return 0; +} From 70a8cf0d639b3827e37a1862a7a7f161f3e81177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Fri, 22 Jul 2022 09:28:16 +0200 Subject: [PATCH 4/7] sys/shell/commands: add recursive unlink to sc_vfs --- sys/shell/commands/sc_vfs.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/sys/shell/commands/sc_vfs.c b/sys/shell/commands/sc_vfs.c index 2f7c33ea88..75201d6dee 100644 --- a/sys/shell/commands/sc_vfs.c +++ b/sys/shell/commands/sc_vfs.c @@ -31,8 +31,10 @@ #include "shell.h" #include "vfs.h" -#if MODULE_VFS_UTIL #include "vfs_util.h" + +#ifndef SHELL_VFS_PATH_SIZE_MAX +#define SHELL_VFS_PATH_SIZE_MAX 256 #endif #define SHELL_VFS_BUFSIZE 256 @@ -62,7 +64,11 @@ static void _vfs_usage(char **argv) printf("%s cp \n", argv[0]); printf("%s mv \n", argv[0]); printf("%s mkdir \n", argv[0]); - printf("%s rm \n", argv[0]); + printf("%s rm" +#if IS_USED(MODULE_VFS_UTIL) + " [-r]" +#endif + " \n", argv[0]); printf("%s df [path]\n", argv[0]); if (MOUNTPOINTS_NUMOF > 0) { printf("%s mount [path]\n", argv[0]); @@ -79,7 +85,7 @@ static void _vfs_usage(char **argv) puts("mv: Move file to "); puts("mkdir: Create directory "); puts("cp: Copy file to "); - puts("rm: Unlink (delete) "); + puts("rm: Unlink (delete) a file or a directory at "); puts("df: Show file system space utilization stats"); } @@ -558,10 +564,21 @@ static int _rm_handler(int argc, char **argv) _vfs_usage(argv); return 1; } - char *rm_name = argv[1]; + bool recursive = !strcmp(argv[1], "-r"); + if (recursive && (argc < 3 || !IS_USED(MODULE_VFS_UTIL))) { + _vfs_usage(argv); + return 1; + } + char *rm_name = recursive ? argv[2] : argv[1]; printf("%s: unlink: %s\n", argv[0], rm_name); - - int res = vfs_unlink(rm_name); + int res; + if (IS_USED(MODULE_VFS_UTIL) && recursive) { + char pbuf[SHELL_VFS_PATH_SIZE_MAX]; + res = vfs_unlink_recursive(rm_name, pbuf, sizeof(pbuf)); + } + else { + res = vfs_unlink(rm_name); + } if (res < 0) { char errbuf[16]; _errno_string(res, (char *)errbuf, sizeof(errbuf)); From f357d993969e75316fc2c4f282622e07658352bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Tue, 26 Jul 2022 00:24:39 +0200 Subject: [PATCH 5/7] gcoap/fileserver: recursive directory deletion as default --- sys/include/net/gcoap/fileserver.h | 12 ++++++++++-- sys/net/application_layer/gcoap/fileserver.c | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/sys/include/net/gcoap/fileserver.h b/sys/include/net/gcoap/fileserver.h index 5a874f9785..7e40dff344 100644 --- a/sys/include/net/gcoap/fileserver.h +++ b/sys/include/net/gcoap/fileserver.h @@ -26,8 +26,10 @@ * In opposite, the If-None-Match option requires a request to be processed, * only if the resource does not yet exist, and is most useful for file creation. * - * Directories are expressed to URIs with trailing slashes and can be DELETEd - * when empty. + * Directories are expressed to URIs with trailing slashes. Directories and + * their content are deleted as if one would do an `$rm -r`. If you only would + * like to delete a directory if it is empty, you must supply an If-Match option + * with the special value @ref COAPFILESERVER_DIR_DELETE_ETAG. * * @note The file server uses ETag for cache validation. The ETags are built * from the file system stat values. As clients rely on the ETag to differ when @@ -80,6 +82,12 @@ extern "C" { #include "net/nanocoap.h" +/** + * @brief Randomly generated Etag, used by a client when a directory should only be + * deleted, if it is empty + */ +#define COAPFILESERVER_DIR_DELETE_ETAG (0x6ce88b56u) + /** * @brief File server handler * diff --git a/sys/net/application_layer/gcoap/fileserver.c b/sys/net/application_layer/gcoap/fileserver.c index 7a46206265..3b0df43553 100644 --- a/sys/net/application_layer/gcoap/fileserver.c +++ b/sys/net/application_layer/gcoap/fileserver.c @@ -25,6 +25,7 @@ #include "net/gcoap/fileserver.h" #include "net/gcoap.h" #include "vfs.h" +#include "vfs_util.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -456,10 +457,19 @@ static ssize_t _delete_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, { int err; if (request->options.exists.if_match && request->options.if_match_len) { - return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); + if (request->options.if_match != byteorder_htonl(COAPFILESERVER_DIR_DELETE_ETAG).u32) { + return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED); + } + if ((err = vfs_rmdir(request->namebuf)) < 0) { + return gcoap_fileserver_error_handler(pdu, buf, len, err); + } } - if ((err = vfs_rmdir(request->namebuf)) < 0) { - return gcoap_fileserver_error_handler(pdu, buf, len, err); + else if (IS_USED(MODULE_VFS_UTIL)) { + if ((err = vfs_unlink_recursive(request->namebuf, + request->namebuf, + sizeof(request->namebuf))) < 0) { + gcoap_fileserver_error_handler(pdu, buf, len, err); + } } gcoap_resp_init(pdu, buf, len, COAP_CODE_DELETED); return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); From 9333970b77171cbf808a7ca3d91b0aa0cc3cba29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Mon, 1 Aug 2022 13:51:52 +0200 Subject: [PATCH 6/7] gcoap/fileserver: make PUT and DELETE pseudomodules --- examples/gcoap_fileserver/Makefile | 2 ++ examples/gcoap_fileserver/main.c | 11 ++++++++++- makefiles/pseudomodules.inc.mk | 2 ++ sys/Makefile.dep | 9 +++++++++ sys/include/net/gcoap/fileserver.h | 2 ++ sys/net/application_layer/gcoap/fileserver.c | 17 +++++++++++++++++ 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/examples/gcoap_fileserver/Makefile b/examples/gcoap_fileserver/Makefile index 364ebfa52f..96a6aac045 100644 --- a/examples/gcoap_fileserver/Makefile +++ b/examples/gcoap_fileserver/Makefile @@ -21,6 +21,8 @@ USEMODULE += shell_commands # enable the fileserver module USEMODULE += gcoap_fileserver +USEMODULE += gcoap_fileserver_delete +USEMODULE += gcoap_fileserver_put # select network modules USEMODULE += gnrc_ipv6_default diff --git a/examples/gcoap_fileserver/main.c b/examples/gcoap_fileserver/main.c index 59f7d3d567..dd081e5e9f 100644 --- a/examples/gcoap_fileserver/main.c +++ b/examples/gcoap_fileserver/main.c @@ -17,6 +17,7 @@ * @} */ +#include "kernel_defines.h" #include "net/gcoap.h" #include "net/gcoap/fileserver.h" #include "shell.h" @@ -27,7 +28,15 @@ static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; /* CoAP resources. Must be sorted by path (ASCII order). */ static const coap_resource_t _resources[] = { - { "/vfs", COAP_GET | COAP_PUT | COAP_DELETE | COAP_MATCH_SUBTREE, + { "/vfs", + COAP_GET | +#if IS_USED(MODULE_GCOAP_FILESERVER_PUT) + COAP_PUT | +#endif +#if IS_USED(MODULE_GCOAP_FILESERVER_DELETE) + COAP_DELETE | +#endif + COAP_MATCH_SUBTREE, gcoap_fileserver_handler, VFS_DEFAULT_DATA }, }; diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 85b06c3021..8b05650bca 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -67,6 +67,8 @@ PSEUDOMODULES += fatfs_vfs_format PSEUDOMODULES += fmt_% PSEUDOMODULES += gcoap_forward_proxy PSEUDOMODULES += gcoap_fileserver +PSEUDOMODULES += gcoap_fileserver_delete +PSEUDOMODULES += gcoap_fileserver_put PSEUDOMODULES += gcoap_dtls ## Enable @ref net_gcoap_dns PSEUDOMODULES += gcoap_dns diff --git a/sys/Makefile.dep b/sys/Makefile.dep index a7d01f3939..d248dcfd4c 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -652,6 +652,15 @@ ifneq (,$(filter gcoap_fileserver,$(USEMODULE))) USEMODULE += vfs endif +ifneq (,$(filter gcoap_fileserver_delete,$(USEMODULE))) + USEMODULE += gcoap_fileserver + USEMODULE += vfs_util +endif + +ifneq (,$(filter gcoap_fileserver_put,$(USEMODULE))) + USEMODULE += gcoap_fileserver +endif + ifneq (,$(filter gcoap_forward_proxy,$(USEMODULE))) USEMODULE += gcoap USEMODULE += uri_parser diff --git a/sys/include/net/gcoap/fileserver.h b/sys/include/net/gcoap/fileserver.h index 7e40dff344..02a19ef95b 100644 --- a/sys/include/net/gcoap/fileserver.h +++ b/sys/include/net/gcoap/fileserver.h @@ -64,6 +64,8 @@ * * The allowed methods dictate whether it's read-only (``COAP_GET``) or * read-write (``COAP_GET | COAP_PUT | COAP_DELETE``). + * If you want to support ``PUT`` and `DELETE`, you need to enable the modules + * ``gcoap_fileserver_put`` and ``gcoap_fileserver_delete``. * * @{ * diff --git a/sys/net/application_layer/gcoap/fileserver.c b/sys/net/application_layer/gcoap/fileserver.c index 3b0df43553..5057cb0b71 100644 --- a/sys/net/application_layer/gcoap/fileserver.c +++ b/sys/net/application_layer/gcoap/fileserver.c @@ -21,6 +21,7 @@ #include #include +#include "kernel_defines.h" #include "checksum/fletcher32.h" #include "net/gcoap/fileserver.h" #include "net/gcoap.h" @@ -225,6 +226,7 @@ late_err: return coap_get_total_hdr_len(pdu); } +#if IS_USED(MODULE_GCOAP_FILESERVER_PUT) static ssize_t _put_file(coap_pkt_t *pdu, uint8_t *buf, size_t len, struct requestdata *request) { @@ -327,7 +329,9 @@ unlink_on_error: } return gcoap_fileserver_error_handler(pdu, buf, len, ret); } +#endif +#if IS_USED(MODULE_GCOAP_FILESERVER_DELETE) static ssize_t _delete_file(coap_pkt_t *pdu, uint8_t *buf, size_t len, struct requestdata *request) { @@ -349,6 +353,7 @@ static ssize_t _delete_file(coap_pkt_t *pdu, uint8_t *buf, size_t len, gcoap_resp_init(pdu, buf, len, COAP_CODE_DELETED); return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); } +#endif static ssize_t gcoap_fileserver_file_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, struct requestdata *request) @@ -356,10 +361,14 @@ static ssize_t gcoap_fileserver_file_handler(coap_pkt_t *pdu, uint8_t *buf, size switch (coap_get_code(pdu)) { case COAP_METHOD_GET: return _get_file(pdu, buf, len, request); +#if IS_USED(MODULE_GCOAP_FILESERVER_PUT) case COAP_METHOD_PUT: return _put_file(pdu, buf, len, request); +#endif +#if IS_USED(MODULE_GCOAP_FILESERVER_DELETE) case COAP_METHOD_DELETE: return _delete_file(pdu, buf, len, request); +#endif default: return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED); } @@ -428,6 +437,7 @@ static ssize_t _get_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, return (uintptr_t)buf - (uintptr_t)pdu->hdr; } +#if IS_USED(MODULE_GCOAP_FILESERVER_PUT) static ssize_t _put_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, struct requestdata *request) { @@ -451,7 +461,9 @@ static ssize_t _put_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, } return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); } +#endif +#if IS_USED(MODULE_GCOAP_FILESERVER_DELETE) static ssize_t _delete_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, struct requestdata *request) { @@ -474,6 +486,7 @@ static ssize_t _delete_directory(coap_pkt_t *pdu, uint8_t *buf, size_t len, gcoap_resp_init(pdu, buf, len, COAP_CODE_DELETED); return coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); } +#endif static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, struct requestdata *request, @@ -482,10 +495,14 @@ static ssize_t gcoap_fileserver_directory_handler(coap_pkt_t *pdu, uint8_t *buf, switch (coap_get_code(pdu)) { case COAP_METHOD_GET: return _get_directory(pdu, buf, len, request, root, resource_dir); +#if IS_USED(MODULE_GCOAP_FILESERVER_PUT) case COAP_METHOD_PUT: return _put_directory(pdu, buf, len, request); +#endif +#if IS_USED(MODULE_GCOAP_FILESERVER_DELETE) case COAP_METHOD_DELETE: return _delete_directory(pdu, buf, len, request); +#endif default: return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED); } From 199e31ef43ac724556c0bfe50e3d6d5267592002 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 4 Aug 2022 12:04:59 +0200 Subject: [PATCH 7/7] gcoap/fileserver: ignore URI HOST option --- sys/net/application_layer/gcoap/fileserver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sys/net/application_layer/gcoap/fileserver.c b/sys/net/application_layer/gcoap/fileserver.c index 5057cb0b71..eaf14a252b 100644 --- a/sys/net/application_layer/gcoap/fileserver.c +++ b/sys/net/application_layer/gcoap/fileserver.c @@ -561,6 +561,9 @@ ssize_t gcoap_fileserver_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, } request.options.exists.if_none_match = true; break; + case COAP_OPT_URI_HOST: + /* ignore host name option */ + continue; case COAP_OPT_URI_PATH: if (strip_remaining != 0) { strip_remaining -= 1;