blob: 31515596ae6cb0e8bae4228875c4fedc6d8b3756 [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Licensed 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 "ipmi.hpp"
#include <cstring>
#include <span>
#include <string>
#include <unordered_map>
namespace blobs
{
bool validateRequestLength(BlobOEMCommands command, size_t requestLen)
{
/* The smallest string is one letter and the nul-terminator. */
static const int kMinStrLen = 2;
static const std::unordered_map<BlobOEMCommands, size_t> minimumLengths = {
{BlobOEMCommands::bmcBlobEnumerate, sizeof(struct BmcBlobEnumerateTx)},
{BlobOEMCommands::bmcBlobOpen,
sizeof(struct BmcBlobOpenTx) + kMinStrLen},
{BlobOEMCommands::bmcBlobClose, sizeof(struct BmcBlobCloseTx)},
{BlobOEMCommands::bmcBlobDelete,
sizeof(struct BmcBlobDeleteTx) + kMinStrLen},
{BlobOEMCommands::bmcBlobStat,
sizeof(struct BmcBlobStatTx) + kMinStrLen},
{BlobOEMCommands::bmcBlobSessionStat,
sizeof(struct BmcBlobSessionStatTx)},
{BlobOEMCommands::bmcBlobCommit, sizeof(struct BmcBlobCommitTx)},
{BlobOEMCommands::bmcBlobRead, sizeof(struct BmcBlobReadTx)},
{BlobOEMCommands::bmcBlobWrite,
sizeof(struct BmcBlobWriteTx) + sizeof(uint8_t)},
{BlobOEMCommands::bmcBlobWriteMeta,
sizeof(struct BmcBlobWriteMetaTx) + sizeof(uint8_t)},
};
auto results = minimumLengths.find(command);
if (results == minimumLengths.end())
{
/* Valid length by default if we don't care. */
return true;
}
/* If the request is shorter than the minimum, it's invalid. */
if (requestLen < results->second)
{
return false;
}
return true;
}
std::string stringFromBuffer(std::span<const uint8_t> data)
{
if (data.empty() || data.back() != '\0')
{
return std::string();
}
// Last index is nul-terminator.
return std::string(data.begin(), data.end() - 1);
}
Resp getBlobCount(ManagerInterface* mgr, std::span<const uint8_t>)
{
struct BmcBlobCountRx resp;
resp.crc = 0;
resp.blobCount = mgr->buildBlobList();
/* Copy the response into the reply buffer */
std::vector<uint8_t> output(sizeof(BmcBlobCountRx), 0);
std::memcpy(output.data(), &resp, sizeof(resp));
return ipmi::responseSuccess(output);
}
Resp enumerateBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
/* Verify datalen is >= sizeof(request) */
struct BmcBlobEnumerateTx request;
std::memcpy(&request, data.data(), sizeof(request));
std::string blobId = mgr->getBlobId(request.blobIdx);
if (blobId.empty())
{
return ipmi::responseInvalidFieldRequest();
}
std::vector<uint8_t> output(sizeof(BmcBlobEnumerateRx), 0);
output.insert(output.end(), blobId.c_str(),
blobId.c_str() + blobId.length() + 1);
return ipmi::responseSuccess(output);
}
Resp openBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(data.data());
uint16_t session;
std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobOpenTx)));
if (path.empty())
{
return ipmi::responseReqDataLenInvalid();
}
/* Attempt to open. */
if (!mgr->open(request->flags, path, &session))
{
return ipmi::responseUnspecifiedError();
}
struct BmcBlobOpenRx reply;
reply.crc = 0;
reply.sessionId = session;
std::vector<uint8_t> output(sizeof(BmcBlobOpenRx), 0);
std::memcpy(output.data(), &reply, sizeof(reply));
return ipmi::responseSuccess(output);
}
Resp closeBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
struct BmcBlobCloseTx request;
if (data.size() < sizeof(request))
{
return ipmi::responseReqDataLenInvalid();
}
std::memcpy(&request, data.data(), sizeof(request));
/* Attempt to close. */
if (!mgr->close(request.sessionId))
{
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess(std::vector<uint8_t>{});
}
Resp deleteBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobDeleteTx)));
if (path.empty())
{
return ipmi::responseReqDataLenInvalid();
}
/* Attempt to delete. */
if (!mgr->deleteBlob(path))
{
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess(std::vector<uint8_t>{});
}
static Resp returnStatBlob(BlobMeta* meta)
{
struct BmcBlobStatRx reply;
reply.crc = 0;
reply.blobState = meta->blobState;
reply.size = meta->size;
reply.metadataLen = meta->metadata.size();
std::vector<uint8_t> output(sizeof(BmcBlobStatRx), 0);
std::memcpy(output.data(), &reply, sizeof(reply));
/* If there is metadata, insert it to output. */
if (!meta->metadata.empty())
{
output.insert(output.end(), meta->metadata.begin(),
meta->metadata.end());
}
return ipmi::responseSuccess(output);
}
Resp statBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobStatTx)));
if (path.empty())
{
return ipmi::responseReqDataLenInvalid();
}
/* Attempt to stat. */
BlobMeta meta;
if (!mgr->stat(path, &meta))
{
return ipmi::responseUnspecifiedError();
}
return returnStatBlob(&meta);
}
Resp sessionStatBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
struct BmcBlobSessionStatTx request;
if (data.size() < sizeof(request))
{
return ipmi::responseReqDataLenInvalid();
}
std::memcpy(&request, data.data(), sizeof(request));
/* Attempt to stat. */
BlobMeta meta;
if (!mgr->stat(request.sessionId, &meta))
{
return ipmi::responseUnspecifiedError();
}
return returnStatBlob(&meta);
}
Resp commitBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(data.data());
/* Sanity check the commitDataLen */
if (request->commitDataLen > (data.size() - sizeof(struct BmcBlobCommitTx)))
{
return ipmi::responseReqDataLenInvalid();
}
data = data.subspan(sizeof(struct BmcBlobCommitTx), request->commitDataLen);
if (!mgr->commit(request->sessionId,
std::vector<uint8_t>(data.begin(), data.end())))
{
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess(std::vector<uint8_t>{});
}
Resp readBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
struct BmcBlobReadTx request;
if (data.size() < sizeof(request))
{
return ipmi::responseReqDataLenInvalid();
}
std::memcpy(&request, data.data(), sizeof(request));
std::vector<uint8_t> result = mgr->read(request.sessionId, request.offset,
request.requestedSize);
/* If the Read fails, it returns success but with only the crc and 0 bytes
* of data.
* If there was data returned, copy into the reply buffer.
*/
std::vector<uint8_t> output(sizeof(BmcBlobReadRx), 0);
if (!result.empty())
{
output.insert(output.end(), result.begin(), result.end());
}
return ipmi::responseSuccess(output);
}
Resp writeBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
{
auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(data.data());
data = data.subspan(sizeof(struct BmcBlobWriteTx));
/* Attempt to write the bytes. */
if (!mgr->write(request->sessionId, request->offset,
std::vector<uint8_t>(data.begin(), data.end())))
{
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess(std::vector<uint8_t>{});
}
Resp writeMeta(ManagerInterface* mgr, std::span<const uint8_t> data)
{
struct BmcBlobWriteMetaTx request;
if (data.size() < sizeof(request))
{
return ipmi::responseReqDataLenInvalid();
}
/* Copy over the request. */
std::memcpy(&request, data.data(), sizeof(request));
/* Nothing really else to validate, we just copy those bytes. */
data = data.subspan(sizeof(struct BmcBlobWriteMetaTx));
/* Attempt to write the bytes. */
if (!mgr->writeMeta(request.sessionId, request.offset,
std::vector<uint8_t>(data.begin(), data.end())))
{
return ipmi::responseUnspecifiedError();
}
return ipmi::responseSuccess(std::vector<uint8_t>{});
}
} // namespace blobs