summaryrefslogblamecommitdiffstats
path: root/cpukit/libnetworking/lib/ftpfs.c
blob: 0d2f7423130b4870c33d4e67fa609b4dd0895cc7 (plain) (tree)










































                                                                              
                     



























































































































































                                                                             
                                                  





















































































































































                                                                             
                                       
























                                                                             
                                       






























                                                                               
                                         































                                                                
                                       















































































                                                                      

                      







                                                 

                     




                                      
                  
                          
































































































                                                                                     
                                              
                            





























































































































































                                                                               
                                                                                                      

































































































































































                                                                             
                       

                        
                      





                        
                  










































                                                    
                        

                        
                      





                        
                  




































































































































                                                                              
/*
 * 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 <inttypes.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 (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_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+1);
      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+1);
      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+1);
	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+1);
      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,
  uint32_t       flag,
  uint32_t       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];
  uint32_t   my_ip;
  uint16_t   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 = 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;
    }
    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) {
	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;
      }
    }
  }
  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%" 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);
    }
  }
  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
 */
ssize_t rtems_ftp_read(
  rtems_libio_t *iop,
  void          *buffer,
  size_t         count
)
{
  int eno = 0;
  struct ftpStream *fsp;
  size_t want_cnt;
  ssize_t rd_cnt;
  int msg_tmp = 0;

  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;
}

ssize_t rtems_ftp_write(
  rtems_libio_t *iop,
  const void    *buffer,
  size_t         count
)
{
  int eno = EIO;
  struct ftpStream *fsp;
  size_t want_cnt;
  ssize_t wr_cnt;
  int msg_tmp = 0;

  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 */
};