Index: cpukit/ChangeLog
===================================================================
--- cpukit/ChangeLog	(revision facca98f29f96eb35eb8781a41da79a9ba023d62)
+++ cpukit/ChangeLog	(revision d2ec6433fad6c8ebc942a5bf0142697dcdc7a881)
@@ -1,2 +1,7 @@
+2009-05-07	Sebastian Huber <sebastian.huber@embedded-brains.de>
+
+	* libnetworking/lib/ftpfs.c, libnetworking/rtems/ftpfs.h: Added
+	timeouts.  Options are now per file system instance.
+
 2009-05-06	Joel Sherrill <joel.sherrill@oarcorp.com>
 
Index: cpukit/libnetworking/lib/ftpfs.c
===================================================================
--- cpukit/libnetworking/lib/ftpfs.c	(revision facca98f29f96eb35eb8781a41da79a9ba023d62)
+++ cpukit/libnetworking/lib/ftpfs.c	(revision d2ec6433fad6c8ebc942a5bf0142697dcdc7a881)
@@ -2,5 +2,5 @@
  * @file
  *
- * @brief File Transfer Protocol file system (FTP client).
+ * File Transfer Protocol file system (FTP client).
  */
 
@@ -62,24 +62,68 @@
 
 /**
- * @brief Connection entry for each open file stream.
+ * Connection entry for each open file stream.
  */
 typedef struct {
   /**
-   * @brief Control connection socket.
+   * Control connection socket.
    */
   int ctrl_socket;
 
   /**
-   * @brief Data transfer socket.
+   * Data transfer socket.
    */
   int data_socket;
 
   /**
-   * @brief End of file flag.
+   * End of file flag.
    */
   bool eof;
 } rtems_ftpfs_entry;
 
-static bool rtems_ftpfs_verbose = false;
+/**
+ * Mount entry for each file system instance.
+ */
+typedef struct {
+  /**
+   * Verbose mode enabled or disabled.
+   */
+  bool verbose;
+
+  /**
+   * Timeout value
+   */
+  struct timeval timeout;
+} rtems_ftpfs_mount_entry;
+
+static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
+
+static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers;
+
+static bool rtems_ftpfs_use_timeout( const struct timeval *to)
+{
+  return to->tv_sec != 0 || to->tv_usec != 0;
+}
+
+static int rtems_ftpfs_set_connection_timeout(
+  int socket,
+  const struct timeval *to
+)
+{
+  if (rtems_ftpfs_use_timeout( to)) {
+    int rv = 0;
+    
+    rv = setsockopt( socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof( *to));
+    if (rv != 0) {
+      return EIO;
+    }
+
+    rv = setsockopt( socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof( *to));
+    if (rv != 0) {
+      return EIO;
+    }
+  }
+
+  return 0;
+}
 
 rtems_status_code rtems_ftpfs_mount( const char *mount_point)
@@ -110,20 +154,79 @@
 }
 
-rtems_status_code rtems_ftpfs_set_verbose( bool verbose)
-{
-  rtems_ftpfs_verbose = verbose;
-
-  return RTEMS_SUCCESSFUL;
-}
-
-rtems_status_code rtems_ftpfs_get_verbose( bool *verbose)
-{
-  if (verbose == NULL) {
-    return RTEMS_INVALID_ADDRESS;
-  }
-
-  *verbose = rtems_ftpfs_verbose;
-
-  return RTEMS_SUCCESSFUL;
+static rtems_status_code rtems_ftpfs_do_ioctl(
+  const char *mount_point,
+  int req,
+  ...
+)
+{
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
+  int rv = 0;
+  int fd = 0;
+  va_list ap;
+
+  if (mount_point == NULL) {
+    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
+  }
+
+  fd = open( mount_point, O_RDWR);
+  if (fd < 0) {
+    return RTEMS_INVALID_NAME;
+  }
+  
+  va_start( ap, req);
+  rv = ioctl( fd, req, va_arg( ap, void *));
+  va_end( ap);
+  if (rv != 0) {
+    sc = RTEMS_INVALID_NUMBER;
+  }
+
+  rv = close( fd);
+  if (rv != 0 && sc == RTEMS_SUCCESSFUL) {
+    sc = RTEMS_IO_ERROR;
+  }
+  
+  return sc;
+}
+
+rtems_status_code rtems_ftpfs_get_verbose( const char *mount_point, bool *verbose)
+{
+  return rtems_ftpfs_do_ioctl(
+    mount_point,
+    RTEMS_FTPFS_IOCTL_GET_VERBOSE,
+    verbose
+  );
+}
+
+rtems_status_code rtems_ftpfs_set_verbose( const char *mount_point, bool verbose)
+{
+  return rtems_ftpfs_do_ioctl(
+    mount_point,
+    RTEMS_FTPFS_IOCTL_SET_VERBOSE,
+    &verbose
+  );
+}
+
+rtems_status_code rtems_ftpfs_get_timeout(
+  const char *mount_point,
+  struct timeval *timeout
+)
+{
+  return rtems_ftpfs_do_ioctl(
+    mount_point,
+    RTEMS_FTPFS_IOCTL_GET_TIMEOUT,
+    timeout
+  );
+}
+
+rtems_status_code rtems_ftpfs_set_timeout(
+  const char *mount_point,
+  const struct timeval *timeout
+)
+{
+  return rtems_ftpfs_do_ioctl(
+    mount_point,
+    RTEMS_FTPFS_IOCTL_SET_TIMEOUT,
+    timeout
+  );
 }
 
@@ -170,9 +273,9 @@
   int socket,
   rtems_ftpfs_reply_parser parser,
-  void *parser_arg
+  void *parser_arg,
+  bool verbose
 )
 {
   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' };
@@ -275,9 +378,9 @@
   const char *arg,
   rtems_ftpfs_reply_parser parser,
-  void *parser_arg
+  void *parser_arg,
+  bool verbose
 )
 {
   const char *const eol = "\r\n";
-  bool verbose = rtems_ftpfs_verbose;
   int rv = 0;
 
@@ -312,5 +415,5 @@
 
   /* Return reply */
-  return rtems_ftpfs_get_reply( socket, parser, parser_arg);
+  return rtems_ftpfs_get_reply( socket, parser, parser_arg, verbose);
 }
 
@@ -318,8 +421,16 @@
   int socket,
   const char *cmd,
-  const char *arg
-)
-{
-  return rtems_ftpfs_send_command_with_parser( socket, cmd, arg, NULL, NULL);
+  const char *arg,
+  bool verbose
+)
+{
+  return rtems_ftpfs_send_command_with_parser(
+    socket,
+    cmd,
+    arg,
+    NULL,
+    NULL,
+    verbose
+  );
 }
 
@@ -444,4 +555,6 @@
   int rv = 0;
   rtems_ftpfs_entry *e = iop->data1;
+  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
+  bool verbose = me->verbose;
 
   if (e != NULL) {
@@ -460,5 +573,5 @@
       ) {
         rtems_ftpfs_reply reply =
-          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
+          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL, verbose);
 
         if (reply != RTEMS_FTPFS_REPLY_2) {
@@ -491,8 +604,11 @@
   const char *password,
   const char *hostname,
-  uint32_t *client_address
+  uint32_t *client_address,
+  bool verbose,
+  const struct timeval *timeout
 )
 {
   int rv = 0;
+  int eno = 0;
   rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
   struct in_addr address = { .s_addr = 0 };
@@ -533,4 +649,10 @@
   }
 
+  /* Set control connection timeout */
+  eno = rtems_ftpfs_set_connection_timeout( e->ctrl_socket, timeout);
+  if (eno != 0) {
+    return eno;
+  }
+
   /* Get client address */
   size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
@@ -547,5 +669,5 @@
 
   /* Now we should get a welcome message from the server */
-  reply = rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
+  reply = rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL, verbose);
   if (reply != RTEMS_FTPFS_REPLY_2) {
     return ENOENT;
@@ -553,8 +675,13 @@
 
   /* Send USER command */
-  reply = rtems_ftpfs_send_command( e->ctrl_socket, "USER ", user);
+  reply = rtems_ftpfs_send_command( e->ctrl_socket, "USER ", user, verbose);
   if (reply == RTEMS_FTPFS_REPLY_3) {
     /* Send PASS command */
-    reply = rtems_ftpfs_send_command( e->ctrl_socket, "PASS ", password);
+    reply = rtems_ftpfs_send_command(
+      e->ctrl_socket,
+      "PASS ",
+      password,
+      verbose
+    );
     if (reply != RTEMS_FTPFS_REPLY_2) {
       return EACCES;
@@ -567,5 +694,5 @@
 
   /* Send TYPE command to set binary mode for all data transfers */
-  reply = rtems_ftpfs_send_command( e->ctrl_socket, "TYPE I", NULL);
+  reply = rtems_ftpfs_send_command( e->ctrl_socket, "TYPE I", NULL, verbose);
   if (reply != RTEMS_FTPFS_REPLY_2) {
     return EIO;
@@ -579,5 +706,7 @@
   uint32_t client_address,
   const char *file_command,
-  const char *filename
+  const char *filename,
+  bool verbose,
+  const struct timeval *timeout
 )
 {
@@ -590,9 +719,4 @@
   char port_command [] = "PORT 000,000,000,000,000,000";
   uint16_t data_port = 0;
-  fd_set fds;
-  struct timeval timeout = {
-    .tv_sec = 60,
-    .tv_usec = 0
-  };
 
   /* Create port socket to establish a data data connection */
@@ -640,5 +764,10 @@
     (data_port >> 0) & 0xffUL
   );
-  reply = rtems_ftpfs_send_command( e->ctrl_socket, port_command, NULL);
+  reply = rtems_ftpfs_send_command(
+    e->ctrl_socket,
+    port_command,
+    NULL,
+    verbose
+  );
   if (reply != RTEMS_FTPFS_REPLY_2) {
     eno = ENOTSUP;
@@ -654,5 +783,10 @@
 
   /* Send RETR or STOR command with filename */
-  reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
+  reply = rtems_ftpfs_send_command(
+    e->ctrl_socket,
+    file_command,
+    filename,
+    verbose
+  );
   if (reply != RTEMS_FTPFS_REPLY_1) {
     eno = EIO;
@@ -660,11 +794,17 @@
   }
 
-  /* Wait for connect on data connection  */
-  FD_ZERO( &fds);
-  FD_SET( port_socket, &fds);
-  rv = select( port_socket + 1, &fds, NULL, NULL, &timeout);
-  if (rv <= 0) {
-    eno = EIO;
-    goto cleanup;
+  /* Wait for connect on data connection if necessary */
+  if (rtems_ftpfs_use_timeout( timeout)) {
+    struct timeval to = *timeout;
+    fd_set fds;
+
+    FD_ZERO( &fds);
+    FD_SET( port_socket, &fds);
+
+    rv = select( port_socket + 1, &fds, NULL, NULL, &to);
+    if (rv <= 0) {
+      eno = EIO;
+      goto cleanup;
+    }
   }
 
@@ -756,5 +896,7 @@
   uint32_t client_address,
   const char *file_command,
-  const char *filename
+  const char *filename,
+  bool verbose,
+  const struct timeval *timeout
 )
 {
@@ -775,5 +917,6 @@
     NULL,
     rtems_ftpfs_pasv_parser,
-    &pe
+    &pe,
+    verbose
   );
   if (reply != RTEMS_FTPFS_REPLY_2) {
@@ -807,5 +950,10 @@
 
   /* Send RETR or STOR command with filename */
-  reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
+  reply = rtems_ftpfs_send_command(
+    e->ctrl_socket,
+    file_command,
+    filename,
+    verbose
+  );
   if (reply != RTEMS_FTPFS_REPLY_1) {
     return EIO;
@@ -825,4 +973,7 @@
   bool ok = false;
   rtems_ftpfs_entry *e = NULL;
+  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
+  bool verbose = me->verbose;
+  const struct timeval *timeout = &me->timeout;
   const char *user = NULL;
   const char *password = NULL;
@@ -843,12 +994,4 @@
   }
 
-  /* Check for either read-only or write-only flags */
-  if (
-    (iop->flags & LIBIO_FLAGS_WRITE) != 0
-      && (iop->flags & LIBIO_FLAGS_READ) != 0
-  ) {
-    rtems_set_errno_and_return_minus_one( ENOTSUP);
-  }
-
   /* Split location into parts */
   ok = rtems_ftpfs_split_names(
@@ -860,5 +1003,15 @@
   );
   if (!ok) {
-    rtems_set_errno_and_return_minus_one( ENOENT);
+    if (strlen( location) == 0) {
+      /*
+       * This is an access to the root node that will be used for file system
+       * option settings.
+       */
+      iop->handlers = &rtems_ftpfs_root_handlers;
+
+      return 0;
+    } else {
+      rtems_set_errno_and_return_minus_one( ENOENT);
+    }
   }
   DEBUG_PRINTF(
@@ -868,4 +1021,12 @@
     filename
   );
+
+  /* Check for either read-only or write-only flags */
+  if (
+    (iop->flags & LIBIO_FLAGS_WRITE) != 0
+      && (iop->flags & LIBIO_FLAGS_READ) != 0
+  ) {
+    rtems_set_errno_and_return_minus_one( ENOTSUP);
+  }
 
   /* Allocate connection entry */
@@ -889,5 +1050,7 @@
     password,
     hostname,
-    &client_address
+    &client_address,
+    verbose,
+    timeout
   );
   if (eno != 0) {
@@ -900,5 +1063,7 @@
     client_address,
     file_command,
-    filename
+    filename,
+    verbose,
+    timeout
   );
   if (eno == ENOTSUP) {
@@ -908,7 +1073,15 @@
       client_address,
       file_command,
-      filename
+      filename,
+      verbose,
+      timeout
     );
   }
+  if (eno != 0) {
+    goto cleanup;
+  }
+
+  /* Set data connection timeout */
+  eno = rtems_ftpfs_set_connection_timeout( e->data_socket, timeout);
 
 cleanup:
@@ -931,4 +1104,6 @@
 {
   rtems_ftpfs_entry *e = iop->data1;
+  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
+  bool verbose = me->verbose;
   char *in = buffer;
   size_t todo = count;
@@ -944,5 +1119,5 @@
       if (rv == 0) {
         rtems_ftpfs_reply reply =
-          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
+          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL, verbose);
 
         if (reply == RTEMS_FTPFS_REPLY_2) {
@@ -1037,17 +1212,23 @@
 }
 
-static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
-
 static int rtems_ftpfs_mount_me(
   rtems_filesystem_mount_table_entry_t *e
 )
 {
+  rtems_ftpfs_mount_entry *me = malloc( sizeof( rtems_ftpfs_mount_entry));
+
+  /* Mount entry for FTP file system instance */
+  e->fs_info = me;
+  if (e->fs_info == NULL) {
+    rtems_set_errno_and_return_minus_one( ENOMEM);
+  }
+  me->verbose = false;
+  me->timeout.tv_sec = 0;
+  me->timeout.tv_usec = 0;
+
   /* 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;
@@ -1055,4 +1236,47 @@
   /* Just use the limits from IMFS */
   e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
+
+  return 0;
+}
+
+static int rtems_ftpfs_unmount_me(
+  rtems_filesystem_mount_table_entry_t *e
+)
+{
+  free( e->fs_info);
+
+  return 0;
+}
+
+static int rtems_ftpfs_ioctl(
+  rtems_libio_t *iop,
+  uint32_t command,
+  void *arg
+)
+{
+  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
+  bool *verbose = arg;
+  struct timeval *timeout = arg;
+
+  if (arg == NULL) {
+    rtems_set_errno_and_return_minus_one( EINVAL);
+  }
+
+  switch (command) {
+    case RTEMS_FTPFS_IOCTL_GET_VERBOSE:
+      *verbose = me->verbose;
+      break;
+    case RTEMS_FTPFS_IOCTL_SET_VERBOSE:
+      me->verbose = *verbose;
+      break;
+    case RTEMS_FTPFS_IOCTL_GET_TIMEOUT:
+      *timeout = me->timeout;
+      break;
+    case RTEMS_FTPFS_IOCTL_SET_TIMEOUT:
+      me->timeout = *timeout;
+      break;
+    default:
+      rtems_set_errno_and_return_minus_one( EBADRQC);
+  }
 
   return 0;
@@ -1063,5 +1287,5 @@
  * 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.
+ * a remote to remote copy.  This is not a very sophisticated method.
  */
 static int rtems_ftpfs_fstat(
@@ -1095,5 +1319,5 @@
   .fsmount_me_h = rtems_ftpfs_mount_me,
   .unmount_h = NULL,
-  .fsunmount_me_h = NULL,
+  .fsunmount_me_h = rtems_ftpfs_unmount_me,
   .utime_h = NULL,
   .eval_link_h = NULL,
@@ -1118,2 +1342,19 @@
   .rmnod_h = NULL
 };
+
+static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = {
+  .open_h = NULL,
+  .close_h = NULL,
+  .read_h = NULL,
+  .write_h = NULL,
+  .ioctl_h = rtems_ftpfs_ioctl,
+  .lseek_h = NULL,
+  .fstat_h = NULL,
+  .fchmod_h = NULL,
+  .ftruncate_h = NULL,
+  .fpathconf_h = NULL,
+  .fsync_h = NULL,
+  .fdatasync_h = NULL,
+  .fcntl_h = NULL,
+  .rmnod_h = NULL
+};
Index: cpukit/libnetworking/rtems/ftpfs.h
===================================================================
--- cpukit/libnetworking/rtems/ftpfs.h	(revision facca98f29f96eb35eb8781a41da79a9ba023d62)
+++ cpukit/libnetworking/rtems/ftpfs.h	(revision d2ec6433fad6c8ebc942a5bf0142697dcdc7a881)
@@ -2,5 +2,5 @@
  * @file
  *
- * @brief File Transfer Protocol file system (FTP client).
+ * File Transfer Protocol file system (FTP client).
  */
 
@@ -34,4 +34,7 @@
 #ifndef _RTEMS_FTPFS_H
 #define _RTEMS_FTPFS_H
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
 
 #include <rtems/libio.h>
@@ -72,21 +75,30 @@
 
 /**
- * @brief Well-known port number for FTP control connection.
+ * Well-known port number for FTP control connection.
  */
 #define RTEMS_FTPFS_CTRL_PORT 21
 
 /**
- * @brief Default mount point for FTP file system.
+ * Default mount point for FTP file system.
  */
 #define RTEMS_FTPFS_MOUNT_POINT_DEFAULT "/FTP"
 
 /**
- * @brief FTP file system operations table.
+ * FTP file system IO control requests.
+ */
+typedef enum {
+  RTEMS_FTPFS_IOCTL_GET_VERBOSE = _IOR( 'd', 1, bool *),
+  RTEMS_FTPFS_IOCTL_SET_VERBOSE = _IOW( 'd', 1, bool *),
+  RTEMS_FTPFS_IOCTL_GET_TIMEOUT = _IOR( 'd', 2, struct timeval *),
+  RTEMS_FTPFS_IOCTL_SET_TIMEOUT = _IOW( 'd', 2, struct timeval *)
+} rtems_ftpfs_ioctl_numbers;
+
+/**
+ * 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.
+ * Creates the mount point @a mount_point and mounts the FTP file system.
  *
  * If @a mount_point is @c NULL the default mount point
@@ -94,28 +106,60 @@
  *
  * 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.
+ * Returns in @a verbose if the verbose mode is enabled or disabled for the
+ * file system at @a mount_point.
+ *
+ * If @a mount_point is @c NULL the default mount point
+ * @ref RTEMS_FTPFS_MOUNT_POINT_DEFAULT will be used.
+ */
+rtems_status_code rtems_ftpfs_get_verbose( const char *mount_point, bool *verbose);
+
+/**
+ * Enables or disables the verbose mode if @a verbose is @c true or
+ * @c false respectively for the file system at @a mount_point.
  *
  * In the enabled verbose mode the commands and replies of the FTP control
  * connections will be printed to standard error.
+ *
+ * If @a mount_point is @c NULL the default mount point
+ * @ref RTEMS_FTPFS_MOUNT_POINT_DEFAULT will be used.
  */
-rtems_status_code rtems_ftpfs_set_verbose( bool verbose);
+rtems_status_code rtems_ftpfs_set_verbose( const char *mount_point, bool verbose);
 
 /**
- * @brief Returns in @a verbose if the verbose mode is enabled or disabled.
+ * Returns the current timeout value in @a timeout for the file system at
+ * @a mount_point.
+ *
+ * If @a mount_point is @c NULL the default mount point
+ * @ref RTEMS_FTPFS_MOUNT_POINT_DEFAULT will be used.
  */
-rtems_status_code rtems_ftpfs_get_verbose( bool *verbose);
+rtems_status_code rtems_ftpfs_get_timeout(
+  const char *mount_point,
+  struct timeval *timeout
+);
+
+/**
+ * Sets the timeout value to @a timeout for the file system at @a mount_point.
+ *
+ * The timeout value will be used during connection establishment of active
+ * data connections.  It will be also used for send and receive operations on
+ * data and control connections.
+ *
+ * If @a mount_point is @c NULL the default mount point
+ * @ref RTEMS_FTPFS_MOUNT_POINT_DEFAULT will be used.
+ */
+rtems_status_code rtems_ftpfs_set_timeout(
+  const char *mount_point,
+  const struct timeval *timeout
+);
 
 /** @} */
 
 /**
- * @brief Creates the default mount point @ref RTEMS_FTPFS_MOUNT_POINT_DEFAULT
- * and mounts the FTP file system.
+ * 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.
