summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-03-26 14:11:53 +0000
committerThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-03-26 14:11:53 +0000
commit8916bdc71bfcc3bb0a7c84808e8d2391bd7d44b0 (patch)
tree1ccc9f2542bdea2b8bab6ffceb193a9c9ede81f3
parent2009-03-26 Joel Sherrill <joel.sherrill@oarcorp.com> (diff)
downloadrtems-8916bdc71bfcc3bb0a7c84808e8d2391bd7d44b0.tar.bz2
* libmisc/shell/shell.h: Pointer to
oparations table for mount command is now const. * libnetworking/lib/ftpfs.c, libnetworking/rtems/ftpfs.h: Rewrite of the FTP file system which implements now the trivial command state machines of RFC 959. For the data transfer passive (= default) and active (= fallback) modes are now supported. * libmisc/shell/main_mount_ftp.c: Update for FTP file system changes.
-rw-r--r--cpukit/ChangeLog10
-rw-r--r--cpukit/libmisc/shell/main_mount_ftp.c2
-rw-r--r--cpukit/libmisc/shell/shell.h8
-rw-r--r--cpukit/libnetworking/lib/ftpfs.c1946
-rw-r--r--cpukit/libnetworking/rtems/ftpfs.h118
5 files changed, 1064 insertions, 1020 deletions
diff --git a/cpukit/ChangeLog b/cpukit/ChangeLog
index 0f819b4a41..49666f77db 100644
--- a/cpukit/ChangeLog
+++ b/cpukit/ChangeLog
@@ -1,3 +1,13 @@
+2009-03-26 Sebastian Huber <sebastian.huber@embedded-brains.de>
+
+ * libmisc/shell/shell.h: Pointer to
+ oparations table for mount command is now const.
+ * libnetworking/lib/ftpfs.c, libnetworking/rtems/ftpfs.h: Rewrite of
+ the FTP file system which implements now the trivial command state
+ machines of RFC 959. For the data transfer passive (= default) and
+ active (= fallback) modes are now supported.
+ * libmisc/shell/main_mount_ftp.c: Update for FTP file system changes.
+
2009-03-12 Santosh G Vattam <vattam.santosh@gmail.com>
PR 1378/filesystem
diff --git a/cpukit/libmisc/shell/main_mount_ftp.c b/cpukit/libmisc/shell/main_mount_ftp.c
index a30e9bd18b..2712812fc0 100644
--- a/cpukit/libmisc/shell/main_mount_ftp.c
+++ b/cpukit/libmisc/shell/main_mount_ftp.c
@@ -32,6 +32,6 @@
rtems_shell_filesystems_t rtems_shell_Mount_FTP = {
name: "ftp",
driver_needed: 1,
- fs_ops: &rtems_ftp_ops,
+ fs_ops: &rtems_ftpfs_ops,
mounter: rtems_shell_libc_mounter
};
diff --git a/cpukit/libmisc/shell/shell.h b/cpukit/libmisc/shell/shell.h
index b1d589031c..9e5baccf42 100644
--- a/cpukit/libmisc/shell/shell.h
+++ b/cpukit/libmisc/shell/shell.h
@@ -222,10 +222,10 @@ typedef int (*rtems_shell_filesystems_mounter_t)(
);
struct rtems_shell_filesystems_tt {
- const char* name;
- int driver_needed;
- rtems_filesystem_operations_table* fs_ops;
- rtems_shell_filesystems_mounter_t mounter;
+ const char *name;
+ int driver_needed;
+ const rtems_filesystem_operations_table *fs_ops;
+ rtems_shell_filesystems_mounter_t mounter;
};
/**
diff --git a/cpukit/libnetworking/lib/ftpfs.c b/cpukit/libnetworking/lib/ftpfs.c
index bff0546489..dc32823038 100644
--- a/cpukit/libnetworking/lib/ftpfs.c
+++ b/cpukit/libnetworking/lib/ftpfs.c
@@ -1,1159 +1,1099 @@
-/*
- * File Transfer Protocol client
- *
- * Transfer file to/from remote host
- *
- * This driver can be added to the RTEMS file system with a call to
- * "rtems_bsdnet_initialize_ftp_filesystem () ".
- * From then on, you can open, read and close files on a remote FTP server
- * using the following syntax:
- * To open a file "myfile.txt" in the directory "mydir" (relative to home
- * directory) on a server named "myserver" using the user id
- * "myuserid" and the password "my_very_secret_password" you must
- * specify the following path:
- *
- * /FTP/myuserid:my_very_secret_password/@myserver/mydirectory/myfile.txt
- *
- * If the server is the default server specified in BOOTP, it can be ommitted:
- *
- * /FTP/myuserid:my_very_secret_password/mydirectory/myfile.txt
+/**
+ * @file
*
- * WARNING: write accesses have not yet been tested.
- *
+ * @brief File Transfer Protocol file system (FTP client).
+ */
+
+/*
+ * Copyright (c) 2009
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
*
* (c) Copyright 2002
* Thomas Doerfler
* IMD Ingenieurbuero fuer Microcomputertechnik
* Herbststr. 8
- * 82178 Puchheim, Germany
+ * 82178 Puchheim, Germany
* <Thomas.Doerfler@imd-systems.de>
*
- * This code has been created after closly inspecting
- * "tftpdriver.c" from Eric Norum.
- *
- * $Id$
+ * Modified by Sebastian Huber <sebastian.huber@embedded-brains.de>.
+ *
+ * This code has been created after closly inspecting "tftpdriver.c" from Eric
+ * Norum.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * $Id$
*/
-#include <stdio.h>
+#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <malloc.h>
-#include <string.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <fcntl.h>
+#include <string.h>
#include <unistd.h>
-#include <inttypes.h>
-#include <ctype.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
#include <rtems.h>
+#include <rtems/ftpfs.h>
+#include <rtems/imfs.h>
#include <rtems/libio.h>
#include <rtems/rtems_bsdnet.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <rtems/ftpfs.h>
+#include <rtems/seterr.h>
-
-#ifndef set_errno_and_return_minus_one
-#define set_errno_and_return_minus_one( _error ) \
- do { errno = (_error); return -1; } while(0)
+#ifdef DEBUG
+ #define DEBUG_PRINTF( ...) printf( __VA_ARGS__)
+#else
+ #define DEBUG_PRINTF( ...)
#endif
-/* #define DEBUG_OUT */
-
-/*
- * Well-known port for FTP
- */
-#define FTP_PORT_NUM 21
-
-/*
- * Pathname prefix
+/**
+ * @brief Connection entry for each open file stream.
*/
-#define FTP_PATHNAME_PREFIX "/FTP/"
-/*
- * reply codes
- */
-#define FTP_REPLY_CONNECT 220 /* Connection established */
-#define FTP_REPLY_PASSREQ 331 /* user ok, password required */
-#define FTP_REPLY_LOGIN 230 /* login finished */
-#define FTP_REPLY_SUCCESS 200 /* xxx successful */
-#define FTP_REPLY_OPENCONN 150 /* opening connection for tfer */
-#define FTP_REPLY_TFERCMPL 226 /* transfer complete */
-
-extern rtems_filesystem_operations_table rtems_ftp_ops;
-extern rtems_filesystem_file_handlers_r rtems_ftp_handlers;
-
-/*
- * FTP command strings
- */
-#define FTP_USER_CMD "USER "
-#define FTP_PASS_CMD "PASS "
-#define FTP_BINARY_CMD "TYPE I"
-#define FTP_PORT_CMD "PORT "
-#define FTP_STOR_CMD "STOR "
-#define FTP_RETR_CMD "RETR "
-#define FTP_QUIT_CMD "QUIT"
-
-/*
- * State of each FTP stream
- */
-struct ftpStream {
- /*
- * Control connection socket
+typedef struct {
+ /**
+ * @brief Control connection socket.
*/
int ctrl_socket;
- struct sockaddr_in myCtrlAddress;
- struct sockaddr_in farCtrlAddress;
- /*
- * Data transfer socket
+
+ /**
+ * @brief Data transfer socket.
*/
- int port_socket;
int data_socket;
- struct sockaddr_in myDataAddress;
- struct sockaddr_in farDataAddress;
- /*
- * other stuff to remember
+
+ /**
+ * @brief End of file flag.
*/
- bool eof_reached;
-};
+ bool eof;
+} rtems_ftpfs_entry;
-/*
- * Number of streams open at the same time
- */
-static rtems_id ftp_mutex;
-static int nStreams;
-static struct ftpStream ** volatile ftpStreams;
+static bool rtems_ftpfs_verbose = false;
-extern rtems_filesystem_operations_table rtems_tftp_ops;
-extern rtems_filesystem_file_handlers_r rtems_tftp_handlers;
+rtems_status_code rtems_ftpfs_mount( const char *mount_point)
+{
+ int rv = 0;
-/*
- * Direct copy from the IMFS/TFTP. Look at this.
- */
+ if (mount_point == NULL) {
+ mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
+ }
-rtems_filesystem_limits_and_options_t rtems_ftp_limits_and_options = {
- 5, /* link_max */
- 6, /* max_canon */
- 7, /* max_input */
- 255, /* name_max */
- 255, /* path_max */
- 2, /* pipe_buf */
- 1, /* posix_async_io */
- 2, /* posix_chown_restrictions */
- 3, /* posix_no_trunc */
- 4, /* posix_prio_io */
- 5, /* posix_sync_io */
- 6 /* posix_vdisable */
-};
+ rv = mkdir( mount_point, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (rv != 0) {
+ return RTEMS_IO_ERROR;
+ }
-int rtems_ftp_mount_me(
- rtems_filesystem_mount_table_entry_t *temp_mt_entry
-)
-{
- rtems_status_code sc;
+ rv = mount(
+ NULL,
+ &rtems_ftpfs_ops,
+ RTEMS_FILESYSTEM_READ_WRITE,
+ NULL,
+ mount_point
+ );
+ if (rv != 0) {
+ return RTEMS_IO_ERROR;
+ }
- temp_mt_entry->mt_fs_root.handlers = &rtems_ftp_handlers;
- temp_mt_entry->mt_fs_root.ops = &rtems_ftp_ops;
+ return RTEMS_SUCCESSFUL;
+}
- /*
- * We have no ftp filesystem specific data to maintain. This
- * filesystem may only be mounted ONCE.
- *
- * And we maintain no real filesystem nodes, so there is no real root.
- */
+rtems_status_code rtems_ftpfs_set_verbose( bool verbose)
+{
+ rtems_ftpfs_verbose = verbose;
- temp_mt_entry->fs_info = NULL;
- temp_mt_entry->mt_fs_root.node_access = NULL;
+ return RTEMS_SUCCESSFUL;
+}
- /*
- * These need to be looked at for full POSIX semantics.
- */
+rtems_status_code rtems_ftpfs_get_verbose( bool *verbose)
+{
+ if (verbose == NULL) {
+ return RTEMS_INVALID_ADDRESS;
+ }
- temp_mt_entry->pathconf_limits_and_options = rtems_ftp_limits_and_options;
+ *verbose = rtems_ftpfs_verbose;
+ return RTEMS_SUCCESSFUL;
+}
- /*
- * Now allocate a semaphore for mutual exclusion.
- *
- * NOTE: This could be in an fsinfo for this filesystem type.
- */
-
- sc = rtems_semaphore_create (rtems_build_name('F','T','P',' '),
- 1,
- RTEMS_FIFO |
- RTEMS_BINARY_SEMAPHORE |
- RTEMS_NO_INHERIT_PRIORITY |
- RTEMS_NO_PRIORITY_CEILING |
- RTEMS_LOCAL,
- 0,
- &ftp_mutex);
-
- if (sc != RTEMS_SUCCESSFUL)
- set_errno_and_return_minus_one( ENOMEM );
+int rtems_bsdnet_initialize_ftp_filesystem( void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
- return 0;
+ sc = rtems_ftpfs_mount( NULL);
+
+ if (sc == RTEMS_SUCCESSFUL) {
+ return 0;
+ } else {
+ return -1;
+ }
}
-/*
- * Initialize the FTP driver
- */
+typedef void (*rtems_ftpfs_reply_parser)(
+ const char * /* reply fragment */,
+ size_t /* reply fragment length */,
+ void * /* parser argument */
+);
-int rtems_bsdnet_initialize_ftp_filesystem (void)
-{
- int status;
- rtems_filesystem_mount_table_entry_t *entry;
-
- status = mkdir( FTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
- if ( status == -1 )
- return status;
-
- status = mount(
- &entry,
- &rtems_ftp_ops,
- RTEMS_FILESYSTEM_READ_WRITE,
- NULL,
- FTP_PATHNAME_PREFIX
- );
+typedef enum {
+ RTEMS_FTPFS_REPLY_START,
+ RTEMS_FTPFS_REPLY_SINGLE_LINE,
+ RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE,
+ RTEMS_FTPFS_REPLY_MULTI_LINE,
+ RTEMS_FTPFS_REPLY_NEW_LINE,
+ RTEMS_FTPFS_REPLY_NEW_LINE_START
+} rtems_ftpfs_reply_state;
- if ( status )
- perror( "FTP mount failed" );
+typedef enum {
+ RTEMS_FTPFS_REPLY_ERROR = 0,
+ RTEMS_FTPFS_REPLY_1 = '1',
+ RTEMS_FTPFS_REPLY_2 = '2',
+ RTEMS_FTPFS_REPLY_3 = '3',
+ RTEMS_FTPFS_REPLY_4 = '4',
+ RTEMS_FTPFS_REPLY_5 = '5'
+} rtems_ftpfs_reply;
- return status;
-}
+#define RTEMS_FTPFS_REPLY_SIZE 3
-/*
- * read and return message code from ftp control connection
- */
-int rtems_ftp_get_message
-(
- const struct ftpStream *fsp, /* ptr to ftp control structure */
- int *msg_code /* ptr to return message code */
+static rtems_ftpfs_reply rtems_ftpfs_get_reply(
+ int socket,
+ rtems_ftpfs_reply_parser parser,
+ void *parser_arg
)
{
- char rd_buffer[4];
- size_t rd_size;
- ssize_t tmp_size;
- int eno = 0;
- bool finished = false;
- do {
- /*
- * fetch (at least) 4 characters from control connection
- * FIXME: how about a timeout?
- */
- rd_size = 0;
- while ((eno == 0) &&
- (rd_size < sizeof(rd_buffer))) {
- tmp_size = read(fsp->ctrl_socket,
- (&rd_buffer)+rd_size,
- sizeof(rd_buffer)-rd_size);
- if (tmp_size < 0) {
- eno = EIO;
- }
- else {
-#ifdef DEBUG_OUT
- write(1,(&rd_buffer)+rd_size,tmp_size);
-#endif
- rd_size += tmp_size;
- }
- }
- /*
- * check for 3 digits and space, otherwise not finished
- */
- if ((eno == 0) &&
- (isdigit((unsigned int)rd_buffer[0])) &&
- (isdigit((unsigned int)rd_buffer[1])) &&
- (isdigit((unsigned int)rd_buffer[2])) &&
- (rd_buffer[3] == ' ')) {
- finished = true;
- rd_buffer[3] = '\0';
- *msg_code = atol(rd_buffer);
- }
- /*
- * skip rest until end-of-line
- */
- do {
- tmp_size = read(fsp->ctrl_socket,
- &rd_buffer,
- 1);
- if (tmp_size < 0) {
- eno = EIO;
- }
-#ifdef DEBUG_OUT
- else {
- write(1,(&rd_buffer),tmp_size);
- }
-#endif
- } while ((eno == 0) &&
- (rd_buffer[0] != '\n'));
- } while ((eno == 0) && !finished);
- return eno;
-}
+ rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START;
+ bool verbose = rtems_ftpfs_verbose;
+ char reply_first [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' };
+ char reply_last [RTEMS_FTPFS_REPLY_SIZE] = { 'b', 'b', 'b' };
+ size_t reply_first_index = 0;
+ size_t reply_last_index = 0;
+ char buf [128];
-/*
- * split a pseudo file name into host, user, password, filename
- * NOTE: this function will allocate space for these strings,
- * the calling function should free the space, when no longer needed
- * exception: when we return any error, we will also cleanup
- * the strings
- * valid forms:
- * /FTP/user:pass/filepath
- * /FTP/user:pass@hostname/filepath
-
- * /FTP/user:pass/filepath
- * /FTP/user:pass/@hostname/filepath
- * NOTE: /FTP is already stripped from the name
- */
-int rtems_ftp_split_names
-( const char *pathname, /* total path name (including prefix) */
- char **usernamep, /* ptr to ptr to user name */
- char **passwordp, /* ptr to ptr to password */
- char **hostnamep, /* ptr to ptr to host name */
- char **filenamep) /* ptr to ptr to hostremaining file name */
-{
- const char *chunk_start;
- const char *chunk_end;
- size_t chunk_len;
- int rc = 0;
+ while (true) {
+ /* Receive reply fragment from socket */
+ ssize_t i = 0;
+ ssize_t rv = recv( socket, buf, sizeof( buf), 0);
- /*
- * ensure, that result pointers are NULL...
- */
- *usernamep = NULL;
- *passwordp = NULL;
- *hostnamep = NULL;
- *filenamep = NULL;
-
-#if 1
- chunk_start = pathname;
-#else /* no longer needed with IMFS */
- /*
- * check, that total path is long enough, skip prefix
- */
- if (rc == 0) {
- if (strlen (pathname) <= strlen (FTP_PATHNAME_PREFIX)) {
- rc = ENOENT;
+ if (rv <= 0) {
+ return RTEMS_FTPFS_REPLY_ERROR;
}
- else {
- chunk_start = pathname + strlen (FTP_PATHNAME_PREFIX);
+
+ /* Be verbose if necessary */
+ if (verbose) {
+ write( STDERR_FILENO, buf, (size_t) rv);
}
- }
-#endif
- /*
- * fetch user name: terminated with ":"
- */
- if (rc == 0) {
- chunk_end = strchr(chunk_start,':');
- if ((chunk_end == NULL) || /* No ':' found or */
- (chunk_end == chunk_start)) { /* ':' is first character-> no name */
- rc = ENOENT;
+
+ /* Invoke parser if necessary */
+ if (parser != NULL) {
+ parser( buf, (size_t) rv, parser_arg);
}
- else {
- chunk_len = chunk_end-chunk_start;
- *usernamep = malloc(chunk_len+1);
- if (*usernamep == NULL) {
- rc = ENOMEM;
- }
- else {
- memcpy(*usernamep,chunk_start,chunk_len);
- (*usernamep)[chunk_len] = '\0';
+
+ /* Parse reply fragment */
+ for (i = 0; i < rv; ++i) {
+ char c = buf [i];
+
+ switch (state) {
+ case RTEMS_FTPFS_REPLY_START:
+ if (reply_first_index < RTEMS_FTPFS_REPLY_SIZE) {
+ reply_first [reply_first_index] = c;
+ ++reply_first_index;
+ } else if (c == '-') {
+ state = RTEMS_FTPFS_REPLY_MULTI_LINE;
+ } else {
+ state = RTEMS_FTPFS_REPLY_SINGLE_LINE;
+ }
+ break;
+ case RTEMS_FTPFS_REPLY_SINGLE_LINE:
+ if (c == '\n') {
+ state = RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE;
+ }
+ break;
+ case RTEMS_FTPFS_REPLY_MULTI_LINE:
+ if (c == '\n') {
+ state = RTEMS_FTPFS_REPLY_NEW_LINE_START;
+ reply_last_index = 0;
+ }
+ break;
+ case RTEMS_FTPFS_REPLY_NEW_LINE:
+ case RTEMS_FTPFS_REPLY_NEW_LINE_START:
+ if (reply_last_index < RTEMS_FTPFS_REPLY_SIZE) {
+ state = RTEMS_FTPFS_REPLY_NEW_LINE;
+ reply_last [reply_last_index] = c;
+ ++reply_last_index;
+ } else {
+ state = RTEMS_FTPFS_REPLY_MULTI_LINE;
+ }
+ break;
+ default:
+ return RTEMS_FTPFS_REPLY_ERROR;
}
}
- }
- /*
- * fetch password: terminated with "/" or "@"
- */
- if (rc == 0) {
- chunk_start = chunk_end + 1; /* skip ":" after user name */
- chunk_end = strchr(chunk_start,'/');
- if ((chunk_end == NULL) || /* No '/' found or */
- (chunk_end == chunk_start)) { /* '/' is first character-> no pwd */
- rc = ENOENT;
- }
- else {
- /*
- * we have found a proper '/'
- * this is the end of the password
- */
- chunk_len = chunk_end-chunk_start;
- *passwordp = malloc(chunk_len+1);
- if (*passwordp == NULL) {
- rc = ENOMEM;
- }
- else {
- memcpy(*passwordp,chunk_start,chunk_len);
- (*passwordp)[chunk_len] = '\0';
+
+ /* Check reply */
+ if (state == RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE) {
+ if (
+ isdigit( reply_first [0])
+ && isdigit( reply_first [1])
+ && isdigit( reply_first [2])
+ ) {
+ break;
+ } else {
+ return RTEMS_FTPFS_REPLY_ERROR;
}
- }
- }
- /*
- * if first char after '/' is '@', then this is the hostname
- * fetch hostname terminated with "/"
- * if exists at all. otherwise take default server from bootp
- */
- if (rc == 0) {
- chunk_start = chunk_end+1;
- if (*chunk_start == '@') {
- /*
- * hostname follows
- */
- chunk_start = chunk_start + 1; /* skip "@" after password */
- chunk_end = strchr(chunk_start,'/');
- if ((chunk_end == NULL) || /* No '/' found or */
- (chunk_end == chunk_start)) { /* '/' is first character-> no host */
- rc = ENOENT;
+ } else if (state == RTEMS_FTPFS_REPLY_NEW_LINE_START) {
+ bool ok = true;
+
+ for (i = 0; i < RTEMS_FTPFS_REPLY_SIZE; ++i) {
+ ok = ok
+ && reply_first [i] == reply_last [i]
+ && isdigit( reply_first [i]);
}
- else {
- /*
- * we have found a proper '/'
- */
- chunk_len = chunk_end-chunk_start;
- *hostnamep = malloc(chunk_len+1);
- if (*hostnamep == NULL) {
- rc = ENOMEM;
- }
- else {
- memcpy(*hostnamep,chunk_start,chunk_len);
- (*hostnamep)[chunk_len] = '\0';
- }
+
+ if (ok) {
+ break;
}
}
- else { /* chunk_start != '@' */
- /*
- * no host name given, keep string empty
- */
- *hostnamep = malloc(1);
- if (*hostnamep == NULL) {
- rc = ENOMEM;
- }
- else {
- (*hostnamep)[0] = '\0';
- }
- }
}
- /*
- * fetch filename. This is all the rest...
- */
- if (rc == 0) {
- chunk_start = chunk_end+1;
- if (*chunk_start == '\0') { /* nothing left for filename */
- rc = ENOENT;
- }
- else {
- chunk_len = strlen(chunk_start);
- *filenamep = malloc(chunk_len+1);
- if (*filenamep == NULL) {
- rc = ENOMEM;
- }
- else {
- memcpy(*filenamep,chunk_start,chunk_len);
- (*filenamep)[chunk_len] = '\0';
- }
- }
+
+ return reply_first [0];
+}
+
+static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser(
+ int socket,
+ const char *cmd,
+ const char *arg,
+ rtems_ftpfs_reply_parser parser,
+ void *parser_arg
+)
+{
+ const char *const eol = "\r\n";
+ bool verbose = rtems_ftpfs_verbose;
+ int rv = 0;
+
+ /* Send command */
+ rv = send( socket, cmd, strlen( cmd), 0);
+ if (rv < 0) {
+ return RTEMS_FTPFS_REPLY_ERROR;
}
-
- /*
- * cleanup anything, if error occured
- */
- if (rc != 0) {
- if (*hostnamep != NULL) {
- free(*hostnamep);
- *hostnamep = NULL;
- }
- if (*usernamep != NULL) {
- free(*usernamep);
- *usernamep = NULL;
- }
- if (*passwordp != NULL) {
- free(*passwordp);
- *passwordp = NULL;
- }
- if (*filenamep != NULL) {
- free(*filenamep);
- *filenamep = NULL;
+ if (verbose) {
+ write( STDERR_FILENO, cmd, strlen( cmd));
+ }
+
+ /* Send command argument if necessary */
+ if (arg != NULL) {
+ rv = send( socket, arg, strlen( arg), 0);
+ if (rv < 0) {
+ return RTEMS_FTPFS_REPLY_ERROR;
+ }
+ if (verbose) {
+ write( STDERR_FILENO, arg, strlen( arg));
}
}
- return rc;
+
+ /* Send end of line */
+ rv = send( socket, eol, 2, 0);
+ if (rv < 0) {
+ return RTEMS_FTPFS_REPLY_ERROR;
+ }
+ if (verbose) {
+ write( STDERR_FILENO, &eol [1], 1);
+ }
+
+ /* Return reply */
+ return rtems_ftpfs_get_reply( socket, parser, parser_arg);
}
-
-int rtems_ftp_evaluate_for_make(
- const char *path, /* IN */
- rtems_filesystem_location_info_t *pathloc, /* IN/OUT */
- const char **name /* OUT */
+
+static rtems_ftpfs_reply rtems_ftpfs_send_command(
+ int socket,
+ const char *cmd,
+ const char *arg
)
-{
- set_errno_and_return_minus_one( EIO );
+{
+ return rtems_ftpfs_send_command_with_parser( socket, cmd, arg, NULL, NULL);
}
-/*
- * XXX - Fix return values.
- */
+typedef enum {
+ STATE_USER_NAME,
+ STATE_START_PASSWORD,
+ STATE_START_HOST_NAME,
+ STATE_START_HOST_NAME_OR_PATH,
+ STATE_START_PATH,
+ STATE_PASSWORD,
+ STATE_HOST_NAME,
+ STATE_DONE,
+ STATE_INVALID
+} split_state;
-int rtems_ftp_eval_path(
- const char *pathname, /* IN */
- int flags, /* IN */
- rtems_filesystem_location_info_t *pathloc /* IN/OUT */
+static bool rtems_ftpfs_split_names (
+ char *s,
+ const char **user,
+ const char **password,
+ const char **hostname,
+ const char **path
)
{
+ split_state state = STATE_USER_NAME;
+ size_t len = strlen( s);
+ size_t i = 0;
- /*
- * Read-only for now
- */
-
- if ( ((flags & O_RDONLY) != O_RDONLY ) &&
- ((flags & O_WRONLY) != O_WRONLY )) {
- set_errno_and_return_minus_one( ENOENT );
+ *user = s;
+ *password = NULL;
+ *hostname = NULL;
+ *path = NULL;
+
+ for (i = 0; i < len; ++i) {
+ char c = s [i];
+
+ switch (state) {
+ case STATE_USER_NAME:
+ if (c == ':') {
+ state = STATE_START_PASSWORD;
+ s [i] = '\0';
+ } else if (c == '@') {
+ state = STATE_START_HOST_NAME;
+ s [i] = '\0';
+ } else if (c == '/') {
+ state = STATE_START_HOST_NAME_OR_PATH;
+ s [i] = '\0';
+ }
+ break;
+ case STATE_START_PASSWORD:
+ state = STATE_PASSWORD;
+ *password = &s [i];
+ --i;
+ break;
+ case STATE_START_HOST_NAME:
+ state = STATE_HOST_NAME;
+ *hostname = &s [i];
+ --i;
+ break;
+ case STATE_START_HOST_NAME_OR_PATH:
+ if (c == '@') {
+ state = STATE_START_HOST_NAME;
+ } else {
+ state = STATE_DONE;
+ *path = &s [i];
+ goto done;
+ }
+ break;
+ case STATE_START_PATH:
+ state = STATE_DONE;
+ *path = &s [i];
+ goto done;
+ case STATE_PASSWORD:
+ if (c == '@') {
+ state = STATE_START_HOST_NAME;
+ s [i] = '\0';
+ } else if (c == '/') {
+ state = STATE_START_HOST_NAME_OR_PATH;
+ s [i] = '\0';
+ }
+ break;
+ case STATE_HOST_NAME:
+ if (c == '/') {
+ state = STATE_START_PATH;
+ s [i] = '\0';
+ }
+ break;
+ default:
+ state = STATE_INVALID;
+ goto done;
+ }
}
- /*
- * The File system is mounted at FTP_PATHNAME_PREFIX
- * the caller of this routine has striped off this part of the
- * name. Save the remainder of the name for use by the open routine.
- */
- pathloc->node_access = (void * ) pathname;
- pathloc->handlers = &rtems_ftp_handlers;
+done:
- return 0;
+ /* If we have no password use the user name */
+ if (*password == NULL) {
+ *password = *user;
+ }
+
+ return state == STATE_DONE;
}
-/*
- * Open a FTP stream
- */
-int rtems_ftp_open(
- rtems_libio_t *iop,
- const char *new_name,
- uint32_t flag,
- uint32_t mode
+static socklen_t rtems_ftpfs_create_address(
+ struct sockaddr_in *sa,
+ unsigned long address,
+ unsigned short port
)
{
- int s = 0;
- char *filename = NULL;
- char *uname = NULL;
- char *upass = NULL;
- char *hostname = NULL;
- char port_buffer[sizeof(FTP_PORT_CMD)+6*4+1+1];
- uint32_t my_ip;
- uint16_t my_port;
- int eno = 0;
- rtems_status_code rc;
- bool is_write = false;
- bool sema_obtained = false;
- struct ftpStream *fsp = NULL;
- int msg_tmp = 0;
- socklen_t sockaddr_size;
- /*
- * check for R/O or W/O flags
- */
- if (eno == 0) {
- if ((0 != (iop->flags & LIBIO_FLAGS_WRITE)) &&
- (0 != (iop->flags & LIBIO_FLAGS_READ))) {
- eno = ENOTSUP;
- }
- else {
- is_write = (0 != (iop->flags & LIBIO_FLAGS_WRITE));
- }
- }
- /*
- * split pathname into parts
- */
- if (eno == 0) {
- eno = rtems_ftp_split_names(iop->file_info,
- &uname,
- &upass,
- &hostname,
- &filename);
- }
-
- /*
- * Find a free stream
- */
- if (eno == 0) {
- rc = rtems_semaphore_obtain (ftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
- if (rc == RTEMS_SUCCESSFUL) {
- sema_obtained = true;
+ memset( sa, sizeof( *sa), 0);
+
+ sa->sin_family = AF_INET;
+ sa->sin_addr.s_addr = address;
+ sa->sin_port = port;
+ sa->sin_len = sizeof( *sa);
+
+ return sizeof( *sa);
+}
+
+static void rtems_ftpfs_terminate( rtems_ftpfs_entry *e, rtems_libio_t *iop)
+{
+ if (e != NULL) {
+ /* Close data connection if necessary */
+ if (e->data_socket >= 0) {
+ close( e->data_socket);
}
- else {
- eno = EBUSY;
+
+ /* Close control connection if necessary */
+ if (e->ctrl_socket >= 0) {
+ close( e->ctrl_socket);
}
+
+ /* Free connection entry */
+ free( e);
}
- if (eno == 0) {
- for (s = 0 ; s < nStreams ; s++) {
- if (ftpStreams[s] == NULL)
- break;
- }
- if (s == nStreams) {
- /*
- * Reallocate stream pointers
- * Guard against the case where realloc() returns NULL.
- */
- struct ftpStream **np;
-
- np = realloc (ftpStreams, ++nStreams * sizeof *ftpStreams);
- if (np == NULL) {
- eno = ENOMEM;
- }
- else {
- ftpStreams = np;
- }
- }
+
+ /* Invalidate IO entry */
+ iop->data1 = NULL;
+}
+
+static int rtems_ftpfs_open_ctrl_connection(
+ rtems_ftpfs_entry *e,
+ const char *user,
+ const char *password,
+ const char *hostname,
+ uint32_t *client_address
+)
+{
+ int rv = 0;
+ rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
+ struct in_addr address = { .s_addr = 0 };
+ struct sockaddr_in sa;
+ socklen_t size = 0;
+
+ /* Create the socket for the control connection */
+ e->ctrl_socket = socket( AF_INET, SOCK_STREAM, 0);
+ if (e->ctrl_socket < 0) {
+ return ENOMEM;
}
- if (eno == 0) {
- fsp = ftpStreams[s] = malloc (sizeof (struct ftpStream));
- rtems_semaphore_release (ftp_mutex);
- sema_obtained = false;
- if (fsp == NULL) {
- eno = ENOMEM;
- }
- else {
- iop->data0 = s;
- iop->data1 = fsp;
- fsp->ctrl_socket = -1; /* mark, that sockets not yet created */
- fsp->port_socket = -1;
- fsp->data_socket = -1;
- fsp->eof_reached = false;
+
+ /* Set up the server address from the hostname */
+ if (hostname == NULL || strlen( hostname) == 0) {
+ /* Default to BOOTP server address */
+ address = rtems_bsdnet_bootp_server_address;
+ } else if (inet_aton( hostname, &address) == 0) {
+ /* Try to get the address by name */
+ struct hostent *he = gethostbyname( hostname);
+
+ if (he != NULL) {
+ memcpy( &address, he->h_addr, sizeof( address));
+ } else {
+ return ENOENT;
}
}
- if (eno == 0) {
- /*
- * Create the socket for control connection
- */
- if ((fsp->ctrl_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
- eno = ENOMEM;
- }
+ rtems_ftpfs_create_address( &sa, address.s_addr, htons( RTEMS_FTPFS_CTRL_PORT));
+ DEBUG_PRINTF( "server = %s\n", inet_ntoa( sa.sin_addr));
+
+ /* Open control connection */
+ rv = connect(
+ e->ctrl_socket,
+ (struct sockaddr *) &sa,
+ sizeof( sa)
+ );
+ if (rv != 0) {
+ return ENOENT;
}
- if (eno == 0) {
- /*
- * Set the destination to the FTP server
- * port on the remote machine.
- */
- memset(&(fsp->farCtrlAddress),sizeof(fsp->farCtrlAddress),0);
- fsp->farCtrlAddress.sin_family = AF_INET;
- if (*hostname == '\0') {
- fsp->farCtrlAddress.sin_addr.s_addr = rtems_bsdnet_bootp_server_address.s_addr;
- }
- else if (1 != inet_aton(hostname,&(fsp->farCtrlAddress.sin_addr))) {
- struct hostent *hent;
-
- hent = gethostbyname(hostname);
- if (hent != NULL) {
- memcpy(&fsp->farCtrlAddress.sin_addr,
- hent->h_addr,
- sizeof(fsp->farCtrlAddress.sin_addr));
- }
- else {
- eno = ENOENT;
- }
- }
- if (eno == 0) {
- fsp->farCtrlAddress.sin_port = htons (FTP_PORT_NUM);
- fsp->farCtrlAddress.sin_len = sizeof(fsp->farCtrlAddress);
- if (0 > connect(fsp->ctrl_socket,
- (struct sockaddr *)&(fsp->farCtrlAddress),
- sizeof(fsp->farCtrlAddress))) {
- eno = ENOENT;
- }
- }
- if (eno == 0) {
- /*
- * fetch IP address of interface used
- */
- memset(&(fsp->myCtrlAddress),sizeof(fsp->myCtrlAddress),0);
- fsp->myCtrlAddress.sin_family = AF_INET;
- fsp->myCtrlAddress.sin_addr.s_addr = INADDR_ANY;
- fsp->myCtrlAddress.sin_port = 0;
- fsp->myCtrlAddress.sin_len = sizeof(fsp->myDataAddress);
- sockaddr_size = sizeof(fsp->myCtrlAddress);
- if (0 > getsockname(fsp->ctrl_socket,
- (struct sockaddr *)&(fsp->myCtrlAddress),
- &sockaddr_size)) {
- eno = ENOMEM;
- }
- }
+ /* Get client address */
+ size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
+ rv = getsockname(
+ e->ctrl_socket,
+ (struct sockaddr *) &sa,
+ &size
+ );
+ if (rv != 0) {
+ return ENOMEM;
}
- if (eno == 0) {
- /*
- * now we should get a connect message from the FTP server
- */
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
- if ((eno == 0) &&
- (msg_tmp != FTP_REPLY_CONNECT)) {
- eno = ENOENT;
- }
+ *client_address = ntohl( sa.sin_addr.s_addr);
+ DEBUG_PRINTF( "client = %s\n", inet_ntoa( sa.sin_addr));
+
+ /* Now we should get a welcome message from the server */
+ reply = rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
+ if (reply != RTEMS_FTPFS_REPLY_2) {
+ return ENOENT;
}
- if (eno == 0) {
- /*
- * send user ID to server
- * NOTE: the following lines will be executed in order
- * and will be aborted whenever an error occures... (see your ANSI C book)
- */
- if ((0 > send(fsp->ctrl_socket,FTP_USER_CMD,strlen(FTP_USER_CMD),0)) ||
- (0 > send(fsp->ctrl_socket,uname, strlen(uname), 0)) ||
- (0 > send(fsp->ctrl_socket,"\n", 1, 0))) {
- eno = EIO;
+
+ /* Send USER command */
+ reply = rtems_ftpfs_send_command( e->ctrl_socket, "USER ", user);
+ if (reply == RTEMS_FTPFS_REPLY_3) {
+ /* Send PASS command */
+ reply = rtems_ftpfs_send_command( e->ctrl_socket, "PASS ", password);
+ if (reply != RTEMS_FTPFS_REPLY_2) {
+ return EACCES;
}
+
+ /* TODO: Some server may require an account */
+ } else if (reply != RTEMS_FTPFS_REPLY_2) {
+ return EACCES;
}
- if (eno == 0) {
- /*
- * now we should get a request for the password or a login...
- */
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
- if (eno == 0) {
- if (msg_tmp == FTP_REPLY_PASSREQ) {
- /*
- * send password to server
- */
-#ifdef DEBUG_OUT
- write(1,FTP_PASS_CMD,strlen(FTP_PASS_CMD));
- write(1,upass, strlen(upass) );
- write(1,"\n", 1 );
-#endif
- if ((0 > send(fsp->ctrl_socket,FTP_PASS_CMD,strlen(FTP_PASS_CMD),0)) ||
- (0 > send(fsp->ctrl_socket,upass, strlen(upass), 0)) ||
- (0 > send(fsp->ctrl_socket,"\n", 1, 0))) {
- eno = EIO;
- }
- /*
- * at least now a login reply should come up...
- * this is checked some lines downwards the code
- */
- if (eno == 0) {
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
- }
- }
- }
+
+ /* Send TYPE command to set binary mode for all data transfers */
+ reply = rtems_ftpfs_send_command( e->ctrl_socket, "TYPE I", NULL);
+ if (reply != RTEMS_FTPFS_REPLY_2) {
+ return EIO;
}
- if (eno == 0) {
- /*
- * check for a login reply. this should be present now...
- */
- if (msg_tmp != FTP_REPLY_LOGIN) {
- eno = EACCES; /* pseudo for wrong user/pass */
- }
+
+ return 0;
+}
+
+static int rtems_ftpfs_open_data_connection_active(
+ rtems_ftpfs_entry *e,
+ uint32_t client_address,
+ const char *file_command,
+ const char *filename
+)
+{
+ int rv = 0;
+ int eno = 0;
+ rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
+ struct in_addr address = { .s_addr = 0 };
+ struct sockaddr_in sa;
+ socklen_t size = 0;
+ int port_socket = -1;
+ char port_command [] = "PORT 000,000,000,000,000,000";
+ uint16_t data_port = 0;
+
+ /* Create port socket to establish a data data connection */
+ port_socket = socket( AF_INET, SOCK_STREAM, 0);
+ if (port_socket < 0) {
+ eno = ENOMEM;
+ goto cleanup;
}
- if (eno == 0) {
- /*
- * set binary mode for all transfers
- */
-#ifdef DEBUG_OUT
- write(1,FTP_BINARY_CMD,strlen(FTP_BINARY_CMD));
- write(1,"\n", 1 );
-#endif
- if ((0 > send(fsp->ctrl_socket,FTP_BINARY_CMD,strlen(FTP_BINARY_CMD),0)) ||
- (0 > send(fsp->ctrl_socket,"\n", 1, 0))) {
- eno = EIO;
- }
- else {
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
- }
+
+ /* Bind port socket */
+ rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
+ rv = bind(
+ port_socket,
+ (struct sockaddr *) &sa,
+ sizeof( sa)
+ );
+ if (rv != 0) {
+ eno = EBUSY;
+ goto cleanup;
}
- if (eno == 0) {
- /*
- * check for a "BINARY TYPE command successful" reply
- */
- if (msg_tmp != FTP_REPLY_SUCCESS) {
- eno = EIO;
- }
+
+ /* Get port number for data socket */
+ size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
+ rv = getsockname(
+ port_socket,
+ (struct sockaddr *) &sa,
+ &size
+ );
+ if (rv != 0) {
+ eno = ENOMEM;
+ goto cleanup;
}
- if (eno == 0) {
- /*
- * create and bind socket for data connection
- */
- if ((fsp->port_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
- eno = ENOMEM;
- }
- else {
- memset(&(fsp->myDataAddress),sizeof(fsp->myDataAddress),0);
- fsp->myDataAddress.sin_family = AF_INET;
- fsp->myDataAddress.sin_addr.s_addr = INADDR_ANY;
- fsp->myDataAddress.sin_port = 0; /* unique port will be assigned */
- fsp->myDataAddress.sin_len = sizeof(fsp->myDataAddress);
- if (0 > bind(fsp->port_socket,
- (struct sockaddr *)&(fsp->myDataAddress),
- sizeof(fsp->myDataAddress))) {
- eno = EBUSY;
- }
- else {
- /*
- * fetch port number of data socket
- */
- memset(&(fsp->myDataAddress),sizeof(fsp->myDataAddress),0);
- fsp->myDataAddress.sin_family = AF_INET;
- fsp->myDataAddress.sin_addr.s_addr = INADDR_ANY;
- fsp->myDataAddress.sin_port = 0;
- fsp->myDataAddress.sin_len = sizeof(fsp->myDataAddress);
- sockaddr_size = sizeof(fsp->myDataAddress);
- if (0 > getsockname(fsp->port_socket,
- (struct sockaddr *)&(fsp->myDataAddress),
- &sockaddr_size)) {
- eno = ENOMEM;
- }
- }
- }
+ data_port = ntohs( sa.sin_port);
+
+ /* Send PORT command to set data connection port for server */
+ snprintf(
+ port_command,
+ sizeof( port_command),
+ "PORT %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%" PRIu16,
+ (client_address >> 24) & 0xff,
+ (client_address >> 16) & 0xff,
+ (client_address >> 8) & 0xff,
+ (client_address >> 0) & 0xff,
+ (data_port >> 8) & 0xff,
+ (data_port >> 0) & 0xff
+ );
+ reply = rtems_ftpfs_send_command( e->ctrl_socket, port_command, NULL);
+ if (reply != RTEMS_FTPFS_REPLY_2) {
+ eno = ENOTSUP;
+ goto cleanup;
}
- if (eno == 0) {
- /*
- * propagate data connection port to server
- */
- my_ip = ntohl(fsp->myCtrlAddress.sin_addr.s_addr);
- my_port = ntohs(fsp->myDataAddress.sin_port);
- sprintf(port_buffer,"%s%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%" PRIu16 "\n",
- FTP_PORT_CMD,
- (my_ip >> 24) & 0x00ff,
- (my_ip >> 16) & 0x00ff,
- (my_ip >> 8) & 0x00ff,
- (my_ip >> 0) & 0x00ff,
- (my_port>> 8) & 0x00ff,
- (my_port>> 0) & 0x00ff);
-#ifdef DEBUG_OUT
- write(1,port_buffer,strlen(port_buffer));
-#endif
- if (0 > send(fsp->ctrl_socket,port_buffer,strlen(port_buffer),0)) {
- eno = EIO;
- }
- else {
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
- }
+
+ /* Listen on port socket for incoming data connections */
+ rv = listen( port_socket, 1);
+ if (rv != 0) {
+ eno = EBUSY;
+ goto cleanup;
}
- if (eno == 0) {
- /*
- * check for a "PORT command successful" reply
- */
- if (msg_tmp != FTP_REPLY_SUCCESS) {
- eno = EBUSY;
- }
+
+ /* Send RETR or STOR command with filename */
+ reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
+ if (reply != RTEMS_FTPFS_REPLY_1) {
+ eno = EIO;
+ goto cleanup;
}
+
/*
- * prepare port socket to listen for incoming connections
+ * Wait for connect on data connection.
+ *
+ * FIXME: This should become a select instead with a timeout.
*/
- if (eno == 0) {
- if (0 > listen(fsp->port_socket,1)) {
- eno = EBUSY;
- }
+ size = sizeof( sa);
+ e->data_socket = accept(
+ port_socket,
+ (struct sockaddr *) &sa,
+ &size
+ );
+ if (e->data_socket < 0) {
+ eno = EIO;
+ goto cleanup;
}
- if (eno == 0) {
- /*
- * send retrive/store command with filename
- */
- if (is_write) {
-#ifdef DEBUG_OUT
- write(1,FTP_STOR_CMD,strlen(FTP_STOR_CMD));
- write(1,filename ,strlen(filename) );
- write(1,"\n",1);
-#endif
- if ((0 > send(fsp->ctrl_socket,FTP_STOR_CMD,strlen(FTP_STOR_CMD),0)) ||
- (0 > send(fsp->ctrl_socket,filename, strlen(filename), 0)) ||
- (0 > send(fsp->ctrl_socket,"\n", 1, 0))) {
- eno = EIO;
- }
+
+ /* FIXME: Check, that server data address is really from our server */
+
+cleanup:
+
+ /* Close port socket if necessary */
+ if (port_socket >= 0) {
+ close( port_socket);
+ }
+
+ return eno;
+}
+
+typedef enum {
+ RTEMS_FTPFS_PASV_START = 0,
+ RTEMS_FTPFS_PASV_JUNK,
+ RTEMS_FTPFS_PASV_DATA,
+ RTEMS_FTPFS_PASV_DONE
+} rtems_ftpfs_pasv_state;
+
+typedef struct {
+ rtems_ftpfs_pasv_state state;
+ uint8_t data [6];
+ size_t index;
+} rtems_ftpfs_pasv_entry;
+
+static void rtems_ftpfs_pasv_parser(
+ const char* buf,
+ size_t len,
+ void *arg
+)
+{
+ rtems_ftpfs_pasv_entry *e = (rtems_ftpfs_pasv_entry *) arg;
+ size_t i = 0;
+
+ for (i = 0; i < len; ++i) {
+ char c = buf [i];
+
+ switch (e->state) {
+ case RTEMS_FTPFS_PASV_START:
+ if (!isdigit( c)) {
+ e->state = RTEMS_FTPFS_PASV_JUNK;
+ e->index = 0;
+ }
+ break;
+ case RTEMS_FTPFS_PASV_JUNK:
+ if (isdigit( c)) {
+ e->state = RTEMS_FTPFS_PASV_DATA;
+ e->data [e->index] = (uint8_t) (c - '0');
+ }
+ break;
+ case RTEMS_FTPFS_PASV_DATA:
+ if (isdigit( c)) {
+ e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0');
+ } else if (c == ',') {
+ ++e->index;
+ if (e->index < sizeof( e->data)) {
+ e->data [e->index] = 0;
+ } else {
+ e->state = RTEMS_FTPFS_PASV_DONE;
+ }
+ } else {
+ e->state = RTEMS_FTPFS_PASV_DONE;
+ }
+ break;
+ default:
+ return;
}
- else {
-#ifdef DEBUG_OUT
- write(1,FTP_RETR_CMD,strlen(FTP_RETR_CMD));
- write(1,filename ,strlen(filename) );
- write(1,"\n",1);
-#endif
- if ((0 > send(fsp->ctrl_socket,FTP_RETR_CMD,strlen(FTP_RETR_CMD),0)) ||
- (0 > send(fsp->ctrl_socket,filename, strlen(filename), 0)) ||
- (0 > send(fsp->ctrl_socket,"\n", 1, 0))) {
- eno = EIO;
- }
- }
}
-#if 1
- if (eno == 0) {
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
+}
+
+static int rtems_ftpfs_open_data_connection_passive(
+ rtems_ftpfs_entry *e,
+ uint32_t client_address,
+ const char *file_command,
+ const char *filename
+)
+{
+ int rv = 0;
+ rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
+ struct sockaddr_in sa;
+ socklen_t size = 0;
+ uint32_t data_address = 0;
+ uint16_t data_port = 0;
+
+ rtems_ftpfs_pasv_entry pe = {
+ .state = RTEMS_FTPFS_PASV_START
+ };
+
+ /* Send PASV command */
+ reply = rtems_ftpfs_send_command_with_parser(
+ e->ctrl_socket,
+ "PASV",
+ NULL,
+ rtems_ftpfs_pasv_parser,
+ &pe
+ );
+ if (reply != RTEMS_FTPFS_REPLY_2) {
+ return ENOTSUP;
}
- if (eno == 0) {
- /*
- * check for a "OPENING binary connection" reply
- */
- if (msg_tmp != FTP_REPLY_OPENCONN) {
- eno = EACCES;
- }
+ data_address = (uint32_t) ((pe.data [0] << 24) + (pe.data [1] << 16)
+ + (pe.data [2] << 8) + pe.data [3]);
+ data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]);
+ rtems_ftpfs_create_address( &sa, htonl( data_address), htons( data_port));
+ DEBUG_PRINTF(
+ "server data = %s:%u\n",
+ inet_ntoa( sa.sin_addr),
+ (unsigned) ntohs( sa.sin_port)
+ );
+
+ /* Create data socket */
+ e->data_socket = socket( AF_INET, SOCK_STREAM, 0);
+ if (e->data_socket < 0) {
+ return ENOMEM;
}
-#endif
- /*
- * wait for connect on data connection
- * FIXME: this should become a select instead with a timeout
- */
- if (eno == 0) {
- sockaddr_size = sizeof(fsp->farDataAddress);
- fsp->data_socket = accept(fsp->port_socket,
- (struct sockaddr *)&(fsp->farDataAddress),
- &sockaddr_size);
- if (0 > fsp->data_socket) {
- eno = EIO;
- }
+
+ /* Open data connection */
+ rv = connect(
+ e->data_socket,
+ (struct sockaddr *) &sa,
+ sizeof( sa)
+ );
+ if (rv != 0) {
+ return EIO;
}
- /*
- * FIXME: check, that fardataAddr is really from our ftp server
- */
-
- /*
- * clean up temp strings...
- */
- if (uname != NULL) {
- free(uname);
- uname = NULL;
+
+ /* Send RETR or STOR command with filename */
+ reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
+ if (reply != RTEMS_FTPFS_REPLY_1) {
+ return EIO;
}
- if (upass != NULL) {
- free(upass);
- upass = NULL;
+
+ return 0;
+}
+
+static int rtems_ftpfs_open(
+ rtems_libio_t *iop,
+ const char *path,
+ uint32_t flags,
+ uint32_t mode
+)
+{
+ int rv = 0;
+ int eno = 0;
+ bool ok = false;
+ rtems_ftpfs_entry *e = NULL;
+ const char *user = NULL;
+ const char *password = NULL;
+ const char *hostname = NULL;
+ const char *filename = NULL;
+ const char *file_command = (iop->flags & LIBIO_FLAGS_WRITE) != 0
+ ? "STOR "
+ : "RETR ";
+ uint32_t client_address = 0;
+ char *location = strdup( (const char *) iop->file_info);
+
+ /* Check allocation */
+ if (location == NULL) {
+ return ENOMEM;
}
- if (hostname != NULL) {
- free(hostname);
- hostname = NULL;
+
+ /* Check for either read-only or write-only flags */
+ if (
+ (iop->flags & LIBIO_FLAGS_WRITE) != 0
+ && (iop->flags & LIBIO_FLAGS_READ) != 0
+ ) {
+ eno = ENOTSUP;
+ goto cleanup;
}
- if (filename != NULL) {
- free(filename);
- filename = NULL;
+
+ /* Split location into parts */
+ ok = rtems_ftpfs_split_names(
+ location,
+ &user,
+ &password,
+ &hostname,
+ &filename
+ );
+ if (!ok) {
+ eno = ENOENT;
+ goto cleanup;
}
- /*
- * close part socket, no longer needed
- */
- if (fsp->port_socket != -1) {
- close(fsp->port_socket);
- fsp->port_socket = -1;
+ DEBUG_PRINTF(
+ "user = '%s', password = '%s', filename = '%s'\n",
+ user,
+ password,
+ filename
+ );
+
+ /* Allocate connection entry */
+ e = malloc( sizeof( *e));
+ if (e == NULL) {
+ eno = ENOMEM;
+ goto cleanup;
}
- /*
- * if error, clean up everything
- */
+
+ /* Initialize connection entry */
+ e->ctrl_socket = -1;
+ e->data_socket = -1;
+ e->eof = false;
+
+ /* Open control connection */
+ eno = rtems_ftpfs_open_ctrl_connection(
+ e,
+ user,
+ password,
+ hostname,
+ &client_address
+ );
if (eno != 0) {
- if (fsp != NULL) {
- /*
- * check and close ctrl/data connection
- */
- if (fsp->data_socket != -1) {
- close(fsp->data_socket);
- fsp->data_socket = -1;
- }
- if (fsp->ctrl_socket != -1) {
- close(fsp->ctrl_socket);
- fsp->ctrl_socket = -1;
- }
- /*
- * free ftpStream structure
- */
- ftpStreams[s] = NULL;
- free(fsp);
- fsp = NULL;
- }
+ goto cleanup;
}
- /*
- * return sema, if still occupied
- */
- if (sema_obtained) {
- rtems_semaphore_release (ftp_mutex);
- sema_obtained = false;
+
+ /* Open passive data connection */
+ eno = rtems_ftpfs_open_data_connection_passive(
+ e,
+ client_address,
+ file_command,
+ filename
+ );
+ if (eno == ENOTSUP) {
+ /* Open active data connection */
+ eno = rtems_ftpfs_open_data_connection_active(
+ e,
+ client_address,
+ file_command,
+ filename
+ );
}
-#if 0
- if (eno != 0) {
- set_errno_and_return_minus_one(eno);
+
+cleanup:
+
+ /* Free location parts buffer */
+ free( location);
+
+ if (eno == 0) {
+ /* Save connection state */
+ iop->data1 = e;
+ } else {
+ /* Free all resources if an error occured */
+ rtems_ftpfs_terminate( e, iop);
}
- return 0;
-#else
+
return eno;
-#endif
}
-/*
- * Read from a FTP stream
- */
-ssize_t rtems_ftp_read(
+static ssize_t rtems_ftpfs_read(
rtems_libio_t *iop,
- void *buffer,
- size_t count
+ void *buffer,
+ size_t count
)
{
- int eno = 0;
- struct ftpStream *fsp;
- size_t want_cnt;
- ssize_t rd_cnt;
- int msg_tmp = 0;
+ rtems_ftpfs_entry *e = (rtems_ftpfs_entry *) iop->data1;
+ char *in = (char *) buffer;
+ size_t todo = count;
- fsp = iop->data1;
- want_cnt = count;
- /*
- * check, that data connection present
- */
- if (eno == 0) {
- if ((fsp == NULL) ||
- (fsp->data_socket < 0)) {
- eno = EBADF;
- }
- }
-
- /*
- * perform read from data socket
- * read multiple junks, if smaller than wanted
- */
- while ((eno == 0) &&
- (want_cnt > 0) &&
- !(fsp->eof_reached) ) {
- rd_cnt = read(fsp->data_socket,buffer,want_cnt);
- if (rd_cnt > 0) {
- buffer += rd_cnt;
- want_cnt -= rd_cnt;
- }
- else {
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
- fsp->eof_reached = true;
- if ((eno == 0) &&
- (msg_tmp != FTP_REPLY_TFERCMPL)) {
- eno = EIO;
- }
- if (rd_cnt < 0) {
- eno = EIO;
+ if (e->eof) {
+ return 0;
+ }
+
+ while (todo > 0) {
+ ssize_t rv = recv( e->data_socket, in, todo, 0);
+
+ if (rv <= 0) {
+ if (rv == 0) {
+ rtems_ftpfs_reply reply =
+ rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
+
+ if (reply == RTEMS_FTPFS_REPLY_2) {
+ e->eof = true;
+ break;
+ }
}
+
+ rtems_set_errno_and_return_minus_one( EIO);
}
+
+ in += rv;
+ todo -= (size_t) rv;
}
- if (eno != 0) {
- set_errno_and_return_minus_one(eno);
- }
- return count - want_cnt;
+
+ return (ssize_t) (count - todo);
}
-ssize_t rtems_ftp_write(
+static ssize_t rtems_ftpfs_write(
rtems_libio_t *iop,
- const void *buffer,
- size_t count
+ const void *buffer,
+ size_t count
)
{
- int eno = 0;
- struct ftpStream *fsp;
- size_t want_cnt;
- ssize_t wr_cnt;
- int msg_tmp = 0;
+ rtems_ftpfs_entry *e = (rtems_ftpfs_entry *) iop->data1;
+ const char *out = (const char *) buffer;
+ size_t todo = count;
- fsp = iop->data1;
- want_cnt = count;
- /*
- * check, that data connection present
- */
- if (eno == 0) {
- if ((fsp == NULL) ||
- (fsp->data_socket < 0)) {
- eno = EBADF;
- }
- }
-
- /*
- * perform write to data socket
- */
- if (eno == 0) {
- wr_cnt = write(fsp->data_socket,buffer,want_cnt);
- if (wr_cnt > 0) {
- buffer += wr_cnt;
- want_cnt -= wr_cnt;
- }
- else {
- eno = rtems_ftp_get_message(fsp,&msg_tmp);
- if ((eno == 0) &&
- (msg_tmp != FTP_REPLY_TFERCMPL)) {
- eno = EIO;
- }
- if (wr_cnt < 0) {
- eno = EIO;
+ if (e->eof) {
+ return 0;
+ }
+
+ while (todo > 0) {
+ ssize_t rv = send( e->data_socket, out, todo, 0);
+
+ if (rv <= 0) {
+ if (rv == 0) {
+ rtems_ftpfs_reply reply =
+ rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
+
+ if (reply == RTEMS_FTPFS_REPLY_2) {
+ e->eof = true;
+ break;
+ }
}
+
+ rtems_set_errno_and_return_minus_one( EIO);
}
+
+ out += rv;
+ todo -= (size_t) rv;
}
- if (eno != 0) {
- set_errno_and_return_minus_one(eno);
- }
- return count - want_cnt;
+
+ return (ssize_t) (count - todo);
}
-/*
- * Close a FTP stream
- */
-int rtems_ftp_close(
- rtems_libio_t *iop
-)
+static int rtems_ftpfs_close( rtems_libio_t *iop)
{
- int s = iop->data0;
- struct ftpStream *fsp = iop->data1;
+ rtems_ftpfs_entry *e = (rtems_ftpfs_entry *) iop->data1;
+ rtems_ftpfs_terminate( e, iop);
+
+ return 0;
+}
+
+/* Dummy version to let fopen( *,"w") work properly */
+static int rtems_ftpfs_ftruncate( rtems_libio_t *iop, off_t count)
+{
+ return 0;
+}
+
+static int rtems_ftpfs_eval_path(
+ const char *pathname,
+ int flags,
+ rtems_filesystem_location_info_t *pathloc
+)
+{
/*
- * close ctrl/data connection, if needed
- */
- if (fsp->data_socket >= 0) {
- close(fsp->data_socket);
- fsp->data_socket = -1;
- }
- if (fsp->ctrl_socket >= 0) {
- close(fsp->ctrl_socket);
- fsp->ctrl_socket = -1;
- }
- /*
- * free any used space...
+ * The caller of this routine has striped off the mount prefix from the path.
+ * We need to store this path here or otherwise we would have to do this job
+ * again. It is not possible to allocate resources here since there is no
+ * way to free them later in every case. The path is used in
+ * rtems_ftpfs_open() via iop->file_info.
+ *
+ * FIXME: Avoid to discard the const qualifier.
*/
- rtems_semaphore_obtain (ftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
- free (ftpStreams[s]);
- ftpStreams[s] = NULL;
- rtems_semaphore_release (ftp_mutex);
+ pathloc->node_access = (void *) pathname;
return 0;
}
-rtems_device_driver rtems_ftp_control(
- rtems_device_major_number major,
- rtems_device_minor_number minor,
- void *pargp
+static int rtems_ftpfs_eval_for_make(
+ const char *pathname,
+ rtems_filesystem_location_info_t *pathloc,
+ const char **name
)
{
- return RTEMS_NOT_CONFIGURED;
+ rtems_set_errno_and_return_minus_one( EIO);
}
-/*
- * Dummy version to let fopen(xxxx,"w") work properly.
- */
-static int rtems_ftp_ftruncate(
- rtems_libio_t *iop,
- off_t count
+static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
+ rtems_filesystem_location_info_t *pathloc
)
{
- return 0;
+ return RTEMS_FILESYSTEM_MEMORY_FILE;
+}
+
+static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
+
+static int rtems_ftpfs_mount_me(
+ rtems_filesystem_mount_table_entry_t *e
+)
+{
+ /* Set handler and oparations table */
+ e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
+ e->mt_fs_root.ops = &rtems_ftpfs_ops;
+
+ /* We have no FTP file system specific data to maintain */
+ e->fs_info = NULL;
+
+ /* We maintain no real file system nodes, so there is no real root */
+ e->mt_fs_root.node_access = NULL;
+
+ /* Just use the limits from IMFS */
+ e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
+
+ return 0;
}
-rtems_filesystem_node_types_t rtems_ftp_node_type(
- rtems_filesystem_location_info_t *pathloc /* IN */
+/*
+ * 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
+ * permissions for every one. The node index uses a global counter to support
+ * a remote to remote copy. Is not a very sophisticated method.
+ */
+static int rtems_ftpfs_fstat(
+ rtems_filesystem_location_info_t *loc,
+ struct stat *st
)
{
- return RTEMS_FILESYSTEM_MEMORY_FILE;
+ static unsigned ino = 0;
+
+ memset( st, 0, sizeof( *st));
+
+ /* FIXME */
+ st->st_ino = ++ino;
+
+ st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ return 0;
}
-rtems_filesystem_operations_table rtems_ftp_ops = {
- rtems_ftp_eval_path, /* eval_path */
- rtems_ftp_evaluate_for_make, /* evaluate_for_make */
- NULL, /* link */
- NULL, /* unlink */
- rtems_ftp_node_type, /* node_type */
- NULL, /* mknod */
- NULL, /* chown */
- NULL, /* freenodinfo */
- NULL, /* mount */
- rtems_ftp_mount_me, /* initialize */
- NULL, /* unmount */
- NULL, /* fsunmount */
- NULL, /* utime, */
- NULL, /* evaluate_link */
- NULL, /* symlink */
- NULL, /* readlin */
+const rtems_filesystem_operations_table rtems_ftpfs_ops = {
+ .evalpath_h = rtems_ftpfs_eval_path,
+ .evalformake_h = rtems_ftpfs_eval_for_make,
+ .link_h = NULL,
+ .unlink_h = NULL,
+ .node_type_h = rtems_ftpfs_node_type,
+ .mknod_h = NULL,
+ .chown_h = NULL,
+ .freenod_h = NULL,
+ .mount_h = NULL,
+ .fsmount_me_h = rtems_ftpfs_mount_me,
+ .unmount_h = NULL,
+ .fsunmount_me_h = NULL,
+ .utime_h = NULL,
+ .eval_link_h = NULL,
+ .symlink_h = NULL,
+ .readlink_h = NULL
};
-
-rtems_filesystem_file_handlers_r rtems_ftp_handlers = {
- rtems_ftp_open, /* open */
- rtems_ftp_close, /* close */
- rtems_ftp_read, /* read */
- rtems_ftp_write, /* write */
- NULL, /* ioctl */
- NULL, /* lseek */
- NULL, /* fstat */
- NULL, /* fchmod */
- rtems_ftp_ftruncate, /* ftruncate */
- NULL, /* fpathconf */
- NULL, /* fsync */
- NULL, /* fdatasync */
- NULL, /* fcntl */
- NULL /* rmnod */
+
+static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
+ .open_h = rtems_ftpfs_open,
+ .close_h = rtems_ftpfs_close,
+ .read_h = rtems_ftpfs_read,
+ .write_h = rtems_ftpfs_write,
+ .ioctl_h = NULL,
+ .lseek_h = NULL,
+ .fstat_h = rtems_ftpfs_fstat,
+ .fchmod_h = NULL,
+ .ftruncate_h = rtems_ftpfs_ftruncate,
+ .fpathconf_h = NULL,
+ .fsync_h = NULL,
+ .fdatasync_h = NULL,
+ .fcntl_h = NULL,
+ .rmnod_h = NULL
};
diff --git a/cpukit/libnetworking/rtems/ftpfs.h b/cpukit/libnetworking/rtems/ftpfs.h
index e0592394f7..d3275ad4c1 100644
--- a/cpukit/libnetworking/rtems/ftpfs.h
+++ b/cpukit/libnetworking/rtems/ftpfs.h
@@ -1,12 +1,17 @@
/**
- * @file rtems/ftpfs.h
+ * @file
*
- * File Transfer Protocol client declarations
- *
- * Transfer file to/from remote host
+ * @brief File Transfer Protocol file system (FTP client).
*/
/*
+ * Copyright (c) 2009
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
* (c) Copyright 2002
* Thomas Doerfler
* IMD Ingenieurbuero fuer Microcomputertechnik
@@ -14,26 +19,115 @@
* 82178 Puchheim, Germany
* <Thomas.Doerfler@imd-systems.de>
*
- * This code has been created after closly inspecting
- * "tftpdriver.c" from Eric Norum.
+ * Modified by Sebastian Huber <sebastian.huber@embedded-brains.de>.
+ *
+ * This code has been created after closly inspecting "tftpdriver.c" from Eric
+ * Norum.
*
- * $Id$
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * $Id$
*/
#ifndef _RTEMS_FTPFS_H
#define _RTEMS_FTPFS_H
+#include <rtems/libio.h>
+
#ifdef __cplusplus
extern "C" {
#endif
-#include <rtems/libio.h>
+/**
+ * @defgroup rtems_ftpfs File Transfer Protocol File System
+ *
+ * The FTP file system (FTP client) can be used to transfer files from or to
+ * remote hosts.
+ *
+ * You can mount the FTP file system with a call to rtems_ftpfs_mount().
+ * Alternatively you can use mount() with the @ref rtems_ftpfs_ops operations
+ * table.
+ *
+ * You can open files either read-only or write-only. A seek is not allowed.
+ * A close terminates the control and data connections.
+ *
+ * To open a file @c file.txt in the directory @c dir (relative to home
+ * directory of the server) on a server named @c host using the user name
+ * @c user and the password @c pw you must specify the following path:
+ *
+ * @c /FTP/user:pw@host/dir/file.txt
+ *
+ * If the server is the default server specified in BOOTP, it can be ommitted:
+ *
+ * @c /FTP/user:pw/dir/file.txt
+ *
+ * The user name will be used for the password if it is ommitted:
+ *
+ * @c /FTP/user@host/dir/file.txt
+ *
+ * For the data transfer passive (= default) and active (= fallback) mode are
+ * supported.
+ *
+ * @{
+ */
- /* create mount point and mount ftp file system */
-extern int rtems_bsdnet_initialize_ftp_filesystem (void);
+/**
+ * @brief Well-known port number for FTP control connection.
+ */
+#define RTEMS_FTPFS_CTRL_PORT 21
- /* FTP File sysem operations table. */
-extern rtems_filesystem_operations_table rtems_ftp_ops;
+/**
+ * @brief Default mount point for FTP file system.
+ */
+#define RTEMS_FTPFS_MOUNT_POINT_DEFAULT "/FTP"
+
+/**
+ * @brief FTP file system operations table.
+ */
+extern const rtems_filesystem_operations_table rtems_ftpfs_ops;
+
+/**
+ * @brief Creates the mount point @a mount_point and mounts the FTP file
+ * system.
+ *
+ * If @a mount_point is @c NULL the default mount point
+ * @ref RTEMS_FTPFS_MOUNT_POINT_DEFAULT will be used.
+ *
+ * It is mounted with read and write access.
+ *
+ * @note The parent directories of the mount point have to exist.
+ */
+rtems_status_code rtems_ftpfs_mount( const char *mount_point);
+
+/**
+ * @brief Enables or disables the verbose mode if @a verbose is @c true or
+ * @c false respectively.
+ *
+ * In the enabled verbose mode the commands and replies of the FTP control
+ * connections will be printed to standard error.
+ */
+rtems_status_code rtems_ftpfs_set_verbose( bool verbose);
+
+/**
+ * @brief Returns in @a verbose if the verbose mode is enabled or disabled.
+ */
+rtems_status_code rtems_ftpfs_get_verbose( bool *verbose);
+
+/** @} */
+
+/**
+ * @brief Creates the default mount point @ref RTEMS_FTPFS_MOUNT_POINT_DEFAULT
+ * and mounts the FTP file system.
+ *
+ * It is mounted with read and write access.
+ *
+ * On success, zero is returned. On error, -1 is returned.
+ *
+ * @deprecated Use rtems_ftpfs_mount() instead.
+ */
+int rtems_bsdnet_initialize_ftp_filesystem( void);
#ifdef __cplusplus
}