#include "filesystem/filesystem_instance.h"
+#include <linux/limits.h>
#include <cstdint>
#include <functional>
#include <stdexcept>
#include <sys/stat.h>
+#include <system_error>
#include "common/logger.h"
#include "common/picojson.h"
#include "common/platform_exception.h"
using namespace common;
using namespace extension::filesystem;
+using namespace std::string_literals;
+
+FileHandle::~FileHandle() {
+ ScopeLogger();
+
+ if (file_handle && std::fclose(file_handle)) {
+ int errsv = errno;
+ LoggerE("close file failed, error message: %s", strerror(errsv));
+ }
+}
FilesystemInstance::FilesystemInstance() {
ScopeLogger();
}
static constexpr std::size_t NPOS = (std::size_t)(-1);
+
+/**
+ * On failure throws std::system_error
+ */
+static std::size_t file_size(FILE* file) {
+ ScopeLogger();
+
+ struct ::stat buf;
+ int status = ::fstat(::fileno(file), &buf);
+ if (status != 0) {
+ throw std::system_error{errno, std::generic_category(), "failed to get file size"};
+ }
+
+ return buf.st_size;
+}
+
+static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length = NPOS);
+
/**
* Returns a buffer. If length is NPOS, then it reads whole file, up to the end.
* On failure throws std::runtime_error
*/
-static std::vector<std::uint8_t> read_file(std::string path, std::size_t offset,
+static std::vector<std::uint8_t> read_file(std::string path, long offset = 0,
std::size_t length = NPOS) {
ScopeLogger();
FILE* file = std::fopen(path.c_str(), "r");
if (!file) {
- throw std::runtime_error("cannot open file to read");
+ std::string err_msg = std::string("cannot open file to read") + strerror(errno);
+ throw std::system_error{errno, std::generic_category(), err_msg};
}
SCOPE_EXIT {
}
};
- if (std::fseek(file, offset, SEEK_SET) != 0) {
- throw std::runtime_error("cannot perform seek");
+ if (0 != offset && 0 != std::fseek(file, offset, SEEK_SET)) {
+ std::string err_msg = std::string("cannot perform seek") + strerror(errno);
+ throw std::system_error{errno, std::generic_category(), err_msg};
+ }
+
+ if (NPOS == length) {
+ length = file_size(file) - offset;
}
+ return read_file(file, length);
+}
+
+/**
+ * Returns a buffer. If length is NPOS, then it reads whole file, up to the end.
+ * On failure throws std::runtime_error
+ */
+static std::vector<std::uint8_t> read_file(FILE* file, std::size_t length /*= NPOS*/) {
+ ScopeLogger();
+
// By default reads whole file. Get the file size.
- if (length == NPOS) {
- struct ::stat buf;
- if (::fstat(::fileno(file), &buf) != 0) {
- throw std::runtime_error("cannot fstat");
- }
- length = buf.st_size - offset;
+ if (NPOS == length) {
+ length = file_size(file);
}
std::vector<std::uint8_t> out_buf(length);
data_p += std::fread(data_p, 1, end_p - data_p, file);
if (std::ferror(file)) {
- throw std::runtime_error("error during file read");
+ std::string err_msg = std::string("error during file read") + strerror(errno);
+ throw std::runtime_error(err_msg);
}
if (std::feof(file)) {
/**
* On failure throws std::runtime_error
*/
-void write_file(const std::uint8_t* data, std::size_t len, std::string path, std::size_t offset,
- bool truncate) {
+void write_file(const std::uint8_t* data, std::size_t len, FILE* file) {
ScopeLogger();
- FILE* file = fopen(path.c_str(), truncate ? "w" : "r+");
+ const std::uint8_t* data_p = data;
+ const std::uint8_t* end_p = data + len;
+ while (data_p != end_p) {
+ data_p += fwrite(data_p, 1, end_p - data_p, file);
+
+ if (std::ferror(file)) {
+ std::string err_msg = std::string("error during file write") + strerror(errno);
+ throw std::runtime_error(err_msg);
+ }
+ }
+
+ if (std::fflush(file)) {
+ std::string err_msg = std::string("error during file write") + strerror(errno);
+ throw std::runtime_error(err_msg);
+ }
+}
+
+/**
+ * On failure throws std::runtime_error
+ */
+void write_file(const std::uint8_t* data, std::size_t len, std::string path, long offset,
+ const char* mode) {
+ ScopeLogger();
+
+ FILE* file = std::fopen(path.c_str(), mode);
if (!file) {
- throw std::runtime_error("cannot open file to write");
+ std::string err_msg = std::string("cannot open file to write") + strerror(errno);
+ throw std::runtime_error(err_msg);
}
SCOPE_EXIT {
}
};
- if (std::fseek(file, offset, SEEK_SET) != 0) {
- throw std::runtime_error("cannot perform seek");
+ if (offset != 0 && std::fseek(file, offset, SEEK_SET) != 0) {
+ std::string err_msg = std::string("cannot perform seek") + strerror(errno);
+ throw std::system_error{errno, std::generic_category(), err_msg};
}
- const std::uint8_t* data_p = data;
- const std::uint8_t* end_p = data + len;
- while (data_p != end_p) {
- data_p += fwrite(data_p, 1, end_p - data_p, file);
-
- if (std::ferror(file)) {
- throw std::runtime_error("error during file write");
- }
- }
-
- if (std::fflush(file)) {
- throw std::runtime_error("error during file write");
- }
+ write_file(data, len, file);
}
namespace base64 {
const std::string& str = args.get("data").get<std::string>();
size_t offset = static_cast<size_t>(args.get("offset").get<double>());
bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
+ const char* mode = truncate ? "w" : "r+";
const std::string& encoding =
args.contains("encoding") ? args.get("encoding").get<std::string>() : "utf-8";
if (encoding == "iso-8859-1") {
std::vector<std::uint8_t> data;
latin1::from_utf8(str, data);
- write_file(data.data(), data.size(), location, offset, truncate);
+ write_file(data.data(), data.size(), location, offset, mode);
} else { // default: UTF-8
const std::uint8_t* buf = (const std::uint8_t*)str.c_str();
std::size_t len = str.length();
- write_file(buf, len, location, offset, truncate);
+ write_file(buf, len, location, offset, mode);
}
} catch (std::runtime_error& e) {
LoggerE("Cannot write to file %s, cause: %s", location.c_str(), e.what());
const std::string& str = args.get("data").get<std::string>();
size_t offset = static_cast<size_t>(args.get("offset").get<double>());
bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
+ const char* mode = truncate ? "w" : "r+";
try {
std::vector<std::uint8_t> data;
decode_binary_from_string(str, data);
- write_file(data.data(), data.size(), location, offset, truncate);
+ write_file(data.data(), data.size(), location, offset, mode);
} catch (std::runtime_error& e) {
LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());
PrepareError(FilesystemError::Other, out);
const std::string& str = args.get("data").get<std::string>();
size_t offset = static_cast<size_t>(args.get("offset").get<double>());
bool truncate = static_cast<bool>(args.get("truncate").get<bool>());
+ const char* mode = truncate ? "w" : "r+";
std::vector<std::uint8_t> data;
try {
}
try {
- write_file(data.data(), data.size(), location, offset, truncate);
+ write_file(data.data(), data.size(), location, offset, mode);
ReportSuccess(picojson::value{(double)data.size()}, out);
} catch (std::runtime_error& e) {
LoggerE("Cannot write to %s, cause: %s", location.c_str(), e.what());