From 1c5084e08e47054f51bfb9933e6ae7073f2a367d Mon Sep 17 00:00:00 2001 From: Michael Theall Date: Tue, 12 Jan 2016 16:24:01 -0600 Subject: [PATCH] Improve FTP throughput --- Makefile.3ds | 4 +- Makefile.linux | 2 +- source/ftp.c | 140 +++++++++++++++++++++++++++++++++++-------------- source/main.c | 1 + 4 files changed, 104 insertions(+), 43 deletions(-) diff --git a/Makefile.3ds b/Makefile.3ds index 189d609..4200b63 100644 --- a/Makefile.3ds +++ b/Makefile.3ds @@ -33,7 +33,7 @@ DATA := data INCLUDES := include APP_TITLE := ftBRONY -APP_DESCRIPTION := Like ftPONY but magical. +APP_DESCRIPTION := ftBRONY v1.2 APP_AUTHOR := mtheall ICON := ftbrony.png @@ -44,7 +44,7 @@ ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard CFLAGS := -g -Wall -O3 -mword-relocations \ $(ARCH) \ - -DSTATUS_STRING="\"ftbrony v1.1\"" + -DSTATUS_STRING="\"ftbrony v1.2\"" CFLAGS += $(INCLUDE) -DARM11 -D_3DS diff --git a/Makefile.linux b/Makefile.linux index 3494ee5..7928caa 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -3,7 +3,7 @@ TARGET := $(notdir $(CURDIR)) CFILES := $(wildcard source/*.c) OFILES := $(patsubst source/%,build.linux/%,$(CFILES:.c=.o)) -CFLAGS := -g -Wall -Iinclude -DSTATUS_STRING="\"ftpd v1.0\"" +CFLAGS := -g -Wall -Iinclude -DSTATUS_STRING="\"ftpd v1.2\"" LDFLAGS := .PHONY: all clean diff --git a/source/ftp.c b/source/ftp.c index 88e0a35..4a787a6 100644 --- a/source/ftp.c +++ b/source/ftp.c @@ -22,10 +22,12 @@ #define POLL_UNKNOWN (~(POLLIN|POLLOUT)) -#define XFER_BUFFERSIZE 4096 +#define XFER_BUFFERSIZE 32768 +#define SOCK_BUFFERSIZE 32768 +#define FILE_BUFFERSIZE 65536 #define CMD_BUFFERSIZE 1024 -#define SOC_ALIGN 0x1000 -#define SOC_BUFFERSIZE 0x100000 +#define SOCU_ALIGN 0x1000 +#define SOCU_BUFFERSIZE 0x100000 #define LISTEN_PORT 5000 #ifdef _3DS #define DATA_PORT (LISTEN_PORT+1) @@ -99,16 +101,18 @@ struct ftp_session_t ftp_session_t *next; /*!< link to next session */ ftp_session_t *prev; /*!< link to prev session */ - int (*transfer)(ftp_session_t*); /*! data transfer callback */ - char buffer[XFER_BUFFERSIZE]; /*! persistent data between callbacks */ - size_t bufferpos; /*! persistent buffer position between callbacks */ - size_t buffersize; /*! persistent buffer size between callbacks */ - uint64_t filepos; /*! persistent file position between callbacks */ - uint64_t filesize; /*! persistent file size between callbacks */ + int (*transfer)(ftp_session_t*); /*! data transfer callback */ + char buffer[XFER_BUFFERSIZE]; /*! persistent data between callbacks */ + char tmp_buffer[XFER_BUFFERSIZE]; /*! persistent data between callbacks */ + char file_buffer[FILE_BUFFERSIZE]; /*! stdio file buffer */ + size_t bufferpos; /*! persistent buffer position between callbacks */ + size_t buffersize; /*! persistent buffer size between callbacks */ + uint64_t filepos; /*! persistent file position between callbacks */ + uint64_t filesize; /*! persistent file size between callbacks */ union { - DIR *dp; /*! persistent open directory pointer between callbacks */ - int fd; /*! persistent open file descriptor between callbacks */ + DIR *dp; /*! persistent open directory pointer between callbacks */ + FILE *fp; /*! persistent open file pointer between callbacks */ }; }; @@ -181,17 +185,21 @@ ftp_command_cmp(const void *p1, #ifdef _3DS /*! SOC service buffer */ -static u32 *SOC_buffer = NULL; +static u32 *SOCU_buffer = NULL; #endif /*! server listen address */ static struct sockaddr_in serv_addr; /*! listen file descriptor */ static int listenfd = -1; +#ifdef _3DS /*! current data port */ static in_port_t data_port = DATA_PORT; +#endif /*! list of ftp sessions */ static ftp_session_t *sessions = NULL; +/*! socket buffersize */ +static int sock_buffersize = SOCK_BUFFERSIZE; /*! Allocate a new data port * @@ -237,6 +245,32 @@ ftp_set_socket_nonblocking(int fd) return 0; } +/*! set socket options + * + * @param[in] fd socket + */ +static void +ftp_set_socket_options(int fd) +{ + int rc; + + /* it's okay if this fails */ + rc = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, + &sock_buffersize, sizeof(sock_buffersize)); + if(rc != 0) + { + console_print(RED "setsockopt: %d %s\n" RESET, errno, strerror(errno)); + } + + /* it's okay if this fails */ + rc = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, + &sock_buffersize, sizeof(sock_buffersize)); + if(rc != 0) + { + console_print(RED "setsockopt: %d %s\n" RESET, errno, strerror(errno)); + } +} + /*! close a socket * * @param[in] fd socket to close @@ -325,10 +359,10 @@ ftp_session_close_file(ftp_session_t *session) { int rc; - rc = close(session->fd); + rc = fclose(session->fp); if(rc != 0) - console_print(RED "close: %d %s\n" RESET, errno, strerror(errno)); - session->fd = -1; + console_print(RED "fclose: %d %s\n" RESET, errno, strerror(errno)); + session->fp = NULL; } /*! open file for reading for ftp session @@ -344,15 +378,23 @@ ftp_session_open_file_read(ftp_session_t *session) struct stat st; /* open file in read mode */ - session->fd = open(session->buffer, O_RDONLY); - if(session->fd < 0) + session->fp = fopen(session->buffer, "rb"); + if(session->fp == NULL) { - console_print(RED "open '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno)); + console_print(RED "fopen '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno)); return -1; } + /* it's okay if this fails */ + errno = 0; + rc = setvbuf(session->fp, session->file_buffer, _IOFBF, FILE_BUFFERSIZE); + if(rc != 0) + { + console_print(RED "setvbuf: %d %s\n" RESET, errno, strerror(errno)); + } + /* get the file size */ - rc = fstat(session->fd, &st); + rc = fstat(fileno(session->fp), &st); if(rc != 0) { console_print(RED "fstat '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno)); @@ -380,10 +422,10 @@ ftp_session_read_file(ftp_session_t *session) ssize_t rc; /* read file at current position */ - rc = read(session->fd, session->buffer, sizeof(session->buffer)); + rc = fread(session->buffer, 1, sizeof(session->buffer), session->fp); if(rc < 0) { - console_print(RED "read: %d %s\n" RESET, errno, strerror(errno)); + console_print(RED "fread: %d %s\n" RESET, errno, strerror(errno)); return -1; } @@ -404,14 +446,24 @@ ftp_session_read_file(ftp_session_t *session) static int ftp_session_open_file_write(ftp_session_t *session) { + int rc; + /* open file in write and create mode with truncation */ - session->fd = open(session->buffer, O_WRONLY|O_CREAT|O_TRUNC, 0644); - if(session->fd < 0) + session->fp = fopen(session->buffer, "wb"); + if(session->fp == NULL) { - console_print(RED "open '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno)); + console_print(RED "fopen '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno)); return -1; } + /* it's okay if this fails */ + errno = 0; + rc = setvbuf(session->fp, session->file_buffer, _IOFBF, FILE_BUFFERSIZE); + if(rc != 0) + { + console_print(RED "setvbuf: %d %s\n" RESET, errno, strerror(errno)); + } + /* reset file position */ /* TODO: support REST command */ session->filepos = 0; @@ -431,15 +483,16 @@ ftp_session_write_file(ftp_session_t *session) ssize_t rc; /* write to file at current position */ - rc = write(session->fd, session->buffer + session->bufferpos, - session->buffersize - session->bufferpos); + rc = fwrite(session->buffer + session->bufferpos, + 1, session->buffersize - session->bufferpos, + session->fp); if(rc < 0) { - console_print(RED "write: %d %s\n" RESET, errno, strerror(errno)); + console_print(RED "fwrite: %d %s\n" RESET, errno, strerror(errno)); return -1; } else if(rc == 0) - console_print(RED "write: wrote 0 bytes\n" RESET); + console_print(RED "fwrite: wrote 0 bytes\n" RESET); /* adjust file position */ session->filepos += rc; @@ -766,6 +819,9 @@ ftp_session_connect(ftp_session_t *session) return -1; } + /* set socket options */ + ftp_set_socket_options(session->data_fd); + /* connect to peer */ rc = connect(session->data_fd, (struct sockaddr*)&session->peer_addr, sizeof(session->peer_addr)); @@ -986,18 +1042,18 @@ ftp_init(void) #endif /* allocate buffer for SOC service */ - SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE); - if(SOC_buffer == NULL) + SOCU_buffer = (u32*)memalign(SOCU_ALIGN, SOCU_BUFFERSIZE); + if(SOCU_buffer == NULL) { console_print(RED "memalign: failed to allocate\n" RESET); goto memalign_fail; } /* initialize SOC service */ - ret = socInit(SOC_buffer, SOC_BUFFERSIZE); + ret = socInit(SOCU_buffer, SOCU_BUFFERSIZE); if(ret != 0) { - console_print(RED "SOC_Initialize: 0x%08X\n" RESET, (unsigned int)ret); + console_print(RED "socInit: 0x%08X\n" RESET, (unsigned int)ret); goto soc_fail; } #endif @@ -1011,6 +1067,9 @@ ftp_init(void) return -1; } + /* set socket options */ + ftp_set_socket_options(listenfd); + /* get address to listen on */ serv_addr.sin_family = AF_INET; #ifdef _3DS @@ -1019,6 +1078,7 @@ ftp_init(void) #else serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(LISTEN_PORT); +#endif /* reuse address */ { @@ -1031,7 +1091,6 @@ ftp_init(void) return -1; } } -#endif /* bind socket to listen address */ rc = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); @@ -1092,7 +1151,7 @@ ftp_init(void) #ifdef _3DS soc_fail: - free(SOC_buffer); + free(SOCU_buffer); memalign_fail: #ifdef ENABLE_LOGGING @@ -1127,8 +1186,8 @@ ftp_exit(void) /* deinitialize SOC service */ ret = socExit(); if(ret != 0) - console_print(RED "SOC_Shutdown: 0x%08X\n" RESET, (unsigned int)ret); - free(SOC_buffer); + console_print(RED "socExit: 0x%08X\n" RESET, (unsigned int)ret); + free(SOCU_buffer); #ifdef ENABLE_LOGGING /* close log file */ @@ -1671,6 +1730,8 @@ FTP_DECLARE(PASV) return ftp_send_response(session, 451, "\r\n"); } + ftp_set_socket_options(session->pasv_fd); + session->pasv_addr.sin_port = htons(next_data_port()); #ifdef _3DS @@ -1946,8 +2007,7 @@ FTP_DECLARE(RNFR) FTP_DECLARE(RNTO) { - int rc; - char buffer[XFER_BUFFERSIZE]; + int rc; console_print(CYAN "%s %s\n" RESET, __func__, args ? args : ""); @@ -1958,12 +2018,12 @@ FTP_DECLARE(RNTO) session->flags &= ~SESSION_RENAME; - memcpy(buffer, session->buffer, XFER_BUFFERSIZE); + memcpy(session->tmp_buffer, session->buffer, XFER_BUFFERSIZE); if(build_path(session, args) != 0) return ftp_send_response(session, 554, "%s\r\n", strerror(errno)); - rc = rename(buffer, session->buffer); + rc = rename(session->tmp_buffer, session->buffer); if(rc != 0) { console_print(RED "rename: %d %s\n" RESET, errno, strerror(errno)); diff --git a/source/main.c b/source/main.c index fa4b095..8d15fc9 100644 --- a/source/main.c +++ b/source/main.c @@ -65,6 +65,7 @@ main(int argc, /* initialize needed 3DS services */ gfxInitDefault(); gfxSet3D(false); + sdmcWriteSafe(false); #endif /* initialize console subsystem */