371 lines
10 KiB
C++
371 lines
10 KiB
C++
#include "utils/devoptab_common.hpp"
|
|
#include "defines.hpp"
|
|
#include "log.hpp"
|
|
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <smb2/smb2.h>
|
|
#include <smb2/libsmb2.h>
|
|
#include <minIni.h>
|
|
|
|
namespace sphaira::devoptab {
|
|
namespace {
|
|
|
|
struct Device final : common::MountDevice {
|
|
Device(const common::MountConfig& cfg) : MountDevice{cfg} {}
|
|
~Device();
|
|
|
|
private:
|
|
bool fix_path(const char* str, char* out, bool strip_leading_slash = false) override {
|
|
return common::fix_path(str, out, true);
|
|
}
|
|
|
|
bool Mount() override;
|
|
int devoptab_open(void *fileStruct, const char *path, int flags, int mode) override;
|
|
int devoptab_close(void *fd) override;
|
|
ssize_t devoptab_read(void *fd, char *ptr, size_t len) override;
|
|
ssize_t devoptab_write(void *fd, const char *ptr, size_t len) override;
|
|
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
|
|
int devoptab_fstat(void *fd, struct stat *st) override;
|
|
int devoptab_unlink(const char *path) override;
|
|
int devoptab_rename(const char *oldName, const char *newName) override;
|
|
int devoptab_mkdir(const char *path, int mode) override;
|
|
int devoptab_rmdir(const char *path) override;
|
|
int devoptab_diropen(void* fd, const char *path) override;
|
|
int devoptab_dirreset(void* fd) override;
|
|
int devoptab_dirnext(void* fd, char *filename, struct stat *filestat) override;
|
|
int devoptab_dirclose(void* fd) override;
|
|
int devoptab_lstat(const char *path, struct stat *st) override;
|
|
int devoptab_ftruncate(void *fd, off_t len) override;
|
|
int devoptab_statvfs(const char *path, struct statvfs *buf) override;
|
|
int devoptab_fsync(void *fd) override;
|
|
|
|
private:
|
|
smb2_context* smb2{};
|
|
bool mounted{};
|
|
};
|
|
|
|
struct File {
|
|
smb2fh* fd;
|
|
};
|
|
|
|
struct Dir {
|
|
smb2dir* dir;
|
|
};
|
|
|
|
void fill_stat(struct stat* st, const smb2_stat_64* smb2_st) {
|
|
if (smb2_st->smb2_type == SMB2_TYPE_FILE) {
|
|
st->st_mode = S_IFREG;
|
|
} else if (smb2_st->smb2_type == SMB2_TYPE_DIRECTORY) {
|
|
st->st_mode = S_IFDIR;
|
|
} else if (smb2_st->smb2_type == SMB2_TYPE_LINK) {
|
|
st->st_mode = S_IFLNK;
|
|
} else {
|
|
log_write("[SMB2] Unknown file type: %u\n", smb2_st->smb2_type);
|
|
st->st_mode = S_IFCHR; // will be skipped by stdio readdir wrapper.
|
|
}
|
|
|
|
st->st_ino = smb2_st->smb2_ino;
|
|
st->st_nlink = smb2_st->smb2_nlink;
|
|
st->st_size = smb2_st->smb2_size;
|
|
st->st_atime = smb2_st->smb2_atime;
|
|
st->st_mtime = smb2_st->smb2_mtime;
|
|
st->st_ctime = smb2_st->smb2_ctime;
|
|
}
|
|
|
|
Device::~Device() {
|
|
if (this->smb2) {
|
|
if (this->mounted) {
|
|
smb2_disconnect_share(this->smb2);
|
|
}
|
|
|
|
smb2_destroy_context(this->smb2);
|
|
}
|
|
}
|
|
|
|
bool Device::Mount() {
|
|
if (mounted) {
|
|
return true;
|
|
}
|
|
|
|
if (!this->smb2) {
|
|
this->smb2 = smb2_init_context();
|
|
if (!this->smb2) {
|
|
log_write("[SMB2] smb2_init_context() failed\n");
|
|
return false;
|
|
}
|
|
|
|
smb2_set_security_mode(this->smb2, SMB2_NEGOTIATE_SIGNING_ENABLED);
|
|
|
|
if (!this->config.user.empty()) {
|
|
smb2_set_user(this->smb2, this->config.user.c_str());
|
|
}
|
|
|
|
if (!this->config.pass.empty()) {
|
|
smb2_set_password(this->smb2, this->config.pass.c_str());
|
|
}
|
|
|
|
const auto domain = this->config.extra.find("domain");
|
|
if (domain != this->config.extra.end()) {
|
|
smb2_set_domain(this->smb2, domain->second.c_str());
|
|
}
|
|
|
|
const auto workstation = this->config.extra.find("workstation");
|
|
if (workstation != this->config.extra.end()) {
|
|
smb2_set_workstation(this->smb2, workstation->second.c_str());
|
|
}
|
|
|
|
smb2_set_timeout(this->smb2, this->config.timeout);
|
|
}
|
|
|
|
auto smb2_url = smb2_parse_url(this->smb2, this->config.url.c_str());
|
|
if (!smb2_url) {
|
|
log_write("[SMB2] smb2_parse_url() failed: %s\n", smb2_get_error(this->smb2));
|
|
return false;
|
|
}
|
|
ON_SCOPE_EXIT(smb2_destroy_url(smb2_url));
|
|
|
|
const auto ret = smb2_connect_share(this->smb2, smb2_url->server, smb2_url->share, smb2_url->user);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_connect_share() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return false;
|
|
}
|
|
|
|
this->mounted = true;
|
|
return true;
|
|
}
|
|
|
|
int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mode) {
|
|
auto file = static_cast<File*>(fileStruct);
|
|
|
|
file->fd = smb2_open(this->smb2, path, flags);
|
|
if (!file->fd) {
|
|
log_write("[SMB2] smb2_open() failed: %s\n", smb2_get_error(this->smb2));
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_close(void *fd) {
|
|
auto file = static_cast<File*>(fd);
|
|
|
|
smb2_close(this->smb2, file->fd);
|
|
return 0;
|
|
}
|
|
|
|
ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
|
|
auto file = static_cast<File*>(fd);
|
|
|
|
const auto ret = smb2_read(this->smb2, file->fd, reinterpret_cast<uint8_t*>(ptr), len);
|
|
if (ret < 0) {
|
|
log_write("[SMB2] smb2_read() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ssize_t Device::devoptab_write(void *fd, const char *ptr, size_t len) {
|
|
auto file = static_cast<File*>(fd);
|
|
|
|
const auto ret = smb2_write(this->smb2, file->fd, reinterpret_cast<const uint8_t*>(ptr), len);
|
|
if (ret < 0) {
|
|
log_write("[SMB2] smb2_write() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
|
|
auto file = static_cast<File*>(fd);
|
|
|
|
u64 current_offset = 0;
|
|
const auto ret = smb2_lseek(this->smb2, file->fd, pos, dir, ¤t_offset);
|
|
if (ret < 0) {
|
|
log_write("[SMB2] smb2_lseek() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return current_offset;
|
|
}
|
|
|
|
int Device::devoptab_fstat(void *fd, struct stat *st) {
|
|
auto file = static_cast<File*>(fd);
|
|
|
|
smb2_stat_64 smb2_st{};
|
|
const auto ret = smb2_fstat(this->smb2, file->fd, &smb2_st);
|
|
if (ret < 0) {
|
|
log_write("[SMB2] smb2_fstat() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
fill_stat(st, &smb2_st);
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_unlink(const char *path) {
|
|
const auto ret = smb2_unlink(this->smb2, path);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_unlink() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_rename(const char *oldName, const char *newName) {
|
|
const auto ret = smb2_rename(this->smb2, oldName, newName);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_rename() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_mkdir(const char *path, int mode) {
|
|
const auto ret = smb2_mkdir(this->smb2, path);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_mkdir() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_rmdir(const char *path) {
|
|
const auto ret = smb2_rmdir(this->smb2, path);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_rmdir() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_diropen(void* fd, const char *path) {
|
|
auto dir = static_cast<Dir*>(fd);
|
|
|
|
dir->dir = smb2_opendir(this->smb2, path);
|
|
if (!dir->dir) {
|
|
log_write("[SMB2] smb2_opendir() failed: %s\n", smb2_get_error(this->smb2));
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_dirreset(void* fd) {
|
|
auto dir = static_cast<Dir*>(fd);
|
|
if (!dir->dir) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
smb2_rewinddir(this->smb2, dir->dir);
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_dirnext(void* fd, char *filename, struct stat *filestat) {
|
|
auto dir = static_cast<Dir*>(fd);
|
|
|
|
if (!dir->dir) {
|
|
return EINVAL;
|
|
}
|
|
|
|
const auto entry = smb2_readdir(this->smb2, dir->dir);
|
|
if (!entry) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
std::strncpy(filename, entry->name, NAME_MAX);
|
|
filename[NAME_MAX - 1] = '\0';
|
|
fill_stat(filestat, &entry->st);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_dirclose(void* fd) {
|
|
auto dir = static_cast<Dir*>(fd);
|
|
|
|
smb2_closedir(this->smb2, dir->dir);
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
|
smb2_stat_64 smb2_st{};
|
|
const auto ret = smb2_stat(this->smb2, path, &smb2_st);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_stat() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
fill_stat(st, &smb2_st);
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_ftruncate(void *fd, off_t len) {
|
|
auto file = static_cast<File*>(fd);
|
|
|
|
const auto ret = smb2_ftruncate(this->smb2, file->fd, len);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_ftruncate() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_statvfs(const char *path, struct statvfs *buf) {
|
|
struct smb2_statvfs smb2_st{};
|
|
const auto ret = smb2_statvfs(this->smb2, path, &smb2_st);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_statvfs() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
buf->f_bsize = smb2_st.f_bsize;
|
|
buf->f_frsize = smb2_st.f_frsize;
|
|
buf->f_blocks = smb2_st.f_blocks;
|
|
buf->f_bfree = smb2_st.f_bfree;
|
|
buf->f_bavail = smb2_st.f_bavail;
|
|
buf->f_files = smb2_st.f_files;
|
|
buf->f_ffree = smb2_st.f_ffree;
|
|
buf->f_favail = smb2_st.f_favail;
|
|
buf->f_fsid = smb2_st.f_fsid;
|
|
buf->f_flag = smb2_st.f_flag;
|
|
buf->f_namemax = smb2_st.f_namemax;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Device::devoptab_fsync(void *fd) {
|
|
auto file = static_cast<File*>(fd);
|
|
|
|
const auto ret = smb2_fsync(this->smb2, file->fd);
|
|
if (ret) {
|
|
log_write("[SMB2] smb2_fsync() failed: %s errno: %s\n", smb2_get_error(this->smb2), std::strerror(-ret));
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Result MountSmb2All() {
|
|
return common::MountNetworkDevice([](const common::MountConfig& cfg) {
|
|
return std::make_unique<Device>(cfg);
|
|
},
|
|
sizeof(File), sizeof(Dir),
|
|
"/config/sphaira/smb.ini",
|
|
"SMB"
|
|
);
|
|
}
|
|
|
|
} // namespace sphaira::devoptab
|