lots of changes, see description.

- enable sftp by default.
- add more descriptive stdio errors.
- disable devoptab timeout by default.
- handle errors for devoptab seek.
- add r/d/w progress events for threaded_file_transfer
- remove system album from file browser, only show sd card.
- do not clear game selection in games menu. useful for selecting games to backup, then delete after.
- change smb2 r/w to only send max amount, matches nfs behaviour. not sure if its needed as smb probably handles it for us.
This commit is contained in:
ITotalJustice
2025-09-13 13:16:18 +01:00
parent 931531e799
commit 8b2e541b1d
24 changed files with 254 additions and 122 deletions

View File

@@ -26,7 +26,7 @@ option(ENABLE_DEVOPTAB_WEBDAV "" ON)
# max speed is 8MiB/s, which is fine for wifi, but awful for ethernet.
# other clients get 36-40MiB/s.
# it also adds 230k to binary size, and i don't think anyone will use it.
option(ENABLE_DEVOPTAB_SFTP "" OFF)
option(ENABLE_DEVOPTAB_SFTP "" ON)
set(sphaira_VERSION 0.13.3)

View File

@@ -511,7 +511,22 @@ enum class SphairaResult : Result {
FsNewPathEmpty,
FsLoadingCancelled,
FsBrokenRoot,
FsUnknownStdioError,
FsStdioFailedToSeek,
FsStdioFailedToRead,
FsStdioFailedToWrite,
FsStdioFailedToOpenFile,
FsStdioFailedToCreate,
FsStdioFailedToTruncate,
FsStdioFailedToFlush,
FsStdioFailedToCreateDirectory,
FsStdioFailedToDeleteFile,
FsStdioFailedToDeleteDirectory,
FsStdioFailedToOpenDirectory,
FsStdioFailedToRename,
FsStdioFailedToStat,
FsReadOnly,
FsNotActive,
FsFailedStdioStat,
@@ -680,6 +695,19 @@ enum : Result {
MAKE_SPHAIRA_RESULT_ENUM(FsLoadingCancelled),
MAKE_SPHAIRA_RESULT_ENUM(FsBrokenRoot),
MAKE_SPHAIRA_RESULT_ENUM(FsUnknownStdioError),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToSeek),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToRead),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToWrite),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToOpenFile),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToCreate),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToTruncate),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToFlush),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToCreateDirectory),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToDeleteFile),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToDeleteDirectory),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToOpenDirectory),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToRename),
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToStat),
MAKE_SPHAIRA_RESULT_ENUM(FsReadOnly),
MAKE_SPHAIRA_RESULT_ENUM(FsNotActive),
MAKE_SPHAIRA_RESULT_ENUM(FsFailedStdioStat),

View File

@@ -91,6 +91,8 @@ 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 {
static constexpr size_t MAX_BUFFER_SIZE = 1024 * 64; // 64KB max buffer
PushPullThreadData(CURL* _curl);
virtual ~PushPullThreadData();
Result CreateAndStart();
@@ -130,7 +132,7 @@ struct MountConfig {
std::string pass{};
std::string dump_path{};
std::optional<long> port{};
int timeout{5000}; // 5 seconds.
long timeout{};
bool read_only{};
bool no_stat_file{true};
bool no_stat_dir{true};
@@ -164,7 +166,7 @@ struct MountDevice {
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 ssize_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; }

View File

@@ -321,12 +321,12 @@ Result CreateFile(const FsPathReal& path, u64 size, u32 option, bool ignore_read
}
R_TRY(fsdevGetLastResult());
return Result_FsUnknownStdioError;
return Result_FsStdioFailedToCreate;
}
ON_SCOPE_EXIT(close(fd));
if (size) {
R_UNLESS(!ftruncate(fd, size), Result_FsUnknownStdioError);
R_UNLESS(!ftruncate(fd, size), Result_FsStdioFailedToTruncate);
}
R_SUCCEED();
@@ -341,7 +341,7 @@ Result CreateDirectory(const FsPathReal& path, bool ignore_read_only) {
}
R_TRY(fsdevGetLastResult());
return Result_FsUnknownStdioError;
return Result_FsStdioFailedToCreateDirectory;
}
R_SUCCEED();
}
@@ -363,7 +363,7 @@ Result DeleteFile(const FsPathReal& path, bool ignore_read_only) {
if (unlink(path)) {
R_TRY(fsdevGetLastResult());
return Result_FsUnknownStdioError;
return Result_FsStdioFailedToDeleteFile;
}
R_SUCCEED();
}
@@ -373,7 +373,7 @@ Result DeleteDirectory(const FsPathReal& path, bool ignore_read_only) {
if (rmdir(path)) {
R_TRY(fsdevGetLastResult());
return Result_FsUnknownStdioError;
return Result_FsStdioFailedToDeleteDirectory;
}
R_SUCCEED();
}
@@ -405,7 +405,7 @@ Result RenameFile(const FsPathReal& src, const FsPathReal& dst, bool ignore_read
if (rename(src, dst)) {
R_TRY(fsdevGetLastResult());
return Result_FsUnknownStdioError;
return Result_FsStdioFailedToRename;
}
R_SUCCEED();
}
@@ -421,7 +421,7 @@ Result GetEntryType(const FsPathReal& path, FsDirEntryType* out) {
struct stat st;
if (stat(path, &st)) {
R_TRY(fsdevGetLastResult());
return Result_FsUnknownStdioError;
return Result_FsStdioFailedToStat;
}
*out = S_ISREG(st.st_mode) ? FsDirEntryType_File : FsDirEntryType_Dir;
R_SUCCEED();
@@ -431,7 +431,7 @@ Result GetFileTimeStampRaw(const FsPathReal& path, FsTimeStampRaw *out) {
struct stat st;
if (stat(path, &st)) {
R_TRY(fsdevGetLastResult());
return Result_FsUnknownStdioError;
return Result_FsStdioFailedToStat;
}
out->is_valid = true;
@@ -489,7 +489,7 @@ Result OpenFile(fs::Fs* fs, const FsPathReal& path, u32 mode, File* f) {
f->m_stdio = std::fopen(path, "rb+");
}
R_UNLESS(f->m_stdio, Result_FsUnknownStdioError);
R_UNLESS(f->m_stdio, Result_FsStdioFailedToOpenFile);
// disable buffering to match native fs behavior.
// this also causes problems with network io as it will do double reads.
@@ -515,9 +515,11 @@ Result File::Read( s64 off, void* buf, u64 read_size, u32 option, u64* bytes_rea
} else {
R_UNLESS(m_stdio, Result_FsUnknownStdioError);
if (std::ftell(m_stdio) != off) {
if (off != std::ftell(m_stdio)) {
const auto ret = std::fseek(m_stdio, off, SEEK_SET);
R_UNLESS(ret == 0, Result_FsUnknownStdioError);
log_write("[FS] fseek to %ld ret: %d new_off: %zd\n", off, ret, std::ftell(m_stdio));
R_UNLESS(ret == 0, Result_FsStdioFailedToSeek);
R_UNLESS(off == std::ftell(m_stdio), Result_FsStdioFailedToSeek);
}
*bytes_read = std::fread(buf, 1, read_size, m_stdio);
@@ -526,7 +528,7 @@ Result File::Read( s64 off, void* buf, u64 read_size, u32 option, u64* bytes_rea
if (*bytes_read < read_size) {
if (!std::feof(m_stdio) && std::ferror(m_stdio)) {
log_write("[FS] fread error: %d\n", std::ferror(m_stdio));
R_THROW(Result_FsUnknownStdioError);
R_THROW(Result_FsStdioFailedToRead);
}
}
}
@@ -542,14 +544,15 @@ Result File::Write(s64 off, const void* buf, u64 write_size, u32 option) {
} else {
R_UNLESS(m_stdio, Result_FsUnknownStdioError);
if (std::ftell(m_stdio) != off) {
if (off != std::ftell(m_stdio)) {
const auto ret = std::fseek(m_stdio, off, SEEK_SET);
R_UNLESS(ret == 0, Result_FsUnknownStdioError);
R_UNLESS(ret == 0, Result_FsStdioFailedToSeek);
R_UNLESS(off == std::ftell(m_stdio), Result_FsStdioFailedToSeek);
}
const auto result = std::fwrite(buf, 1, write_size, m_stdio);
// log_write("[FS] fwrite res: %zu vs %zu\n", result, write_size);
R_UNLESS(result == write_size, Result_FsUnknownStdioError);
R_UNLESS(result == write_size, Result_FsStdioFailedToWrite);
}
R_SUCCEED();
@@ -563,8 +566,8 @@ Result File::SetSize(s64 sz) {
} else {
R_UNLESS(m_stdio, Result_FsUnknownStdioError);
const auto fd = fileno(m_stdio);
R_UNLESS(fd > 0, Result_FsUnknownStdioError);
R_UNLESS(!ftruncate(fd, sz), Result_FsUnknownStdioError);
R_UNLESS(fd > 0, Result_FsStdioFailedToTruncate);
R_UNLESS(!ftruncate(fd, sz), Result_FsStdioFailedToTruncate);
}
R_SUCCEED();
@@ -579,7 +582,7 @@ Result File::GetSize(s64* out) {
R_UNLESS(m_stdio, Result_FsUnknownStdioError);
struct stat st;
R_UNLESS(!fstat(fileno(m_stdio), &st), Result_FsUnknownStdioError);
R_UNLESS(!fstat(fileno(m_stdio), &st), Result_FsStdioFailedToStat);
*out = st.st_size;
}
@@ -617,7 +620,7 @@ Result OpenDirectory(fs::Fs* fs, const FsPathReal& path, u32 mode, Dir* d) {
R_TRY(fsFsOpenDirectory(&fs->m_fs, path, mode, &d->m_native));
} else {
d->m_stdio = opendir(path);
R_UNLESS(d->m_stdio, Result_FsUnknownStdioError);
R_UNLESS(d->m_stdio, Result_FsStdioFailedToOpenDirectory);
}
R_SUCCEED();

View File

@@ -82,6 +82,14 @@ struct ThreadData {
return read_running || decompress_running || write_running;
}
auto GetReadOffset() volatile const -> s64 {
return read_offset;
}
auto GetDecompressOffset() volatile const -> s64 {
return decompress_offset;
}
auto GetWriteOffset() volatile const -> s64 {
return write_offset;
}
@@ -94,8 +102,16 @@ struct ThreadData {
return &m_uevent_done;
}
auto GetProgressEvent() {
return &m_uevent_progres;
auto GetReadProgressEvent() {
return &m_uevent_read_progress;
}
auto GetDecompressProgressEvent() {
return &m_uevent_decompress_progress;
}
auto GetWriteProgressEvent() {
return &m_uevent_write_progress;
}
void SetReadResult(Result result) {
@@ -174,7 +190,9 @@ private:
CondVar can_pull_write{};
UEvent m_uevent_done{};
UEvent m_uevent_progres{};
UEvent m_uevent_read_progress{};
UEvent m_uevent_decompress_progress{};
UEvent m_uevent_write_progress{};
RingBuf<2> read_buffers{};
RingBuf<2> write_buffers{};
@@ -219,8 +237,10 @@ ThreadData::ThreadData(ui::ProgressBox* _pbox, s64 size, const ReadCallback& _rf
condvarInit(std::addressof(can_pull));
condvarInit(std::addressof(can_pull_write));
ueventCreate(&m_uevent_done, false);
ueventCreate(&m_uevent_progres, true);
ueventCreate(GetDoneEvent(), false);
ueventCreate(GetReadProgressEvent(), true);
ueventCreate(GetDecompressProgressEvent(), true);
ueventCreate(GetWriteProgressEvent(), true);
}
auto ThreadData::GetResults() volatile -> Result {
@@ -379,6 +399,7 @@ Result ThreadData::readFuncInternal() {
break;
}
ueventSignal(GetReadProgressEvent());
auto buf_size = bytes_read;
R_TRY(this->SetDecompressBuf(buf, buffer_offset, buf_size));
}
@@ -423,25 +444,17 @@ Result ThreadData::decompressFuncInternal() {
}
size -= rsize;
this->decompress_offset += rsize;
data += rsize;
// const auto buf_off = temp_buf.size();
// temp_buf.resize(buf_off + size);
// std::memcpy(temp_buf.data() + buf_off, data, size);
// this->decompress_offset += size;
// if (temp_buf.size() >= temp_buf_flush_max) {
// // log_write("flushing data: %zu %.2f MiB\n", temp_buf.size(), temp_buf.size() / 1024.0 / 1024.0);
// R_TRY(this->SetWriteBuf(temp_buf, temp_buf.size()));
// temp_buf.resize(0);
// }
this->decompress_offset += rsize;
ueventSignal(GetDecompressProgressEvent());
}
R_SUCCEED();
}));
} else {
this->decompress_offset += buf.size();
ueventSignal(GetDecompressProgressEvent());
R_TRY(this->SetWriteBuf(buf, buf.size()));
}
}
@@ -479,7 +492,7 @@ Result ThreadData::writeFuncInternal() {
}
this->write_offset += size;
ueventSignal(GetProgressEvent());
ueventSignal(GetWriteProgressEvent());
}
log_write("finished write thread success!\n");
@@ -586,7 +599,11 @@ Result TransferInternal(ui::ProgressBox* pbox, s64 size, const ReadCallback& rfu
R_TRY(start_threads());
log_write("[THREAD] started threads\n");
const auto waiter_progress = waiterForUEvent(t_data.GetProgressEvent());
// use the read progress as the write output may be smaller due to compressing
// so read will show a more accurate progress.
// TODO: show progress bar for all 3 threads.
// NOTE: went back to using write progress for now.
const auto waiter_progress = waiterForUEvent(t_data.GetWriteProgressEvent());
const auto waiter_cancel = waiterForUEvent(pbox->GetCancelEvent());
const auto waiter_done = waiterForUEvent(t_data.GetDoneEvent());

View File

@@ -51,6 +51,19 @@ auto GetCodeMessage(Result rc) -> const char* {
case Result_FsLoadingCancelled: return "SphairaError_FsLoadingCancelled";
case Result_FsBrokenRoot: return "SphairaError_FsBrokenRoot";
case Result_FsUnknownStdioError: return "SphairaError_FsUnknownStdioError";
case Result_FsStdioFailedToSeek: return "SphairaError_FsStdioFailedToSeek";
case Result_FsStdioFailedToRead: return "SphairaError_FsStdioFailedToRead";
case Result_FsStdioFailedToWrite: return "SphairaError_FsStdioFailedToWrite";
case Result_FsStdioFailedToOpenFile: return "SphairaError_FsStdioFailedToOpenFile";
case Result_FsStdioFailedToCreate: return "SphairaError_FsStdioFailedToCreate";
case Result_FsStdioFailedToTruncate: return "SphairaError_FsStdioFailedToTruncate";
case Result_FsStdioFailedToFlush: return "SphairaError_FsStdioFailedToFlush";
case Result_FsStdioFailedToCreateDirectory: return "SphairaError_FsStdioFailedToCreateDirectory";
case Result_FsStdioFailedToDeleteFile: return "SphairaError_FsStdioFailedToDeleteFile";
case Result_FsStdioFailedToDeleteDirectory: return "SphairaError_FsStdioFailedToDeleteDirectory";
case Result_FsStdioFailedToOpenDirectory: return "SphairaError_FsStdioFailedToOpenDirectory";
case Result_FsStdioFailedToRename: return "SphairaError_FsStdioFailedToRename";
case Result_FsStdioFailedToStat: return "SphairaError_FsStdioFailedToStat";
case Result_FsReadOnly: return "SphairaError_FsReadOnly";
case Result_FsNotActive: return "SphairaError_FsNotActive";
case Result_FsFailedStdioStat: return "SphairaError_FsFailedStdioStat";

View File

@@ -81,8 +81,7 @@ constexpr FsEntry FS_ENTRY_DEFAULT{
constexpr FsEntry FS_ENTRIES[]{
FS_ENTRY_DEFAULT,
{ "Image System memory", "/", FsType::ImageNand },
{ "Image microSD card", "/", FsType::ImageSd},
{ "Album", "/", FsType::ImageSd},
};
constexpr std::string_view AUDIO_EXTENSIONS[] = {

View File

@@ -706,7 +706,6 @@ void Menu::ExportOptions(bool to_nsz) {
void Menu::DumpGames(u32 flags, bool to_nsz) {
auto targets = GetSelectedEntries();
ClearSelection();
std::vector<NspEntry> nsp_entries;
for (auto& e : targets) {

View File

@@ -88,7 +88,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -134,7 +134,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return bytes_read;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto& info = file->info;

View File

@@ -171,9 +171,14 @@ off_t devoptab_seek(struct _reent *r, void *fd, off_t pos, int dir) {
SCOPED_RWLOCK(&g_rwlock, false);
SCOPED_MUTEX(&file->device->mutex);
const auto off = file->device->mount_device->devoptab_seek(file->fd, pos, dir);
const auto ret = file->device->mount_device->devoptab_seek(file->fd, pos, dir);
if (ret < 0) {
set_errno(r, -ret);
return 0;
}
r->_errno = 0;
return off;
return ret;
}
int devoptab_fstat(struct _reent *r, void *fd, struct stat *st) {
@@ -960,11 +965,17 @@ PushPullThreadData::PushPullThreadData(CURL* _curl) : curl{_curl} {
PushPullThreadData::~PushPullThreadData() {
Cancel();
if (started) {
threadWaitForExit(&thread);
}
threadClose(&thread);
}
Result PushPullThreadData::CreateAndStart() {
SCOPED_MUTEX(&mutex);
if (started) {
R_SUCCEED();
}
@@ -989,6 +1000,10 @@ bool PushPullThreadData::IsRunning() {
}
size_t PushPullThreadData::PullData(char* data, size_t total_size) {
if (!data || !total_size) {
return 0;
}
SCOPED_MUTEX(&mutex);
ON_SCOPE_EXIT(condvarWakeOne(&can_push));
@@ -1014,12 +1029,16 @@ size_t PushPullThreadData::PullData(char* data, size_t total_size) {
}
size_t PushPullThreadData::PushData(const char* data, size_t total_size) {
if (!data || !total_size) {
return 0;
}
SCOPED_MUTEX(&mutex);
ON_SCOPE_EXIT(condvarWakeOne(&can_pull));
size_t bytes_written = 0;
while (bytes_written < total_size && !error && !finished) {
const size_t space_left = (1024 * 64) - buffer.size(); // 64K max buffer
const size_t space_left = MAX_BUFFER_SIZE - buffer.size();
if (space_left == 0) {
condvarWakeOne(&can_pull);
condvarWait(&can_push, &mutex);
@@ -1035,11 +1054,19 @@ size_t PushPullThreadData::PushData(const char* data, size_t total_size) {
}
size_t PushPullThreadData::push_thread_callback(const char *ptr, size_t size, size_t nmemb, void *userdata) {
if (!ptr || !userdata || !size || !nmemb) {
return 0;
}
auto* data = static_cast<PushPullThreadData*>(userdata);
return data->PushData(ptr, size * nmemb);
}
size_t PushPullThreadData::pull_thread_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
if (!ptr || !userdata || !size || !nmemb) {
return 0;
}
auto* data = static_cast<PushPullThreadData*>(userdata);
return data->PullData(ptr, size * nmemb);
}
@@ -1067,10 +1094,29 @@ PullThreadData::~PullThreadData() {
if (started) {
SCOPED_MUTEX(&mutex);
// for now, always wait until the dat is flushed.
// may enable a timeout later on, however i don't want to risk
// data loss for users that have slow hdd / connections.
#if 1
while (!finished && !error && !buffer.empty()) {
condvarWakeOne(&can_pull);
condvarWaitTimeout(&can_push, &mutex, 5e+9);
condvarWait(&can_push, &mutex);
}
#else
u64 timeout = 5e+9;
const auto deadline = armGetSystemTick() + armNsToTicks(timeout);
while (!finished && !error && !buffer.empty()) {
const s64 remaining = deadline - armGetSystemTick();
timeout = remaining > 0 ? armTicksToNs(remaining) : 0;
condvarWakeOne(&can_pull);
if (R_FAILED(condvarWaitTimeout(&can_push, &mutex, timeout))) {
log_write("[PullThreadData] condvarWaitTimeout() timed out flushing data: %zu\n", buffer.size());
break;
}
}
#endif
}
}
@@ -1276,11 +1322,6 @@ void MountCurlDevice::curl_set_common_options(CURL* curl, const std::string& url
// NOTE: port, user and pass are set in the curl_url.
curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
// cancel if speed is less than 1 bytes/sec for timeout seconds.
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
// todo: change config to accept seconds rather than ms.
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config.timeout / 1000L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, config.timeout);
curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 15L);
@@ -1291,6 +1332,14 @@ void MountCurlDevice::curl_set_common_options(CURL* curl, const std::string& url
curl_easy_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 1024L * 64L);
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
if (config.timeout > 0) {
// cancel if speed is less than 1 bytes/sec for timeout seconds.
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
// todo: change config to accept seconds rather than ms.
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, config.timeout / 1000L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, config.timeout);
}
if (m_curl_share) {
curl_easy_setopt(curl, CURLOPT_SHARE, m_curl_share);
}

View File

@@ -97,7 +97,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -331,7 +331,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return total_bytes_read;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto size = get_size_from_files(file);

View File

@@ -38,7 +38,7 @@ private:
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;
ssize_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;
@@ -546,7 +546,7 @@ ssize_t Device::devoptab_write(void *fd, const char *ptr, size_t len) {
return ret;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
if (dir == SEEK_CUR) {

View File

@@ -51,7 +51,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -318,7 +318,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return ret;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
if (dir == SEEK_CUR) {

View File

@@ -159,7 +159,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -207,7 +207,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return bytes_read;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto& entry = file->entry;
@@ -343,9 +343,11 @@ Result MountNcaInternal(fs::Fs* fs, const std::shared_ptr<yati::source::Base>& s
// check if this is a ncz.
ncz::Header ncz_header{};
if (size >= NCZ_NORMAL_SIZE) {
R_TRY(source->Read2(&ncz_header, NCZ_NORMAL_SIZE, sizeof(ncz_header)));
}
if (ncz_header.magic == NCZ_SECTION_MAGIC) {
if (size >= NCZ_NORMAL_SIZE && ncz_header.magic == NCZ_SECTION_MAGIC) {
// read all the sections.
s64 ncz_offset = NCZ_SECTION_OFFSET;
ncz::Sections ncz_sections(ncz_header.total_sections);

View File

@@ -1,5 +1,3 @@
#ifdef ENABLE_DEVOPTAB_NFS
#include "utils/devoptab_common.hpp"
#include "defines.hpp"
#include "log.hpp"
@@ -25,7 +23,7 @@ private:
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;
ssize_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;
@@ -111,8 +109,10 @@ bool Device::Mount() {
}
}
if (this->config.timeout > 0) {
nfs_set_timeout(nfs, this->config.timeout);
nfs_set_readonly(nfs, this->config.read_only);
}
// nfs_set_mountport(nfs, url->port);
}
@@ -123,7 +123,7 @@ bool Device::Mount() {
url = "nfs://" + url;
}
auto nfs_url = nfs_parse_url_dir(nfs, url.c_str());
auto nfs_url = nfs_parse_url_full(nfs, url.c_str());
if (!nfs_url) {
log_write("[NFS] nfs_parse_url() failed for url: %s\n", url.c_str());
return false;
@@ -225,7 +225,7 @@ ssize_t Device::devoptab_write(void *fd, const char *ptr, size_t len) {
return written;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
u64 current_offset = 0;
@@ -414,17 +414,3 @@ Result MountNfsAll() {
}
} // namespace sphaira::devoptab
#else // ENABLE_DEVOPTAB_NFS
#include "defines.hpp"
namespace sphaira::devoptab {
Result MountNfsAll() {
R_SUCCEED();
}
} // namespace sphaira::devoptab
#endif // ENABLE_DEVOPTAB_NFS

View File

@@ -118,7 +118,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -166,7 +166,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return bytes_read;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto& entry = file->entry;

View File

@@ -41,7 +41,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -90,7 +90,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return bytes_read;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto& collection = file->collection;
@@ -171,7 +171,7 @@ int Device::devoptab_lstat(const char *path, struct stat *st) {
st->st_size = it->size;
}
return -ENOENT;
return 0;
}
} // namespace

View File

@@ -49,7 +49,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -101,7 +101,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return bytes_read;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
if (dir == SEEK_CUR) {

View File

@@ -40,7 +40,7 @@ private:
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;
ssize_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;
@@ -247,11 +247,14 @@ bool Device::Mount() {
libssh2_session_set_blocking(m_session, 1);
libssh2_session_flag(m_session, LIBSSH2_FLAG_COMPRESS, 1);
if (this->config.timeout > 0) {
libssh2_session_set_timeout(m_session, this->config.timeout);
// dkp libssh2 is too old for this.
#if LIBSSH2_VERSION_NUM >= 0x010B00
#if LIBSSH2_VERSION_NUM >= 0x010B00
libssh2_session_set_read_timeout(m_session, this->config.timeout);
#endif
#endif
}
}
if (this->config.user.empty() || this->config.pass.empty()) {
@@ -353,7 +356,7 @@ ssize_t Device::devoptab_write(void *fd, const char *ptr, size_t len) {
return ret;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto current_pos = libssh2_sftp_tell64(file->fd);

View File

@@ -28,7 +28,7 @@ private:
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;
ssize_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;
@@ -118,8 +118,10 @@ bool Device::Mount() {
smb2_set_workstation(this->smb2, workstation->second.c_str());
}
if (config.timeout > 0) {
smb2_set_timeout(this->smb2, this->config.timeout);
}
}
auto smb2_url = smb2_parse_url(this->smb2, this->config.url.c_str());
if (!smb2_url) {
@@ -160,28 +162,56 @@ int Device::devoptab_close(void *fd) {
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);
const auto max_read = smb2_get_max_read_size(this->smb2);
size_t bytes_read = 0;
while (bytes_read < len) {
const auto to_read = std::min<size_t>(len - bytes_read, max_read);
const auto ret = smb2_read(this->smb2, file->fd, (u8*)ptr, to_read);
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;
ptr += ret;
bytes_read += ret;
if (ret < to_read) {
break;
}
}
return bytes_read;
}
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);
const auto max_write = smb2_get_max_write_size(this->smb2);
size_t written = 0;
while (written < len) {
const auto to_write = std::min<size_t>(len - written, max_write);
const auto ret = smb2_write(this->smb2, file->fd, (const u8*)ptr, to_write);
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;
ptr += ret;
written += ret;
if (ret < to_write) {
break;
}
}
return written;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
u64 current_offset = 0;

View File

@@ -34,7 +34,7 @@ private:
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;
ssize_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;
@@ -124,7 +124,7 @@ ssize_t Device::devoptab_write(void *fd, const char *ptr, size_t len) {
return ret;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
return lseek(file->fd, pos, dir);

View File

@@ -58,7 +58,7 @@ private:
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;
ssize_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;
@@ -188,6 +188,7 @@ int Device::webdav_dirlist(const std::string& path, DirEntries& out) {
continue;
}
// todo: fix requested path still being displayed.
const auto href = url_decode(href_x.node().text().as_string());
if (href.empty() || href == requested_path || href == requested_path + '/') {
continue;
@@ -483,7 +484,7 @@ ssize_t Device::devoptab_write(void *fd, const char *ptr, size_t len) {
return ret;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
if (dir == SEEK_CUR) {

View File

@@ -39,7 +39,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -90,7 +90,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return bytes_read;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
const auto& collection = file->collection;

View File

@@ -199,7 +199,7 @@ private:
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;
off_t devoptab_seek(void *fd, off_t pos, int dir) override;
ssize_t devoptab_seek(void *fd, off_t pos, int dir) override;
int devoptab_fstat(void *fd, struct stat *st) override;
int devoptab_diropen(void* fd, const char *path) override;
int devoptab_dirreset(void* fd) override;
@@ -338,7 +338,7 @@ ssize_t Device::devoptab_read(void *fd, char *ptr, size_t len) {
return len;
}
off_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
ssize_t Device::devoptab_seek(void *fd, off_t pos, int dir) {
auto file = static_cast<File*>(fd);
// seek like normal.