* Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
* 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 <fcntl.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <cerrno>
#include <map>
#include <string>
#include <iostream>
#include "hcom_log.h"
#include "common_includes.h"
#include "memfs_sdk_api.h"
namespace py = pybind11;
namespace ock {
namespace memfs {
static FILE *g_sLogger = stderr;
static std::map<int, std::string> s_loggerLevels;
static constexpr uint32_t RENAME_FLAG_NOREPLACE = (1U << 0);
static constexpr uint32_t RENAME_FLAG_EXCHANGE = (1U << 1);
static constexpr uint32_t RENAME_FLAG_FORCE = (1U << 10);
static void ClientLog(int level, const char *msg)
{
auto pos = s_loggerLevels.find(level);
if (pos == s_loggerLevels.end()) {
return;
}
(void)fprintf(g_sLogger, "[%s] %s\n", pos->second.c_str(), msg);
(void)fflush(g_sLogger);
}
static int Initialize(bool tls, const std::string &cert, const std::string &ca, const std::string &pk,
const std::string &pw, const std::string &pmt, const std::string &crl)
{
if (!tls) {
return MemFsClientInitialize();
}
ClientInitParam param;
param.ipcTlsEnabled = true;
param.ipcTlsCertPath = cert;
param.ipcTlsCrlPath = crl;
param.ipcTlsCaPath = ca;
param.ipcTlsPriKeyPath = pk;
param.ipcTlsPasswordPath = pw;
param.pmtPath = pmt;
return MemFsClientInitialize(param);
}
static void Destroy()
{
MemFsClientUnInitialize();
}
static void SetLogger(const std::string &path)
{
s_loggerLevels[0] = "DEBUG";
s_loggerLevels[1] = "INFO";
s_loggerLevels[2] = "WARN";
s_loggerLevels[3] = "ERROR";
auto file = fopen(path.c_str(), "a");
if (file == nullptr) {
LOG_ERROR("open logger file failed.");
return;
}
g_sLogger = file;
auto serviceLog = ock::hcom::UBSHcomNetOutLogger::Instance();
serviceLog->SetExternalLogFunction(ClientLog);
ock::memfs::OutLogger::Instance()->SetExternalLogFunction(ClientLog);
}
static int CreateFile(const std::string &path)
{
auto fd = MemFsOpenFile(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC);
if (fd < 0) {
LOG_ERROR("MemFs create file for write failed: " << errno << " : " << strerror(errno));
return -1;
}
return fd;
}
static int OpenFile(const std::string &path)
{
auto fd = MemFsOpenFile(path.c_str(), O_RDONLY);
if (fd < 0) {
LOG_ERROR("MemFs open file for read failed: " << errno << " : " << strerror(errno));
return -1;
}
return fd;
}
static ssize_t WriteFile(int fd, const py::bytes &data)
{
char *buffer;
ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(data.ptr(), &buffer, &length)) {
errno = EINVAL;
return -1L;
}
auto ret = MemFsWrite(fd, reinterpret_cast<uintptr_t>(buffer), static_cast<uint64_t>(length));
if (ret < 0) {
LOG_ERROR("MemFs write file failed: " << errno << " : " << strerror(errno));
return -1;
}
return ret;
}
static ssize_t WriteFileV(int fd, const std::vector<py::bytes> &buffers)
{
std::vector<Buffer> realBuffers;
realBuffers.reserve(buffers.size());
for (auto &buf : buffers) {
char *buffer;
ssize_t length;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(buf.ptr(), &buffer, &length)) {
errno = EINVAL;
return -1L;
}
realBuffers.emplace_back(buffer, static_cast<uint64_t>(length));
}
auto ret = MemFsWriteV(fd, realBuffers);
if (ret < 0) {
LOG_ERROR("MemFs write file failed: " << errno << " : " << strerror(errno));
return -1;
}
return ret;
}
static std::pair<int, py::bytes> ReadFile(int fd, uint64_t offset, uint64_t length)
{
auto data = new (std::nothrow) char[length];
if (data == nullptr) {
LOG_ERROR("MemFs allocate memory failed.");
return std::make_pair(-1, py::bytes());
}
auto fileSize = MemFsGetSize(fd);
if (fileSize < 0) {
LOG_ERROR("get file size failed for fd(" << fd << ") return : " << fileSize);
return std::make_pair(-1, py::bytes());
}
if (offset >= fileSize) {
return std::make_pair(0, py::bytes());
}
auto readSize = std::min(length, fileSize - offset);
auto ret = MemFsRead(fd, (uintptr_t)data, offset, readSize);
if (ret < 0) {
LOG_ERROR("MemFs read file failed: " << errno << " : " << strerror(errno));
return std::make_pair(-1, py::bytes());
}
auto result = std::string(data, readSize);
delete[] data;
data = nullptr;
return std::make_pair(0, py::bytes(result));
}
static int CloseFile(int fd)
{
auto ret = MemFsClose(fd);
if (ret < 0) {
LOG_ERROR("MemFs close file failed: " << errno << " : " << strerror(errno));
return ret;
}
return 0;
}
static int RenameFile(const std::string &oldPath, const std::string &newPath, uint32_t flags)
{
auto ret = MemFsRenameFile(oldPath.c_str(), newPath.c_str(), flags);
if (ret < 0) {
LOG_ERROR("MemFs rename file failed: " << errno << " : " << strerror(errno));
return ret;
}
return 0;
}
static int WatchDirectories(const std::map<std::string, uint64_t> &info, uint64_t timeout,
const std::function<void(int)> &callback)
{
uint64_t eventId;
std::list<std::pair<std::string, uint64_t>> inputInfo;
for (const auto &it : info) {
inputInfo.emplace_back(it.first, it.second);
}
auto ret = MemfsRegisterWatchDir(
inputInfo, timeout,
[callback](uint64_t id, int result) {
LOG_INFO("event id: " << id << " result :" << result);
callback(result);
},
eventId);
if (ret != 0) {
return -1;
}
return 0;
}
static int WatchDirectory(const std::string &path, int count, uint64_t timeout,
const std::function<void(int)> &callback)
{
uint64_t eventId;
std::list<std::pair<std::string, uint64_t>> inputInfo;
inputInfo.emplace_back(path, count);
auto ret = MemfsRegisterWatchDir(
inputInfo,
timeout,
[callback](uint64_t id, int result) {
LOG_INFO("event id: " << id << " result :" << result);
callback(result);
},
eventId);
if (ret != 0) {
return -1;
}
return 0;
}
PYBIND11_MODULE(memfs, m)
{
m.doc() = "memfs python API";
m.def("logger", &SetLogger, py::arg("path"), "set logger path before initialize instead of standard output.");
m.def("init", &Initialize, "initialize client for memfs.", py::arg("tls") = false, py::arg("cert") = "",
py::arg("ca") = "", py::arg("pk") = "", py::arg("pw") = "", py::arg("pmt") = "", py::arg("crl") = "");
m.def("destroy", &Destroy, "destroy client for memfs.");
m.def("create", &CreateFile, "create new file for write on memfs.", py::arg("path"));
m.def("open", &OpenFile, "open exist file for read on memfs.", py::arg("path"));
m.def("write", &WriteFile, "write file on memfs.", py::arg("fd"), py::arg("data"));
m.def("writev", &WriteFileV, "write file on memfs.", py::arg("fd"), py::arg("data"));
m.def("read", &ReadFile, "read file on memfs.", py::arg("fd"), py::arg("off"), py::arg("len"));
m.def("close", &CloseFile, "close file on memfs.", py::arg("fd"));
m.def("watch", &WatchDirectories, "watch files for backup finished.", py::arg("info"), py::arg("timeout"),
py::arg("callback"));
m.def("watch", &WatchDirectory, "watch files for backup finished.", py::arg("path"), py::arg("count"),
py::arg("timeout"), py::arg("callback"));
m.def("rename", &RenameFile, "change the name or location of a file.", py::arg("source"), py::arg("target"),
py::arg("flags") = 0);
m.attr("RF_NO_REPLACE") = RENAME_FLAG_NOREPLACE;
m.attr("RF_EXCHANGE") = RENAME_FLAG_EXCHANGE;
m.attr("RF_FORCE") = RENAME_FLAG_FORCE;
}
}
}