source: rtems/cpukit/libnetworking/lib/ftpfs.c @ b25b88e7

4.104.11
Last change on this file since b25b88e7 was b25b88e7, checked in by Ralf Corsepius <ralf.corsepius@…>, on Mar 28, 2010 at 5:50:29 AM

Add HAVE_CONFIG_H support to let files receive configure defines.

  • Property mode set to 100644
File size: 29.0 KB
Line 
1/**
2 * @file
3 *
4 * File Transfer Protocol file system (FTP client).
5 */
6
7/*
8 * Copyright (c) 2009
9 * embedded brains GmbH
10 * Obere Lagerstr. 30
11 * D-82178 Puchheim
12 * Germany
13 * <rtems@embedded-brains.de>
14 *
15 * (c) Copyright 2002
16 * Thomas Doerfler
17 * IMD Ingenieurbuero fuer Microcomputertechnik
18 * Herbststr. 8
19 * 82178 Puchheim, Germany
20 * <Thomas.Doerfler@imd-systems.de>
21 *
22 * Modified by Sebastian Huber <sebastian.huber@embedded-brains.de>.
23 *
24 * This code has been created after closly inspecting "tftpdriver.c" from Eric
25 * Norum.
26 *
27 * The license and distribution terms for this file may be
28 * found in the file LICENSE in this distribution or at
29 * http://www.rtems.com/license/LICENSE.
30 *
31 * $Id$
32 */
33
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
38#include <ctype.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <malloc.h>
42#include <netdb.h>
43#include <stdarg.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <arpa/inet.h>
49#include <netinet/in.h>
50#include <sys/select.h>
51#include <sys/socket.h>
52#include <sys/types.h>
53
54#include <rtems.h>
55#include <rtems/ftpfs.h>
56#include <rtems/imfs.h>
57#include <rtems/libio.h>
58#include <rtems/rtems_bsdnet.h>
59#include <rtems/seterr.h>
60
61#ifdef DEBUG
62  #define DEBUG_PRINTF( ...) printf( __VA_ARGS__)
63#else
64  #define DEBUG_PRINTF( ...)
65#endif
66
67/**
68 * Connection entry for each open file stream.
69 */
70typedef struct {
71  /**
72   * Control connection socket.
73   */
74  int ctrl_socket;
75
76  /**
77   * Data transfer socket.
78   */
79  int data_socket;
80
81  /**
82   * End of file flag.
83   */
84  bool eof;
85} rtems_ftpfs_entry;
86
87/**
88 * Mount entry for each file system instance.
89 */
90typedef struct {
91  /**
92   * Verbose mode enabled or disabled.
93   */
94  bool verbose;
95
96  /**
97   * Timeout value
98   */
99  struct timeval timeout;
100} rtems_ftpfs_mount_entry;
101
102static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
103
104static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers;
105
106static bool rtems_ftpfs_use_timeout( const struct timeval *to)
107{
108  return to->tv_sec != 0 || to->tv_usec != 0;
109}
110
111static int rtems_ftpfs_set_connection_timeout(
112  int socket,
113  const struct timeval *to
114)
115{
116  if (rtems_ftpfs_use_timeout( to)) {
117    int rv = 0;
118   
119    rv = setsockopt( socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof( *to));
120    if (rv != 0) {
121      return EIO;
122    }
123
124    rv = setsockopt( socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof( *to));
125    if (rv != 0) {
126      return EIO;
127    }
128  }
129
130  return 0;
131}
132
133rtems_status_code rtems_ftpfs_mount( const char *mount_point)
134{
135  int rv = 0;
136
137  if (mount_point == NULL) {
138    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
139  }
140
141  rv = mkdir( mount_point, S_IRWXU | S_IRWXG | S_IRWXO);
142  if (rv != 0) {
143    return RTEMS_IO_ERROR;
144  }
145
146  rv = mount(
147    NULL,
148    &rtems_ftpfs_ops,
149    RTEMS_FILESYSTEM_READ_WRITE,
150    NULL,
151    mount_point
152  );
153  if (rv != 0) {
154    return RTEMS_IO_ERROR;
155  }
156
157  return RTEMS_SUCCESSFUL;
158}
159
160static rtems_status_code rtems_ftpfs_do_ioctl(
161  const char *mount_point,
162  int req,
163  ...
164)
165{
166  rtems_status_code sc = RTEMS_SUCCESSFUL;
167  int rv = 0;
168  int fd = 0;
169  va_list ap;
170
171  if (mount_point == NULL) {
172    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
173  }
174
175  fd = open( mount_point, O_RDWR);
176  if (fd < 0) {
177    return RTEMS_INVALID_NAME;
178  }
179 
180  va_start( ap, req);
181  rv = ioctl( fd, req, va_arg( ap, void *));
182  va_end( ap);
183  if (rv != 0) {
184    sc = RTEMS_INVALID_NUMBER;
185  }
186
187  rv = close( fd);
188  if (rv != 0 && sc == RTEMS_SUCCESSFUL) {
189    sc = RTEMS_IO_ERROR;
190  }
191 
192  return sc;
193}
194
195rtems_status_code rtems_ftpfs_get_verbose( const char *mount_point, bool *verbose)
196{
197  return rtems_ftpfs_do_ioctl(
198    mount_point,
199    RTEMS_FTPFS_IOCTL_GET_VERBOSE,
200    verbose
201  );
202}
203
204rtems_status_code rtems_ftpfs_set_verbose( const char *mount_point, bool verbose)
205{
206  return rtems_ftpfs_do_ioctl(
207    mount_point,
208    RTEMS_FTPFS_IOCTL_SET_VERBOSE,
209    &verbose
210  );
211}
212
213rtems_status_code rtems_ftpfs_get_timeout(
214  const char *mount_point,
215  struct timeval *timeout
216)
217{
218  return rtems_ftpfs_do_ioctl(
219    mount_point,
220    RTEMS_FTPFS_IOCTL_GET_TIMEOUT,
221    timeout
222  );
223}
224
225rtems_status_code rtems_ftpfs_set_timeout(
226  const char *mount_point,
227  const struct timeval *timeout
228)
229{
230  return rtems_ftpfs_do_ioctl(
231    mount_point,
232    RTEMS_FTPFS_IOCTL_SET_TIMEOUT,
233    timeout
234  );
235}
236
237int rtems_bsdnet_initialize_ftp_filesystem( void)
238{
239  rtems_status_code sc = RTEMS_SUCCESSFUL;
240
241  sc = rtems_ftpfs_mount( NULL);
242
243  if (sc == RTEMS_SUCCESSFUL) {
244    return 0;
245  } else {
246    return -1;
247  }
248}
249
250typedef void (*rtems_ftpfs_reply_parser)(
251  const char * /* reply fragment */,
252  size_t /* reply fragment length */,
253  void * /* parser argument */
254);
255
256typedef enum {
257  RTEMS_FTPFS_REPLY_START,
258  RTEMS_FTPFS_REPLY_SINGLE_LINE,
259  RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE,
260  RTEMS_FTPFS_REPLY_MULTI_LINE,
261  RTEMS_FTPFS_REPLY_NEW_LINE,
262  RTEMS_FTPFS_REPLY_NEW_LINE_START
263} rtems_ftpfs_reply_state;
264
265typedef enum {
266  RTEMS_FTPFS_REPLY_ERROR = 0,
267  RTEMS_FTPFS_REPLY_1 = '1',
268  RTEMS_FTPFS_REPLY_2 = '2',
269  RTEMS_FTPFS_REPLY_3 = '3',
270  RTEMS_FTPFS_REPLY_4 = '4',
271  RTEMS_FTPFS_REPLY_5 = '5'
272} rtems_ftpfs_reply;
273
274#define RTEMS_FTPFS_REPLY_SIZE 3
275
276static rtems_ftpfs_reply rtems_ftpfs_get_reply(
277  int socket,
278  rtems_ftpfs_reply_parser parser,
279  void *parser_arg,
280  bool verbose
281)
282{
283  rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START;
284  unsigned char reply_first [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' };
285  unsigned char reply_last [RTEMS_FTPFS_REPLY_SIZE] = { 'b', 'b', 'b' };
286  size_t reply_first_index = 0;
287  size_t reply_last_index = 0;
288  char buf [128];
289
290  while (true) {
291    /* Receive reply fragment from socket */
292    ssize_t i = 0;
293    ssize_t rv = recv( socket, buf, sizeof( buf), 0);
294
295    if (rv <= 0) {
296      return RTEMS_FTPFS_REPLY_ERROR;
297    }
298
299    /* Be verbose if necessary */
300    if (verbose) {
301      write( STDERR_FILENO, buf, (size_t) rv);
302    }
303
304    /* Invoke parser if necessary */
305    if (parser != NULL) {
306      parser( buf, (size_t) rv, parser_arg);
307    }
308
309    /* Parse reply fragment */
310    for (i = 0; i < rv; ++i) {
311      char c = buf [i];
312
313      switch (state) {
314        case RTEMS_FTPFS_REPLY_START:
315          if (reply_first_index < RTEMS_FTPFS_REPLY_SIZE) {
316            reply_first [reply_first_index] = c;
317            ++reply_first_index;
318          } else if (c == '-') {
319            state = RTEMS_FTPFS_REPLY_MULTI_LINE;
320          } else {
321            state = RTEMS_FTPFS_REPLY_SINGLE_LINE;
322          }
323          break;
324        case RTEMS_FTPFS_REPLY_SINGLE_LINE:
325          if (c == '\n') {
326            state = RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE;
327          }
328          break;
329        case RTEMS_FTPFS_REPLY_MULTI_LINE:
330          if (c == '\n') {
331            state = RTEMS_FTPFS_REPLY_NEW_LINE_START;
332            reply_last_index = 0;
333          }
334          break;
335        case RTEMS_FTPFS_REPLY_NEW_LINE:
336        case RTEMS_FTPFS_REPLY_NEW_LINE_START:
337          if (reply_last_index < RTEMS_FTPFS_REPLY_SIZE) {
338            state = RTEMS_FTPFS_REPLY_NEW_LINE;
339            reply_last [reply_last_index] = c;
340            ++reply_last_index;
341          } else {
342            state = RTEMS_FTPFS_REPLY_MULTI_LINE;
343          }
344          break;
345        default:
346          return RTEMS_FTPFS_REPLY_ERROR;
347      }
348    }
349
350    /* Check reply */
351    if (state == RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE) {
352      if (
353        isdigit( reply_first [0])
354          && isdigit( reply_first [1])
355          && isdigit( reply_first [2])
356      ) {
357        break;
358      } else {
359        return RTEMS_FTPFS_REPLY_ERROR;
360      }
361    } else if (state == RTEMS_FTPFS_REPLY_NEW_LINE_START) {
362      bool ok = true;
363
364      for (i = 0; i < RTEMS_FTPFS_REPLY_SIZE; ++i) {
365        ok = ok
366          && reply_first [i] == reply_last [i]
367          && isdigit( reply_first [i]);
368      }
369
370      if (ok) {
371        break;
372      }
373    }
374  }
375
376  return reply_first [0];
377}
378
379static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser(
380  int socket,
381  const char *cmd,
382  const char *arg,
383  rtems_ftpfs_reply_parser parser,
384  void *parser_arg,
385  bool verbose
386)
387{
388  const char *const eol = "\r\n";
389  int rv = 0;
390
391  /* Send command */
392  rv = send( socket, cmd, strlen( cmd), 0);
393  if (rv < 0) {
394    return RTEMS_FTPFS_REPLY_ERROR;
395  }
396  if (verbose) {
397    write( STDERR_FILENO, cmd, strlen( cmd));
398  }
399
400  /* Send command argument if necessary */
401  if (arg != NULL) {
402    rv = send( socket, arg, strlen( arg), 0);
403    if (rv < 0) {
404      return RTEMS_FTPFS_REPLY_ERROR;
405    }
406    if (verbose) {
407      write( STDERR_FILENO, arg, strlen( arg));
408    }
409  }
410
411  /* Send end of line */
412  rv = send( socket, eol, 2, 0);
413  if (rv < 0) {
414    return RTEMS_FTPFS_REPLY_ERROR;
415  }
416  if (verbose) {
417    write( STDERR_FILENO, &eol [1], 1);
418  }
419
420  /* Return reply */
421  return rtems_ftpfs_get_reply( socket, parser, parser_arg, verbose);
422}
423
424static rtems_ftpfs_reply rtems_ftpfs_send_command(
425  int socket,
426  const char *cmd,
427  const char *arg,
428  bool verbose
429)
430{
431  return rtems_ftpfs_send_command_with_parser(
432    socket,
433    cmd,
434    arg,
435    NULL,
436    NULL,
437    verbose
438  );
439}
440
441typedef enum {
442  STATE_USER_NAME,
443  STATE_START_PASSWORD,
444  STATE_START_HOST_NAME,
445  STATE_START_HOST_NAME_OR_PATH,
446  STATE_START_PATH,
447  STATE_PASSWORD,
448  STATE_HOST_NAME,
449  STATE_DONE,
450  STATE_INVALID
451} split_state;
452
453static bool rtems_ftpfs_split_names (
454  char *s,
455  const char **user,
456  const char **password,
457  const char **hostname,
458  const char **path
459)
460{
461  split_state state = STATE_USER_NAME;
462  size_t len = strlen( s);
463  size_t i = 0;
464
465  *user = s;
466  *password = NULL;
467  *hostname = NULL;
468  *path = NULL;
469
470  for (i = 0; i < len; ++i) {
471    char c = s [i];
472
473    switch (state) {
474      case STATE_USER_NAME:
475        if (c == ':') {
476          state = STATE_START_PASSWORD;
477          s [i] = '\0';
478        } else if (c == '@') {
479          state = STATE_START_HOST_NAME;
480          s [i] = '\0';
481        } else if (c == '/') {
482          state = STATE_START_HOST_NAME_OR_PATH;
483          s [i] = '\0';
484        }
485        break;
486      case STATE_START_PASSWORD:
487        state = STATE_PASSWORD;
488        *password = &s [i];
489        --i;
490        break;
491      case STATE_START_HOST_NAME:
492        state = STATE_HOST_NAME;
493        *hostname = &s [i];
494        --i;
495        break;
496      case STATE_START_HOST_NAME_OR_PATH:
497        if (c == '@') {
498          state = STATE_START_HOST_NAME;
499        } else {
500          state = STATE_DONE;
501          *path = &s [i];
502          goto done;
503        }
504        break;
505      case STATE_START_PATH:
506        state = STATE_DONE;
507        *path = &s [i];
508        goto done;
509      case STATE_PASSWORD:
510        if (c == '@') {
511          state = STATE_START_HOST_NAME;
512          s [i] = '\0';
513        } else if (c == '/') {
514          state = STATE_START_HOST_NAME_OR_PATH;
515          s [i] = '\0';
516        }
517        break;
518      case STATE_HOST_NAME:
519        if (c == '/') {
520          state = STATE_START_PATH;
521          s [i] = '\0';
522        }
523        break;
524      default:
525        state = STATE_INVALID;
526        goto done;
527    }
528  }
529
530done:
531
532  /* If we have no password use the user name */
533  if (*password == NULL) {
534    *password = *user;
535  }
536
537  return state == STATE_DONE;
538}
539
540static socklen_t rtems_ftpfs_create_address(
541  struct sockaddr_in *sa,
542  unsigned long address,
543  unsigned short port
544)
545{
546  memset( sa, sizeof( *sa), 0);
547
548  sa->sin_family = AF_INET;
549  sa->sin_addr.s_addr = address;
550  sa->sin_port = port;
551  sa->sin_len = sizeof( *sa);
552
553  return sizeof( *sa);
554}
555
556static int rtems_ftpfs_terminate( rtems_libio_t *iop, bool error)
557{
558  int eno = 0;
559  int rv = 0;
560  rtems_ftpfs_entry *e = iop->data1;
561  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
562  bool verbose = me->verbose;
563
564  if (e != NULL) {
565    /* Close data connection if necessary */
566    if (e->data_socket >= 0) {
567      rv = close( e->data_socket);
568      if (rv != 0) {
569        eno = EIO;
570      }
571
572      /* For write connections we have to obtain the transfer reply  */
573      if (
574        e->ctrl_socket >= 0
575          && (iop->flags & LIBIO_FLAGS_WRITE) != 0
576          && !error
577      ) {
578        rtems_ftpfs_reply reply =
579          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL, verbose);
580
581        if (reply != RTEMS_FTPFS_REPLY_2) {
582          eno = EIO;
583        }
584      }
585    }
586
587    /* Close control connection if necessary */
588    if (e->ctrl_socket >= 0) {
589      rv = close( e->ctrl_socket);
590      if (rv != 0) {
591        eno = EIO;
592      }
593    }
594
595    /* Free connection entry */
596    free( e);
597  }
598
599  /* Invalidate IO entry */
600  iop->data1 = NULL;
601
602  return eno;
603}
604
605static int rtems_ftpfs_open_ctrl_connection(
606  rtems_ftpfs_entry *e,
607  const char *user,
608  const char *password,
609  const char *hostname,
610  uint32_t *client_address,
611  bool verbose,
612  const struct timeval *timeout
613)
614{
615  int rv = 0;
616  int eno = 0;
617  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
618  struct in_addr address = { .s_addr = 0 };
619  struct sockaddr_in sa;
620  socklen_t size = 0;
621
622  /* Create the socket for the control connection */
623  e->ctrl_socket = socket( AF_INET, SOCK_STREAM, 0);
624  if (e->ctrl_socket < 0) {
625    return ENOMEM;
626  }
627
628  /* Set up the server address from the hostname */
629  if (hostname == NULL || strlen( hostname) == 0) {
630    /* Default to BOOTP server address */
631    address = rtems_bsdnet_bootp_server_address;
632  } else if (inet_aton( hostname, &address) == 0) {
633    /* Try to get the address by name */
634    struct hostent *he = gethostbyname( hostname);
635
636    if (he != NULL) {
637      memcpy( &address, he->h_addr, sizeof( address));
638    } else {
639      return ENOENT;
640    }
641  }
642  rtems_ftpfs_create_address( &sa, address.s_addr, htons( RTEMS_FTPFS_CTRL_PORT));
643  DEBUG_PRINTF( "server = %s\n", inet_ntoa( sa.sin_addr));
644
645  /* Open control connection */
646  rv = connect(
647    e->ctrl_socket,
648    (struct sockaddr *) &sa,
649    sizeof( sa)
650  );
651  if (rv != 0) {
652    return ENOENT;
653  }
654
655  /* Set control connection timeout */
656  eno = rtems_ftpfs_set_connection_timeout( e->ctrl_socket, timeout);
657  if (eno != 0) {
658    return eno;
659  }
660
661  /* Get client address */
662  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
663  rv = getsockname(
664    e->ctrl_socket,
665    (struct sockaddr *) &sa,
666    &size
667  );
668  if (rv != 0) {
669    return ENOMEM;
670  }
671  *client_address = ntohl( sa.sin_addr.s_addr);
672  DEBUG_PRINTF( "client = %s\n", inet_ntoa( sa.sin_addr));
673
674  /* Now we should get a welcome message from the server */
675  reply = rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL, verbose);
676  if (reply != RTEMS_FTPFS_REPLY_2) {
677    return ENOENT;
678  }
679
680  /* Send USER command */
681  reply = rtems_ftpfs_send_command( e->ctrl_socket, "USER ", user, verbose);
682  if (reply == RTEMS_FTPFS_REPLY_3) {
683    /* Send PASS command */
684    reply = rtems_ftpfs_send_command(
685      e->ctrl_socket,
686      "PASS ",
687      password,
688      verbose
689    );
690    if (reply != RTEMS_FTPFS_REPLY_2) {
691      return EACCES;
692    }
693
694    /* TODO: Some server may require an account */
695  } else if (reply != RTEMS_FTPFS_REPLY_2) {
696    return EACCES;
697  }
698
699  /* Send TYPE command to set binary mode for all data transfers */
700  reply = rtems_ftpfs_send_command( e->ctrl_socket, "TYPE I", NULL, verbose);
701  if (reply != RTEMS_FTPFS_REPLY_2) {
702    return EIO;
703  }
704
705  return 0;
706}
707
708static int rtems_ftpfs_open_data_connection_active(
709  rtems_ftpfs_entry *e,
710  uint32_t client_address,
711  const char *file_command,
712  const char *filename,
713  bool verbose,
714  const struct timeval *timeout
715)
716{
717  int rv = 0;
718  int eno = 0;
719  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
720  struct sockaddr_in sa;
721  socklen_t size = 0;
722  int port_socket = -1;
723  char port_command [] = "PORT 000,000,000,000,000,000";
724  uint16_t data_port = 0;
725
726  /* Create port socket to establish a data data connection */
727  port_socket = socket( AF_INET, SOCK_STREAM, 0);
728  if (port_socket < 0) {
729    eno = ENOMEM;
730    goto cleanup;
731  }
732
733  /* Bind port socket */
734  rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
735  rv = bind(
736    port_socket,
737    (struct sockaddr *) &sa,
738    sizeof( sa)
739  );
740  if (rv != 0) {
741    eno = EBUSY;
742    goto cleanup;
743  }
744
745  /* Get port number for data socket */
746  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
747  rv = getsockname(
748    port_socket,
749    (struct sockaddr *) &sa,
750    &size
751  );
752  if (rv != 0) {
753    eno = ENOMEM;
754    goto cleanup;
755  }
756  data_port = ntohs( sa.sin_port);
757
758  /* Send PORT command to set data connection port for server */
759  snprintf(
760    port_command,
761    sizeof( port_command),
762    "PORT %lu,%lu,%lu,%lu,%lu,%lu",
763    (client_address >> 24) & 0xffUL,
764    (client_address >> 16) & 0xffUL,
765    (client_address >> 8) & 0xffUL,
766    (client_address >> 0) & 0xffUL,
767    (data_port >> 8) & 0xffUL,
768    (data_port >> 0) & 0xffUL
769  );
770  reply = rtems_ftpfs_send_command(
771    e->ctrl_socket,
772    port_command,
773    NULL,
774    verbose
775  );
776  if (reply != RTEMS_FTPFS_REPLY_2) {
777    eno = ENOTSUP;
778    goto cleanup;
779  }
780
781  /* Listen on port socket for incoming data connections */
782  rv = listen( port_socket, 1);
783  if (rv != 0) {
784    eno = EBUSY;
785    goto cleanup;
786  }
787
788  /* Send RETR or STOR command with filename */
789  reply = rtems_ftpfs_send_command(
790    e->ctrl_socket,
791    file_command,
792    filename,
793    verbose
794  );
795  if (reply != RTEMS_FTPFS_REPLY_1) {
796    eno = EIO;
797    goto cleanup;
798  }
799
800  /* Wait for connect on data connection if necessary */
801  if (rtems_ftpfs_use_timeout( timeout)) {
802    struct timeval to = *timeout;
803    fd_set fds;
804
805    FD_ZERO( &fds);
806    FD_SET( port_socket, &fds);
807
808    rv = select( port_socket + 1, &fds, NULL, NULL, &to);
809    if (rv <= 0) {
810      eno = EIO;
811      goto cleanup;
812    }
813  }
814
815  /* Accept data connection  */
816  size = sizeof( sa);
817  e->data_socket = accept(
818    port_socket,
819    (struct sockaddr *) &sa,
820    &size
821  );
822  if (e->data_socket < 0) {
823    eno = EIO;
824    goto cleanup;
825  }
826
827cleanup:
828
829  /* Close port socket if necessary */
830  if (port_socket >= 0) {
831    rv = close( port_socket);
832    if (rv != 0) {
833      eno = EIO;
834    }
835  }
836
837  return eno;
838}
839
840typedef enum {
841  RTEMS_FTPFS_PASV_START = 0,
842  RTEMS_FTPFS_PASV_JUNK,
843  RTEMS_FTPFS_PASV_DATA,
844  RTEMS_FTPFS_PASV_DONE
845} rtems_ftpfs_pasv_state;
846
847typedef struct {
848  rtems_ftpfs_pasv_state state;
849  uint8_t data [6];
850  size_t index;
851} rtems_ftpfs_pasv_entry;
852
853static void rtems_ftpfs_pasv_parser(
854  const char* buf,
855  size_t len,
856  void *arg
857)
858{
859  rtems_ftpfs_pasv_entry *e = arg;
860  size_t i = 0;
861
862  for (i = 0; i < len; ++i) {
863    int c = buf [i];
864
865    switch (e->state) {
866      case RTEMS_FTPFS_PASV_START:
867        if (!isdigit( c)) {
868          e->state = RTEMS_FTPFS_PASV_JUNK;
869          e->index = 0;
870        }
871        break;
872      case RTEMS_FTPFS_PASV_JUNK:
873        if (isdigit( c)) {
874          e->state = RTEMS_FTPFS_PASV_DATA;
875          e->data [e->index] = (uint8_t) (c - '0');
876        }
877        break;
878      case RTEMS_FTPFS_PASV_DATA:
879        if (isdigit( c)) {
880          e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0');
881        } else if (c == ',') {
882          ++e->index;
883          if (e->index < sizeof( e->data)) {
884            e->data [e->index] = 0;
885          } else {
886            e->state = RTEMS_FTPFS_PASV_DONE;
887          }
888        } else {
889          e->state = RTEMS_FTPFS_PASV_DONE;
890        }
891        break;
892      default:
893        return;
894    }
895  }
896}
897
898static int rtems_ftpfs_open_data_connection_passive(
899  rtems_ftpfs_entry *e,
900  uint32_t client_address,
901  const char *file_command,
902  const char *filename,
903  bool verbose,
904  const struct timeval *timeout
905)
906{
907  int rv = 0;
908  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
909  struct sockaddr_in sa;
910  uint32_t data_address = 0;
911  uint16_t data_port = 0;
912
913  rtems_ftpfs_pasv_entry pe = {
914    .state = RTEMS_FTPFS_PASV_START
915  };
916
917  /* Send PASV command */
918  reply = rtems_ftpfs_send_command_with_parser(
919    e->ctrl_socket,
920    "PASV",
921    NULL,
922    rtems_ftpfs_pasv_parser,
923    &pe,
924    verbose
925  );
926  if (reply != RTEMS_FTPFS_REPLY_2) {
927    return ENOTSUP;
928  }
929  data_address = (uint32_t) ((pe.data [0] << 24) + (pe.data [1] << 16)
930    + (pe.data [2] << 8) + pe.data [3]);
931  data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]);
932  rtems_ftpfs_create_address( &sa, htonl( data_address), htons( data_port));
933  DEBUG_PRINTF(
934    "server data = %s:%u\n",
935    inet_ntoa( sa.sin_addr),
936    (unsigned) ntohs( sa.sin_port)
937  );
938
939  /* Create data socket */
940  e->data_socket = socket( AF_INET, SOCK_STREAM, 0);
941  if (e->data_socket < 0) {
942    return ENOMEM;
943  }
944
945  /* Open data connection */
946  rv = connect(
947    e->data_socket,
948    (struct sockaddr *) &sa,
949    sizeof( sa)
950  );
951  if (rv != 0) {
952    return EIO;
953  }
954
955  /* Send RETR or STOR command with filename */
956  reply = rtems_ftpfs_send_command(
957    e->ctrl_socket,
958    file_command,
959    filename,
960    verbose
961  );
962  if (reply != RTEMS_FTPFS_REPLY_1) {
963    return EIO;
964  }
965
966  return 0;
967}
968
969static int rtems_ftpfs_open(
970  rtems_libio_t *iop,
971  const char *path,
972  uint32_t flags,
973  uint32_t mode
974)
975{
976  int eno = 0;
977  bool ok = false;
978  rtems_ftpfs_entry *e = NULL;
979  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
980  bool verbose = me->verbose;
981  const struct timeval *timeout = &me->timeout;
982  const char *user = NULL;
983  const char *password = NULL;
984  const char *hostname = NULL;
985  const char *filename = NULL;
986  const char *file_command = (iop->flags & LIBIO_FLAGS_WRITE) != 0
987    ? "STOR "
988    : "RETR ";
989  uint32_t client_address = 0;
990  char *location = iop->file_info;
991
992  /* Invalidate data handle */
993  iop->data1 = NULL;
994
995  /* Check location, it was allocated during path evaluation */
996  if (location == NULL) {
997    rtems_set_errno_and_return_minus_one( ENOMEM);
998  }
999
1000  /* Split location into parts */
1001  ok = rtems_ftpfs_split_names(
1002      location,
1003      &user,
1004      &password,
1005      &hostname,
1006      &filename
1007  );
1008  if (!ok) {
1009    if (strlen( location) == 0) {
1010      /*
1011       * This is an access to the root node that will be used for file system
1012       * option settings.
1013       */
1014      iop->handlers = &rtems_ftpfs_root_handlers;
1015
1016      return 0;
1017    } else {
1018      rtems_set_errno_and_return_minus_one( ENOENT);
1019    }
1020  }
1021  DEBUG_PRINTF(
1022    "user = '%s', password = '%s', filename = '%s'\n",
1023    user,
1024    password,
1025    filename
1026  );
1027
1028  /* Check for either read-only or write-only flags */
1029  if (
1030    (iop->flags & LIBIO_FLAGS_WRITE) != 0
1031      && (iop->flags & LIBIO_FLAGS_READ) != 0
1032  ) {
1033    rtems_set_errno_and_return_minus_one( ENOTSUP);
1034  }
1035
1036  /* Allocate connection entry */
1037  e = malloc( sizeof( *e));
1038  if (e == NULL) {
1039    rtems_set_errno_and_return_minus_one( ENOMEM);
1040  }
1041
1042  /* Initialize connection entry */
1043  e->ctrl_socket = -1;
1044  e->data_socket = -1;
1045  e->eof = false;
1046
1047  /* Save connection state */
1048  iop->data1 = e;
1049
1050  /* Open control connection */
1051  eno = rtems_ftpfs_open_ctrl_connection(
1052    e,
1053    user,
1054    password,
1055    hostname,
1056    &client_address,
1057    verbose,
1058    timeout
1059  );
1060  if (eno != 0) {
1061    goto cleanup;
1062  }
1063
1064  /* Open passive data connection */
1065  eno = rtems_ftpfs_open_data_connection_passive(
1066    e,
1067    client_address,
1068    file_command,
1069    filename,
1070    verbose,
1071    timeout
1072  );
1073  if (eno == ENOTSUP) {
1074    /* Open active data connection */
1075    eno = rtems_ftpfs_open_data_connection_active(
1076      e,
1077      client_address,
1078      file_command,
1079      filename,
1080      verbose,
1081      timeout
1082    );
1083  }
1084  if (eno != 0) {
1085    goto cleanup;
1086  }
1087
1088  /* Set data connection timeout */
1089  eno = rtems_ftpfs_set_connection_timeout( e->data_socket, timeout);
1090
1091cleanup:
1092
1093  if (eno == 0) {
1094    return 0;
1095  } else {
1096    /* Free all resources if an error occured */
1097    rtems_ftpfs_terminate( iop, true);
1098
1099    rtems_set_errno_and_return_minus_one( eno);
1100  }
1101}
1102
1103static ssize_t rtems_ftpfs_read(
1104  rtems_libio_t *iop,
1105  void *buffer,
1106  size_t count
1107)
1108{
1109  rtems_ftpfs_entry *e = iop->data1;
1110  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1111  bool verbose = me->verbose;
1112  char *in = buffer;
1113  size_t todo = count;
1114
1115  if (e->eof) {
1116    return 0;
1117  }
1118
1119  while (todo > 0) {
1120    ssize_t rv = recv( e->data_socket, in, todo, 0);
1121
1122    if (rv <= 0) {
1123      if (rv == 0) {
1124        rtems_ftpfs_reply reply =
1125          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL, verbose);
1126
1127        if (reply == RTEMS_FTPFS_REPLY_2) {
1128          e->eof = true;
1129          break;
1130        }
1131      }
1132
1133      rtems_set_errno_and_return_minus_one( EIO);
1134    }
1135
1136    in += rv;
1137    todo -= (size_t) rv;
1138  }
1139
1140  return (ssize_t) (count - todo);
1141}
1142
1143static ssize_t rtems_ftpfs_write(
1144  rtems_libio_t *iop,
1145  const void *buffer,
1146  size_t count
1147)
1148{
1149  rtems_ftpfs_entry *e = iop->data1;
1150  const char *out = buffer;
1151  size_t todo = count;
1152
1153  while (todo > 0) {
1154    ssize_t rv = send( e->data_socket, out, todo, 0);
1155
1156    if (rv <= 0) {
1157      if (rv == 0) {
1158        break;
1159      } else {
1160        rtems_set_errno_and_return_minus_one( EIO);
1161      }
1162    }
1163
1164    out += rv;
1165    todo -= (size_t) rv;
1166  }
1167
1168  return (ssize_t) (count - todo);
1169}
1170
1171static int rtems_ftpfs_close( rtems_libio_t *iop)
1172{
1173  int eno = rtems_ftpfs_terminate( iop, false);
1174
1175  if (eno == 0) {
1176    return 0;
1177  } else {
1178    rtems_set_errno_and_return_minus_one( eno);
1179  }
1180}
1181
1182/* Dummy version to let fopen( *,"w") work properly */
1183static int rtems_ftpfs_ftruncate( rtems_libio_t *iop, rtems_off64_t count)
1184{
1185  return 0;
1186}
1187
1188static int rtems_ftpfs_eval_path(
1189  const char *pathname,
1190  int pathnamelen,
1191  int flags,
1192  rtems_filesystem_location_info_t *pathloc
1193)
1194{
1195  /*
1196   * The caller of this routine has striped off the mount prefix from the path.
1197   * We need to store this path here or otherwise we would have to do this job
1198   * again.  The path is used in rtems_ftpfs_open() via iop->file_info.
1199   */
1200  pathloc->node_access = malloc(pathnamelen + 1);
1201  if (pathloc->node_access) {
1202    memset(pathloc->node_access, 0, pathnamelen + 1);
1203    memcpy(pathloc->node_access, pathname, pathnamelen);
1204  }   
1205  pathloc->node_access = strdup( pathname);
1206
1207  return 0;
1208}
1209
1210static int rtems_ftpfs_free_node( rtems_filesystem_location_info_t *pathloc)
1211{
1212  free( pathloc->node_access);
1213
1214  return 0;
1215}
1216
1217static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1218  rtems_filesystem_location_info_t *pathloc
1219)
1220{
1221  return RTEMS_FILESYSTEM_MEMORY_FILE;
1222}
1223
1224static int rtems_ftpfs_mount_me(
1225  rtems_filesystem_mount_table_entry_t *e
1226)
1227{
1228  rtems_ftpfs_mount_entry *me = malloc( sizeof( rtems_ftpfs_mount_entry));
1229
1230  /* Mount entry for FTP file system instance */
1231  e->fs_info = me;
1232  if (e->fs_info == NULL) {
1233    rtems_set_errno_and_return_minus_one( ENOMEM);
1234  }
1235  me->verbose = false;
1236  me->timeout.tv_sec = 0;
1237  me->timeout.tv_usec = 0;
1238
1239  /* Set handler and oparations table */
1240  e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
1241  e->mt_fs_root.ops = &rtems_ftpfs_ops;
1242
1243  /* We maintain no real file system nodes, so there is no real root */
1244  e->mt_fs_root.node_access = NULL;
1245
1246  /* Just use the limits from IMFS */
1247  e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
1248
1249  return 0;
1250}
1251
1252static int rtems_ftpfs_unmount_me(
1253  rtems_filesystem_mount_table_entry_t *e
1254)
1255{
1256  free( e->fs_info);
1257
1258  return 0;
1259}
1260
1261static int rtems_ftpfs_ioctl(
1262  rtems_libio_t *iop,
1263  uint32_t command,
1264  void *arg
1265)
1266{
1267  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1268  bool *verbose = arg;
1269  struct timeval *timeout = arg;
1270
1271  if (arg == NULL) {
1272    rtems_set_errno_and_return_minus_one( EINVAL);
1273  }
1274
1275  switch (command) {
1276    case RTEMS_FTPFS_IOCTL_GET_VERBOSE:
1277      *verbose = me->verbose;
1278      break;
1279    case RTEMS_FTPFS_IOCTL_SET_VERBOSE:
1280      me->verbose = *verbose;
1281      break;
1282    case RTEMS_FTPFS_IOCTL_GET_TIMEOUT:
1283      *timeout = me->timeout;
1284      break;
1285    case RTEMS_FTPFS_IOCTL_SET_TIMEOUT:
1286      me->timeout = *timeout;
1287      break;
1288    default:
1289      rtems_set_errno_and_return_minus_one(EINVAL);
1290  }
1291
1292  return 0;
1293}
1294
1295/*
1296 * The stat() support is intended only for the cp shell command.  Each request
1297 * will return that we have a regular file with read, write and execute
1298 * permissions for every one.  The node index uses a global counter to support
1299 * a remote to remote copy.  This is not a very sophisticated method.
1300 */
1301static int rtems_ftpfs_fstat(
1302  rtems_filesystem_location_info_t *loc,
1303  struct stat *st
1304)
1305{
1306  static unsigned ino = 0;
1307
1308  memset( st, 0, sizeof( *st));
1309
1310  /* FIXME */
1311  st->st_ino = ++ino;
1312  st->st_dev = rtems_filesystem_make_dev_t( 0xcc494cd6U, 0x1d970b4dU);
1313
1314  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1315
1316  return 0;
1317}
1318
1319const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1320  .evalpath_h = rtems_ftpfs_eval_path,
1321  .evalformake_h = NULL,
1322  .link_h = NULL,
1323  .unlink_h = NULL,
1324  .node_type_h = rtems_ftpfs_node_type,
1325  .mknod_h = NULL,
1326  .chown_h = NULL,
1327  .freenod_h = rtems_ftpfs_free_node,
1328  .mount_h = NULL,
1329  .fsmount_me_h = rtems_ftpfs_mount_me,
1330  .unmount_h = NULL,
1331  .fsunmount_me_h = rtems_ftpfs_unmount_me,
1332  .utime_h = NULL,
1333  .eval_link_h = NULL,
1334  .symlink_h = NULL,
1335  .readlink_h = NULL
1336};
1337
1338static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1339  .open_h = rtems_ftpfs_open,
1340  .close_h = rtems_ftpfs_close,
1341  .read_h = rtems_ftpfs_read,
1342  .write_h = rtems_ftpfs_write,
1343  .ioctl_h = NULL,
1344  .lseek_h = NULL,
1345  .fstat_h = rtems_ftpfs_fstat,
1346  .fchmod_h = NULL,
1347  .ftruncate_h = rtems_ftpfs_ftruncate,
1348  .fpathconf_h = NULL,
1349  .fsync_h = NULL,
1350  .fdatasync_h = NULL,
1351  .fcntl_h = NULL,
1352  .rmnod_h = NULL
1353};
1354
1355static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = {
1356  .open_h = NULL,
1357  .close_h = NULL,
1358  .read_h = NULL,
1359  .write_h = NULL,
1360  .ioctl_h = rtems_ftpfs_ioctl,
1361  .lseek_h = NULL,
1362  .fstat_h = NULL,
1363  .fchmod_h = NULL,
1364  .ftruncate_h = NULL,
1365  .fpathconf_h = NULL,
1366  .fsync_h = NULL,
1367  .fdatasync_h = NULL,
1368  .fcntl_h = NULL,
1369  .rmnod_h = NULL
1370};
Note: See TracBrowser for help on using the repository browser.