Fix static analysis reports

This commit is contained in:
Michael Theall 2024-11-03 17:38:51 -06:00
parent 9a73b92497
commit 41e467f47b
18 changed files with 369 additions and 267 deletions

View File

@ -2,6 +2,18 @@ cmake_minimum_required(VERSION 3.12)
project(ftpd VERSION 3.2.0) project(ftpd VERSION 3.2.0)
if(CMAKE_EXPORT_COMPILE_COMMANDS)
list(APPEND CMAKE_C_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES})
list(APPEND CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
include(FetchContent)
FetchContent_Declare(gsl
GIT_REPOSITORY https://github.com/microsoft/GSL.git
GIT_TAG v4.0.0
)
FetchContent_Populate(gsl)
option(FTPD_CLASSIC "Build ${PROJECT_NAME} classic" OFF) option(FTPD_CLASSIC "Build ${PROJECT_NAME} classic" OFF)
if(FTPD_CLASSIC AND (NINTENDO_SWITCH OR NINTENDO_3DS)) if(FTPD_CLASSIC AND (NINTENDO_SWITCH OR NINTENDO_3DS))
@ -12,6 +24,8 @@ endif()
add_executable(${FTPD_TARGET}) add_executable(${FTPD_TARGET})
target_include_directories(${FTPD_TARGET} PRIVATE ${gsl_SOURCE_DIR}/include)
if(NINTENDO_SWITCH OR NINTENDO_3DS OR NINTENDO_DS) if(NINTENDO_SWITCH OR NINTENDO_3DS OR NINTENDO_DS)
dkp_target_generate_symbol_list(${FTPD_TARGET}) dkp_target_generate_symbol_list(${FTPD_TARGET})
endif() endif()

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2020 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -22,6 +22,8 @@
#include "ioBuffer.h" #include "ioBuffer.h"
#include <gsl/gsl>
#include <dirent.h> #include <dirent.h>
#include <cstdint> #include <cstdint>
@ -29,6 +31,7 @@
#include <cstdlib> #include <cstdlib>
#include <memory> #include <memory>
#include <string_view> #include <string_view>
#include <vector>
namespace fs namespace fs
{ {
@ -69,7 +72,7 @@ public:
/// \brief Open file /// \brief Open file
/// \param path_ Path to open /// \param path_ Path to open
/// \param mode_ Access mode (\sa std::fopen) /// \param mode_ Access mode (\sa std::fopen)
bool open (char const *path_, char const *mode_ = "rb"); bool open (gsl::not_null<gsl::czstring> path_, gsl::not_null<gsl::czstring> mode_ = "rb");
/// \brief Close file /// \brief Close file
void close (); void close ();
@ -77,13 +80,13 @@ public:
/// \brief Seek to file position /// \brief Seek to file position
/// \param pos_ File position /// \param pos_ File position
/// \param origin_ Reference position (\sa std::fseek) /// \param origin_ Reference position (\sa std::fseek)
std::make_signed_t<std::size_t> seek (std::size_t pos_, int origin_); std::make_signed_t<std::size_t> seek (std::make_signed_t<std::size_t> pos_, int origin_);
/// \brief Read data /// \brief Read data
/// \param buffer_ Output buffer /// \param buffer_ Output buffer
/// \param size_ Size to read /// \param size_ Size to read
/// \note Can return partial reads /// \note Can return partial reads
std::make_signed_t<std::size_t> read (void *buffer_, std::size_t size_); std::make_signed_t<std::size_t> read (gsl::not_null<void *> buffer_, std::size_t size_);
/// \brief Read data /// \brief Read data
/// \param buffer_ Output buffer /// \param buffer_ Output buffer
@ -97,13 +100,13 @@ public:
/// \param buffer_ Output buffer /// \param buffer_ Output buffer
/// \param size_ Size to read /// \param size_ Size to read
/// \note Fails on partial reads and errors /// \note Fails on partial reads and errors
bool readAll (void *buffer_, std::size_t size_); bool readAll (gsl::not_null<void *> buffer_, std::size_t size_);
/// \brief Write data /// \brief Write data
/// \param buffer_ Input data /// \param buffer_ Input data
/// \param size_ Size to write /// \param size_ Size to write
/// \note Can return partial writes /// \note Can return partial writes
std::make_signed_t<std::size_t> write (void const *buffer_, std::size_t size_); std::make_signed_t<std::size_t> write (gsl::not_null<void const *> buffer_, std::size_t size_);
/// \brief Write data /// \brief Write data
/// \param buffer_ Input data /// \param buffer_ Input data
@ -114,20 +117,17 @@ public:
/// \param buffer_ Input data /// \param buffer_ Input data
/// \param size_ Size to write /// \param size_ Size to write
/// \note Fails on partials writes and errors /// \note Fails on partials writes and errors
bool writeAll (void const *buffer_, std::size_t size_); bool writeAll (gsl::not_null<void const *> buffer_, std::size_t size_);
private: private:
/// \brief Underlying std::FILE* /// \brief Underlying std::FILE*
std::unique_ptr<std::FILE, int (*) (std::FILE *)> m_fp{nullptr, nullptr}; std::unique_ptr<std::FILE, int (*) (std::FILE *)> m_fp{nullptr, nullptr};
/// \brief Buffer /// \brief Buffer
std::unique_ptr<char[]> m_buffer; std::vector<char> m_buffer;
/// \brief Buffer size
std::size_t m_bufferSize = 0;
/// \brief Line buffer /// \brief Line buffer
char *m_lineBuffer = nullptr; gsl::owner<char *> m_lineBuffer = nullptr;
/// \brief Line buffer size /// \brief Line buffer size
std::size_t m_lineBufferSize = 0; std::size_t m_lineBufferSize = 0;
@ -161,14 +161,14 @@ public:
/// \brief Open directory /// \brief Open directory
/// \param path_ Path to open /// \param path_ Path to open
bool open (char const *const path_); bool open (gsl::not_null<gsl::czstring> path_);
/// \brief Close directory /// \brief Close directory
void close (); void close ();
/// \brief Read a directory entry /// \brief Read a directory entry
/// \note Returns nullptr on end-of-directory or error; check errno /// \note Returns nullptr on end-of-directory or error; check errno
struct dirent *read (); dirent *read ();
private: private:
/// \brief Underlying DIR* /// \brief Underlying DIR*

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2023 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -22,10 +22,13 @@
#include "platform.h" #include "platform.h"
#include <gsl/gsl>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <string_view>
class FtpConfig; class FtpConfig;
using UniqueFtpConfig = std::unique_ptr<FtpConfig>; using UniqueFtpConfig = std::unique_ptr<FtpConfig>;
@ -41,7 +44,7 @@ public:
/// \brief Load config /// \brief Load config
/// \param path_ Path to config file /// \param path_ Path to config file
static UniqueFtpConfig load (char const *path_); static UniqueFtpConfig load (gsl::not_null<gsl::czstring> path_);
#ifndef __NDS__ #ifndef __NDS__
std::scoped_lock<platform::Mutex> lockGuard (); std::scoped_lock<platform::Mutex> lockGuard ();
@ -49,7 +52,7 @@ public:
/// \brief Save config /// \brief Save config
/// \param path_ Path to config file /// \param path_ Path to config file
bool save (char const *path_); bool save (gsl::not_null<gsl::czstring> path_);
/// \brief Get user /// \brief Get user
std::string const &user () const; std::string const &user () const;
@ -79,15 +82,15 @@ public:
/// \brief Set user /// \brief Set user
/// \param user_ User /// \param user_ User
void setUser (std::string const &user_); void setUser (std::string user_);
/// \brief Set password /// \brief Set password
/// \param pass_ Password /// \param pass_ Password
void setPass (std::string const &pass_); void setPass (std::string pass_);
/// \brief Set listen port /// \brief Set listen port
/// \param port_ Listen port /// \param port_ Listen port
bool setPort (std::string const &port_); bool setPort (std::string_view port_);
/// \brief Set listen port /// \brief Set listen port
/// \param port_ Listen port /// \param port_ Listen port
@ -106,11 +109,11 @@ public:
/// \brief Set access point SSID /// \brief Set access point SSID
/// \param ssid_ SSID /// \param ssid_ SSID
void setSSID (std::string const &ssid_); void setSSID (std::string_view ssid_);
/// \brief Set access point passphrase /// \brief Set access point passphrase
/// \param passphrase_ Passphrase /// \param passphrase_ Passphrase
void setPassphrase (std::string const &passphrase_); void setPassphrase (std::string_view passphrase_);
#endif #endif
private: private:

View File

@ -117,7 +117,7 @@ private:
std::vector<UniqueFtpSession> m_sessions; std::vector<UniqueFtpSession> m_sessions;
/// \brief Whether thread should quit /// \brief Whether thread should quit
std::atomic<bool> m_quit; std::atomic_bool m_quit = false;
#ifndef CLASSIC #ifndef CLASSIC
/// \brief Log upload cURL context /// \brief Log upload cURL context
@ -152,7 +152,7 @@ private:
std::string m_passSetting; std::string m_passSetting;
/// \brief Port setting /// \brief Port setting
std::uint16_t m_portSetting; std::uint16_t m_portSetting = 0;
#ifdef __3DS__ #ifdef __3DS__
/// \brief getMTime setting /// \brief getMTime setting
@ -161,7 +161,7 @@ private:
#ifdef __SWITCH__ #ifdef __SWITCH__
/// \brief Whether an error occurred enabling access point /// \brief Whether an error occurred enabling access point
std::atomic<bool> m_apError = false; std::atomic_bool m_apError = false;
/// \brief Enable access point setting /// \brief Enable access point setting
bool m_enableAPSetting; bool m_enableAPSetting;

View File

@ -27,10 +27,12 @@
#include "socket.h" #include "socket.h"
#include <sys/stat.h> #include <sys/stat.h>
using stat_t = struct stat;
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <memory> #include <memory>
#include <optional>
#include <string_view> #include <string_view>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -165,18 +167,18 @@ private:
/// \brief Perform stat and apply tz offset to mtime /// \brief Perform stat and apply tz offset to mtime
/// \param path_ Path to stat /// \param path_ Path to stat
/// \param st_ Output stat /// \param st_ Output stat
int tzStat (char const *const path_, struct stat *st_); int tzStat (char const *const path_, stat_t *st_);
/// \brief Perform lstat and apply tz offset to mtime /// \brief Perform lstat and apply tz offset to mtime
/// \param path_ Path to lstat /// \param path_ Path to lstat
/// \param st_ Output stat /// \param st_ Output stat
int tzLStat (char const *const path_, struct stat *st_); int tzLStat (char const *const path_, stat_t *st_);
/// \brief Fill directory entry /// \brief Fill directory entry
/// \param st_ Entry status /// \param st_ Entry status
/// \param path_ Path name /// \param path_ Path name
/// \param type_ MLST type /// \param type_ MLST type
int fillDirent (struct stat const &st_, std::string_view path_, char const *type_ = nullptr); int fillDirent (stat_t const &st_, std::string_view path_, char const *type_ = nullptr);
/// \brief Fill directory entry /// \brief Fill directory entry
/// \param path_ Path name /// \param path_ Path name

View File

@ -58,44 +58,46 @@ public:
SockAddr &operator= (SockAddr &&that_); SockAddr &operator= (SockAddr &&that_);
/// \brief Parameterized constructor /// \brief Parameterized constructor
/// \param addr_ Address /// \param addr_ Address (network byte order)
SockAddr (struct sockaddr_in const &addr_); SockAddr (sockaddr_in const &addr_);
#ifndef NO_IPV6 #ifndef NO_IPV6
/// \brief Parameterized constructor /// \brief Parameterized constructor
/// \param addr_ Address /// \param addr_ Address (network byte order)
SockAddr (struct sockaddr_in6 const &addr_); SockAddr (sockaddr_in6 const &addr_);
#endif #endif
/// \brief Parameterized constructor /// \brief Parameterized constructor
/// \param addr_ Address /// \param addr_ Address (network byte order)
SockAddr (struct sockaddr_storage const &addr_); SockAddr (sockaddr_storage const &addr_);
/// \brief sockaddr_in cast operator /// \brief sockaddr_in cast operator (network byte order)
operator struct sockaddr_in const & () const; operator sockaddr_in const & () const;
#ifndef NO_IPV6 #ifndef NO_IPV6
/// \brief sockaddr_in6 cast operator /// \brief sockaddr_in6 cast operator (network byte order)
operator struct sockaddr_in6 const & () const; operator sockaddr_in6 const & () const;
#endif #endif
/// \brief sockaddr_storage cast operator /// \brief sockaddr_storage cast operator (network byte order)
operator struct sockaddr_storage const & () const; operator sockaddr_storage const & () const;
/// \brief sockaddr* cast operator (network byte order)
operator sockaddr * ();
/// \brief sockaddr const* cast operator (network byte order)
operator sockaddr const * () const;
/// \brief sockaddr* cast operator
operator struct sockaddr * ();
/// \brief sockaddr const* cast operator
operator struct sockaddr const * () const;
/// \brief sockaddr size /// \brief sockaddr size
socklen_t size () const; socklen_t size () const;
/// \brief Address port /// \brief Address port (host byte order)
std::uint16_t port () const; std::uint16_t port () const;
/// \brief Set address port /// \brief Set address port
/// \param port_ Port to set /// \param port_ Port to set (host byte order)
bool setPort (std::uint16_t port_); void setPort (std::uint16_t port_);
/// \brief Address name /// \brief Address name
/// \param buffer_ Buffer to hold name /// \param buffer_ Buffer to hold name
@ -110,6 +112,6 @@ public:
char const *name () const; char const *name () const;
private: private:
/// \brief Address storage /// \brief Address storage (network byte order)
struct sockaddr_storage m_addr = {}; sockaddr_storage m_addr = {};
}; };

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2023 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -37,7 +37,7 @@ struct pollfd
using socklen_t = int; using socklen_t = int;
using nfds_t = unsigned int; using nfds_t = unsigned int;
extern "C" int poll (struct pollfd *fds_, nfds_t nfds_, int timeout_); extern "C" int poll (pollfd *fds_, nfds_t nfds_, int timeout_);
#define POLLIN (1 << 0) #define POLLIN (1 << 0)
#define POLLPRI (1 << 1) #define POLLPRI (1 << 1)

View File

@ -70,7 +70,7 @@ static_assert (SOCU_BUFFERSIZE % SOCU_ALIGN == 0);
bool s_ndmuLocked = false; bool s_ndmuLocked = false;
/// \brief Whether soc:u is active /// \brief Whether soc:u is active
std::atomic<bool> s_socuActive = false; std::atomic_bool s_socuActive = false;
/// \brief soc:u buffer /// \brief soc:u buffer
u32 *s_socuBuffer = nullptr; u32 *s_socuBuffer = nullptr;
/// \brief ac:u fence /// \brief ac:u fence
@ -569,7 +569,7 @@ bool platform::networkVisible ()
bool platform::networkAddress (SockAddr &addr_) bool platform::networkAddress (SockAddr &addr_)
{ {
struct sockaddr_in addr; sockaddr_in addr;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = gethostid (); addr.sin_addr.s_addr = gethostid ();

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2023 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -19,11 +19,24 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "fs.h" #include "fs.h"
#include "ioBuffer.h"
#include <gsl/pointers>
#include <gsl/util>
#include <array>
#include <cassert> #include <cassert>
#include <cerrno>
#include <cinttypes> #include <cinttypes>
#include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <dirent.h>
#include <memory>
#include <string> #include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__) #if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
#define getline __getline #define getline __getline
@ -38,7 +51,7 @@ std::string fs::printSize (std::uint64_t const size_)
constexpr std::uint64_t const PiB = 1024 * TiB; constexpr std::uint64_t const PiB = 1024 * TiB;
constexpr std::uint64_t const EiB = 1024 * PiB; constexpr std::uint64_t const EiB = 1024 * PiB;
char buffer[64] = {}; std::array<char, 64> buffer{};
for (auto const &[name, bin] : { for (auto const &[name, bin] : {
// clang-format off // clang-format off
@ -56,30 +69,32 @@ std::string fs::printSize (std::uint64_t const size_)
if (size_ >= 100 * bin) if (size_ >= 100 * bin)
{ {
// >= 100, print xxxXiB // >= 100, print xxxXiB
std::sprintf (buffer, "%" PRIu64 "%s", whole, name); std::size_t const size = std::sprintf (buffer.data (), "%" PRIu64 "%s", whole, name);
return buffer; return {buffer.data (), size};
} }
// get the fractional portion of the number // get the fractional portion of the number
auto const frac = size_ - whole * bin; auto const frac = size_ - (whole * bin);
if (size_ >= 10 * bin) if (size_ >= 10 * bin)
{ {
// >= 10, print xx.xXiB // >= 10, print xx.xXiB
std::sprintf (buffer, "%" PRIu64 ".%" PRIu64 "%s", whole, frac * 10 / bin, name); std::size_t const size = std::sprintf (
return buffer; buffer.data (), "%" PRIu64 ".%" PRIu64 "%s", whole, frac * 10 / bin, name);
return {buffer.data (), size};
} }
if (size_ >= 1000 * (bin / KiB)) if (size_ >= 1000 * (bin / KiB))
{ {
// >= 1000 of lesser bin, print x.xxXiB // >= 1000 of lesser bin, print x.xxXiB
std::sprintf (buffer, "%" PRIu64 ".%02" PRIu64 "%s", whole, frac * 100 / bin, name); std::size_t const size = std::sprintf (
return buffer; buffer.data (), "%" PRIu64 ".%02" PRIu64 "%s", whole, frac * 100 / bin, name);
return {buffer.data (), size};
} }
} }
// < 1KiB, just print the number // < 1KiB, just print the number
std::sprintf (buffer, "%" PRIu64 "B", size_); std::size_t const size = std::sprintf (buffer.data (), "%" PRIu64 "B", size_);
return buffer; return {buffer.data (), size};
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -106,26 +121,24 @@ fs::File::operator FILE * () const
void fs::File::setBufferSize (std::size_t const size_) void fs::File::setBufferSize (std::size_t const size_)
{ {
if (m_bufferSize != size_) if (m_buffer.size () != size_)
{ m_buffer.resize (size_);
m_buffer = std::make_unique<char[]> (size_);
m_bufferSize = size_;
}
if (m_fp) if (m_fp)
std::setvbuf (m_fp.get (), m_buffer.get (), _IOFBF, m_bufferSize); (void)std::setvbuf (m_fp.get (), m_buffer.data (), _IOFBF, m_buffer.size ());
} }
bool fs::File::open (char const *const path_, char const *const mode_) bool fs::File::open (gsl::not_null<char const *> const path_,
gsl::not_null<char const *> const mode_)
{ {
auto const fp = std::fopen (path_, mode_); gsl::owner<FILE *> fp = std::fopen (path_, mode_);
if (!fp) if (!fp)
return false; return false;
m_fp = std::unique_ptr<std::FILE, int (*) (std::FILE *)> (fp, &std::fclose); m_fp = std::unique_ptr<std::FILE, int (*) (std::FILE *)> (fp, &std::fclose);
if (m_buffer) if (!m_buffer.empty ())
std::setvbuf (m_fp.get (), m_buffer.get (), _IOFBF, m_bufferSize); (void)std::setvbuf (m_fp.get (), m_buffer.data (), _IOFBF, m_buffer.size ());
return true; return true;
} }
@ -135,17 +148,27 @@ void fs::File::close ()
m_fp.reset (); m_fp.reset ();
} }
std::make_signed_t<std::size_t> fs::File::seek (std::size_t const pos_, int const origin_) std::make_signed_t<std::size_t> fs::File::seek (std::make_signed_t<std::size_t> const pos_,
int const origin_)
{ {
return std::fseek (m_fp.get (), pos_, origin_); return std::fseek (m_fp.get (), pos_, origin_);
} }
std::make_signed_t<std::size_t> fs::File::read (void *const buffer_, std::size_t const size_) std::make_signed_t<std::size_t> fs::File::read (gsl::not_null<void *> const buffer_,
std::size_t const size_)
{ {
assert (buffer_); assert (buffer_);
assert (size_ > 0); assert (size_ > 0);
return std::fread (buffer_, 1, size_, m_fp.get ()); auto const rc = std::fread (buffer_, 1, size_, m_fp.get ());
if (rc == 0)
{
if (std::feof (m_fp.get ()))
return 0;
return -1;
}
return gsl::narrow_cast<std::make_signed_t<std::size_t>> (rc);
} }
std::make_signed_t<std::size_t> fs::File::read (IOBuffer &buffer_) std::make_signed_t<std::size_t> fs::File::read (IOBuffer &buffer_)
@ -176,37 +199,41 @@ std::string_view fs::File::readLine ()
} }
if (rc > 0) if (rc > 0)
return std::string_view (m_lineBuffer, rc); return {m_lineBuffer, gsl::narrow_cast<std::size_t> (rc)};
} }
} }
bool fs::File::readAll (void *const buffer_, std::size_t const size_) bool fs::File::readAll (gsl::not_null<void *> const buffer_, std::size_t const size_)
{ {
assert (buffer_); assert (buffer_);
assert (size_ > 0); assert (size_ > 0);
auto p = static_cast<char *> (buffer_); auto const p = static_cast<char *> (buffer_.get ());
std::size_t bytes = 0; std::size_t bytes = 0;
while (bytes < size_) while (bytes < size_)
{ {
auto const rc = read (p, size_ - bytes); auto const rc = read (p + bytes, size_ - bytes);
if (rc <= 0) if (rc <= 0)
return false; return false;
p += rc;
bytes += rc; bytes += rc;
} }
return true; return true;
} }
std::make_signed_t<std::size_t> fs::File::write (void const *const buffer_, std::size_t const size_) std::make_signed_t<std::size_t> fs::File::write (gsl::not_null<void const *> const buffer_,
std::size_t const size_)
{ {
assert (buffer_); assert (buffer_);
assert (size_ > 0); assert (size_ > 0);
return std::fwrite (buffer_, 1, size_, m_fp.get ()); auto const rc = std::fwrite (buffer_, 1, size_, m_fp.get ());
if (rc == 0)
return -1;
return gsl::narrow_cast<std::make_signed_t<std::size_t>> (rc);
} }
std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_) std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_)
@ -220,21 +247,20 @@ std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_)
return rc; return rc;
} }
bool fs::File::writeAll (void const *const buffer_, std::size_t const size_) bool fs::File::writeAll (gsl::not_null<void const *> const buffer_, std::size_t const size_)
{ {
assert (buffer_); assert (buffer_);
assert (size_ > 0); assert (size_ > 0);
auto p = static_cast<char const *> (buffer_); auto const p = static_cast<char const *> (buffer_.get ());
std::size_t bytes = 0; std::size_t bytes = 0;
while (bytes < size_) while (bytes < size_)
{ {
auto const rc = write (p, size_ - bytes); auto const rc = write (p + bytes, size_ - bytes);
if (rc <= 0) if (rc <= 0)
return false; return false;
p += rc;
bytes += rc; bytes += rc;
} }
@ -260,7 +286,7 @@ fs::Dir::operator DIR * () const
return m_dp.get (); return m_dp.get ();
} }
bool fs::Dir::open (char const *const path_) bool fs::Dir::open (gsl::not_null<char const *> const path_)
{ {
auto const dp = ::opendir (path_); auto const dp = ::opendir (path_);
if (!dp) if (!dp)
@ -275,7 +301,7 @@ void fs::Dir::close ()
m_dp.reset (); m_dp.reset ();
} }
struct dirent *fs::Dir::read () dirent *fs::Dir::read ()
{ {
errno = 0; errno = 0;
return ::readdir (m_dp.get ()); return ::readdir (m_dp.get ());

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2023 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -22,24 +22,36 @@
#include "fs.h" #include "fs.h"
#include "log.h" #include "log.h"
#include "platform.h"
#include <gsl/pointers>
#include <sys/stat.h> #include <sys/stat.h>
using stat_t = struct stat;
#include <limits> #include <cctype>
#include <cerrno>
#include <charconv>
#include <cstdint>
#include <cstdio>
#include <mutex> #include <mutex>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
namespace namespace
{ {
constexpr std::uint16_t DEFAULT_PORT = 5000; constexpr std::uint16_t DEFAULT_PORT = 5000;
bool mkdirParent (std::string const &path_) bool mkdirParent (std::string_view const path_)
{ {
auto pos = path_.find_first_of ('/'); auto pos = path_.find_first_of ('/');
while (pos != std::string::npos) while (pos != std::string::npos)
{ {
auto const dir = path_.substr (0, pos); auto const dir = std::string (path_.substr (0, pos));
struct stat st; stat_t st{};
auto const rc = ::stat (dir.c_str (), &st); auto const rc = ::stat (dir.c_str (), &st);
if (rc < 0 && errno != ENOENT) if (rc < 0 && errno != ENOENT)
return false; return false;
@ -57,14 +69,13 @@ bool mkdirParent (std::string const &path_)
return true; return true;
} }
std::string strip (std::string const &str_) std::string_view strip (std::string_view const str_)
{ {
auto const start = str_.find_first_not_of (" \t"); auto const start = str_.find_first_not_of (" \t");
if (start == std::string::npos) if (start == std::string::npos)
return {}; return {};
auto const end = str_.find_last_not_of (" \t"); auto const end = str_.find_last_not_of (" \t");
if (end == std::string::npos) if (end == std::string::npos)
return str_.substr (start); return str_.substr (start);
@ -72,37 +83,21 @@ std::string strip (std::string const &str_)
} }
template <typename T> template <typename T>
bool parseInt (T &out_, std::string const &val_) bool parseInt (T &out_, std::string_view const val_)
{ {
T val = 0; auto const rc = std::from_chars (val_.data (), val_.data () + val_.size (), out_);
if (rc.ec != std::errc{})
for (auto const &c : val_)
{ {
if (!std::isdigit (c)) errno = static_cast<int> (rc.ec);
{ return false;
errno = EINVAL; }
return false;
} if (rc.ptr != val_.data () + val_.size ())
{
if (std::numeric_limits<T>::max () / 10 < val) errno = EINVAL;
{ return false;
errno = EOVERFLOW;
return false;
}
val *= 10;
auto const v = c - '0';
if (std::numeric_limits<T>::max () - v < val)
{
errno = EOVERFLOW;
return false;
}
val += v;
} }
out_ = val;
return true; return true;
} }
} }
@ -119,7 +114,7 @@ UniqueFtpConfig FtpConfig::create ()
return UniqueFtpConfig (new FtpConfig ()); return UniqueFtpConfig (new FtpConfig ());
} }
UniqueFtpConfig FtpConfig::load (char const *const path_) UniqueFtpConfig FtpConfig::load (gsl::not_null<gsl::czstring> const path_)
{ {
auto config = create (); auto config = create ();
@ -139,8 +134,8 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
continue; continue;
} }
auto const key = strip (line.substr (0, pos)); auto const key = strip (std::string_view (line).substr (0, pos));
auto const val = strip (line.substr (pos + 1)); auto const val = strip (std::string_view (line).substr (pos + 1));
if (key.empty () || val.empty ()) if (key.empty () || val.empty ())
{ {
error ("Ignoring '%s'\n", line.c_str ()); error ("Ignoring '%s'\n", line.c_str ());
@ -161,7 +156,9 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
else if (val == "1") else if (val == "1")
config->m_getMTime = true; config->m_getMTime = true;
else else
error ("Invalid value for mtime: %s\n", val.c_str ()); error ("Invalid value for mtime: %.*s\n",
gsl::narrow_cast<int> (val.size ()),
val.data ());
} }
#endif #endif
#ifdef __SWITCH__ #ifdef __SWITCH__
@ -172,7 +169,9 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
else if (val == "1") else if (val == "1")
config->m_enableAP = true; config->m_enableAP = true;
else else
error ("Invalid value for ap: %s\n", val.c_str ()); error ("Invalid value for ap: %.*s\n",
gsl::narrow_cast<int> (val.size ()),
val.data ());
} }
else if (key == "ssid") else if (key == "ssid")
config->m_ssid = val; config->m_ssid = val;
@ -193,9 +192,9 @@ std::scoped_lock<platform::Mutex> FtpConfig::lockGuard ()
} }
#endif #endif
bool FtpConfig::save (char const *const path_) bool FtpConfig::save (gsl::not_null<gsl::czstring> const path_)
{ {
if (!mkdirParent (path_)) if (!mkdirParent (path_.get ()))
return false; return false;
auto fp = fs::File (); auto fp = fs::File ();
@ -203,21 +202,21 @@ bool FtpConfig::save (char const *const path_)
return false; return false;
if (!m_user.empty ()) if (!m_user.empty ())
std::fprintf (fp, "user=%s\n", m_user.c_str ()); (void)std::fprintf (fp, "user=%s\n", m_user.c_str ());
if (!m_pass.empty ()) if (!m_pass.empty ())
std::fprintf (fp, "pass=%s\n", m_pass.c_str ()); (void)std::fprintf (fp, "pass=%s\n", m_pass.c_str ());
std::fprintf (fp, "port=%u\n", m_port); (void)std::fprintf (fp, "port=%u\n", m_port);
#ifdef __3DS__ #ifdef __3DS__
std::fprintf (fp, "mtime=%u\n", m_getMTime); (void)std::fprintf (fp, "mtime=%u\n", m_getMTime);
#endif #endif
#ifdef __SWITCH__ #ifdef __SWITCH__
std::fprintf (fp, "ap=%u\n", m_enableAP); (void)std::fprintf (fp, "ap=%u\n", m_enableAP);
if (!m_ssid.empty ()) if (!m_ssid.empty ())
std::fprintf (fp, "ssid=%s\n", m_ssid.c_str ()); (void)std::fprintf (fp, "ssid=%s\n", m_ssid.c_str ());
if (!m_passphrase.empty ()) if (!m_passphrase.empty ())
std::fprintf (fp, "passphrase=%s\n", m_passphrase.c_str ()); (void)std::fprintf (fp, "passphrase=%s\n", m_passphrase.c_str ());
#endif #endif
return true; return true;
@ -262,19 +261,19 @@ std::string const &FtpConfig::passphrase () const
} }
#endif #endif
void FtpConfig::setUser (std::string const &user_) void FtpConfig::setUser (std::string user_)
{ {
m_user = user_.substr (0, user_.find_first_of ('\0')); m_user = std::move (user_);
} }
void FtpConfig::setPass (std::string const &pass_) void FtpConfig::setPass (std::string pass_)
{ {
m_pass = pass_.substr (0, pass_.find_first_of ('\0')); m_pass = std::move (pass_);
} }
bool FtpConfig::setPort (std::string const &port_) bool FtpConfig::setPort (std::string_view const port_)
{ {
std::uint16_t parsed; std::uint16_t parsed{};
if (!parseInt (parsed, port_)) if (!parseInt (parsed, port_))
return false; return false;
@ -317,12 +316,12 @@ void FtpConfig::setEnableAP (bool const enable_)
m_enableAP = enable_; m_enableAP = enable_;
} }
void FtpConfig::setSSID (std::string const &ssid_) void FtpConfig::setSSID (std::string_view const ssid_)
{ {
m_ssid = ssid_.substr (0, ssid_.find_first_of ('\0')); m_ssid = ssid_.substr (0, ssid_.find_first_of ('\0'));
} }
void FtpConfig::setPassphrase (std::string const &passphrase_) void FtpConfig::setPassphrase (std::string_view const passphrase_)
{ {
m_passphrase = passphrase_.substr (0, passphrase_.find_first_of ('\0')); m_passphrase = passphrase_.substr (0, passphrase_.find_first_of ('\0'));
} }

View File

@ -21,9 +21,12 @@
#include "ftpServer.h" #include "ftpServer.h"
#include "fs.h" #include "fs.h"
#include "ftpConfig.h"
#include "ftpSession.h"
#include "licenses.h" #include "licenses.h"
#include "log.h" #include "log.h"
#include "platform.h" #include "platform.h"
#include "sockAddr.h"
#include "socket.h" #include "socket.h"
#include "imgui.h" #include "imgui.h"
@ -32,22 +35,38 @@
#include <dswifi9.h> #include <dswifi9.h>
#endif #endif
#ifndef CLASSIC #ifdef __3DS__
#include <jansson.h> #include <citro3d.h>
#endif
#ifndef CLASSIC
#include <jansson.h>
#include <curl/easy.h>
#include <curl/multi.h>
#ifndef NDEBUG
#include <curl/curl.h>
#endif
#endif #endif
#include <arpa/inet.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <unistd.h> using statvfs_t = struct statvfs;
#include <algorithm> #include <algorithm>
#include <array>
#include <atomic>
#include <cctype> #include <cctype>
#include <chrono> #include <chrono>
#include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <ctime>
#include <functional>
#include <mutex> #include <mutex>
#include <string>
#include <string_view> #include <string_view>
#include <thread> #include <utility>
#include <vector>
using namespace std::chrono_literals; using namespace std::chrono_literals;
#ifdef __NDS__ #ifdef __NDS__
@ -81,21 +100,33 @@ std::string s_freeSpace;
#ifndef CLASSIC #ifndef CLASSIC
#ifndef NDEBUG #ifndef NDEBUG
std::string printable (char *const data_, std::size_t const size_) std::string printable (std::string_view const data_)
{ {
std::string result; unsigned count = 0;
result.reserve (size_); for (auto const &c : data_)
for (std::size_t i = 0; i < size_; ++i)
{ {
if (std::isprint (data_[i]) || std::isspace (data_[i])) if (c != '%' && (std::isprint (c) || std::isspace (c)))
result.push_back (data_[i]); ++count;
else
count += 3;
}
std::string result;
result.reserve (count);
for (auto const &c : data_)
{
if (c != '%' && (std::isprint (c) || std::isspace (c)))
result.push_back (c);
else else
{ {
char buffer[5]; result.push_back ('%');
std::snprintf (
buffer, sizeof (buffer), "%%%02u", static_cast<unsigned char> (data_[i])); auto const upper = (static_cast<unsigned char> (c) >> 4u) & 0xF;
result += buffer; auto const lower = (static_cast<unsigned char> (c) >> 0u) & 0xF;
result.push_back (gsl::narrow_cast<char> (upper < 10 ? upper + '0' : upper + 'A' - 10));
result.push_back (gsl::narrow_cast<char> (lower < 10 ? lower + '0' : lower + 'A' - 10));
} }
} }
@ -111,7 +142,7 @@ int curlDebug (CURL *const handle_,
(void)handle_; (void)handle_;
(void)user_; (void)user_;
auto const text = printable (data_, size_); auto const text = printable (std::string_view (data_, size_));
switch (type_) switch (type_)
{ {
@ -190,7 +221,8 @@ FtpServer::~FtpServer ()
#endif #endif
} }
FtpServer::FtpServer (UniqueFtpConfig config_) : m_config (std::move (config_)), m_quit (false) FtpServer::FtpServer (UniqueFtpConfig config_)
: m_config (std::move (config_))
{ {
#ifndef __NDS__ #ifndef __NDS__
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this)); m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
@ -279,17 +311,17 @@ void FtpServer::draw ()
ImGui::SetNextWindowSize (ImVec2 (width, height)); ImGui::SetNextWindowSize (ImVec2 (width, height));
#endif #endif
{ {
char title[64]; std::array<char, 64> title{};
{ {
auto const serverLock = std::scoped_lock (m_lock); auto const serverLock = std::scoped_lock (m_lock);
std::snprintf (title, std::snprintf (title.data (),
sizeof (title), title.size (),
STATUS_STRING " %s###ftpd", STATUS_STRING " %s###ftpd",
m_socket ? m_name.c_str () : "Waiting for WiFi..."); m_socket ? m_name.c_str () : "Waiting for WiFi...");
} }
ImGui::Begin (title, ImGui::Begin (title.data (),
nullptr, nullptr,
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
#ifndef __3DS__ #ifndef __3DS__
@ -361,7 +393,7 @@ std::string FtpServer::getFreeSpace ()
void FtpServer::updateFreeSpace () void FtpServer::updateFreeSpace ()
{ {
struct statvfs st; statvfs_t st = {};
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__) #if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
if (::statvfs ("sdmc:/", &st) != 0) if (::statvfs ("sdmc:/", &st) != 0)
#else #else
@ -440,8 +472,9 @@ void FtpServer::handleNetworkLost ()
} }
{ {
// destroy command socket
UniqueSocket sock; UniqueSocket sock;
// destroy command socket
LOCKED (sock = std::move (m_socket)); LOCKED (sock = std::move (m_socket));
} }

View File

@ -38,6 +38,7 @@
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <mutex> #include <mutex>
#include <string>
using namespace std::chrono_literals; using namespace std::chrono_literals;
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__) #if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
@ -60,6 +61,33 @@ namespace
/// \brief Idle timeout /// \brief Idle timeout
constexpr auto IDLE_TIMEOUT = 60; constexpr auto IDLE_TIMEOUT = 60;
/// \brief Check if string view is a C string
/// \param str_ String to check
bool isCString (std::string_view const str_)
{
return str_.find_first_of ('\0') != std::string_view::npos;
}
/// \brief Case-insensitive string compare
/// \param lhs_ Left string
/// \param rhs_ Right string
int compare (std::string_view const lhs_, std::string_view const rhs_)
{
if (isCString (lhs_) && isCString (rhs_))
return ::strcasecmp (lhs_.data (), rhs_.data ());
auto const maxLen = std::min (lhs_.size (), rhs_.size ());
for (unsigned i = 0; i < maxLen; ++i)
{
auto const l = std::tolower (lhs_[i]);
auto const r = std::tolower (rhs_[i]);
if (l != r)
return l - r;
}
return gsl::narrow_cast<int> (lhs_.size ()) - gsl::narrow_cast<int> (rhs_.size ());
}
/// \brief Parse command /// \brief Parse command
/// \param buffer_ Buffer to parse /// \param buffer_ Buffer to parse
/// \param size_ Size of buffer /// \param size_ Size of buffer
@ -171,7 +199,7 @@ std::string resolvePath (std::string_view const path_)
assert (path_[0] == '/'); assert (path_[0] == '/');
// make sure parent is a directory // make sure parent is a directory
struct stat st; stat_t st;
if (::stat (dirName (path_).c_str (), &st) != 0) if (::stat (dirName (path_).c_str (), &st) != 0)
return {}; return {};
@ -385,7 +413,8 @@ void FtpSession::draw ()
auto const timeDiff = now - m_filePositionTime; auto const timeDiff = now - m_filePositionTime;
m_filePositionTime = now; m_filePositionTime = now;
auto const rate = diff / std::chrono::duration<float> (timeDiff).count (); auto const rate =
gsl::narrow_cast<float> (diff) / std::chrono::duration<float> (timeDiff).count ();
auto const alpha = 0.01f; auto const alpha = 0.01f;
m_xferRate = alpha * rate + (1.0f - alpha) * m_xferRate; m_xferRate = alpha * rate + (1.0f - alpha) * m_xferRate;
} }
@ -469,7 +498,7 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
for (auto &pending : session->m_pendingCloseSocket) for (auto &pending : session->m_pendingCloseSocket)
{ {
assert (pending.unique ()); assert (pending.unique ());
pollInfo.emplace_back (Socket::PollInfo{*pending, POLLIN, 0}); pollInfo.emplace_back (*pending, POLLIN, 0);
} }
} }
@ -513,8 +542,7 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
{ {
if (session->m_commandSocket) if (session->m_commandSocket)
{ {
pollInfo.emplace_back ( pollInfo.emplace_back (*session->m_commandSocket, POLLIN | POLLPRI, 0);
Socket::PollInfo{*session->m_commandSocket, POLLIN | POLLPRI, 0});
if (session->m_responseBuffer.usedSize () != 0) if (session->m_responseBuffer.usedSize () != 0)
pollInfo.back ().events |= POLLOUT; pollInfo.back ().events |= POLLOUT;
} }
@ -530,12 +558,12 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
{ {
assert (!session->m_port); assert (!session->m_port);
// we are waiting for a PASV connection // we are waiting for a PASV connection
pollInfo.emplace_back (Socket::PollInfo{*session->m_pasvSocket, POLLIN, 0}); pollInfo.emplace_back (*session->m_pasvSocket, POLLIN, 0);
} }
else else
{ {
// we are waiting to complete a PORT connection // we are waiting to complete a PORT connection
pollInfo.emplace_back (Socket::PollInfo{*session->m_dataSocket, POLLOUT, 0}); pollInfo.emplace_back (*session->m_dataSocket, POLLOUT, 0);
} }
break; break;
@ -544,12 +572,12 @@ bool FtpSession::poll (std::vector<UniqueFtpSession> const &sessions_)
if (session->m_recv) if (session->m_recv)
{ {
assert (!session->m_send); assert (!session->m_send);
pollInfo.emplace_back (Socket::PollInfo{*session->m_dataSocket, POLLIN, 0}); pollInfo.emplace_back (*session->m_dataSocket, POLLIN, 0);
} }
else else
{ {
assert (session->m_send); assert (session->m_send);
pollInfo.emplace_back (Socket::PollInfo{*session->m_dataSocket, POLLOUT, 0}); pollInfo.emplace_back (*session->m_dataSocket, POLLOUT, 0);
} }
break; break;
} }
@ -751,7 +779,7 @@ bool FtpSession::changeDir (char const *const args_)
if (path.empty ()) if (path.empty ())
return false; return false;
struct stat st; stat_t st;
if (tzStat (path.c_str (), &st) != 0) if (tzStat (path.c_str (), &st) != 0)
return false; return false;
@ -834,7 +862,7 @@ bool FtpSession::dataConnect ()
return true; return true;
} }
int FtpSession::tzStat (char const *const path_, struct stat *st_) int FtpSession::tzStat (char const *const path_, stat_t *st_)
{ {
auto const rc = ::stat (path_, st_); auto const rc = ::stat (path_, st_);
if (rc != 0) if (rc != 0)
@ -855,7 +883,7 @@ int FtpSession::tzStat (char const *const path_, struct stat *st_)
return 0; return 0;
} }
int FtpSession::tzLStat (char const *const path_, struct stat *st_) int FtpSession::tzLStat (char const *const path_, stat_t *st_)
{ {
auto const rc = ::lstat (path_, st_); auto const rc = ::lstat (path_, st_);
if (rc != 0) if (rc != 0)
@ -876,7 +904,7 @@ int FtpSession::tzLStat (char const *const path_, struct stat *st_)
return 0; return 0;
} }
int FtpSession::fillDirent (struct stat const &st_, std::string_view const path_, char const *type_) int FtpSession::fillDirent (stat_t const &st_, std::string_view const path_, char const *type_)
{ {
auto const buffer = m_xferBuffer.freeArea (); auto const buffer = m_xferBuffer.freeArea ();
auto const size = m_xferBuffer.freeSize (); auto const size = m_xferBuffer.freeSize ();
@ -1162,7 +1190,7 @@ int FtpSession::fillDirent (struct stat const &st_, std::string_view const path_
int FtpSession::fillDirent (std::string const &path_, char const *type_) int FtpSession::fillDirent (std::string const &path_, char const *type_)
{ {
struct stat st; stat_t st;
if (tzStat (path_.c_str (), &st) != 0) if (tzStat (path_.c_str (), &st) != 0)
return errno; return errno;
@ -1189,7 +1217,7 @@ void FtpSession::xferFile (char const *const args_, XferFileMode const mode_)
else if (mode_ == XferFileMode::RETR) else if (mode_ == XferFileMode::RETR)
{ {
// stat the file // stat the file
struct stat st; stat_t st;
if (tzStat (path.c_str (), &st) != 0) if (tzStat (path.c_str (), &st) != 0)
{ {
sendResponse ("450 %s\r\n", std::strerror (errno)); sendResponse ("450 %s\r\n", std::strerror (errno));
@ -1321,7 +1349,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
return; return;
} }
struct stat st; stat_t st;
if (tzStat (path.c_str (), &st) != 0) if (tzStat (path.c_str (), &st) != 0)
{ {
if (needWorkaround) if (needWorkaround)
@ -1600,12 +1628,10 @@ void FtpSession::readCommand (int const events_)
auto const it = std::lower_bound (std::begin (handlers), auto const it = std::lower_bound (std::begin (handlers),
std::end (handlers), std::end (handlers),
command, command,
[] (auto const &lhs_, auto const &rhs_) { [] (auto const &lhs_, auto const &rhs_) { return compare (lhs_.first, rhs_) < 0; });
return ::strcasecmp (lhs_.first.data (), rhs_) < 0;
});
m_timestamp = std::time (nullptr); m_timestamp = std::time (nullptr);
if (it == std::end (handlers) || ::strcasecmp (it->first.data (), command) != 0) if (it == std::end (handlers) || compare (it->first, command) != 0)
{ {
std::string response = "502 Invalid command \""; std::string response = "502 Invalid command \"";
response += encodePath (command); response += encodePath (command);
@ -1623,9 +1649,9 @@ void FtpSession::readCommand (int const events_)
else if (m_state != State::COMMAND) else if (m_state != State::COMMAND)
{ {
// only some commands are available during data transfer // only some commands are available during data transfer
if (::strcasecmp (command, "ABOR") != 0 && ::strcasecmp (command, "NOOP") != 0 && if (compare (command, "ABOR") != 0 && compare (command, "NOOP") != 0 &&
::strcasecmp (command, "PWD") != 0 && ::strcasecmp (command, "QUIT") != 0 && compare (command, "PWD") != 0 && compare (command, "QUIT") != 0 &&
::strcasecmp (command, "STAT") != 0 && ::strcasecmp (command, "XPWD") != 0) compare (command, "STAT") != 0 && compare (command, "XPWD") != 0)
{ {
sendResponse ("503 Invalid command during transfer\r\n"); sendResponse ("503 Invalid command during transfer\r\n");
setState (State::COMMAND, true, true); setState (State::COMMAND, true, true);
@ -1640,7 +1666,7 @@ void FtpSession::readCommand (int const events_)
else else
{ {
// clear rename for all commands except RNTO // clear rename for all commands except RNTO
if (::strcasecmp (command, "RNTO") != 0) if (compare (command, "RNTO") != 0)
m_rename.clear (); m_rename.clear ();
auto const handler = it->second; auto const handler = it->second;
@ -1791,7 +1817,7 @@ bool FtpSession::listTransfer ()
{ {
// build the path // build the path
auto const fullPath = buildPath (m_lwd, dent->d_name); auto const fullPath = buildPath (m_lwd, dent->d_name);
struct stat st; stat_t st;
#ifdef __3DS__ #ifdef __3DS__
// the sdmc directory entry already has the type and size, so no need to do a slow stat // the sdmc directory entry already has the type and size, so no need to do a slow stat
@ -2220,7 +2246,7 @@ void FtpSession::MODE (char const *args_)
setState (State::COMMAND, false, false); setState (State::COMMAND, false, false);
// we only accept S (stream) mode // we only accept S (stream) mode
if (::strcasecmp (args_, "S") == 0) if (compare (args_, "S") == 0)
{ {
sendResponse ("200 OK\r\n"); sendResponse ("200 OK\r\n");
return; return;
@ -2254,8 +2280,8 @@ void FtpSession::OPTS (char const *args_)
setState (State::COMMAND, false, false); setState (State::COMMAND, false, false);
// check UTF8 options // check UTF8 options
if (::strcasecmp (args_, "UTF8") == 0 || ::strcasecmp (args_, "UTF8 ON") == 0 || if (compare (args_, "UTF8") == 0 || compare (args_, "UTF8 ON") == 0 ||
::strcasecmp (args_, "UTF8 NLST") == 0) compare (args_, "UTF8 NLST") == 0)
{ {
sendResponse ("200 OK\r\n"); sendResponse ("200 OK\r\n");
return; return;
@ -2371,7 +2397,7 @@ void FtpSession::PASV (char const *args_)
m_pasvSocket->setSendBufferSize (SOCK_BUFFERSIZE); m_pasvSocket->setSendBufferSize (SOCK_BUFFERSIZE);
// create an address to bind // create an address to bind
struct sockaddr_in addr = m_commandSocket->sockName (); sockaddr_in addr = m_commandSocket->sockName ();
#if defined(__NDS__) || defined(__3DS__) #if defined(__NDS__) || defined(__3DS__)
static std::uint16_t ephemeralPort = 5001; static std::uint16_t ephemeralPort = 5001;
if (ephemeralPort > 10000) if (ephemeralPort > 10000)
@ -2455,7 +2481,7 @@ void FtpSession::PORT (char const *args_)
return; return;
} }
struct sockaddr_in addr = {}; sockaddr_in addr{};
// parse the address // parse the address
if (!inet_aton (addrString.data (), &addr.sin_addr)) if (!inet_aton (addrString.data (), &addr.sin_addr))
@ -2630,7 +2656,7 @@ void FtpSession::RNFR (char const *args_)
} }
// make sure the path exists // make sure the path exists
struct stat st; stat_t st;
if (tzLStat (path.c_str (), &st) != 0) if (tzLStat (path.c_str (), &st) != 0)
{ {
sendResponse ("450 %s\r\n", std::strerror (errno)); sendResponse ("450 %s\r\n", std::strerror (errno));
@ -2687,13 +2713,13 @@ void FtpSession::SITE (char const *args_)
{ {
setState (State::COMMAND, false, false); setState (State::COMMAND, false, false);
auto const str = std::string (args_); auto const str = std::string_view (args_);
auto const pos = str.find_first_of (' '); auto const pos = str.find_first_of (' ');
auto const command = str.substr (0, pos); auto const command = str.substr (0, pos);
auto const arg = pos == std::string::npos ? std::string () : str.substr (pos + 1); auto const arg = pos == std::string::npos ? std::string_view () : str.substr (pos + 1);
if (::strcasecmp (command.c_str (), "HELP") == 0) if (compare (command.data (), "HELP") == 0)
{ {
sendResponse ("211-\r\n" sendResponse ("211-\r\n"
" Show this help: SITE HELP\r\n" " Show this help: SITE HELP\r\n"
@ -2714,31 +2740,31 @@ void FtpSession::SITE (char const *args_)
return; return;
} }
if (::strcasecmp (command.c_str (), "USER") == 0) if (compare (command, "USER") == 0)
{ {
{ {
#ifndef __NDS__ #ifndef __NDS__
auto const lock = m_config.lockGuard (); auto const lock = m_config.lockGuard ();
#endif #endif
m_config.setUser (arg); m_config.setUser (std::string (arg));
} }
sendResponse ("200 OK\r\n"); sendResponse ("200 OK\r\n");
return; return;
} }
else if (::strcasecmp (command.c_str (), "PASS") == 0) else if (compare (command, "PASS") == 0)
{ {
{ {
#ifndef __NDS__ #ifndef __NDS__
auto const lock = m_config.lockGuard (); auto const lock = m_config.lockGuard ();
#endif #endif
m_config.setPass (arg); m_config.setPass (std::string (arg));
} }
sendResponse ("200 OK\r\n"); sendResponse ("200 OK\r\n");
return; return;
} }
else if (::strcasecmp (command.c_str (), "PORT") == 0) else if (compare (command, "PORT") == 0)
{ {
bool error = false; bool error = false;
@ -2759,7 +2785,7 @@ void FtpSession::SITE (char const *args_)
return; return;
} }
#ifdef __3DS__ #ifdef __3DS__
else if (::strcasecmp (command.c_str (), "MTIME") == 0) else if (compare (command, "MTIME") == 0)
{ {
if (arg == "0") if (arg == "0")
{ {
@ -2782,7 +2808,7 @@ void FtpSession::SITE (char const *args_)
} }
} }
#endif #endif
else if (::strcasecmp (command.c_str (), "SAVE") == 0) else if (compare (command, "SAVE") == 0)
{ {
bool error; bool error;
@ -2825,7 +2851,7 @@ void FtpSession::SIZE (char const *args_)
} }
// stat the path // stat the path
struct stat st; stat_t st;
if (tzStat (path.c_str (), &st) != 0) if (tzStat (path.c_str (), &st) != 0)
{ {
sendResponse ("550 %s\r\n", std::strerror (errno)); sendResponse ("550 %s\r\n", std::strerror (errno));
@ -2915,7 +2941,7 @@ void FtpSession::STRU (char const *args_)
setState (State::COMMAND, false, false); setState (State::COMMAND, false, false);
// we only support F (no structure) mode // we only support F (no structure) mode
if (::strcasecmp (args_, "F") == 0) if (compare (args_, "F") == 0)
{ {
sendResponse ("200 OK\r\n"); sendResponse ("200 OK\r\n");
return; return;

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2023 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -29,7 +29,10 @@
#include "imgui_impl_glfw.h" #include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h" #include "imgui_impl_opengl3.h"
#include <unistd.h>
#include <cstdio> #include <cstdio>
#include <cstring>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
@ -193,7 +196,7 @@ bool platform::networkVisible ()
bool platform::networkAddress (SockAddr &addr_) bool platform::networkAddress (SockAddr &addr_)
{ {
struct sockaddr_in addr; sockaddr_in addr;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2020 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -40,7 +40,7 @@ PrintConsole g_sessionConsole;
namespace namespace
{ {
/// \brief Host address /// \brief Host address
struct in_addr s_addr = {0}; in_addr s_addr = {0};
/// \brief Which side of double-buffer we're on /// \brief Which side of double-buffer we're on
bool s_backBuffer = false; bool s_backBuffer = false;
/// \brief Whether to power backlight /// \brief Whether to power backlight
@ -75,7 +75,7 @@ bool platform::networkVisible ()
bool platform::networkAddress (SockAddr &addr_) bool platform::networkAddress (SockAddr &addr_)
{ {
struct sockaddr_in addr; sockaddr_in addr;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr = Wifi_GetIPInfo (nullptr, nullptr, nullptr, nullptr); addr.sin_addr = Wifi_GetIPInfo (nullptr, nullptr, nullptr, nullptr);

View File

@ -39,83 +39,80 @@ SockAddr &SockAddr::operator= (SockAddr const &that_) = default;
SockAddr &SockAddr::operator= (SockAddr &&that_) = default; SockAddr &SockAddr::operator= (SockAddr &&that_) = default;
SockAddr::SockAddr (struct sockaddr_in const &addr_) SockAddr::SockAddr (sockaddr_in const &addr_)
{ {
assert (addr_.sin_family == AF_INET); assert (addr_.sin_family == AF_INET);
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in)); std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in));
} }
#ifndef NO_IPV6 #ifndef NO_IPV6
SockAddr::SockAddr (struct sockaddr_in6 const &addr_) SockAddr::SockAddr (sockaddr_in6 const &addr_)
{ {
assert (addr_.sin6_family == AF_INET6); assert (addr_.sin6_family == AF_INET6);
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in6)); std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in6));
} }
#endif #endif
SockAddr::SockAddr (struct sockaddr_storage const &addr_) SockAddr::SockAddr (sockaddr_storage const &addr_)
{ {
switch (addr_.ss_family) switch (addr_.ss_family)
{ {
case AF_INET: case AF_INET:
assert (addr_.ss_family == AF_INET); std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in));
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in));
break; break;
#ifndef NO_IPV6 #ifndef NO_IPV6
case AF_INET6: case AF_INET6:
assert (addr_.ss_family == AF_INET6); std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in6));
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in6));
break; break;
#endif #endif
default: default:
std::abort (); std::abort ();
break;
} }
} }
SockAddr::operator struct sockaddr_in const & () const SockAddr::operator sockaddr_in const & () const
{ {
assert (m_addr.ss_family == AF_INET); assert (m_addr.ss_family == AF_INET);
return reinterpret_cast<struct sockaddr_in const &> (m_addr); return reinterpret_cast<sockaddr_in const &> (m_addr);
} }
#ifndef NO_IPV6 #ifndef NO_IPV6
SockAddr::operator struct sockaddr_in6 const & () const SockAddr::operator sockaddr_in6 const & () const
{ {
assert (m_addr.ss_family == AF_INET6); assert (m_addr.ss_family == AF_INET6);
return reinterpret_cast<struct sockaddr_in6 const &> (m_addr); return reinterpret_cast<sockaddr_in6 const &> (m_addr);
} }
#endif #endif
SockAddr::operator struct sockaddr_storage const & () const SockAddr::operator sockaddr_storage const & () const
{ {
return m_addr; return m_addr;
} }
SockAddr::operator struct sockaddr * () SockAddr::operator sockaddr * ()
{ {
return reinterpret_cast<struct sockaddr *> (&m_addr); return reinterpret_cast<sockaddr *> (&m_addr);
} }
SockAddr::operator struct sockaddr const * () const SockAddr::operator sockaddr const * () const
{ {
return reinterpret_cast<struct sockaddr const *> (&m_addr); return reinterpret_cast<sockaddr const *> (&m_addr);
} }
bool SockAddr::setPort (std::uint16_t const port_) void SockAddr::setPort (std::uint16_t const port_)
{ {
switch (m_addr.ss_family) switch (m_addr.ss_family)
{ {
case AF_INET: case AF_INET:
reinterpret_cast<struct sockaddr_in *> (&m_addr)->sin_port = htons (port_); reinterpret_cast<struct sockaddr_in *> (&m_addr)->sin_port = htons (port_);
return true; break;
#ifndef NO_IPV6 #ifndef NO_IPV6
case AF_INET6: case AF_INET6:
reinterpret_cast<struct sockaddr_in6 *> (&m_addr)->sin6_port = htons (port_); reinterpret_cast<struct sockaddr_in6 *> (&m_addr)->sin6_port = htons (port_);
return true; break;
#endif #endif
default: default:
@ -146,11 +143,11 @@ std::uint16_t SockAddr::port () const
switch (m_addr.ss_family) switch (m_addr.ss_family)
{ {
case AF_INET: case AF_INET:
return ntohs (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_port); return ntohs (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_port);
#ifndef NO_IPV6 #ifndef NO_IPV6
case AF_INET6: case AF_INET6:
return ntohs (reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_port); return ntohs (reinterpret_cast<sockaddr_in6 const *> (&m_addr)->sin6_port);
#endif #endif
default: default:
@ -167,32 +164,27 @@ char const *SockAddr::name (char *buffer_, std::size_t size_) const
#ifdef __NDS__ #ifdef __NDS__
(void)buffer_; (void)buffer_;
(void)size_; (void)size_;
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr); return inet_ntoa (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr);
#else #else
return inet_ntop (AF_INET, return inet_ntop (
&reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr, AF_INET, &reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr, buffer_, size_);
buffer_,
size_);
#endif #endif
#ifndef NO_IPV6 #ifndef NO_IPV6
case AF_INET6: case AF_INET6:
return inet_ntop (AF_INET6, return inet_ntop (
&reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_addr, AF_INET6, &reinterpret_cast<sockaddr_in6 const *> (&m_addr)->sin6_addr, buffer_, size_);
buffer_,
size_);
#endif #endif
default: default:
std::abort (); std::abort ();
break;
} }
} }
char const *SockAddr::name () const char const *SockAddr::name () const
{ {
#ifdef __NDS__ #ifdef __NDS__
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr); return inet_ntoa (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr);
#else #else
#ifdef NO_IPV6 #ifdef NO_IPV6
thread_local static char buffer[INET_ADDRSTRLEN]; thread_local static char buffer[INET_ADDRSTRLEN];

View File

@ -23,6 +23,7 @@
#include "log.h" #include "log.h"
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
@ -65,7 +66,7 @@ Socket::Socket (int const fd_, SockAddr const &sockName_, SockAddr const &peerNa
UniqueSocket Socket::accept () UniqueSocket Socket::accept ()
{ {
SockAddr addr; SockAddr addr;
socklen_t addrLen = sizeof (struct sockaddr_storage); socklen_t addrLen = sizeof (sockaddr_storage);
auto const fd = ::accept (m_fd, addr, &addrLen); auto const fd = ::accept (m_fd, addr, &addrLen);
if (fd < 0) if (fd < 0)
@ -104,7 +105,7 @@ bool Socket::bind (SockAddr const &addr_)
if (addr_.port () == 0) if (addr_.port () == 0)
{ {
// get socket name due to request for ephemeral port // get socket name due to request for ephemeral port
socklen_t addrLen = sizeof (struct sockaddr_storage); socklen_t addrLen = sizeof (sockaddr_storage);
if (::getsockname (m_fd, m_sockName, &addrLen) != 0) if (::getsockname (m_fd, m_sockName, &addrLen) != 0)
error ("getsockname: %s\n", std::strerror (errno)); error ("getsockname: %s\n", std::strerror (errno));
} }
@ -166,7 +167,7 @@ bool Socket::setLinger (bool const enable_, std::chrono::seconds const time_)
errno = ENOSYS; errno = ENOSYS;
return -1; return -1;
#else #else
struct linger linger; linger linger;
linger.l_onoff = enable_; linger.l_onoff = enable_;
linger.l_linger = time_.count (); linger.l_linger = time_.count ();
@ -330,7 +331,7 @@ int Socket::poll (PollInfo *const info_,
if (count_ == 0) if (count_ == 0)
return 0; return 0;
auto const pfd = std::make_unique<struct pollfd[]> (count_); auto const pfd = std::make_unique<pollfd[]> (count_);
for (std::size_t i = 0; i < count_; ++i) for (std::size_t i = 0; i < count_; ++i)
{ {
pfd[i].fd = info_[i].socket.get ().m_fd; pfd[i].fd = info_[i].socket.get ().m_fd;
@ -352,7 +353,7 @@ int Socket::poll (PollInfo *const info_,
} }
#ifdef __NDS__ #ifdef __NDS__
extern "C" int poll (struct pollfd *const fds_, nfds_t const nfds_, int const timeout_) extern "C" int poll (pollfd *const fds_, nfds_t const nfds_, int const timeout_)
{ {
fd_set readFds; fd_set readFds;
fd_set writeFds; fd_set writeFds;
@ -370,7 +371,7 @@ extern "C" int poll (struct pollfd *const fds_, nfds_t const nfds_, int const ti
FD_SET (fds_[i].fd, &writeFds); FD_SET (fds_[i].fd, &writeFds);
} }
struct timeval tv; timeval tv;
tv.tv_sec = timeout_ / 1000; tv.tv_sec = timeout_ / 1000;
tv.tv_usec = (timeout_ % 1000) * 1000; tv.tv_usec = (timeout_ % 1000) * 1000;
auto const rc = ::select (nfds_, &readFds, &writeFds, &exceptFds, &tv); auto const rc = ::select (nfds_, &readFds, &writeFds, &exceptFds, &tv);

View File

@ -5,7 +5,7 @@
// //
// The MIT License (MIT) // The MIT License (MIT)
// //
// Copyright (C) 2023 Michael Theall // Copyright (C) 2024 Michael Theall
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@ -42,6 +42,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
using stat_t = struct stat;
#include <array> #include <array>
#include <cerrno> #include <cerrno>
@ -123,7 +124,7 @@ void loadShaders (dk::UniqueDevice &device_)
/// \param path_ Path to file /// \param path_ Path to file
static std::size_t getSize (char const *const path_) static std::size_t getSize (char const *const path_)
{ {
struct stat st; stat_t st;
auto const rc = ::stat (path_, &st); auto const rc = ::stat (path_, &st);
if (rc != 0) if (rc != 0)
{ {

View File

@ -339,7 +339,7 @@ void loadTextures ()
unsigned imageOffset = 0; unsigned imageOffset = 0;
for (auto const &textureInfo : textureInfos) for (auto const &textureInfo : textureInfos)
{ {
struct stat st; stat_t st;
if (::stat (textureInfo.path, &st) != 0) if (::stat (textureInfo.path, &st) != 0)
{ {
std::fprintf (stderr, "stat(%s): %s\n", textureInfo.path, std::strerror (errno)); std::fprintf (stderr, "stat(%s): %s\n", textureInfo.path, std::strerror (errno));
@ -618,14 +618,14 @@ bool platform::init ()
return true; return true;
} }
bool platform::enableAP (bool const enableAP_, bool platform::enableAP (bool const enable_,
std::string const &ssid_, std::string const &ssid_,
std::string const &passphrase_) std::string const &passphrase_)
{ {
if (s_activeAP == enableAP_) if (s_activeAP == enable_)
return true; return true;
if (enableAP_) if (enable_)
{ {
auto const ssidError = validateSSID (ssid_); auto const ssidError = validateSSID (ssid_);
if (ssidError) if (ssidError)
@ -781,11 +781,11 @@ bool platform::networkAddress (SockAddr &addr_)
return false; return false;
} }
addr_ = *reinterpret_cast<struct sockaddr_in *> (ipConfig.ip_addr); addr_ = *reinterpret_cast<sockaddr_in *> (ipConfig.ip_addr);
return true; return true;
} }
struct sockaddr_in addr; sockaddr_in addr;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = gethostid (); addr.sin_addr.s_addr = gethostid ();