Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .clangd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CompileFlags:
CompilationDatabase: build/ # Search build/ directory for compile_commands.json

# remove command line options clangd doesn't like.
Remove:
- '-Werror'
- '-mtp=soft'
2 changes: 2 additions & 0 deletions source/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ void console_print(const char* fmt, ...)
if (should_log)
{
stdout = stderr = fopen("/config/sys-ftpd/logs/ftpd.log", "a");
setvbuf(stdout, NULL, _IONBF, 0);
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
Expand All @@ -39,6 +40,7 @@ void debug_print(const char* fmt, ...)
if (should_log)
{
stdout = stderr = fopen("/config/sys-ftpd/logs/ftpd.log", "a");
setvbuf(stdout, NULL, _IONBF, 0);
#ifdef ENABLE_LOGGING
va_list ap;
va_start(ap, fmt);
Expand Down
113 changes: 90 additions & 23 deletions source/ftp.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
#define CMD_BUFFERSIZE 0x1000

int LISTEN_PORT;
//#define LISTEN_PORT 5000
// #define LISTEN_PORT 5000
#ifdef _3DS
# define DATA_PORT (LISTEN_PORT + 1)
#else
Expand Down Expand Up @@ -195,12 +195,14 @@ static ftp_command_t ftp_commands[] =
/*! ftp command */
#define FTP_COMMAND(x) \
{ \
# x, x, \
#x, \
x, \
}
/*! ftp alias */
#define FTP_ALIAS(x, y) \
{ \
# x, y, \
#x, \
y, \
}
FTP_COMMAND(ABOR),
FTP_COMMAND(ALLO),
Expand Down Expand Up @@ -379,14 +381,61 @@ ftp_set_socket_options(int fd)
return 0;
}

/// Even though we set linger to 0 seconds,
/// the OS still takes several seconds to clean up socket memory after close(),
/// leading to OOM-induced errors when transferring directory trees.
/// If we shrink socket buffers to 1 byte before closing, we can greatly delay these errors.
static int
ftp_shrink_socket(int fd)
{
int rc;
int ret = 0;

int stub_buffersize = 1;

/* stub out receive buffer */
rc = setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
&stub_buffersize, sizeof(stub_buffersize));
if (rc != 0)
{
console_print(RED "ftp_shrink_socket() setsockopt: SO_RCVBUF %d %s\n" RESET, errno, strerror(errno));
ret = -1;
}

/* stub out send buffer */
rc = setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
&stub_buffersize, sizeof(stub_buffersize));
if (rc != 0)
{
console_print(RED "ftp_shrink_socket() setsockopt: SO_SNDBUF %d %s\n" RESET, errno, strerror(errno));
ret = -1;
}

return ret;
}

typedef enum SocketCloseType
{
SocketListen,
SocketDisconneced = SocketListen,
SocketConnected,
} SocketCloseType;

typedef enum ShouldShrinkSocket
{
ShrinkNo,
ShrinkYes,
} ShouldShrinkSocket;

/*! close a socket
*
* @param[in] fd socket to close
* @param[in] connected whether this socket is connected
*/
static void
ftp_closesocket(int fd,
bool connected)
SocketCloseType connected,
ShouldShrinkSocket shrink)
{
int rc;
struct sockaddr_in addr;
Expand All @@ -395,7 +444,8 @@ ftp_closesocket(int fd,

// console_print("0x%X\n", socketGetLastBsdResult());

if (connected)
// nothing to shutdown for a listen-only socket
if (connected == SocketConnected)
{
/* get peer address and print */
rc = getpeername(fd, (struct sockaddr*)&addr, &addrlen);
Expand Down Expand Up @@ -432,6 +482,16 @@ ftp_closesocket(int fd,
console_print(RED "setsockopt: SO_LINGER %d %s\n" RESET,
errno, strerror(errno));

// pasv_fd -> data_fd have ftp_set_socket_options() called and need to be shrunken.
// listenfd -> cmd_fd do not need to be shrunken.
// I placed this call *after* shutdown(), so if the client is still sending data
// we won't fetch 1 byte at a time over the network.
if (shrink == ShrinkYes)
{
// Do we need to shrink listen sockets? I don't care enough to test.
ftp_shrink_socket(fd);
}

/* close socket */
rc = close(fd);
if (rc != 0)
Expand All @@ -447,7 +507,7 @@ ftp_session_close_cmd(ftp_session_t* session)
{
/* close command socket */
if (session->cmd_fd >= 0)
ftp_closesocket(session->cmd_fd, true);
ftp_closesocket(session->cmd_fd, SocketConnected, ShrinkNo);
session->cmd_fd = -1;
}

Expand All @@ -465,7 +525,7 @@ ftp_session_close_pasv(ftp_session_t* session)
inet_ntoa(session->pasv_addr.sin_addr),
ntohs(session->pasv_addr.sin_port));

ftp_closesocket(session->pasv_fd, false);
ftp_closesocket(session->pasv_fd, SocketListen, ShrinkYes);
}

session->pasv_fd = -1;
Expand All @@ -480,7 +540,7 @@ ftp_session_close_data(ftp_session_t* session)
{
/* close data connection */
if (session->data_fd >= 0 && session->data_fd != session->cmd_fd)
ftp_closesocket(session->data_fd, true);
ftp_closesocket(session->data_fd, SocketConnected, ShrinkYes);
session->data_fd = -1;

/* clear send/recv flags */
Expand Down Expand Up @@ -893,11 +953,17 @@ ftp_session_fill_dirent_type(ftp_session_t* session, const struct stat* st,
session->buffersize +=
sprintf(session->buffer + session->buffersize,
"%c%c%c%c%c%c%c%c%c%c %lu 3DS 3DS %lld ",
S_ISREG(st->st_mode) ? '-' : S_ISDIR(st->st_mode) ? 'd' :
S_ISREG(st->st_mode) ? '-' : S_ISDIR(st->st_mode) ? 'd'
:
#if !defined(_3DS) && !defined(__SWITCH__)
S_ISLNK(st->st_mode) ? 'l' : S_ISCHR(st->st_mode) ? 'c' : S_ISBLK(st->st_mode) ? 'b' : S_ISFIFO(st->st_mode) ? 'p' : S_ISSOCK(st->st_mode) ? 's' :
S_ISLNK(st->st_mode) ? 'l'
: S_ISCHR(st->st_mode) ? 'c'
: S_ISBLK(st->st_mode) ? 'b'
: S_ISFIFO(st->st_mode) ? 'p'
: S_ISSOCK(st->st_mode) ? 's'
:
#endif
'?',
'?',
st->st_mode & S_IRUSR ? 'r' : '-',
st->st_mode & S_IWUSR ? 'w' : '-',
st->st_mode & S_IXUSR ? 'x' : '-',
Expand Down Expand Up @@ -1127,7 +1193,7 @@ ftp_send_response_buffer(ftp_session_t* session,

/* send response */
to_send = len;
console_print(GREEN "%s" RESET, buffer);
console_print(GREEN "%.*s" RESET, (int)len, buffer);
rc = send(session->cmd_fd, buffer, to_send, 0);
if (rc < 0)
{
Expand Down Expand Up @@ -1249,7 +1315,7 @@ ftp_session_new(int listen_fd)
if (session == NULL)
{
console_print(RED "failed to allocate session\n" RESET);
ftp_closesocket(new_fd, true);
ftp_closesocket(new_fd, SocketConnected, ShrinkNo);
return -1;
}

Expand Down Expand Up @@ -1331,7 +1397,8 @@ ftp_session_accept(ftp_session_t* session)
rc = ftp_set_socket_nonblocking(new_fd);
if (rc != 0)
{
ftp_closesocket(new_fd, true);
// pasv_fd has large buffers so new_fd will inherit them, and must be shrunken.
ftp_closesocket(new_fd, SocketConnected, ShrinkYes);
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
ftp_send_response(session, 425, "Failed to establish connection\r\n");
return -1;
Expand Down Expand Up @@ -1380,7 +1447,7 @@ ftp_session_connect(ftp_session_t* session)
rc = ftp_set_socket_options(session->data_fd);
if (rc != 0)
{
ftp_closesocket(session->data_fd, false);
ftp_closesocket(session->data_fd, SocketDisconneced, ShrinkYes);
session->data_fd = -1;
return -1;
}
Expand All @@ -1398,7 +1465,7 @@ ftp_session_connect(ftp_session_t* session)
if (errno != EINPROGRESS)
{
console_print(RED "connect: %d %s\n" RESET, errno, strerror(errno));
ftp_closesocket(session->data_fd, false);
ftp_closesocket(session->data_fd, SocketDisconneced, ShrinkYes);
session->data_fd = -1;
return -1;
}
Expand Down Expand Up @@ -1842,7 +1909,7 @@ update_status(void)
// inet_ntoa(serv_addr.sin_addr),
// ntohs(serv_addr.sin_port));
update_free_space();
#elif 0 //defined(__SWITCH__)
#elif 0 // defined(__SWITCH__)
char hostname[128];
socklen_t addrlen = sizeof(serv_addr);
int rc;
Expand Down Expand Up @@ -2032,7 +2099,7 @@ void ftp_exit(void)

/* stop listening for new clients */
if (listenfd >= 0)
ftp_closesocket(listenfd, false);
ftp_closesocket(listenfd, SocketListen, ShrinkNo);

/* deinitialize socket driver */
console_render();
Expand Down Expand Up @@ -2108,7 +2175,7 @@ ftp_loop(void)
apt_hook(APTHOOK_ONRESTORE, NULL);
}
#elif defined(__SWITCH__)
/* check if the user wants to exit */
/* check if the user wants to exit */
#endif

return LOOP_CONTINUE;
Expand Down Expand Up @@ -2368,12 +2435,12 @@ list_transfer(ftp_session_t* session)

if (rc != 0)
{
#ifndef __SWITCH__
# ifndef __SWITCH__
/* an error occurred */
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
ftp_send_response(session, 550, "unavailable\r\n");
return LOOP_EXIT;
#else
# else
// probably archive bit set; list name with dummy stats
memset(&st, 0, sizeof(st));
console_print(RED "%s: type %u\n" RESET, dent->d_name, dent->d_type);
Expand Down Expand Up @@ -2408,7 +2475,7 @@ list_transfer(ftp_session_t* session)
st.st_mode = S_IFSOCK;
break;
}
#endif
# endif
}
#endif
/* encode \n in path */
Expand Down Expand Up @@ -3558,7 +3625,7 @@ FTP_DECLARE(PASV)
*/
FTP_DECLARE(PORT)
{
char *addrstr, *p, *portstr;
char *addrstr, *p, *portstr = NULL;
int commas = 0, rc;
short port = 0;
unsigned long val;
Expand Down
2 changes: 1 addition & 1 deletion source/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void initPads()
padInitializeAny(&pad);
}

void inputPoller()
void inputPoller(void* /* unused */)
{
do
{
Expand Down