diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..2eb274a --- /dev/null +++ b/.clangd @@ -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' diff --git a/source/console.c b/source/console.c index 0044a7b..21bf2f0 100644 --- a/source/console.c +++ b/source/console.c @@ -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); @@ -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); diff --git a/source/ftp.c b/source/ftp.c index e9c4171..2618f53 100644 --- a/source/ftp.c +++ b/source/ftp.c @@ -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 @@ -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), @@ -379,6 +381,52 @@ 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 @@ -386,7 +434,8 @@ ftp_set_socket_options(int fd) */ static void ftp_closesocket(int fd, - bool connected) + SocketCloseType connected, + ShouldShrinkSocket shrink) { int rc; struct sockaddr_in addr; @@ -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); @@ -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) @@ -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; } @@ -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; @@ -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 */ @@ -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' : '-', @@ -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) { @@ -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; } @@ -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; @@ -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; } @@ -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; } @@ -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; @@ -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(); @@ -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; @@ -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); @@ -2408,7 +2475,7 @@ list_transfer(ftp_session_t* session) st.st_mode = S_IFSOCK; break; } -#endif +# endif } #endif /* encode \n in path */ @@ -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; diff --git a/source/util.c b/source/util.c index 965e695..bdea780 100644 --- a/source/util.c +++ b/source/util.c @@ -26,7 +26,7 @@ void initPads() padInitializeAny(&pad); } -void inputPoller() +void inputPoller(void* /* unused */) { do {