source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 391b4dd

4.104.11
Last change on this file since 391b4dd was 391b4dd, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 11, 2010 at 7:12:30 PM

2010-03-11 Joel Sherrill <joel.sherrill@…>

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