From b63c8f9b50c45d33e12a3776cbf03e498b2a6b99 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 28 Jan 2013 10:23:28 +0100 Subject: ftpfs: Fix SIZE command handling It is invalid to issue a SIZE command once a data transfer is in progress. For reads we issue the SIZE command before the RETR command and get a snapshot of the file size. For writes the file size is initialized to zero and incremented for each write chunk. --- cpukit/libnetworking/lib/ftpfs.c | 174 ++++++++++++++++++++---------------- testsuites/libtests/ftp01/ftp01.scn | 4 + testsuites/libtests/ftp01/init.c | 26 +++++- 3 files changed, 124 insertions(+), 80 deletions(-) diff --git a/cpukit/libnetworking/lib/ftpfs.c b/cpukit/libnetworking/lib/ftpfs.c index 6ce25435c5..784ff48601 100644 --- a/cpukit/libnetworking/lib/ftpfs.c +++ b/cpukit/libnetworking/lib/ftpfs.c @@ -64,6 +64,8 @@ * Connection entry for each open file stream. */ typedef struct { + off_t file_size; + /** * Control connection socket. */ @@ -931,6 +933,93 @@ static int rtems_ftpfs_open_data_connection_passive( return 0; } +typedef enum { + RTEMS_FTPFS_SIZE_START = 0, + RTEMS_FTPFS_SIZE_SPACE, + RTEMS_FTPFS_SIZE_NUMBER, + RTEMS_FTPFS_SIZE_NL +} rtems_ftpfs_size_state; + +typedef struct { + rtems_ftpfs_size_state state; + size_t index; + off_t size; +} rtems_ftpfs_size_entry; + +static void rtems_ftpfs_size_parser( + const char* buf, + size_t len, + void *arg +) +{ + rtems_ftpfs_size_entry *se = arg; + size_t i = 0; + + for (i = 0; se->size >= 0 && i < len; ++i, ++se->index) { + int c = buf [i]; + + switch (se->state) { + case RTEMS_FTPFS_SIZE_START: + if (se->index == 2) { + se->state = RTEMS_FTPFS_SIZE_SPACE; + } + break; + case RTEMS_FTPFS_SIZE_SPACE: + if (c == ' ') { + se->state = RTEMS_FTPFS_SIZE_NUMBER; + } else { + se->size = -1; + } + break; + case RTEMS_FTPFS_SIZE_NUMBER: + if (isdigit(c)) { + se->size = 10 * se->size + c - '0'; + } else if (c == '\r') { + se->state = RTEMS_FTPFS_SIZE_NL; + } else { + se->size = -1; + } + break; + case RTEMS_FTPFS_SIZE_NL: + if (c != '\n') { + se->size = -1; + } + break; + default: + se->size = -1; + break; + } + } +} + +static void rtems_ftpfs_get_file_size(rtems_ftpfs_entry *e, bool verbose) +{ + if (e->file_size < 0) { + if (e->write) { + e->file_size = 0; + } else { + rtems_ftpfs_size_entry se; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + + memset(&se, 0, sizeof(se)); + + reply = rtems_ftpfs_send_command_with_parser( + e, + "SIZE ", + e->filename, + rtems_ftpfs_size_parser, + &se, + verbose + ); + if (reply == RTEMS_FTPFS_REPLY_2 && se.size >= 0) { + e->file_size = se.size; + } else { + e->file_size = 0; + } + } + } +} + static int rtems_ftpfs_open( rtems_libio_t *iop, const char *path, @@ -954,6 +1043,10 @@ static int rtems_ftpfs_open( eno = ENOTSUP; } + if (eno == 0) { + rtems_ftpfs_get_file_size(e, verbose); + } + if (eno == 0) { const char *file_command = e->write ? "STOR " : "RETR "; @@ -1052,6 +1145,8 @@ static ssize_t rtems_ftpfs_write( out += rv; todo -= (size_t) rv; + + e->file_size += rv; } return (ssize_t) (count - todo); @@ -1119,6 +1214,7 @@ static void rtems_ftpfs_eval_path( e->ino = me->ino; rtems_libio_unlock(); + e->file_size = -1; e->ctrl_socket = -1; eno = rtems_ftpfs_open_ctrl_connection( @@ -1238,65 +1334,6 @@ static int rtems_ftpfs_ioctl( return 0; } -typedef enum { - RTEMS_FTPFS_SIZE_START = 0, - RTEMS_FTPFS_SIZE_SPACE, - RTEMS_FTPFS_SIZE_NUMBER, - RTEMS_FTPFS_SIZE_NL -} rtems_ftpfs_size_state; - -typedef struct { - rtems_ftpfs_size_state state; - size_t index; - off_t size; -} rtems_ftpfs_size_entry; - -static void rtems_ftpfs_size_parser( - const char* buf, - size_t len, - void *arg -) -{ - rtems_ftpfs_size_entry *se = arg; - size_t i = 0; - - for (i = 0; se->size >= 0 && i < len; ++i, ++se->index) { - int c = buf [i]; - - switch (se->state) { - case RTEMS_FTPFS_SIZE_START: - if (se->index == 2) { - se->state = RTEMS_FTPFS_SIZE_SPACE; - } - break; - case RTEMS_FTPFS_SIZE_SPACE: - if (c == ' ') { - se->state = RTEMS_FTPFS_SIZE_NUMBER; - } else { - se->size = -1; - } - break; - case RTEMS_FTPFS_SIZE_NUMBER: - if (isdigit(c)) { - se->size = 10 * se->size + c - '0'; - } else if (c == '\r') { - se->state = RTEMS_FTPFS_SIZE_NL; - } else { - se->size = -1; - } - break; - case RTEMS_FTPFS_SIZE_NL: - if (c != '\n') { - se->size = -1; - } - break; - default: - se->size = -1; - break; - } - } -} - /* * The stat() support is intended only for the cp shell command. Each request * will return that we have a regular file with read, write and execute @@ -1319,24 +1356,9 @@ static int rtems_ftpfs_fstat( if (e->do_size_command) { const rtems_ftpfs_mount_entry *me = loc->mt_entry->fs_info; - rtems_ftpfs_size_entry se; - rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; - memset(&se, 0, sizeof(se)); - - reply = rtems_ftpfs_send_command_with_parser( - e, - "SIZE ", - e->filename, - rtems_ftpfs_size_parser, - &se, - me->verbose - ); - if (reply == RTEMS_FTPFS_REPLY_2 && se.size >= 0) { - st->st_size = se.size; - } else { - eno = EIO; - } + rtems_ftpfs_get_file_size(e, me->verbose); + st->st_size = e->file_size; } else { e->do_size_command = true; } diff --git a/testsuites/libtests/ftp01/ftp01.scn b/testsuites/libtests/ftp01/ftp01.scn index a3292961c8..a27da4b698 100644 --- a/testsuites/libtests/ftp01/ftp01.scn +++ b/testsuites/libtests/ftp01/ftp01.scn @@ -17,6 +17,8 @@ USER anonymous 230 User logged in. TYPE I 200 Type set to I. +SIZE a.txt +213 1102 PASV 227 Entering passive mode (127,0,0,1,4,4). RETR a.txt @@ -41,6 +43,8 @@ USER anonymous 230 User logged in. TYPE I 200 Type set to I. +SIZE b.txt +213 1102 PASV 227 Entering passive mode (127,0,0,1,4,10). RETR b.txt diff --git a/testsuites/libtests/ftp01/init.c b/testsuites/libtests/ftp01/init.c index 354c0f045a..369aebe92a 100644 --- a/testsuites/libtests/ftp01/init.c +++ b/testsuites/libtests/ftp01/init.c @@ -104,7 +104,7 @@ static void change_self_priority(void) rtems_test_assert(sc == RTEMS_SUCCESSFUL); } -static void create_file(const char *path) +static void create_file(const char *path, const void *begin, size_t size) { int rv = 0; int fd = open(path, O_WRONLY); @@ -112,8 +112,8 @@ static void create_file(const char *path) rtems_test_assert(fd >= 0); - n = write(fd, &content [0], sizeof(content)); - rtems_test_assert(n == (ssize_t) sizeof(content)); + n = write(fd, begin, size); + rtems_test_assert(n == (ssize_t) size); rv = close(fd); rtems_test_assert(rv == 0); @@ -126,15 +126,33 @@ static void copy_file(const char *src_path, const char *dest_path) int out = open(dest_path, O_WRONLY); ssize_t n_in = 0; char buf [64]; + struct stat st_in; + struct stat st_out; + + memset(&st_in, 0xff, sizeof(st_in)); + memset(&st_out, 0xff, sizeof(st_out)); rtems_test_assert(in >= 0); rtems_test_assert(out >= 0); + rv = fstat(out, &st_out); + rtems_test_assert(rv == 0); + + rtems_test_assert(st_out.st_size == 0); + while ((n_in = read(in, buf, sizeof(buf))) > 0) { ssize_t n_out = write(out, buf, (size_t) n_in); rtems_test_assert(n_out == n_in); } + rv = fstat(out, &st_out); + rtems_test_assert(rv == 0); + + rv = fstat(in, &st_in); + rtems_test_assert(rv == 0); + + rtems_test_assert(st_in.st_size == st_out.st_size); + rv = close(out); rtems_test_assert(rv == 0); @@ -189,7 +207,7 @@ static void test(void) initialize_ftpfs(); change_self_priority(); - create_file(file_a); + create_file(file_a, &content [0], sizeof(content)); copy_file(file_a, file_b); check_file(file_b); check_file_size(file_a, sizeof(content)); -- cgit v1.2.3