summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2002-06-27 21:59:23 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2002-06-27 21:59:23 +0000
commitdda0bffc4112a544ede2dca0e9e409171b0c3dc2 (patch)
treecb2910469708376fb8e4223f238f1404bfec3194
parentb335e9efabd9d482f4162d04f52c983d74f7104c (diff)
downloadrtems-dda0bffc4112a544ede2dca0e9e409171b0c3dc2.tar.bz2
2002-06-27 Thomas Doerfler <Thomas.Doerfler@imd-systems.de>
* Addition of a ftp client filesystem, comparable to the tftp one. * lib/ftpfs.c, rtems/ftpfs.h: New files. * lib/Makefile.am, rtems/Makefile.am: Modified to reflect above.
Diffstat (limited to '')
-rw-r--r--c/src/exec/libnetworking/ChangeLog6
-rw-r--r--c/src/exec/libnetworking/lib/Makefile.am2
-rw-r--r--c/src/exec/libnetworking/lib/ftpfs.c1159
-rw-r--r--c/src/exec/libnetworking/rtems/Makefile.am2
-rw-r--r--c/src/exec/libnetworking/rtems/ftpfs.h34
-rw-r--r--cpukit/libnetworking/ChangeLog6
-rw-r--r--cpukit/libnetworking/lib/Makefile.am2
-rw-r--r--cpukit/libnetworking/lib/ftpfs.c1159
-rw-r--r--cpukit/libnetworking/rtems/Makefile.am2
-rw-r--r--cpukit/libnetworking/rtems/ftpfs.h34
10 files changed, 2402 insertions, 4 deletions
diff --git a/c/src/exec/libnetworking/ChangeLog b/c/src/exec/libnetworking/ChangeLog
index 44b2d829aa..7dfebd7f4c 100644
--- a/c/src/exec/libnetworking/ChangeLog
+++ b/c/src/exec/libnetworking/ChangeLog
@@ -1,3 +1,9 @@
+2002-06-27 Thomas Doerfler <Thomas.Doerfler@imd-systems.de>
+
+ * Addition of a ftp client filesystem, comparable to the tftp one.
+ * lib/ftpfs.c, rtems/ftpfs.h: New files.
+ * lib/Makefile.am, rtems/Makefile.am: Modified to reflect above.
+
2002-06-27 Ralf Corsepius <corsepiu@faw.uni-ulm.de>
* configure.ac: Use AC_CONFIG_AUX_DIR(../..).
diff --git a/c/src/exec/libnetworking/lib/Makefile.am b/c/src/exec/libnetworking/lib/Makefile.am
index e0a2a6de7d..a1cea1841b 100644
--- a/c/src/exec/libnetworking/lib/Makefile.am
+++ b/c/src/exec/libnetworking/lib/Makefile.am
@@ -6,7 +6,7 @@
LIBNAME = lib.a
LIB = $(ARCH)/$(LIBNAME)
-C_FILES = getprotoby.c rtems_bsdnet_ntp.c syslog.c tftpDriver.c
+C_FILES = getprotoby.c rtems_bsdnet_ntp.c ftpfs.c syslog.c tftpDriver.c
C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o)
OBJS = $(C_O_FILES)
diff --git a/c/src/exec/libnetworking/lib/ftpfs.c b/c/src/exec/libnetworking/lib/ftpfs.c
new file mode 100644
index 0000000000..033c1b6bdf
--- /dev/null
+++ b/c/src/exec/libnetworking/lib/ftpfs.c
@@ -0,0 +1,1159 @@
+/*
+ * 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
+ *
+ * WARNING: write accesses have not yet been tested.
+ *
+ *
+ * (c) Copyright 2002
+ * Thomas Doerfler
+ * IMD Ingenieurbuero fuer Microcomputertechnik
+ * Herbststr. 8
+ * 82178 Puchheim, Germany
+ * <Thomas.Doerfler@imd-systems.de>
+ *
+ * This code has been created after closly inspecting
+ * "tftpdriver.c" from Eric Norum.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <rtems.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>
+
+
+#ifndef set_errno_and_return_minus_one
+#define set_errno_and_return_minus_one( _error ) \
+ do { errno = (_error); return -1; } while(0)
+#endif
+
+/* #define DEBUG_OUT */
+
+/*
+ * Well-known port for FTP
+ */
+#define FTP_PORT_NUM 21
+
+/*
+ * Pathname prefix
+ */
+#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
+ */
+ int ctrl_socket;
+ struct sockaddr_in myCtrlAddress;
+ struct sockaddr_in farCtrlAddress;
+ /*
+ * Data transfer socket
+ */
+ int port_socket;
+ int data_socket;
+ struct sockaddr_in myDataAddress;
+ struct sockaddr_in farDataAddress;
+ /*
+ * other stuff to remember
+ */
+ boolean eof_reached;
+};
+
+/*
+ * Number of streams open at the same time
+ */
+static rtems_id ftp_mutex;
+static int nStreams;
+static struct ftpStream ** volatile ftpStreams;
+
+extern rtems_filesystem_operations_table rtems_tftp_ops;
+extern rtems_filesystem_file_handlers_r rtems_tftp_handlers;
+
+/*
+ * Direct copy from the IMFS/TFTP. Look at this.
+ */
+
+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 */
+};
+
+int rtems_ftp_mount_me(
+ rtems_filesystem_mount_table_entry_t *temp_mt_entry
+)
+{
+ rtems_status_code sc;
+
+ temp_mt_entry->mt_fs_root.handlers = &rtems_ftp_handlers;
+ temp_mt_entry->mt_fs_root.ops = &rtems_ftp_ops;
+
+ /*
+ * 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.
+ */
+
+ temp_mt_entry->fs_info = NULL;
+ temp_mt_entry->mt_fs_root.node_access = NULL;
+
+ /*
+ * These need to be looked at for full POSIX semantics.
+ */
+
+ temp_mt_entry->pathconf_limits_and_options = rtems_ftp_limits_and_options;
+
+
+ /*
+ * 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 );
+
+ return 0;
+}
+
+/*
+ * Initialize the FTP driver
+ */
+
+int rtems_bsdnet_initialize_ftp_filesystem ()
+{
+ 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_ONLY,
+ NULL,
+ FTP_PATHNAME_PREFIX
+ );
+
+ if ( status )
+ perror( "FTP mount failed" );
+
+ return status;
+}
+
+/*
+ * 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 */
+)
+{
+ char rd_buffer[4];
+ size_t rd_size;
+ size_t tmp_size;
+ int eno = 0;
+ rtems_boolean 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;
+}
+
+/*
+ * 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;
+
+ /*
+ * 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;
+ }
+ else {
+ chunk_start = pathname + strlen (FTP_PATHNAME_PREFIX);
+ }
+ }
+#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;
+ }
+ else {
+ chunk_len = chunk_end-chunk_start;
+ *usernamep = malloc(chunk_len);
+ if (*usernamep == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*usernamep,chunk_start,chunk_len);
+ (*usernamep)[chunk_len] = '\0';
+ }
+ }
+ }
+ /*
+ * 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);
+ if (*passwordp == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*passwordp,chunk_start,chunk_len);
+ (*passwordp)[chunk_len] = '\0';
+ }
+ }
+ }
+ /*
+ * 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 {
+ /*
+ * we have found a proper '/'
+ */
+ chunk_len = chunk_end-chunk_start;
+ *hostnamep = malloc(chunk_len);
+ if (*hostnamep == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*hostnamep,chunk_start,chunk_len);
+ (*hostnamep)[chunk_len] = '\0';
+ }
+ }
+ }
+ 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);
+ if (*filenamep == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*filenamep,chunk_start,chunk_len);
+ (*filenamep)[chunk_len] = '\0';
+ }
+ }
+ }
+
+ /*
+ * 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;
+ }
+ }
+ return rc;
+}
+
+int rtems_ftp_evaluate_for_make(
+ const char *path, /* IN */
+ rtems_filesystem_location_info_t *pathloc, /* IN/OUT */
+ const char **name /* OUT */
+)
+{
+ set_errno_and_return_minus_one( EIO );
+}
+
+/*
+ * XXX - Fix return values.
+ */
+
+int rtems_ftp_eval_path(
+ const char *pathname, /* IN */
+ int flags, /* IN */
+ rtems_filesystem_location_info_t *pathloc /* IN/OUT */
+)
+{
+
+ /*
+ * Read-only for now
+ */
+
+ if ( ((flags & O_RDONLY) != O_RDONLY ) &&
+ ((flags & O_WRONLY) != O_WRONLY )) {
+ set_errno_and_return_minus_one( ENOENT );
+ }
+ /*
+ * 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;
+
+ return 0;
+}
+
+/*
+ * Open a FTP stream
+ */
+int rtems_ftp_open(
+ rtems_libio_t *iop,
+ const char *new_name,
+ unsigned32 flag,
+ unsigned32 mode
+)
+{
+ 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];
+ rtems_unsigned32 my_ip;
+ rtems_unsigned16 my_port;
+ int eno = 0;
+ rtems_status_code rc;
+ rtems_boolean is_write = FALSE;
+ rtems_boolean sema_obtained = FALSE;
+ struct ftpStream *fsp = NULL;
+ int msg_tmp;
+ int 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;
+ }
+ else {
+ eno = EBUSY;
+ }
+ }
+ 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;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ if (eno == 0) {
+ /*
+ * Create the socket for control connection
+ */
+ if ((fsp->ctrl_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
+ eno = ENOMEM;
+ }
+ }
+
+ 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;
+ struct hostent *gethostbyname(const char *name);
+
+ hent = gethostbyname(hostname);
+ if (hent != NULL) {
+ bcopy(hent->h_addr,
+ (char *)(&(fsp->farCtrlAddress.sin_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;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ 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;
+ }
+ }
+ 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);
+ }
+ }
+ }
+ }
+ 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 */
+ }
+ }
+ 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);
+ }
+ }
+ if (eno == 0) {
+ /*
+ * check for a "BINARY TYPE command successful" reply
+ */
+ if (msg_tmp != FTP_REPLY_SUCCESS) {
+ eno = EIO;
+ }
+ }
+ 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;
+ }
+ }
+ }
+ }
+ 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%u,%u,%u,%u,%u,%u\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);
+ }
+ }
+ if (eno == 0) {
+ /*
+ * check for a "PORT command successful" reply
+ */
+ if (msg_tmp != FTP_REPLY_SUCCESS) {
+ eno = EBUSY;
+ }
+ }
+ /*
+ * prepare port socket to listen for incoming connections
+ */
+ if (eno == 0) {
+ if (0 > listen(fsp->port_socket,1)) {
+ eno = EBUSY;
+ }
+ }
+ 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;
+ }
+ }
+ 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);
+ }
+ if (eno == 0) {
+ /*
+ * check for a "OPENING binary connection" reply
+ */
+ if (msg_tmp != FTP_REPLY_OPENCONN) {
+ eno = EACCES;
+ }
+ }
+#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;
+ }
+ }
+ /*
+ * FIXME: check, that fardataAddr is really from our ftp server
+ */
+
+ /*
+ * clean up temp strings...
+ */
+ if (uname != NULL) {
+ free(uname);
+ uname = NULL;
+ }
+ if (upass != NULL) {
+ free(upass);
+ upass = NULL;
+ }
+ if (hostname != NULL) {
+ free(hostname);
+ hostname = NULL;
+ }
+ if (filename != NULL) {
+ free(filename);
+ filename = NULL;
+ }
+ /*
+ * close part socket, no longer needed
+ */
+ if (fsp->port_socket != -1) {
+ close(fsp->port_socket);
+ fsp->port_socket = -1;
+ }
+ /*
+ * if error, clean up everything
+ */
+ 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;
+ }
+ }
+ /*
+ * return sema, if still occupied
+ */
+ if (sema_obtained) {
+ rtems_semaphore_release (ftp_mutex);
+ sema_obtained = FALSE;
+ }
+#if 0
+ if (eno != 0) {
+ set_errno_and_return_minus_one(eno);
+ }
+ return 0;
+#else
+ return eno;
+#endif
+}
+
+/*
+ * Read from a FTP stream
+ */
+int rtems_ftp_read(
+ rtems_libio_t *iop,
+ void *buffer,
+ unsigned32 count
+)
+{
+ int eno = 0;
+ struct ftpStream *fsp;
+ size_t want_cnt;
+ ssize_t rd_cnt;
+ int msg_tmp;
+
+ 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 (eno != 0) {
+ set_errno_and_return_minus_one(eno);
+ }
+ return count - want_cnt;
+}
+
+int rtems_ftp_write(
+ rtems_libio_t *iop,
+ const void *buffer,
+ unsigned32 count
+)
+{
+ int eno = EIO;
+ struct ftpStream *fsp;
+ size_t want_cnt;
+ ssize_t wr_cnt;
+ int msg_tmp;
+
+ 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 (eno != 0) {
+ set_errno_and_return_minus_one(eno);
+ }
+ return count - want_cnt;
+}
+
+/*
+ * Close a FTP stream
+ */
+int rtems_ftp_close(
+ rtems_libio_t *iop
+)
+{
+ int s = iop->data0;
+ struct ftpStream *fsp = iop->data1;
+
+ /*
+ * 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...
+ */
+ rtems_semaphore_obtain (ftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ free (ftpStreams[s]);
+ ftpStreams[s] = NULL;
+ rtems_semaphore_release (ftp_mutex);
+
+ return 0;
+}
+
+rtems_device_driver rtems_ftp_control(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ return RTEMS_NOT_CONFIGURED;
+}
+
+/*
+ * Dummy version to let fopen(xxxx,"w") work properly.
+ */
+static int rtems_ftp_ftruncate(
+ rtems_libio_t *iop,
+ off_t count
+)
+{
+ return 0;
+}
+
+rtems_filesystem_node_types_t rtems_ftp_node_type(
+ rtems_filesystem_location_info_t *pathloc /* IN */
+)
+{
+ return RTEMS_FILESYSTEM_MEMORY_FILE;
+}
+
+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 */
+};
+
+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 */
+};
diff --git a/c/src/exec/libnetworking/rtems/Makefile.am b/c/src/exec/libnetworking/rtems/Makefile.am
index f0d70abd6b..bce1f51071 100644
--- a/c/src/exec/libnetworking/rtems/Makefile.am
+++ b/c/src/exec/libnetworking/rtems/Makefile.am
@@ -16,7 +16,7 @@ C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o)
OBJS = $(C_O_FILES)
include_rtems_HEADERS = rtems_bsdnet.h rtems_bsdnet_internal.h tftp.h \
- mkrootfs.h
+ ftpfs.h mkrootfs.h
PREINSTALL_FILES = $(PROJECT_INCLUDE)/rtems \
$(include_rtems_HEADERS:%=$(PROJECT_INCLUDE)/rtems/%)
diff --git a/c/src/exec/libnetworking/rtems/ftpfs.h b/c/src/exec/libnetworking/rtems/ftpfs.h
new file mode 100644
index 0000000000..bcd18a4266
--- /dev/null
+++ b/c/src/exec/libnetworking/rtems/ftpfs.h
@@ -0,0 +1,34 @@
+/*
+ * File Transfer Protocol client declarations
+ *
+ * Transfer file to/from remote host
+ *
+ *
+ * (c) Copyright 2002
+ * Thomas Doerfler
+ * IMD Ingenieurbuero fuer Microcomputertechnik
+ * Herbststr. 8
+ * 82178 Puchheim, Germany
+ * <Thomas.Doerfler@imd-systems.de>
+ *
+ * This code has been created after closly inspecting
+ * "tftpdriver.c" from Eric Norum.
+ *
+ * $Id$
+ */
+
+#ifndef _FTP_DRIVER_H
+#define _FTP_DRIVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* create mount point and mount ftp file system */
+ int rtems_bsdnet_initialize_ftp_filesystem () ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/cpukit/libnetworking/ChangeLog b/cpukit/libnetworking/ChangeLog
index 44b2d829aa..7dfebd7f4c 100644
--- a/cpukit/libnetworking/ChangeLog
+++ b/cpukit/libnetworking/ChangeLog
@@ -1,3 +1,9 @@
+2002-06-27 Thomas Doerfler <Thomas.Doerfler@imd-systems.de>
+
+ * Addition of a ftp client filesystem, comparable to the tftp one.
+ * lib/ftpfs.c, rtems/ftpfs.h: New files.
+ * lib/Makefile.am, rtems/Makefile.am: Modified to reflect above.
+
2002-06-27 Ralf Corsepius <corsepiu@faw.uni-ulm.de>
* configure.ac: Use AC_CONFIG_AUX_DIR(../..).
diff --git a/cpukit/libnetworking/lib/Makefile.am b/cpukit/libnetworking/lib/Makefile.am
index e0a2a6de7d..a1cea1841b 100644
--- a/cpukit/libnetworking/lib/Makefile.am
+++ b/cpukit/libnetworking/lib/Makefile.am
@@ -6,7 +6,7 @@
LIBNAME = lib.a
LIB = $(ARCH)/$(LIBNAME)
-C_FILES = getprotoby.c rtems_bsdnet_ntp.c syslog.c tftpDriver.c
+C_FILES = getprotoby.c rtems_bsdnet_ntp.c ftpfs.c syslog.c tftpDriver.c
C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o)
OBJS = $(C_O_FILES)
diff --git a/cpukit/libnetworking/lib/ftpfs.c b/cpukit/libnetworking/lib/ftpfs.c
new file mode 100644
index 0000000000..033c1b6bdf
--- /dev/null
+++ b/cpukit/libnetworking/lib/ftpfs.c
@@ -0,0 +1,1159 @@
+/*
+ * 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
+ *
+ * WARNING: write accesses have not yet been tested.
+ *
+ *
+ * (c) Copyright 2002
+ * Thomas Doerfler
+ * IMD Ingenieurbuero fuer Microcomputertechnik
+ * Herbststr. 8
+ * 82178 Puchheim, Germany
+ * <Thomas.Doerfler@imd-systems.de>
+ *
+ * This code has been created after closly inspecting
+ * "tftpdriver.c" from Eric Norum.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <rtems.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>
+
+
+#ifndef set_errno_and_return_minus_one
+#define set_errno_and_return_minus_one( _error ) \
+ do { errno = (_error); return -1; } while(0)
+#endif
+
+/* #define DEBUG_OUT */
+
+/*
+ * Well-known port for FTP
+ */
+#define FTP_PORT_NUM 21
+
+/*
+ * Pathname prefix
+ */
+#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
+ */
+ int ctrl_socket;
+ struct sockaddr_in myCtrlAddress;
+ struct sockaddr_in farCtrlAddress;
+ /*
+ * Data transfer socket
+ */
+ int port_socket;
+ int data_socket;
+ struct sockaddr_in myDataAddress;
+ struct sockaddr_in farDataAddress;
+ /*
+ * other stuff to remember
+ */
+ boolean eof_reached;
+};
+
+/*
+ * Number of streams open at the same time
+ */
+static rtems_id ftp_mutex;
+static int nStreams;
+static struct ftpStream ** volatile ftpStreams;
+
+extern rtems_filesystem_operations_table rtems_tftp_ops;
+extern rtems_filesystem_file_handlers_r rtems_tftp_handlers;
+
+/*
+ * Direct copy from the IMFS/TFTP. Look at this.
+ */
+
+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 */
+};
+
+int rtems_ftp_mount_me(
+ rtems_filesystem_mount_table_entry_t *temp_mt_entry
+)
+{
+ rtems_status_code sc;
+
+ temp_mt_entry->mt_fs_root.handlers = &rtems_ftp_handlers;
+ temp_mt_entry->mt_fs_root.ops = &rtems_ftp_ops;
+
+ /*
+ * 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.
+ */
+
+ temp_mt_entry->fs_info = NULL;
+ temp_mt_entry->mt_fs_root.node_access = NULL;
+
+ /*
+ * These need to be looked at for full POSIX semantics.
+ */
+
+ temp_mt_entry->pathconf_limits_and_options = rtems_ftp_limits_and_options;
+
+
+ /*
+ * 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 );
+
+ return 0;
+}
+
+/*
+ * Initialize the FTP driver
+ */
+
+int rtems_bsdnet_initialize_ftp_filesystem ()
+{
+ 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_ONLY,
+ NULL,
+ FTP_PATHNAME_PREFIX
+ );
+
+ if ( status )
+ perror( "FTP mount failed" );
+
+ return status;
+}
+
+/*
+ * 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 */
+)
+{
+ char rd_buffer[4];
+ size_t rd_size;
+ size_t tmp_size;
+ int eno = 0;
+ rtems_boolean 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;
+}
+
+/*
+ * 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;
+
+ /*
+ * 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;
+ }
+ else {
+ chunk_start = pathname + strlen (FTP_PATHNAME_PREFIX);
+ }
+ }
+#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;
+ }
+ else {
+ chunk_len = chunk_end-chunk_start;
+ *usernamep = malloc(chunk_len);
+ if (*usernamep == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*usernamep,chunk_start,chunk_len);
+ (*usernamep)[chunk_len] = '\0';
+ }
+ }
+ }
+ /*
+ * 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);
+ if (*passwordp == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*passwordp,chunk_start,chunk_len);
+ (*passwordp)[chunk_len] = '\0';
+ }
+ }
+ }
+ /*
+ * 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 {
+ /*
+ * we have found a proper '/'
+ */
+ chunk_len = chunk_end-chunk_start;
+ *hostnamep = malloc(chunk_len);
+ if (*hostnamep == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*hostnamep,chunk_start,chunk_len);
+ (*hostnamep)[chunk_len] = '\0';
+ }
+ }
+ }
+ 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);
+ if (*filenamep == NULL) {
+ rc = ENOMEM;
+ }
+ else {
+ memcpy(*filenamep,chunk_start,chunk_len);
+ (*filenamep)[chunk_len] = '\0';
+ }
+ }
+ }
+
+ /*
+ * 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;
+ }
+ }
+ return rc;
+}
+
+int rtems_ftp_evaluate_for_make(
+ const char *path, /* IN */
+ rtems_filesystem_location_info_t *pathloc, /* IN/OUT */
+ const char **name /* OUT */
+)
+{
+ set_errno_and_return_minus_one( EIO );
+}
+
+/*
+ * XXX - Fix return values.
+ */
+
+int rtems_ftp_eval_path(
+ const char *pathname, /* IN */
+ int flags, /* IN */
+ rtems_filesystem_location_info_t *pathloc /* IN/OUT */
+)
+{
+
+ /*
+ * Read-only for now
+ */
+
+ if ( ((flags & O_RDONLY) != O_RDONLY ) &&
+ ((flags & O_WRONLY) != O_WRONLY )) {
+ set_errno_and_return_minus_one( ENOENT );
+ }
+ /*
+ * 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;
+
+ return 0;
+}
+
+/*
+ * Open a FTP stream
+ */
+int rtems_ftp_open(
+ rtems_libio_t *iop,
+ const char *new_name,
+ unsigned32 flag,
+ unsigned32 mode
+)
+{
+ 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];
+ rtems_unsigned32 my_ip;
+ rtems_unsigned16 my_port;
+ int eno = 0;
+ rtems_status_code rc;
+ rtems_boolean is_write = FALSE;
+ rtems_boolean sema_obtained = FALSE;
+ struct ftpStream *fsp = NULL;
+ int msg_tmp;
+ int 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;
+ }
+ else {
+ eno = EBUSY;
+ }
+ }
+ 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;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ if (eno == 0) {
+ /*
+ * Create the socket for control connection
+ */
+ if ((fsp->ctrl_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
+ eno = ENOMEM;
+ }
+ }
+
+ 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;
+ struct hostent *gethostbyname(const char *name);
+
+ hent = gethostbyname(hostname);
+ if (hent != NULL) {
+ bcopy(hent->h_addr,
+ (char *)(&(fsp->farCtrlAddress.sin_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;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ 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;
+ }
+ }
+ 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);
+ }
+ }
+ }
+ }
+ 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 */
+ }
+ }
+ 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);
+ }
+ }
+ if (eno == 0) {
+ /*
+ * check for a "BINARY TYPE command successful" reply
+ */
+ if (msg_tmp != FTP_REPLY_SUCCESS) {
+ eno = EIO;
+ }
+ }
+ 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;
+ }
+ }
+ }
+ }
+ 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%u,%u,%u,%u,%u,%u\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);
+ }
+ }
+ if (eno == 0) {
+ /*
+ * check for a "PORT command successful" reply
+ */
+ if (msg_tmp != FTP_REPLY_SUCCESS) {
+ eno = EBUSY;
+ }
+ }
+ /*
+ * prepare port socket to listen for incoming connections
+ */
+ if (eno == 0) {
+ if (0 > listen(fsp->port_socket,1)) {
+ eno = EBUSY;
+ }
+ }
+ 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;
+ }
+ }
+ 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);
+ }
+ if (eno == 0) {
+ /*
+ * check for a "OPENING binary connection" reply
+ */
+ if (msg_tmp != FTP_REPLY_OPENCONN) {
+ eno = EACCES;
+ }
+ }
+#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;
+ }
+ }
+ /*
+ * FIXME: check, that fardataAddr is really from our ftp server
+ */
+
+ /*
+ * clean up temp strings...
+ */
+ if (uname != NULL) {
+ free(uname);
+ uname = NULL;
+ }
+ if (upass != NULL) {
+ free(upass);
+ upass = NULL;
+ }
+ if (hostname != NULL) {
+ free(hostname);
+ hostname = NULL;
+ }
+ if (filename != NULL) {
+ free(filename);
+ filename = NULL;
+ }
+ /*
+ * close part socket, no longer needed
+ */
+ if (fsp->port_socket != -1) {
+ close(fsp->port_socket);
+ fsp->port_socket = -1;
+ }
+ /*
+ * if error, clean up everything
+ */
+ 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;
+ }
+ }
+ /*
+ * return sema, if still occupied
+ */
+ if (sema_obtained) {
+ rtems_semaphore_release (ftp_mutex);
+ sema_obtained = FALSE;
+ }
+#if 0
+ if (eno != 0) {
+ set_errno_and_return_minus_one(eno);
+ }
+ return 0;
+#else
+ return eno;
+#endif
+}
+
+/*
+ * Read from a FTP stream
+ */
+int rtems_ftp_read(
+ rtems_libio_t *iop,
+ void *buffer,
+ unsigned32 count
+)
+{
+ int eno = 0;
+ struct ftpStream *fsp;
+ size_t want_cnt;
+ ssize_t rd_cnt;
+ int msg_tmp;
+
+ 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 (eno != 0) {
+ set_errno_and_return_minus_one(eno);
+ }
+ return count - want_cnt;
+}
+
+int rtems_ftp_write(
+ rtems_libio_t *iop,
+ const void *buffer,
+ unsigned32 count
+)
+{
+ int eno = EIO;
+ struct ftpStream *fsp;
+ size_t want_cnt;
+ ssize_t wr_cnt;
+ int msg_tmp;
+
+ 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 (eno != 0) {
+ set_errno_and_return_minus_one(eno);
+ }
+ return count - want_cnt;
+}
+
+/*
+ * Close a FTP stream
+ */
+int rtems_ftp_close(
+ rtems_libio_t *iop
+)
+{
+ int s = iop->data0;
+ struct ftpStream *fsp = iop->data1;
+
+ /*
+ * 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...
+ */
+ rtems_semaphore_obtain (ftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ free (ftpStreams[s]);
+ ftpStreams[s] = NULL;
+ rtems_semaphore_release (ftp_mutex);
+
+ return 0;
+}
+
+rtems_device_driver rtems_ftp_control(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ return RTEMS_NOT_CONFIGURED;
+}
+
+/*
+ * Dummy version to let fopen(xxxx,"w") work properly.
+ */
+static int rtems_ftp_ftruncate(
+ rtems_libio_t *iop,
+ off_t count
+)
+{
+ return 0;
+}
+
+rtems_filesystem_node_types_t rtems_ftp_node_type(
+ rtems_filesystem_location_info_t *pathloc /* IN */
+)
+{
+ return RTEMS_FILESYSTEM_MEMORY_FILE;
+}
+
+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 */
+};
+
+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 */
+};
diff --git a/cpukit/libnetworking/rtems/Makefile.am b/cpukit/libnetworking/rtems/Makefile.am
index f0d70abd6b..bce1f51071 100644
--- a/cpukit/libnetworking/rtems/Makefile.am
+++ b/cpukit/libnetworking/rtems/Makefile.am
@@ -16,7 +16,7 @@ C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.o)
OBJS = $(C_O_FILES)
include_rtems_HEADERS = rtems_bsdnet.h rtems_bsdnet_internal.h tftp.h \
- mkrootfs.h
+ ftpfs.h mkrootfs.h
PREINSTALL_FILES = $(PROJECT_INCLUDE)/rtems \
$(include_rtems_HEADERS:%=$(PROJECT_INCLUDE)/rtems/%)
diff --git a/cpukit/libnetworking/rtems/ftpfs.h b/cpukit/libnetworking/rtems/ftpfs.h
new file mode 100644
index 0000000000..bcd18a4266
--- /dev/null
+++ b/cpukit/libnetworking/rtems/ftpfs.h
@@ -0,0 +1,34 @@
+/*
+ * File Transfer Protocol client declarations
+ *
+ * Transfer file to/from remote host
+ *
+ *
+ * (c) Copyright 2002
+ * Thomas Doerfler
+ * IMD Ingenieurbuero fuer Microcomputertechnik
+ * Herbststr. 8
+ * 82178 Puchheim, Germany
+ * <Thomas.Doerfler@imd-systems.de>
+ *
+ * This code has been created after closly inspecting
+ * "tftpdriver.c" from Eric Norum.
+ *
+ * $Id$
+ */
+
+#ifndef _FTP_DRIVER_H
+#define _FTP_DRIVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* create mount point and mount ftp file system */
+ int rtems_bsdnet_initialize_ftp_filesystem () ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif