218 lines
7.0 KiB
C++
218 lines
7.0 KiB
C++
#pragma once
|
|
|
|
#include "yati/source/file.hpp"
|
|
#include "utils/lru.hpp"
|
|
#include "location.hpp"
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <span>
|
|
#include <functional>
|
|
#include <unordered_map>
|
|
#include <curl/curl.h>
|
|
|
|
namespace sphaira::devoptab::common {
|
|
|
|
// max entries per devoptab, should be enough.
|
|
enum { MAX_ENTRIES = 4 };
|
|
|
|
struct BufferedDataBase : yati::source::Base {
|
|
BufferedDataBase(const std::shared_ptr<yati::source::Base>& _source, u64 _size)
|
|
: source{_source}
|
|
, capacity{_size} {
|
|
|
|
}
|
|
|
|
virtual Result Read(void* buf, s64 off, s64 size, u64* bytes_read) override {
|
|
return source->Read(buf, off, size, bytes_read);
|
|
}
|
|
|
|
protected:
|
|
std::shared_ptr<yati::source::Base> source;
|
|
const u64 capacity;
|
|
};
|
|
|
|
// buffers data in 512k chunks to maximise throughput.
|
|
// not suitable if random access >= 512k is common.
|
|
// if that is needed, see the LRU cache varient used for fatfs.
|
|
struct BufferedData : BufferedDataBase {
|
|
BufferedData(const std::shared_ptr<yati::source::Base>& _source, u64 _size, u64 _alloc = 1024 * 512)
|
|
: BufferedDataBase{_source, _size} {
|
|
m_data.resize(_alloc);
|
|
}
|
|
|
|
virtual Result Read(void* buf, s64 off, s64 size, u64* bytes_read) override;
|
|
|
|
private:
|
|
u64 m_off{};
|
|
u64 m_size{};
|
|
std::vector<u8> m_data{};
|
|
};
|
|
|
|
struct BufferedFileData {
|
|
u8* data{};
|
|
u64 off{};
|
|
u64 size{};
|
|
|
|
~BufferedFileData() {
|
|
if (data) {
|
|
free(data);
|
|
}
|
|
}
|
|
|
|
void Allocate(u64 new_size) {
|
|
data = (u8*)realloc(data, new_size * sizeof(*data));
|
|
off = 0;
|
|
size = 0;
|
|
}
|
|
};
|
|
|
|
constexpr u64 CACHE_LARGE_ALLOC_SIZE = 1024 * 512;
|
|
constexpr u64 CACHE_LARGE_SIZE = 1024 * 16;
|
|
|
|
struct LruBufferedData : BufferedDataBase {
|
|
LruBufferedData(const std::shared_ptr<yati::source::Base>& _source, u64 _size, u32 small = 1024, u32 large = 2)
|
|
: BufferedDataBase{_source, _size} {
|
|
buffered_small.resize(small);
|
|
buffered_large.resize(large);
|
|
lru_cache[0].Init(buffered_small);
|
|
lru_cache[1].Init(buffered_large);
|
|
}
|
|
|
|
virtual Result Read(void* buf, s64 off, s64 size, u64* bytes_read) override;
|
|
|
|
private:
|
|
utils::Lru<BufferedFileData> lru_cache[2]{};
|
|
std::vector<BufferedFileData> buffered_small{}; // 1MiB (usually).
|
|
std::vector<BufferedFileData> buffered_large{}; // 1MiB
|
|
};
|
|
|
|
bool fix_path(const char* str, char* out, bool strip_leading_slash = false);
|
|
|
|
void update_devoptab_for_read_only(devoptab_t* devoptab, bool read_only);
|
|
|
|
struct PushPullThreadData {
|
|
PushPullThreadData(CURL* _curl);
|
|
virtual ~PushPullThreadData();
|
|
Result CreateAndStart();
|
|
|
|
void Cancel();
|
|
bool IsRunning();
|
|
|
|
size_t PullData(char* data, size_t total_size);
|
|
size_t PushData(const char* data, size_t total_size);
|
|
|
|
static size_t push_thread_callback(const char *ptr, size_t size, size_t nmemb, void *userdata);
|
|
static size_t pull_thread_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
|
|
|
private:
|
|
static void thread_func(void* arg);
|
|
|
|
public:
|
|
CURL* const curl{};
|
|
std::vector<char> buffer{};
|
|
Mutex mutex{};
|
|
CondVar can_push{};
|
|
CondVar can_pull{};
|
|
|
|
long code{};
|
|
bool error{};
|
|
bool finished{};
|
|
bool started{};
|
|
|
|
private:
|
|
Thread thread{};
|
|
};
|
|
|
|
struct MountConfig {
|
|
std::string name{};
|
|
std::string url{};
|
|
std::string user{};
|
|
std::string pass{};
|
|
std::string dump_path{};
|
|
std::optional<long> port{};
|
|
int timeout{3000}; // 3 seconds.
|
|
bool read_only{};
|
|
bool no_stat_file{true};
|
|
bool no_stat_dir{true};
|
|
|
|
std::unordered_map<std::string, std::string> extra{};
|
|
};
|
|
|
|
struct PullThreadData final : PushPullThreadData {
|
|
using PushPullThreadData::PushPullThreadData;
|
|
~PullThreadData();
|
|
};
|
|
|
|
|
|
struct PushThreadData final : PushPullThreadData {
|
|
using PushPullThreadData::PushPullThreadData;
|
|
~PushThreadData();
|
|
};
|
|
|
|
struct MountDevice {
|
|
MountDevice(const MountConfig& _config) : config{_config} {}
|
|
virtual ~MountDevice() = default;
|
|
|
|
virtual bool fix_path(const char* str, char* out, bool strip_leading_slash = false) {
|
|
return common::fix_path(str, out, strip_leading_slash);
|
|
}
|
|
|
|
virtual bool Mount() = 0;
|
|
virtual int devoptab_open(void *fileStruct, const char *path, int flags, int mode) { return -EIO; }
|
|
virtual int devoptab_close(void *fd) { return -EIO; }
|
|
virtual ssize_t devoptab_read(void *fd, char *ptr, size_t len) { return -EIO; }
|
|
virtual ssize_t devoptab_write(void *fd, const char *ptr, size_t len) { return -EIO; }
|
|
virtual off_t devoptab_seek(void *fd, off_t pos, int dir) { return 0; }
|
|
virtual int devoptab_fstat(void *fd, struct stat *st) { return -EIO; }
|
|
virtual int devoptab_unlink(const char *path) { return -EIO; }
|
|
virtual int devoptab_rename(const char *oldName, const char *newName) { return -EIO; }
|
|
virtual int devoptab_mkdir(const char *path, int mode) { return -EIO; }
|
|
virtual int devoptab_rmdir(const char *path) { return -EIO; }
|
|
virtual int devoptab_diropen(void* fd, const char *path) { return -EIO; }
|
|
virtual int devoptab_dirreset(void* fd) { return -EIO; }
|
|
virtual int devoptab_dirnext(void* fd, char *filename, struct stat *filestat) { return -EIO; }
|
|
virtual int devoptab_dirclose(void* fd) { return -EIO; }
|
|
virtual int devoptab_lstat(const char *path, struct stat *st) { return -EIO; }
|
|
virtual int devoptab_ftruncate(void *fd, off_t len) { return -EIO; }
|
|
virtual int devoptab_statvfs(const char *_path, struct statvfs *buf) { return -EIO; }
|
|
virtual int devoptab_fsync(void *fd) { return -EIO; }
|
|
virtual int devoptab_utimes(const char *_path, const struct timeval times[2]) { return -EIO; }
|
|
|
|
const MountConfig config;
|
|
};
|
|
|
|
struct MountCurlDevice : MountDevice {
|
|
using MountDevice::MountDevice;
|
|
// MountCurlDevice(const MountConfig& _config);
|
|
virtual ~MountCurlDevice();
|
|
|
|
PushThreadData* CreatePushData(CURL* curl, const std::string& url, size_t offset);
|
|
PullThreadData* CreatePullData(CURL* curl, const std::string& url, bool append = false);
|
|
|
|
virtual bool Mount();
|
|
virtual void curl_set_common_options(CURL* curl, const std::string& url);
|
|
static size_t write_memory_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
|
static size_t write_data_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
|
static size_t read_data_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
|
static std::string html_decode(const std::string_view& str);
|
|
static std::string url_decode(const std::string& str);
|
|
std::string build_url(const std::string& path, bool is_dir);
|
|
|
|
protected:
|
|
CURL* curl{};
|
|
CURL* transfer_curl{};
|
|
|
|
private:
|
|
// path extracted from the url.
|
|
std::string m_url_path{};
|
|
CURLU* curlu{};
|
|
CURLSH* m_curl_share{};
|
|
RwLock m_rwlocks[CURL_LOCK_DATA_LAST]{};
|
|
bool m_mounted{};
|
|
};
|
|
|
|
using CreateDeviceCallback = std::function<std::unique_ptr<MountDevice>(const MountConfig& config)>;
|
|
Result MountNetworkDevice(const CreateDeviceCallback& create_device, size_t file_size, size_t dir_size, const char* config_path, const char* name);
|
|
|
|
} // namespace sphaira::devoptab::common
|