blob: 2d43ac1883c6629238f8e57ae749019817264e0e [file] [log] [blame]
/*
* 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 <limits.h>
#include <string.h>
#include "cborattr/cborattr.h"
#include "mgmt/mgmt.h"
#include "fs_mgmt/fs_mgmt.h"
#include "fs_mgmt/fs_mgmt_impl.h"
#include "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);
}