/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include "cborattr/cborattr.h" #include "mgmt/mgmt.h" #include "fs_mgmt/fs_mgmt.h" #include "fs_mgmt/fs_mgmt_impl.h" #include "fs_mgmt/fs_mgmt_config.h" static mgmt_handler_fn fs_mgmt_file_download; static mgmt_handler_fn fs_mgmt_file_upload; static struct { /** Whether an upload is currently in progress. */ bool uploading; /** Expected offset of next upload request. */ size_t off; /** Total length of file currently being uploaded. */ size_t len; } fs_mgmt_ctxt; static const struct mgmt_handler fs_mgmt_handlers[] = { [FS_MGMT_ID_FILE] = { .mh_read = fs_mgmt_file_download, .mh_write = fs_mgmt_file_upload, }, }; #define FS_MGMT_HANDLER_CNT \ (sizeof fs_mgmt_handlers / sizeof fs_mgmt_handlers[0]) static struct mgmt_group fs_mgmt_group = { .mg_handlers = fs_mgmt_handlers, .mg_handlers_count = FS_MGMT_HANDLER_CNT, .mg_group_id = MGMT_GROUP_ID_FS, }; /** * Command handler: fs file (read) */ static int fs_mgmt_file_download(struct mgmt_ctxt *ctxt) { uint8_t file_data[FS_MGMT_DL_CHUNK_SIZE]; char path[FS_MGMT_PATH_SIZE + 1]; unsigned long long off; CborError err; size_t bytes_read; size_t file_len; int rc; const struct cbor_attr_t dload_attr[] = { { .attribute = "off", .type = CborAttrUnsignedIntegerType, .addr.uinteger = &off, }, { .attribute = "name", .type = CborAttrTextStringType, .addr.string = path, .len = sizeof path, }, { 0 }, }; off = ULLONG_MAX; rc = cbor_read_object(&ctxt->it, dload_attr); if (rc != 0 || off == ULLONG_MAX) { return MGMT_ERR_EINVAL; } /* Only the response to the first download request contains the total file * length. */ if (off == 0) { rc = fs_mgmt_impl_filelen(path, &file_len); if (rc != 0) { return rc; } } /* Read the requested chunk from the file. */ rc = fs_mgmt_impl_read(path, off, FS_MGMT_DL_CHUNK_SIZE, file_data, &bytes_read); if (rc != 0) { return rc; } /* Encode the response. */ err = 0; err |= cbor_encode_text_stringz(&ctxt->encoder, "off"); err |= cbor_encode_uint(&ctxt->encoder, off); err |= cbor_encode_text_stringz(&ctxt->encoder, "data"); err |= cbor_encode_byte_string(&ctxt->encoder, file_data, bytes_read); err |= cbor_encode_text_stringz(&ctxt->encoder, "rc"); err |= cbor_encode_int(&ctxt->encoder, MGMT_ERR_EOK); if (off == 0) { err |= cbor_encode_text_stringz(&ctxt->encoder, "len"); err |= cbor_encode_uint(&ctxt->encoder, file_len); } if (err != 0) { return MGMT_ERR_ENOMEM; } return 0; } /** * Encodes a file upload response. */ static int fs_mgmt_file_upload_rsp(struct mgmt_ctxt *ctxt, int rc, unsigned long long off) { CborError err; err = 0; err |= cbor_encode_text_stringz(&ctxt->encoder, "rc"); err |= cbor_encode_int(&ctxt->encoder, rc); err |= cbor_encode_text_stringz(&ctxt->encoder, "off"); err |= cbor_encode_uint(&ctxt->encoder, off); if (err != 0) { return MGMT_ERR_ENOMEM; } return 0; } /** * Command handler: fs file (write) */ static int fs_mgmt_file_upload(struct mgmt_ctxt *ctxt) { uint8_t file_data[FS_MGMT_UL_CHUNK_SIZE]; char file_name[FS_MGMT_PATH_SIZE + 1]; unsigned long long len; unsigned long long off; size_t data_len; size_t new_off; int rc; const struct cbor_attr_t uload_attr[5] = { [0] = { .attribute = "off", .type = CborAttrUnsignedIntegerType, .addr.uinteger = &off, .nodefault = true }, [1] = { .attribute = "data", .type = CborAttrByteStringType, .addr.bytestring.data = file_data, .addr.bytestring.len = &data_len, .len = sizeof(file_data) }, [2] = { .attribute = "len", .type = CborAttrUnsignedIntegerType, .addr.uinteger = &len, .nodefault = true }, [3] = { .attribute = "name", .type = CborAttrTextStringType, .addr.string = file_name, .len = sizeof(file_name) }, [4] = { 0 }, }; len = ULLONG_MAX; off = ULLONG_MAX; rc = cbor_read_object(&ctxt->it, uload_attr); if (rc != 0 || off == ULLONG_MAX || file_name[0] == '\0') { return MGMT_ERR_EINVAL; } if (off == 0) { /* Total file length is a required field in the first chunk request. */ if (len == ULLONG_MAX) { return MGMT_ERR_EINVAL; } fs_mgmt_ctxt.uploading = true; fs_mgmt_ctxt.off = 0; fs_mgmt_ctxt.len = len; } else { if (!fs_mgmt_ctxt.uploading) { return MGMT_ERR_EINVAL; } if (off != fs_mgmt_ctxt.off) { /* Invalid offset. Drop the data and send the expected offset. */ return fs_mgmt_file_upload_rsp(ctxt, MGMT_ERR_EINVAL, fs_mgmt_ctxt.off); } } new_off = fs_mgmt_ctxt.off + data_len; if (new_off > fs_mgmt_ctxt.len) { /* Data exceeds image length. */ return MGMT_ERR_EINVAL; } if (data_len > 0) { /* Write the data chunk to the file. */ rc = fs_mgmt_impl_write(file_name, off, file_data, data_len); if (rc != 0) { return rc; } fs_mgmt_ctxt.off = new_off; } if (fs_mgmt_ctxt.off == fs_mgmt_ctxt.len) { /* Upload complete. */ fs_mgmt_ctxt.uploading = false; } /* Send the response. */ return fs_mgmt_file_upload_rsp(ctxt, 0, fs_mgmt_ctxt.off); } void fs_mgmt_register_group(void) { mgmt_register_group(&fs_mgmt_group); }