source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 9eab99a

4.104.115
Last change on this file since 9eab99a was 9eab99a, checked in by Joel Sherrill <joel.sherrill@…>, on 03/30/09 at 17:05:23

2009-03-30 Sebastian Huber <sebastian.huber@…>

  • libcsupport/src/symlink.c: Check if path evaluation handler exists.
  • libnetworking/lib/ftpfs.c: Cleanup.
  • Property mode set to 100644
File size: 23.3 KB
RevLine 
[8916bdc7]1/**
2 * @file
[dda0bffc]3 *
[8916bdc7]4 * @brief 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>
[dda0bffc]14 *
15 * (c) Copyright 2002
16 * Thomas Doerfler
17 * IMD Ingenieurbuero fuer Microcomputertechnik
18 * Herbststr. 8
[8916bdc7]19 * 82178 Puchheim, Germany
[dda0bffc]20 * <Thomas.Doerfler@imd-systems.de>
21 *
[8916bdc7]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$
[dda0bffc]32 */
33
[8916bdc7]34#include <ctype.h>
[dda0bffc]35#include <errno.h>
[8916bdc7]36#include <fcntl.h>
37#include <inttypes.h>
[dda0bffc]38#include <malloc.h>
[8916bdc7]39#include <netdb.h>
40#include <stdarg.h>
41#include <stdio.h>
[dda0bffc]42#include <stdlib.h>
[8916bdc7]43#include <string.h>
[dda0bffc]44#include <unistd.h>
[8916bdc7]45#include <arpa/inet.h>
46#include <netinet/in.h>
47#include <sys/socket.h>
48#include <sys/types.h>
49
[dda0bffc]50#include <rtems.h>
[8916bdc7]51#include <rtems/ftpfs.h>
52#include <rtems/imfs.h>
[dda0bffc]53#include <rtems/libio.h>
54#include <rtems/rtems_bsdnet.h>
[8916bdc7]55#include <rtems/seterr.h>
[dda0bffc]56
[8916bdc7]57#ifdef DEBUG
58  #define DEBUG_PRINTF( ...) printf( __VA_ARGS__)
59#else
60  #define DEBUG_PRINTF( ...)
[dda0bffc]61#endif
62
[8916bdc7]63/**
64 * @brief Connection entry for each open file stream.
[dda0bffc]65 */
[8916bdc7]66typedef struct {
67  /**
68   * @brief Control connection socket.
[dda0bffc]69   */
70  int ctrl_socket;
[8916bdc7]71
72  /**
73   * @brief Data transfer socket.
[dda0bffc]74   */
75  int data_socket;
[8916bdc7]76
77  /**
78   * @brief End of file flag.
[dda0bffc]79   */
[8916bdc7]80  bool eof;
81} rtems_ftpfs_entry;
[dda0bffc]82
[8916bdc7]83static bool rtems_ftpfs_verbose = false;
[dda0bffc]84
[8916bdc7]85rtems_status_code rtems_ftpfs_mount( const char *mount_point)
86{
87  int rv = 0;
[dda0bffc]88
[8916bdc7]89  if (mount_point == NULL) {
90    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
91  }
[dda0bffc]92
[8916bdc7]93  rv = mkdir( mount_point, S_IRWXU | S_IRWXG | S_IRWXO);
94  if (rv != 0) {
95    return RTEMS_IO_ERROR;
96  }
[dda0bffc]97
[8916bdc7]98  rv = mount(
99    NULL,
100    &rtems_ftpfs_ops,
101    RTEMS_FILESYSTEM_READ_WRITE,
102    NULL,
103    mount_point
104  );
105  if (rv != 0) {
106    return RTEMS_IO_ERROR;
107  }
[dda0bffc]108
[8916bdc7]109  return RTEMS_SUCCESSFUL;
110}
[dda0bffc]111
[8916bdc7]112rtems_status_code rtems_ftpfs_set_verbose( bool verbose)
113{
114  rtems_ftpfs_verbose = verbose;
[dda0bffc]115
[8916bdc7]116  return RTEMS_SUCCESSFUL;
117}
[dda0bffc]118
[8916bdc7]119rtems_status_code rtems_ftpfs_get_verbose( bool *verbose)
120{
121  if (verbose == NULL) {
122    return RTEMS_INVALID_ADDRESS;
123  }
[dda0bffc]124
[8916bdc7]125  *verbose = rtems_ftpfs_verbose;
[dda0bffc]126
[8916bdc7]127  return RTEMS_SUCCESSFUL;
128}
[dda0bffc]129
[8916bdc7]130int rtems_bsdnet_initialize_ftp_filesystem( void)
131{
132  rtems_status_code sc = RTEMS_SUCCESSFUL;
[dda0bffc]133
[8916bdc7]134  sc = rtems_ftpfs_mount( NULL);
135
136  if (sc == RTEMS_SUCCESSFUL) {
137    return 0;
138  } else {
139    return -1;
140  }
[dda0bffc]141}
142
[8916bdc7]143typedef void (*rtems_ftpfs_reply_parser)(
144  const char * /* reply fragment */,
145  size_t /* reply fragment length */,
146  void * /* parser argument */
147);
[dda0bffc]148
[8916bdc7]149typedef enum {
150  RTEMS_FTPFS_REPLY_START,
151  RTEMS_FTPFS_REPLY_SINGLE_LINE,
152  RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE,
153  RTEMS_FTPFS_REPLY_MULTI_LINE,
154  RTEMS_FTPFS_REPLY_NEW_LINE,
155  RTEMS_FTPFS_REPLY_NEW_LINE_START
156} rtems_ftpfs_reply_state;
[dda0bffc]157
[8916bdc7]158typedef enum {
159  RTEMS_FTPFS_REPLY_ERROR = 0,
160  RTEMS_FTPFS_REPLY_1 = '1',
161  RTEMS_FTPFS_REPLY_2 = '2',
162  RTEMS_FTPFS_REPLY_3 = '3',
163  RTEMS_FTPFS_REPLY_4 = '4',
164  RTEMS_FTPFS_REPLY_5 = '5'
165} rtems_ftpfs_reply;
[dda0bffc]166
[8916bdc7]167#define RTEMS_FTPFS_REPLY_SIZE 3
[dda0bffc]168
[8916bdc7]169static rtems_ftpfs_reply rtems_ftpfs_get_reply(
170  int socket,
171  rtems_ftpfs_reply_parser parser,
172  void *parser_arg
[dda0bffc]173)
174{
[8916bdc7]175  rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START;
176  bool verbose = rtems_ftpfs_verbose;
177  char reply_first [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' };
178  char reply_last [RTEMS_FTPFS_REPLY_SIZE] = { 'b', 'b', 'b' };
179  size_t reply_first_index = 0;
180  size_t reply_last_index = 0;
181  char buf [128];
[dda0bffc]182
[8916bdc7]183  while (true) {
184    /* Receive reply fragment from socket */
185    ssize_t i = 0;
186    ssize_t rv = recv( socket, buf, sizeof( buf), 0);
[dda0bffc]187
[8916bdc7]188    if (rv <= 0) {
189      return RTEMS_FTPFS_REPLY_ERROR;
[dda0bffc]190    }
[8916bdc7]191
192    /* Be verbose if necessary */
193    if (verbose) {
194      write( STDERR_FILENO, buf, (size_t) rv);
[dda0bffc]195    }
[8916bdc7]196
197    /* Invoke parser if necessary */
198    if (parser != NULL) {
199      parser( buf, (size_t) rv, parser_arg);
[dda0bffc]200    }
[8916bdc7]201
202    /* Parse reply fragment */
203    for (i = 0; i < rv; ++i) {
204      char c = buf [i];
205
206      switch (state) {
207        case RTEMS_FTPFS_REPLY_START:
208          if (reply_first_index < RTEMS_FTPFS_REPLY_SIZE) {
209            reply_first [reply_first_index] = c;
210            ++reply_first_index;
211          } else if (c == '-') {
212            state = RTEMS_FTPFS_REPLY_MULTI_LINE;
213          } else {
214            state = RTEMS_FTPFS_REPLY_SINGLE_LINE;
215          }
216          break;
217        case RTEMS_FTPFS_REPLY_SINGLE_LINE:
218          if (c == '\n') {
219            state = RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE;
220          }
221          break;
222        case RTEMS_FTPFS_REPLY_MULTI_LINE:
223          if (c == '\n') {
224            state = RTEMS_FTPFS_REPLY_NEW_LINE_START;
225            reply_last_index = 0;
226          }
227          break;
228        case RTEMS_FTPFS_REPLY_NEW_LINE:
229        case RTEMS_FTPFS_REPLY_NEW_LINE_START:
230          if (reply_last_index < RTEMS_FTPFS_REPLY_SIZE) {
231            state = RTEMS_FTPFS_REPLY_NEW_LINE;
232            reply_last [reply_last_index] = c;
233            ++reply_last_index;
234          } else {
235            state = RTEMS_FTPFS_REPLY_MULTI_LINE;
236          }
237          break;
238        default:
239          return RTEMS_FTPFS_REPLY_ERROR;
[dda0bffc]240      }
241    }
[8916bdc7]242
243    /* Check reply */
244    if (state == RTEMS_FTPFS_REPLY_SINGLE_LINE_DONE) {
245      if (
246        isdigit( reply_first [0])
247          && isdigit( reply_first [1])
248          && isdigit( reply_first [2])
249      ) {
250        break;
251      } else {
252        return RTEMS_FTPFS_REPLY_ERROR;
[dda0bffc]253      }
[8916bdc7]254    } else if (state == RTEMS_FTPFS_REPLY_NEW_LINE_START) {
255      bool ok = true;
256
257      for (i = 0; i < RTEMS_FTPFS_REPLY_SIZE; ++i) {
258        ok = ok
259          && reply_first [i] == reply_last [i]
260          && isdigit( reply_first [i]);
[dda0bffc]261      }
[8916bdc7]262
263      if (ok) {
264        break;
[dda0bffc]265      }
266    }
267  }
[8916bdc7]268
269  return reply_first [0];
270}
271
272static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser(
273  int socket,
274  const char *cmd,
275  const char *arg,
276  rtems_ftpfs_reply_parser parser,
277  void *parser_arg
278)
279{
280  const char *const eol = "\r\n";
281  bool verbose = rtems_ftpfs_verbose;
282  int rv = 0;
283
284  /* Send command */
285  rv = send( socket, cmd, strlen( cmd), 0);
286  if (rv < 0) {
287    return RTEMS_FTPFS_REPLY_ERROR;
[dda0bffc]288  }
[8916bdc7]289  if (verbose) {
290    write( STDERR_FILENO, cmd, strlen( cmd));
291  }
292
293  /* Send command argument if necessary */
294  if (arg != NULL) {
[9eab99a]295    rv = send( socket, arg, strlen( arg), 0);
296    if (rv < 0) {
297      return RTEMS_FTPFS_REPLY_ERROR;
298    }
[8916bdc7]299    if (verbose) {
300      write( STDERR_FILENO, arg, strlen( arg));
[dda0bffc]301    }
302  }
[8916bdc7]303
304  /* Send end of line */
305  rv = send( socket, eol, 2, 0);
306  if (rv < 0) {
307    return RTEMS_FTPFS_REPLY_ERROR;
308  }
309  if (verbose) {
310    write( STDERR_FILENO, &eol [1], 1);
311  }
312
313  /* Return reply */
314  return rtems_ftpfs_get_reply( socket, parser, parser_arg);
[dda0bffc]315}
[8916bdc7]316
317static rtems_ftpfs_reply rtems_ftpfs_send_command(
318  int socket,
319  const char *cmd,
320  const char *arg
[dda0bffc]321)
[8916bdc7]322{
323  return rtems_ftpfs_send_command_with_parser( socket, cmd, arg, NULL, NULL);
[dda0bffc]324}
325
[8916bdc7]326typedef enum {
327  STATE_USER_NAME,
328  STATE_START_PASSWORD,
329  STATE_START_HOST_NAME,
330  STATE_START_HOST_NAME_OR_PATH,
331  STATE_START_PATH,
332  STATE_PASSWORD,
333  STATE_HOST_NAME,
334  STATE_DONE,
335  STATE_INVALID
336} split_state;
[dda0bffc]337
[8916bdc7]338static bool rtems_ftpfs_split_names (
339  char *s,
340  const char **user,
341  const char **password,
342  const char **hostname,
343  const char **path
[dda0bffc]344)
345{
[8916bdc7]346  split_state state = STATE_USER_NAME;
347  size_t len = strlen( s);
348  size_t i = 0;
[dda0bffc]349
[8916bdc7]350  *user = s;
351  *password = NULL;
352  *hostname = NULL;
353  *path = NULL;
354
355  for (i = 0; i < len; ++i) {
356    char c = s [i];
357
358    switch (state) {
359      case STATE_USER_NAME:
360        if (c == ':') {
361          state = STATE_START_PASSWORD;
362          s [i] = '\0';
363        } else if (c == '@') {
364          state = STATE_START_HOST_NAME;
365          s [i] = '\0';
366        } else if (c == '/') {
367          state = STATE_START_HOST_NAME_OR_PATH;
368          s [i] = '\0';
369        }
370        break;
371      case STATE_START_PASSWORD:
372        state = STATE_PASSWORD;
373        *password = &s [i];
374        --i;
375        break;
376      case STATE_START_HOST_NAME:
377        state = STATE_HOST_NAME;
378        *hostname = &s [i];
379        --i;
380        break;
381      case STATE_START_HOST_NAME_OR_PATH:
382        if (c == '@') {
383          state = STATE_START_HOST_NAME;
384        } else {
385          state = STATE_DONE;
386          *path = &s [i];
387          goto done;
388        }
389        break;
390      case STATE_START_PATH:
391        state = STATE_DONE;
392        *path = &s [i];
393        goto done;
394      case STATE_PASSWORD:
395        if (c == '@') {
396          state = STATE_START_HOST_NAME;
397          s [i] = '\0';
398        } else if (c == '/') {
399          state = STATE_START_HOST_NAME_OR_PATH;
400          s [i] = '\0';
401        }
402        break;
403      case STATE_HOST_NAME:
404        if (c == '/') {
405          state = STATE_START_PATH;
406          s [i] = '\0';
407        }
408        break;
409      default:
410        state = STATE_INVALID;
411        goto done;
412    }
[dda0bffc]413  }
414
[8916bdc7]415done:
[dda0bffc]416
[8916bdc7]417  /* If we have no password use the user name */
418  if (*password == NULL) {
419    *password = *user;
420  }
421
422  return state == STATE_DONE;
[dda0bffc]423}
424
[8916bdc7]425static socklen_t rtems_ftpfs_create_address(
426  struct sockaddr_in *sa,
427  unsigned long address,
428  unsigned short port
[dda0bffc]429)
430{
[8916bdc7]431  memset( sa, sizeof( *sa), 0);
432
433  sa->sin_family = AF_INET;
434  sa->sin_addr.s_addr = address;
435  sa->sin_port = port;
436  sa->sin_len = sizeof( *sa);
437
438  return sizeof( *sa);
439}
440
[9eab99a]441static void rtems_ftpfs_terminate( rtems_libio_t *iop)
[8916bdc7]442{
[9eab99a]443  rtems_ftpfs_entry *e = iop->data1;
444
[8916bdc7]445  if (e != NULL) {
446    /* Close data connection if necessary */
447    if (e->data_socket >= 0) {
448      close( e->data_socket);
[dda0bffc]449    }
[8916bdc7]450
451    /* Close control connection if necessary */
452    if (e->ctrl_socket >= 0) {
453      close( e->ctrl_socket);
[dda0bffc]454    }
[8916bdc7]455
456    /* Free connection entry */
457    free( e);
[dda0bffc]458  }
[8916bdc7]459
460  /* Invalidate IO entry */
461  iop->data1 = NULL;
462}
463
464static int rtems_ftpfs_open_ctrl_connection(
465  rtems_ftpfs_entry *e,
466  const char *user,
467  const char *password,
468  const char *hostname,
469  uint32_t *client_address
470)
471{
472  int rv = 0;
473  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
474  struct in_addr address = { .s_addr = 0 };
475  struct sockaddr_in sa;
476  socklen_t size = 0;
477
478  /* Create the socket for the control connection */
479  e->ctrl_socket = socket( AF_INET, SOCK_STREAM, 0);
480  if (e->ctrl_socket < 0) {
481    return ENOMEM;
[dda0bffc]482  }
[8916bdc7]483
484  /* Set up the server address from the hostname */
485  if (hostname == NULL || strlen( hostname) == 0) {
486    /* Default to BOOTP server address */
487    address = rtems_bsdnet_bootp_server_address;
488  } else if (inet_aton( hostname, &address) == 0) {
489    /* Try to get the address by name */
490    struct hostent *he = gethostbyname( hostname);
491
492    if (he != NULL) {
493      memcpy( &address, he->h_addr, sizeof( address));
494    } else {
495      return ENOENT;
[dda0bffc]496    }
497  }
[8916bdc7]498  rtems_ftpfs_create_address( &sa, address.s_addr, htons( RTEMS_FTPFS_CTRL_PORT));
499  DEBUG_PRINTF( "server = %s\n", inet_ntoa( sa.sin_addr));
500
501  /* Open control connection */
502  rv = connect(
503    e->ctrl_socket,
504    (struct sockaddr *) &sa,
505    sizeof( sa)
506  );
507  if (rv != 0) {
508    return ENOENT;
[dda0bffc]509  }
510
[8916bdc7]511  /* Get client address */
512  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
513  rv = getsockname(
514    e->ctrl_socket,
515    (struct sockaddr *) &sa,
516    &size
517  );
518  if (rv != 0) {
519    return ENOMEM;
[dda0bffc]520  }
[8916bdc7]521  *client_address = ntohl( sa.sin_addr.s_addr);
522  DEBUG_PRINTF( "client = %s\n", inet_ntoa( sa.sin_addr));
523
524  /* Now we should get a welcome message from the server */
525  reply = rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
526  if (reply != RTEMS_FTPFS_REPLY_2) {
527    return ENOENT;
[dda0bffc]528  }
[8916bdc7]529
530  /* Send USER command */
531  reply = rtems_ftpfs_send_command( e->ctrl_socket, "USER ", user);
532  if (reply == RTEMS_FTPFS_REPLY_3) {
533    /* Send PASS command */
534    reply = rtems_ftpfs_send_command( e->ctrl_socket, "PASS ", password);
535    if (reply != RTEMS_FTPFS_REPLY_2) {
536      return EACCES;
[dda0bffc]537    }
[8916bdc7]538
539    /* TODO: Some server may require an account */
540  } else if (reply != RTEMS_FTPFS_REPLY_2) {
541    return EACCES;
[dda0bffc]542  }
[8916bdc7]543
544  /* Send TYPE command to set binary mode for all data transfers */
545  reply = rtems_ftpfs_send_command( e->ctrl_socket, "TYPE I", NULL);
546  if (reply != RTEMS_FTPFS_REPLY_2) {
547    return EIO;
[dda0bffc]548  }
[8916bdc7]549
550  return 0;
551}
552
553static int rtems_ftpfs_open_data_connection_active(
554  rtems_ftpfs_entry *e,
555  uint32_t client_address,
556  const char *file_command,
557  const char *filename
558)
559{
560  int rv = 0;
561  int eno = 0;
562  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
563  struct sockaddr_in sa;
564  socklen_t size = 0;
565  int port_socket = -1;
566  char port_command [] = "PORT 000,000,000,000,000,000";
567  uint16_t data_port = 0;
568
569  /* Create port socket to establish a data data connection */
570  port_socket = socket( AF_INET, SOCK_STREAM, 0);
571  if (port_socket < 0) {
572    eno = ENOMEM;
573    goto cleanup;
[dda0bffc]574  }
[8916bdc7]575
576  /* Bind port socket */
577  rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
578  rv = bind(
579    port_socket,
580    (struct sockaddr *) &sa,
581    sizeof( sa)
582  );
583  if (rv != 0) {
584    eno = EBUSY;
585    goto cleanup;
[dda0bffc]586  }
[8916bdc7]587
588  /* Get port number for data socket */
589  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
590  rv = getsockname(
591    port_socket,
592    (struct sockaddr *) &sa,
593    &size
594  );
595  if (rv != 0) {
596    eno = ENOMEM;
597    goto cleanup;
[dda0bffc]598  }
[8916bdc7]599  data_port = ntohs( sa.sin_port);
600
601  /* Send PORT command to set data connection port for server */
602  snprintf(
603    port_command,
604    sizeof( port_command),
605    "PORT %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%" PRIu16,
606    (client_address >> 24) & 0xff,
607    (client_address >> 16) & 0xff,
608    (client_address >> 8) & 0xff,
609    (client_address >> 0) & 0xff,
610    (data_port >> 8) & 0xff,
611    (data_port >> 0) & 0xff
612  );
613  reply = rtems_ftpfs_send_command( e->ctrl_socket, port_command, NULL);
614  if (reply != RTEMS_FTPFS_REPLY_2) {
615    eno = ENOTSUP;
616    goto cleanup;
[dda0bffc]617  }
[8916bdc7]618
619  /* Listen on port socket for incoming data connections */
620  rv = listen( port_socket, 1);
621  if (rv != 0) {
622    eno = EBUSY;
623    goto cleanup;
[dda0bffc]624  }
[8916bdc7]625
626  /* Send RETR or STOR command with filename */
627  reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
628  if (reply != RTEMS_FTPFS_REPLY_1) {
629    eno = EIO;
630    goto cleanup;
[dda0bffc]631  }
[8916bdc7]632
[dda0bffc]633  /*
[8916bdc7]634   * Wait for connect on data connection.
635   *
636   * FIXME: This should become a select instead with a timeout.
[dda0bffc]637   */
[8916bdc7]638  size = sizeof( sa);
639  e->data_socket = accept(
640    port_socket,
641    (struct sockaddr *) &sa,
642    &size
643  );
644  if (e->data_socket < 0) {
645    eno = EIO;
646    goto cleanup;
[dda0bffc]647  }
[8916bdc7]648
649  /* FIXME: Check, that server data address is really from our server  */
650
651cleanup:
652
653  /* Close port socket if necessary */
654  if (port_socket >= 0) {
655    close( port_socket);
656  }
657
658  return eno;
659}
660
661typedef enum {
662  RTEMS_FTPFS_PASV_START = 0,
663  RTEMS_FTPFS_PASV_JUNK,
664  RTEMS_FTPFS_PASV_DATA,
665  RTEMS_FTPFS_PASV_DONE
666} rtems_ftpfs_pasv_state;
667
668typedef struct {
669  rtems_ftpfs_pasv_state state;
670  uint8_t data [6];
671  size_t index;
672} rtems_ftpfs_pasv_entry;
673
674static void rtems_ftpfs_pasv_parser(
675  const char* buf,
676  size_t len,
677  void *arg
678)
679{
[9eab99a]680  rtems_ftpfs_pasv_entry *e = arg;
[8916bdc7]681  size_t i = 0;
682
683  for (i = 0; i < len; ++i) {
684    char c = buf [i];
685
686    switch (e->state) {
687      case RTEMS_FTPFS_PASV_START:
688        if (!isdigit( c)) {
689          e->state = RTEMS_FTPFS_PASV_JUNK;
690          e->index = 0;
691        }
692        break;
693      case RTEMS_FTPFS_PASV_JUNK:
694        if (isdigit( c)) {
695          e->state = RTEMS_FTPFS_PASV_DATA;
696          e->data [e->index] = (uint8_t) (c - '0');
697        }
698        break;
699      case RTEMS_FTPFS_PASV_DATA:
700        if (isdigit( c)) {
701          e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0');
702        } else if (c == ',') {
703          ++e->index;
704          if (e->index < sizeof( e->data)) {
705            e->data [e->index] = 0;
706          } else {
707            e->state = RTEMS_FTPFS_PASV_DONE;
708          }
709        } else {
710          e->state = RTEMS_FTPFS_PASV_DONE;
711        }
712        break;
713      default:
714        return;
[dda0bffc]715    }
716  }
[8916bdc7]717}
718
719static int rtems_ftpfs_open_data_connection_passive(
720  rtems_ftpfs_entry *e,
721  uint32_t client_address,
722  const char *file_command,
723  const char *filename
724)
725{
726  int rv = 0;
727  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
728  struct sockaddr_in sa;
729  uint32_t data_address = 0;
730  uint16_t data_port = 0;
731
732  rtems_ftpfs_pasv_entry pe = {
733    .state = RTEMS_FTPFS_PASV_START
734  };
735
736  /* Send PASV command */
737  reply = rtems_ftpfs_send_command_with_parser(
738    e->ctrl_socket,
739    "PASV",
740    NULL,
741    rtems_ftpfs_pasv_parser,
742    &pe
743  );
744  if (reply != RTEMS_FTPFS_REPLY_2) {
745    return ENOTSUP;
[dda0bffc]746  }
[8916bdc7]747  data_address = (uint32_t) ((pe.data [0] << 24) + (pe.data [1] << 16)
748    + (pe.data [2] << 8) + pe.data [3]);
749  data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]);
750  rtems_ftpfs_create_address( &sa, htonl( data_address), htons( data_port));
751  DEBUG_PRINTF(
752    "server data = %s:%u\n",
753    inet_ntoa( sa.sin_addr),
754    (unsigned) ntohs( sa.sin_port)
755  );
756
757  /* Create data socket */
758  e->data_socket = socket( AF_INET, SOCK_STREAM, 0);
759  if (e->data_socket < 0) {
760    return ENOMEM;
[dda0bffc]761  }
[8916bdc7]762
763  /* Open data connection */
764  rv = connect(
765    e->data_socket,
766    (struct sockaddr *) &sa,
767    sizeof( sa)
768  );
769  if (rv != 0) {
770    return EIO;
[dda0bffc]771  }
[8916bdc7]772
773  /* Send RETR or STOR command with filename */
774  reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
775  if (reply != RTEMS_FTPFS_REPLY_1) {
776    return EIO;
[dda0bffc]777  }
[8916bdc7]778
779  return 0;
780}
781
782static int rtems_ftpfs_open(
783  rtems_libio_t *iop,
784  const char *path,
785  uint32_t flags,
786  uint32_t mode
787)
788{
789  int eno = 0;
790  bool ok = false;
791  rtems_ftpfs_entry *e = NULL;
792  const char *user = NULL;
793  const char *password = NULL;
794  const char *hostname = NULL;
795  const char *filename = NULL;
796  const char *file_command = (iop->flags & LIBIO_FLAGS_WRITE) != 0
797    ? "STOR "
798    : "RETR ";
799  uint32_t client_address = 0;
[9eab99a]800  char *location = iop->file_info;
[8916bdc7]801
[9eab99a]802  /* Invalidate data handle */
803  iop->data1 = NULL;
804
805  /* Check location, it was allocated during path evaluation */
[8916bdc7]806  if (location == NULL) {
807    return ENOMEM;
[dda0bffc]808  }
[8916bdc7]809
810  /* Check for either read-only or write-only flags */
811  if (
812    (iop->flags & LIBIO_FLAGS_WRITE) != 0
813      && (iop->flags & LIBIO_FLAGS_READ) != 0
814  ) {
[9eab99a]815    return ENOTSUP;
[dda0bffc]816  }
[8916bdc7]817
818  /* Split location into parts */
819  ok = rtems_ftpfs_split_names(
820      location,
821      &user,
822      &password,
823      &hostname,
824      &filename
825  );
826  if (!ok) {
[9eab99a]827    return ENOENT;
[dda0bffc]828  }
[8916bdc7]829  DEBUG_PRINTF(
830    "user = '%s', password = '%s', filename = '%s'\n",
831    user,
832    password,
833    filename
834  );
835
836  /* Allocate connection entry */
837  e = malloc( sizeof( *e));
838  if (e == NULL) {
[9eab99a]839    return ENOMEM;
[dda0bffc]840  }
[8916bdc7]841
842  /* Initialize connection entry */
843  e->ctrl_socket = -1;
844  e->data_socket = -1;
845  e->eof = false;
846
[9eab99a]847  /* Save connection state */
848  iop->data1 = e;
849
[8916bdc7]850  /* Open control connection */
851  eno = rtems_ftpfs_open_ctrl_connection(
852    e,
853    user,
854    password,
855    hostname,
856    &client_address
857  );
[dda0bffc]858  if (eno != 0) {
[8916bdc7]859    goto cleanup;
[dda0bffc]860  }
[8916bdc7]861
862  /* Open passive data connection */
863  eno = rtems_ftpfs_open_data_connection_passive(
864    e,
865    client_address,
866    file_command,
867    filename
868  );
869  if (eno == ENOTSUP) {
870    /* Open active data connection */
871    eno = rtems_ftpfs_open_data_connection_active(
872      e,
873      client_address,
874      file_command,
875      filename
876    );
[dda0bffc]877  }
[8916bdc7]878
879cleanup:
880
[9eab99a]881  if (eno != 0) {
[8916bdc7]882    /* Free all resources if an error occured */
[9eab99a]883    rtems_ftpfs_terminate( iop);
[dda0bffc]884  }
[8916bdc7]885
[dda0bffc]886  return eno;
887}
888
[8916bdc7]889static ssize_t rtems_ftpfs_read(
[dda0bffc]890  rtems_libio_t *iop,
[8916bdc7]891  void *buffer,
892  size_t count
[dda0bffc]893)
894{
[9eab99a]895  rtems_ftpfs_entry *e = iop->data1;
896  char *in = buffer;
[8916bdc7]897  size_t todo = count;
[dda0bffc]898
[8916bdc7]899  if (e->eof) {
900    return 0;
901  }
902
903  while (todo > 0) {
904    ssize_t rv = recv( e->data_socket, in, todo, 0);
905
906    if (rv <= 0) {
907      if (rv == 0) {
908        rtems_ftpfs_reply reply =
909          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
910
911        if (reply == RTEMS_FTPFS_REPLY_2) {
912          e->eof = true;
913          break;
914        }
[dda0bffc]915      }
[8916bdc7]916
917      rtems_set_errno_and_return_minus_one( EIO);
[dda0bffc]918    }
[8916bdc7]919
920    in += rv;
921    todo -= (size_t) rv;
[dda0bffc]922  }
[8916bdc7]923
924  return (ssize_t) (count - todo);
[dda0bffc]925}
926
[8916bdc7]927static ssize_t rtems_ftpfs_write(
[dda0bffc]928  rtems_libio_t *iop,
[8916bdc7]929  const void *buffer,
930  size_t count
[dda0bffc]931)
932{
[9eab99a]933  rtems_ftpfs_entry *e = iop->data1;
934  const char *out = buffer;
[8916bdc7]935  size_t todo = count;
[dda0bffc]936
[8916bdc7]937  if (e->eof) {
938    return 0;
939  }
940
941  while (todo > 0) {
942    ssize_t rv = send( e->data_socket, out, todo, 0);
943
944    if (rv <= 0) {
945      if (rv == 0) {
946        rtems_ftpfs_reply reply =
947          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
948
949        if (reply == RTEMS_FTPFS_REPLY_2) {
950          e->eof = true;
951          break;
952        }
[dda0bffc]953      }
[8916bdc7]954
955      rtems_set_errno_and_return_minus_one( EIO);
[dda0bffc]956    }
[8916bdc7]957
958    out += rv;
959    todo -= (size_t) rv;
[dda0bffc]960  }
[8916bdc7]961
962  return (ssize_t) (count - todo);
[dda0bffc]963}
964
[8916bdc7]965static int rtems_ftpfs_close( rtems_libio_t *iop)
[dda0bffc]966{
[9eab99a]967  rtems_ftpfs_terminate( iop);
[8916bdc7]968
969  return 0;
970}
971
972/* Dummy version to let fopen( *,"w") work properly */
973static int rtems_ftpfs_ftruncate( rtems_libio_t *iop, off_t count)
974{
975  return 0;
976}
977
978static int rtems_ftpfs_eval_path(
979  const char *pathname,
980  int flags,
981  rtems_filesystem_location_info_t *pathloc
982)
983{
[dda0bffc]984  /*
[8916bdc7]985   * The caller of this routine has striped off the mount prefix from the path.
986   * We need to store this path here or otherwise we would have to do this job
[9eab99a]987   * again.  The path is used in rtems_ftpfs_open() via iop->file_info.
[dda0bffc]988   */
[9eab99a]989  pathloc->node_access = strdup( pathname);
[dda0bffc]990
991  return 0;
992}
993
[9eab99a]994static int rtems_ftpfs_free_node( rtems_filesystem_location_info_t *pathloc)
[dda0bffc]995{
[9eab99a]996  free( pathloc->node_access);
997
998  return 0;
[dda0bffc]999}
1000
[8916bdc7]1001static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1002  rtems_filesystem_location_info_t *pathloc
[dda0bffc]1003)
1004{
[8916bdc7]1005  return RTEMS_FILESYSTEM_MEMORY_FILE;
1006}
1007
1008static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
1009
1010static int rtems_ftpfs_mount_me(
1011  rtems_filesystem_mount_table_entry_t *e
1012)
1013{
1014  /* Set handler and oparations table */
1015  e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
1016  e->mt_fs_root.ops = &rtems_ftpfs_ops;
1017
1018  /* We have no FTP file system specific data to maintain */
1019  e->fs_info = NULL;
1020
1021  /* We maintain no real file system nodes, so there is no real root */
1022  e->mt_fs_root.node_access = NULL;
1023
1024  /* Just use the limits from IMFS */
1025  e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
1026
1027  return 0;
[dda0bffc]1028}
1029
[8916bdc7]1030/*
1031 * The stat() support is intended only for the cp shell command.  Each request
1032 * will return that we have a regular file with read, write and execute
1033 * permissions for every one.  The node index uses a global counter to support
1034 * a remote to remote copy.  Is not a very sophisticated method.
1035 */
1036static int rtems_ftpfs_fstat(
1037  rtems_filesystem_location_info_t *loc,
1038  struct stat *st
[dda0bffc]1039)
1040{
[8916bdc7]1041  static unsigned ino = 0;
1042
1043  memset( st, 0, sizeof( *st));
1044
1045  /* FIXME */
1046  st->st_ino = ++ino;
1047
1048  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1049
1050  return 0;
[dda0bffc]1051}
1052
[8916bdc7]1053const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1054  .evalpath_h = rtems_ftpfs_eval_path,
[9eab99a]1055  .evalformake_h = NULL,
[8916bdc7]1056  .link_h = NULL,
1057  .unlink_h = NULL,
1058  .node_type_h = rtems_ftpfs_node_type,
1059  .mknod_h = NULL,
1060  .chown_h = NULL,
[9eab99a]1061  .freenod_h = rtems_ftpfs_free_node,
[8916bdc7]1062  .mount_h = NULL,
1063  .fsmount_me_h = rtems_ftpfs_mount_me,
1064  .unmount_h = NULL,
1065  .fsunmount_me_h = NULL,
1066  .utime_h = NULL,
1067  .eval_link_h = NULL,
1068  .symlink_h = NULL,
1069  .readlink_h = NULL
[dda0bffc]1070};
[8916bdc7]1071
1072static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1073  .open_h = rtems_ftpfs_open,
1074  .close_h = rtems_ftpfs_close,
1075  .read_h = rtems_ftpfs_read,
1076  .write_h = rtems_ftpfs_write,
1077  .ioctl_h = NULL,
1078  .lseek_h = NULL,
1079  .fstat_h = rtems_ftpfs_fstat,
1080  .fchmod_h = NULL,
1081  .ftruncate_h = rtems_ftpfs_ftruncate,
1082  .fpathconf_h = NULL,
1083  .fsync_h = NULL,
1084  .fdatasync_h = NULL,
1085  .fcntl_h = NULL,
1086  .rmnod_h = NULL
[dda0bffc]1087};
Note: See TracBrowser for help on using the repository browser.