source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 1cb5aa14

4.104.115
Last change on this file since 1cb5aa14 was 1cb5aa14, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 04/14/09 at 08:49:12

Fixed return codes of open handler.
Request reply after data write connection close.

  • Property mode set to 100644
File size: 23.9 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
[1cb5aa14]441static int rtems_ftpfs_terminate( rtems_libio_t *iop)
[8916bdc7]442{
[1cb5aa14]443  int eno = 0;
444  int rv = 0;
[9eab99a]445  rtems_ftpfs_entry *e = iop->data1;
446
[8916bdc7]447  if (e != NULL) {
448    /* Close data connection if necessary */
449    if (e->data_socket >= 0) {
[1cb5aa14]450      rv = close( e->data_socket);
451      if (rv < 0) {
452        eno = errno;
453      }
454
455      /* For write connections we have to obtain the transfer reply  */
456      if ((iop->flags & LIBIO_FLAGS_WRITE) != 0 && e->ctrl_socket >= 0) {
457        rtems_ftpfs_reply reply =
458          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
459
460        if (reply != RTEMS_FTPFS_REPLY_2) {
461          eno = EIO;
462        }
463      }
[dda0bffc]464    }
[8916bdc7]465
466    /* Close control connection if necessary */
467    if (e->ctrl_socket >= 0) {
[1cb5aa14]468      rv = close( e->ctrl_socket);
469      if (rv < 0) {
470        eno = errno;
471      }
[dda0bffc]472    }
[8916bdc7]473
474    /* Free connection entry */
475    free( e);
[dda0bffc]476  }
[8916bdc7]477
478  /* Invalidate IO entry */
479  iop->data1 = NULL;
[1cb5aa14]480
481  return eno;
[8916bdc7]482}
483
484static int rtems_ftpfs_open_ctrl_connection(
485  rtems_ftpfs_entry *e,
486  const char *user,
487  const char *password,
488  const char *hostname,
489  uint32_t *client_address
490)
491{
492  int rv = 0;
493  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
494  struct in_addr address = { .s_addr = 0 };
495  struct sockaddr_in sa;
496  socklen_t size = 0;
497
498  /* Create the socket for the control connection */
499  e->ctrl_socket = socket( AF_INET, SOCK_STREAM, 0);
500  if (e->ctrl_socket < 0) {
501    return ENOMEM;
[dda0bffc]502  }
[8916bdc7]503
504  /* Set up the server address from the hostname */
505  if (hostname == NULL || strlen( hostname) == 0) {
506    /* Default to BOOTP server address */
507    address = rtems_bsdnet_bootp_server_address;
508  } else if (inet_aton( hostname, &address) == 0) {
509    /* Try to get the address by name */
510    struct hostent *he = gethostbyname( hostname);
511
512    if (he != NULL) {
513      memcpy( &address, he->h_addr, sizeof( address));
514    } else {
515      return ENOENT;
[dda0bffc]516    }
517  }
[8916bdc7]518  rtems_ftpfs_create_address( &sa, address.s_addr, htons( RTEMS_FTPFS_CTRL_PORT));
519  DEBUG_PRINTF( "server = %s\n", inet_ntoa( sa.sin_addr));
520
521  /* Open control connection */
522  rv = connect(
523    e->ctrl_socket,
524    (struct sockaddr *) &sa,
525    sizeof( sa)
526  );
527  if (rv != 0) {
528    return ENOENT;
[dda0bffc]529  }
530
[8916bdc7]531  /* Get client address */
532  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
533  rv = getsockname(
534    e->ctrl_socket,
535    (struct sockaddr *) &sa,
536    &size
537  );
538  if (rv != 0) {
539    return ENOMEM;
[dda0bffc]540  }
[8916bdc7]541  *client_address = ntohl( sa.sin_addr.s_addr);
542  DEBUG_PRINTF( "client = %s\n", inet_ntoa( sa.sin_addr));
543
544  /* Now we should get a welcome message from the server */
545  reply = rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
546  if (reply != RTEMS_FTPFS_REPLY_2) {
547    return ENOENT;
[dda0bffc]548  }
[8916bdc7]549
550  /* Send USER command */
551  reply = rtems_ftpfs_send_command( e->ctrl_socket, "USER ", user);
552  if (reply == RTEMS_FTPFS_REPLY_3) {
553    /* Send PASS command */
554    reply = rtems_ftpfs_send_command( e->ctrl_socket, "PASS ", password);
555    if (reply != RTEMS_FTPFS_REPLY_2) {
556      return EACCES;
[dda0bffc]557    }
[8916bdc7]558
559    /* TODO: Some server may require an account */
560  } else if (reply != RTEMS_FTPFS_REPLY_2) {
561    return EACCES;
[dda0bffc]562  }
[8916bdc7]563
564  /* Send TYPE command to set binary mode for all data transfers */
565  reply = rtems_ftpfs_send_command( e->ctrl_socket, "TYPE I", NULL);
566  if (reply != RTEMS_FTPFS_REPLY_2) {
567    return EIO;
[dda0bffc]568  }
[8916bdc7]569
570  return 0;
571}
572
573static int rtems_ftpfs_open_data_connection_active(
574  rtems_ftpfs_entry *e,
575  uint32_t client_address,
576  const char *file_command,
577  const char *filename
578)
579{
580  int rv = 0;
581  int eno = 0;
582  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
583  struct sockaddr_in sa;
584  socklen_t size = 0;
585  int port_socket = -1;
586  char port_command [] = "PORT 000,000,000,000,000,000";
587  uint16_t data_port = 0;
588
589  /* Create port socket to establish a data data connection */
590  port_socket = socket( AF_INET, SOCK_STREAM, 0);
591  if (port_socket < 0) {
592    eno = ENOMEM;
593    goto cleanup;
[dda0bffc]594  }
[8916bdc7]595
596  /* Bind port socket */
597  rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
598  rv = bind(
599    port_socket,
600    (struct sockaddr *) &sa,
601    sizeof( sa)
602  );
603  if (rv != 0) {
604    eno = EBUSY;
605    goto cleanup;
[dda0bffc]606  }
[8916bdc7]607
608  /* Get port number for data socket */
609  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
610  rv = getsockname(
611    port_socket,
612    (struct sockaddr *) &sa,
613    &size
614  );
615  if (rv != 0) {
616    eno = ENOMEM;
617    goto cleanup;
[dda0bffc]618  }
[8916bdc7]619  data_port = ntohs( sa.sin_port);
620
621  /* Send PORT command to set data connection port for server */
622  snprintf(
623    port_command,
624    sizeof( port_command),
625    "PORT %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%" PRIu16,
626    (client_address >> 24) & 0xff,
627    (client_address >> 16) & 0xff,
628    (client_address >> 8) & 0xff,
629    (client_address >> 0) & 0xff,
630    (data_port >> 8) & 0xff,
631    (data_port >> 0) & 0xff
632  );
633  reply = rtems_ftpfs_send_command( e->ctrl_socket, port_command, NULL);
634  if (reply != RTEMS_FTPFS_REPLY_2) {
635    eno = ENOTSUP;
636    goto cleanup;
[dda0bffc]637  }
[8916bdc7]638
639  /* Listen on port socket for incoming data connections */
640  rv = listen( port_socket, 1);
641  if (rv != 0) {
642    eno = EBUSY;
643    goto cleanup;
[dda0bffc]644  }
[8916bdc7]645
646  /* Send RETR or STOR command with filename */
647  reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
648  if (reply != RTEMS_FTPFS_REPLY_1) {
649    eno = EIO;
650    goto cleanup;
[dda0bffc]651  }
[8916bdc7]652
[dda0bffc]653  /*
[8916bdc7]654   * Wait for connect on data connection.
655   *
656   * FIXME: This should become a select instead with a timeout.
[dda0bffc]657   */
[8916bdc7]658  size = sizeof( sa);
659  e->data_socket = accept(
660    port_socket,
661    (struct sockaddr *) &sa,
662    &size
663  );
664  if (e->data_socket < 0) {
665    eno = EIO;
666    goto cleanup;
[dda0bffc]667  }
[8916bdc7]668
669  /* FIXME: Check, that server data address is really from our server  */
670
671cleanup:
672
673  /* Close port socket if necessary */
674  if (port_socket >= 0) {
675    close( port_socket);
676  }
677
678  return eno;
679}
680
681typedef enum {
682  RTEMS_FTPFS_PASV_START = 0,
683  RTEMS_FTPFS_PASV_JUNK,
684  RTEMS_FTPFS_PASV_DATA,
685  RTEMS_FTPFS_PASV_DONE
686} rtems_ftpfs_pasv_state;
687
688typedef struct {
689  rtems_ftpfs_pasv_state state;
690  uint8_t data [6];
691  size_t index;
692} rtems_ftpfs_pasv_entry;
693
694static void rtems_ftpfs_pasv_parser(
695  const char* buf,
696  size_t len,
697  void *arg
698)
699{
[9eab99a]700  rtems_ftpfs_pasv_entry *e = arg;
[8916bdc7]701  size_t i = 0;
702
703  for (i = 0; i < len; ++i) {
704    char c = buf [i];
705
706    switch (e->state) {
707      case RTEMS_FTPFS_PASV_START:
708        if (!isdigit( c)) {
709          e->state = RTEMS_FTPFS_PASV_JUNK;
710          e->index = 0;
711        }
712        break;
713      case RTEMS_FTPFS_PASV_JUNK:
714        if (isdigit( c)) {
715          e->state = RTEMS_FTPFS_PASV_DATA;
716          e->data [e->index] = (uint8_t) (c - '0');
717        }
718        break;
719      case RTEMS_FTPFS_PASV_DATA:
720        if (isdigit( c)) {
721          e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0');
722        } else if (c == ',') {
723          ++e->index;
724          if (e->index < sizeof( e->data)) {
725            e->data [e->index] = 0;
726          } else {
727            e->state = RTEMS_FTPFS_PASV_DONE;
728          }
729        } else {
730          e->state = RTEMS_FTPFS_PASV_DONE;
731        }
732        break;
733      default:
734        return;
[dda0bffc]735    }
736  }
[8916bdc7]737}
738
739static int rtems_ftpfs_open_data_connection_passive(
740  rtems_ftpfs_entry *e,
741  uint32_t client_address,
742  const char *file_command,
743  const char *filename
744)
745{
746  int rv = 0;
747  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
748  struct sockaddr_in sa;
749  uint32_t data_address = 0;
750  uint16_t data_port = 0;
751
752  rtems_ftpfs_pasv_entry pe = {
753    .state = RTEMS_FTPFS_PASV_START
754  };
755
756  /* Send PASV command */
757  reply = rtems_ftpfs_send_command_with_parser(
758    e->ctrl_socket,
759    "PASV",
760    NULL,
761    rtems_ftpfs_pasv_parser,
762    &pe
763  );
764  if (reply != RTEMS_FTPFS_REPLY_2) {
765    return ENOTSUP;
[dda0bffc]766  }
[8916bdc7]767  data_address = (uint32_t) ((pe.data [0] << 24) + (pe.data [1] << 16)
768    + (pe.data [2] << 8) + pe.data [3]);
769  data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]);
770  rtems_ftpfs_create_address( &sa, htonl( data_address), htons( data_port));
771  DEBUG_PRINTF(
772    "server data = %s:%u\n",
773    inet_ntoa( sa.sin_addr),
774    (unsigned) ntohs( sa.sin_port)
775  );
776
777  /* Create data socket */
778  e->data_socket = socket( AF_INET, SOCK_STREAM, 0);
779  if (e->data_socket < 0) {
780    return ENOMEM;
[dda0bffc]781  }
[8916bdc7]782
783  /* Open data connection */
784  rv = connect(
785    e->data_socket,
786    (struct sockaddr *) &sa,
787    sizeof( sa)
788  );
789  if (rv != 0) {
790    return EIO;
[dda0bffc]791  }
[8916bdc7]792
793  /* Send RETR or STOR command with filename */
794  reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
795  if (reply != RTEMS_FTPFS_REPLY_1) {
796    return EIO;
[dda0bffc]797  }
[8916bdc7]798
799  return 0;
800}
801
802static int rtems_ftpfs_open(
803  rtems_libio_t *iop,
804  const char *path,
805  uint32_t flags,
806  uint32_t mode
807)
808{
809  int eno = 0;
810  bool ok = false;
811  rtems_ftpfs_entry *e = NULL;
812  const char *user = NULL;
813  const char *password = NULL;
814  const char *hostname = NULL;
815  const char *filename = NULL;
816  const char *file_command = (iop->flags & LIBIO_FLAGS_WRITE) != 0
817    ? "STOR "
818    : "RETR ";
819  uint32_t client_address = 0;
[9eab99a]820  char *location = iop->file_info;
[8916bdc7]821
[9eab99a]822  /* Invalidate data handle */
823  iop->data1 = NULL;
824
825  /* Check location, it was allocated during path evaluation */
[8916bdc7]826  if (location == NULL) {
[1cb5aa14]827    rtems_set_errno_and_return_minus_one( ENOMEM);
[dda0bffc]828  }
[8916bdc7]829
830  /* Check for either read-only or write-only flags */
831  if (
832    (iop->flags & LIBIO_FLAGS_WRITE) != 0
833      && (iop->flags & LIBIO_FLAGS_READ) != 0
834  ) {
[1cb5aa14]835    rtems_set_errno_and_return_minus_one( ENOTSUP);
[dda0bffc]836  }
[8916bdc7]837
838  /* Split location into parts */
839  ok = rtems_ftpfs_split_names(
840      location,
841      &user,
842      &password,
843      &hostname,
844      &filename
845  );
846  if (!ok) {
[1cb5aa14]847    rtems_set_errno_and_return_minus_one( ENOENT);
[dda0bffc]848  }
[8916bdc7]849  DEBUG_PRINTF(
850    "user = '%s', password = '%s', filename = '%s'\n",
851    user,
852    password,
853    filename
854  );
855
856  /* Allocate connection entry */
857  e = malloc( sizeof( *e));
858  if (e == NULL) {
[1cb5aa14]859    rtems_set_errno_and_return_minus_one( ENOMEM);
[dda0bffc]860  }
[8916bdc7]861
862  /* Initialize connection entry */
863  e->ctrl_socket = -1;
864  e->data_socket = -1;
865  e->eof = false;
866
[9eab99a]867  /* Save connection state */
868  iop->data1 = e;
869
[8916bdc7]870  /* Open control connection */
871  eno = rtems_ftpfs_open_ctrl_connection(
872    e,
873    user,
874    password,
875    hostname,
876    &client_address
877  );
[dda0bffc]878  if (eno != 0) {
[8916bdc7]879    goto cleanup;
[dda0bffc]880  }
[8916bdc7]881
882  /* Open passive data connection */
883  eno = rtems_ftpfs_open_data_connection_passive(
884    e,
885    client_address,
886    file_command,
887    filename
888  );
889  if (eno == ENOTSUP) {
890    /* Open active data connection */
891    eno = rtems_ftpfs_open_data_connection_active(
892      e,
893      client_address,
894      file_command,
895      filename
896    );
[dda0bffc]897  }
[8916bdc7]898
899cleanup:
900
[1cb5aa14]901  if (eno == 0) {
902    return 0;
903  } else {
[8916bdc7]904    /* Free all resources if an error occured */
[9eab99a]905    rtems_ftpfs_terminate( iop);
[8916bdc7]906
[1cb5aa14]907    rtems_set_errno_and_return_minus_one( eno);
908  }
[dda0bffc]909}
910
[8916bdc7]911static ssize_t rtems_ftpfs_read(
[dda0bffc]912  rtems_libio_t *iop,
[8916bdc7]913  void *buffer,
914  size_t count
[dda0bffc]915)
916{
[9eab99a]917  rtems_ftpfs_entry *e = iop->data1;
918  char *in = buffer;
[8916bdc7]919  size_t todo = count;
[dda0bffc]920
[8916bdc7]921  if (e->eof) {
922    return 0;
923  }
924
925  while (todo > 0) {
926    ssize_t rv = recv( e->data_socket, in, todo, 0);
927
928    if (rv <= 0) {
929      if (rv == 0) {
930        rtems_ftpfs_reply reply =
931          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
932
933        if (reply == RTEMS_FTPFS_REPLY_2) {
934          e->eof = true;
935          break;
936        }
[dda0bffc]937      }
[8916bdc7]938
939      rtems_set_errno_and_return_minus_one( EIO);
[dda0bffc]940    }
[8916bdc7]941
942    in += rv;
943    todo -= (size_t) rv;
[dda0bffc]944  }
[8916bdc7]945
946  return (ssize_t) (count - todo);
[dda0bffc]947}
948
[8916bdc7]949static ssize_t rtems_ftpfs_write(
[dda0bffc]950  rtems_libio_t *iop,
[8916bdc7]951  const void *buffer,
952  size_t count
[dda0bffc]953)
954{
[9eab99a]955  rtems_ftpfs_entry *e = iop->data1;
956  const char *out = buffer;
[8916bdc7]957  size_t todo = count;
[dda0bffc]958
[8916bdc7]959  while (todo > 0) {
960    ssize_t rv = send( e->data_socket, out, todo, 0);
961
962    if (rv <= 0) {
963      if (rv == 0) {
[1cb5aa14]964        break;
965      } else {
966        rtems_set_errno_and_return_minus_one( EIO);
[dda0bffc]967      }
968    }
[8916bdc7]969
970    out += rv;
971    todo -= (size_t) rv;
[dda0bffc]972  }
[8916bdc7]973
974  return (ssize_t) (count - todo);
[dda0bffc]975}
976
[8916bdc7]977static int rtems_ftpfs_close( rtems_libio_t *iop)
[dda0bffc]978{
[1cb5aa14]979  int eno = rtems_ftpfs_terminate( iop);
[8916bdc7]980
[1cb5aa14]981  if (eno == 0) {
982    return 0;
983  } else {
984    rtems_set_errno_and_return_minus_one( eno);
985  }
[8916bdc7]986}
987
988/* Dummy version to let fopen( *,"w") work properly */
989static int rtems_ftpfs_ftruncate( rtems_libio_t *iop, off_t count)
990{
991  return 0;
992}
993
994static int rtems_ftpfs_eval_path(
995  const char *pathname,
996  int flags,
997  rtems_filesystem_location_info_t *pathloc
998)
999{
[dda0bffc]1000  /*
[8916bdc7]1001   * The caller of this routine has striped off the mount prefix from the path.
1002   * We need to store this path here or otherwise we would have to do this job
[9eab99a]1003   * again.  The path is used in rtems_ftpfs_open() via iop->file_info.
[dda0bffc]1004   */
[9eab99a]1005  pathloc->node_access = strdup( pathname);
[dda0bffc]1006
1007  return 0;
1008}
1009
[9eab99a]1010static int rtems_ftpfs_free_node( rtems_filesystem_location_info_t *pathloc)
[dda0bffc]1011{
[9eab99a]1012  free( pathloc->node_access);
1013
1014  return 0;
[dda0bffc]1015}
1016
[8916bdc7]1017static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1018  rtems_filesystem_location_info_t *pathloc
[dda0bffc]1019)
1020{
[8916bdc7]1021  return RTEMS_FILESYSTEM_MEMORY_FILE;
1022}
1023
1024static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
1025
1026static int rtems_ftpfs_mount_me(
1027  rtems_filesystem_mount_table_entry_t *e
1028)
1029{
1030  /* Set handler and oparations table */
1031  e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
1032  e->mt_fs_root.ops = &rtems_ftpfs_ops;
1033
1034  /* We have no FTP file system specific data to maintain */
1035  e->fs_info = NULL;
1036
1037  /* We maintain no real file system nodes, so there is no real root */
1038  e->mt_fs_root.node_access = NULL;
1039
1040  /* Just use the limits from IMFS */
1041  e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
1042
1043  return 0;
[dda0bffc]1044}
1045
[8916bdc7]1046/*
1047 * The stat() support is intended only for the cp shell command.  Each request
1048 * will return that we have a regular file with read, write and execute
1049 * permissions for every one.  The node index uses a global counter to support
1050 * a remote to remote copy.  Is not a very sophisticated method.
1051 */
1052static int rtems_ftpfs_fstat(
1053  rtems_filesystem_location_info_t *loc,
1054  struct stat *st
[dda0bffc]1055)
1056{
[8916bdc7]1057  static unsigned ino = 0;
1058
1059  memset( st, 0, sizeof( *st));
1060
1061  /* FIXME */
1062  st->st_ino = ++ino;
1063
1064  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1065
1066  return 0;
[dda0bffc]1067}
1068
[8916bdc7]1069const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1070  .evalpath_h = rtems_ftpfs_eval_path,
[9eab99a]1071  .evalformake_h = NULL,
[8916bdc7]1072  .link_h = NULL,
1073  .unlink_h = NULL,
1074  .node_type_h = rtems_ftpfs_node_type,
1075  .mknod_h = NULL,
1076  .chown_h = NULL,
[9eab99a]1077  .freenod_h = rtems_ftpfs_free_node,
[8916bdc7]1078  .mount_h = NULL,
1079  .fsmount_me_h = rtems_ftpfs_mount_me,
1080  .unmount_h = NULL,
1081  .fsunmount_me_h = NULL,
1082  .utime_h = NULL,
1083  .eval_link_h = NULL,
1084  .symlink_h = NULL,
1085  .readlink_h = NULL
[dda0bffc]1086};
[8916bdc7]1087
1088static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1089  .open_h = rtems_ftpfs_open,
1090  .close_h = rtems_ftpfs_close,
1091  .read_h = rtems_ftpfs_read,
1092  .write_h = rtems_ftpfs_write,
1093  .ioctl_h = NULL,
1094  .lseek_h = NULL,
1095  .fstat_h = rtems_ftpfs_fstat,
1096  .fchmod_h = NULL,
1097  .ftruncate_h = rtems_ftpfs_ftruncate,
1098  .fpathconf_h = NULL,
1099  .fsync_h = NULL,
1100  .fdatasync_h = NULL,
1101  .fcntl_h = NULL,
1102  .rmnod_h = NULL
[dda0bffc]1103};
Note: See TracBrowser for help on using the repository browser.