source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 8916bdc7

4.104.11
Last change on this file since 8916bdc7 was 8916bdc7, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on Mar 26, 2009 at 2:11:53 PM
  • libmisc/shell/shell.h: Pointer to oparations table for mount command is now const.
  • libnetworking/lib/ftpfs.c, libnetworking/rtems/ftpfs.h: Rewrite of the FTP file system which implements now the trivial command state machines of RFC 959. For the data transfer passive (= default) and active (= fallback) modes are now supported.
  • libmisc/shell/main_mount_ftp.c: Update for FTP file system changes.
  • Property mode set to 100644
File size: 23.8 KB
Line 
1/**
2 * @file
3 *
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>
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 <inttypes.h>
38#include <malloc.h>
39#include <netdb.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45#include <arpa/inet.h>
46#include <netinet/in.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 * @brief Connection entry for each open file stream.
65 */
66typedef struct {
67  /**
68   * @brief Control connection socket.
69   */
70  int ctrl_socket;
71
72  /**
73   * @brief Data transfer socket.
74   */
75  int data_socket;
76
77  /**
78   * @brief End of file flag.
79   */
80  bool eof;
81} rtems_ftpfs_entry;
82
83static bool rtems_ftpfs_verbose = false;
84
85rtems_status_code rtems_ftpfs_mount( const char *mount_point)
86{
87  int rv = 0;
88
89  if (mount_point == NULL) {
90    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
91  }
92
93  rv = mkdir( mount_point, S_IRWXU | S_IRWXG | S_IRWXO);
94  if (rv != 0) {
95    return RTEMS_IO_ERROR;
96  }
97
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  }
108
109  return RTEMS_SUCCESSFUL;
110}
111
112rtems_status_code rtems_ftpfs_set_verbose( bool verbose)
113{
114  rtems_ftpfs_verbose = verbose;
115
116  return RTEMS_SUCCESSFUL;
117}
118
119rtems_status_code rtems_ftpfs_get_verbose( bool *verbose)
120{
121  if (verbose == NULL) {
122    return RTEMS_INVALID_ADDRESS;
123  }
124
125  *verbose = rtems_ftpfs_verbose;
126
127  return RTEMS_SUCCESSFUL;
128}
129
130int rtems_bsdnet_initialize_ftp_filesystem( void)
131{
132  rtems_status_code sc = RTEMS_SUCCESSFUL;
133
134  sc = rtems_ftpfs_mount( NULL);
135
136  if (sc == RTEMS_SUCCESSFUL) {
137    return 0;
138  } else {
139    return -1;
140  }
141}
142
143typedef void (*rtems_ftpfs_reply_parser)(
144  const char * /* reply fragment */,
145  size_t /* reply fragment length */,
146  void * /* parser argument */
147);
148
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;
157
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;
166
167#define RTEMS_FTPFS_REPLY_SIZE 3
168
169static rtems_ftpfs_reply rtems_ftpfs_get_reply(
170  int socket,
171  rtems_ftpfs_reply_parser parser,
172  void *parser_arg
173)
174{
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];
182
183  while (true) {
184    /* Receive reply fragment from socket */
185    ssize_t i = 0;
186    ssize_t rv = recv( socket, buf, sizeof( buf), 0);
187
188    if (rv <= 0) {
189      return RTEMS_FTPFS_REPLY_ERROR;
190    }
191
192    /* Be verbose if necessary */
193    if (verbose) {
194      write( STDERR_FILENO, buf, (size_t) rv);
195    }
196
197    /* Invoke parser if necessary */
198    if (parser != NULL) {
199      parser( buf, (size_t) rv, parser_arg);
200    }
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;
240      }
241    }
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;
253      }
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]);
261      }
262
263      if (ok) {
264        break;
265      }
266    }
267  }
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;
288  }
289  if (verbose) {
290    write( STDERR_FILENO, cmd, strlen( cmd));
291  }
292
293  /* Send command argument if necessary */
294  if (arg != NULL) {
295          rv = send( socket, arg, strlen( arg), 0);
296          if (rv < 0) {
297            return RTEMS_FTPFS_REPLY_ERROR;
298          }
299    if (verbose) {
300      write( STDERR_FILENO, arg, strlen( arg));
301    }
302  }
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);
315}
316
317static rtems_ftpfs_reply rtems_ftpfs_send_command(
318  int socket,
319  const char *cmd,
320  const char *arg
321)
322{
323  return rtems_ftpfs_send_command_with_parser( socket, cmd, arg, NULL, NULL);
324}
325
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;
337
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
344)
345{
346  split_state state = STATE_USER_NAME;
347  size_t len = strlen( s);
348  size_t i = 0;
349
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    }
413  }
414
415done:
416
417  /* If we have no password use the user name */
418  if (*password == NULL) {
419    *password = *user;
420  }
421
422  return state == STATE_DONE;
423}
424
425static socklen_t rtems_ftpfs_create_address(
426  struct sockaddr_in *sa,
427  unsigned long address,
428  unsigned short port
429)
430{
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
441static void rtems_ftpfs_terminate( rtems_ftpfs_entry *e, rtems_libio_t *iop)
442{
443  if (e != NULL) {
444    /* Close data connection if necessary */
445    if (e->data_socket >= 0) {
446      close( e->data_socket);
447    }
448
449    /* Close control connection if necessary */
450    if (e->ctrl_socket >= 0) {
451      close( e->ctrl_socket);
452    }
453
454    /* Free connection entry */
455    free( e);
456  }
457
458  /* Invalidate IO entry */
459  iop->data1 = NULL;
460}
461
462static int rtems_ftpfs_open_ctrl_connection(
463  rtems_ftpfs_entry *e,
464  const char *user,
465  const char *password,
466  const char *hostname,
467  uint32_t *client_address
468)
469{
470  int rv = 0;
471  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
472  struct in_addr address = { .s_addr = 0 };
473  struct sockaddr_in sa;
474  socklen_t size = 0;
475
476  /* Create the socket for the control connection */
477  e->ctrl_socket = socket( AF_INET, SOCK_STREAM, 0);
478  if (e->ctrl_socket < 0) {
479    return ENOMEM;
480  }
481
482  /* Set up the server address from the hostname */
483  if (hostname == NULL || strlen( hostname) == 0) {
484    /* Default to BOOTP server address */
485    address = rtems_bsdnet_bootp_server_address;
486  } else if (inet_aton( hostname, &address) == 0) {
487    /* Try to get the address by name */
488    struct hostent *he = gethostbyname( hostname);
489
490    if (he != NULL) {
491      memcpy( &address, he->h_addr, sizeof( address));
492    } else {
493      return ENOENT;
494    }
495  }
496  rtems_ftpfs_create_address( &sa, address.s_addr, htons( RTEMS_FTPFS_CTRL_PORT));
497  DEBUG_PRINTF( "server = %s\n", inet_ntoa( sa.sin_addr));
498
499  /* Open control connection */
500  rv = connect(
501    e->ctrl_socket,
502    (struct sockaddr *) &sa,
503    sizeof( sa)
504  );
505  if (rv != 0) {
506    return ENOENT;
507  }
508
509  /* Get client address */
510  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
511  rv = getsockname(
512    e->ctrl_socket,
513    (struct sockaddr *) &sa,
514    &size
515  );
516  if (rv != 0) {
517    return ENOMEM;
518  }
519  *client_address = ntohl( sa.sin_addr.s_addr);
520  DEBUG_PRINTF( "client = %s\n", inet_ntoa( sa.sin_addr));
521
522  /* Now we should get a welcome message from the server */
523  reply = rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
524  if (reply != RTEMS_FTPFS_REPLY_2) {
525    return ENOENT;
526  }
527
528  /* Send USER command */
529  reply = rtems_ftpfs_send_command( e->ctrl_socket, "USER ", user);
530  if (reply == RTEMS_FTPFS_REPLY_3) {
531    /* Send PASS command */
532    reply = rtems_ftpfs_send_command( e->ctrl_socket, "PASS ", password);
533    if (reply != RTEMS_FTPFS_REPLY_2) {
534      return EACCES;
535    }
536
537    /* TODO: Some server may require an account */
538  } else if (reply != RTEMS_FTPFS_REPLY_2) {
539    return EACCES;
540  }
541
542  /* Send TYPE command to set binary mode for all data transfers */
543  reply = rtems_ftpfs_send_command( e->ctrl_socket, "TYPE I", NULL);
544  if (reply != RTEMS_FTPFS_REPLY_2) {
545    return EIO;
546  }
547
548  return 0;
549}
550
551static int rtems_ftpfs_open_data_connection_active(
552  rtems_ftpfs_entry *e,
553  uint32_t client_address,
554  const char *file_command,
555  const char *filename
556)
557{
558  int rv = 0;
559  int eno = 0;
560  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
561  struct in_addr address = { .s_addr = 0 };
562  struct sockaddr_in sa;
563  socklen_t size = 0;
564  int port_socket = -1;
565  char port_command [] = "PORT 000,000,000,000,000,000";
566  uint16_t data_port = 0;
567
568  /* Create port socket to establish a data data connection */
569  port_socket = socket( AF_INET, SOCK_STREAM, 0);
570  if (port_socket < 0) {
571    eno = ENOMEM;
572    goto cleanup;
573  }
574
575  /* Bind port socket */
576  rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
577  rv = bind(
578    port_socket,
579    (struct sockaddr *) &sa,
580    sizeof( sa)
581  );
582  if (rv != 0) {
583    eno = EBUSY;
584    goto cleanup;
585  }
586
587  /* Get port number for data socket */
588  size = rtems_ftpfs_create_address( &sa, INADDR_ANY, 0);
589  rv = getsockname(
590    port_socket,
591    (struct sockaddr *) &sa,
592    &size
593  );
594  if (rv != 0) {
595    eno = ENOMEM;
596    goto cleanup;
597  }
598  data_port = ntohs( sa.sin_port);
599
600  /* Send PORT command to set data connection port for server */
601  snprintf(
602    port_command,
603    sizeof( port_command),
604    "PORT %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%" PRIu16,
605    (client_address >> 24) & 0xff,
606    (client_address >> 16) & 0xff,
607    (client_address >> 8) & 0xff,
608    (client_address >> 0) & 0xff,
609    (data_port >> 8) & 0xff,
610    (data_port >> 0) & 0xff
611  );
612  reply = rtems_ftpfs_send_command( e->ctrl_socket, port_command, NULL);
613  if (reply != RTEMS_FTPFS_REPLY_2) {
614    eno = ENOTSUP;
615    goto cleanup;
616  }
617
618  /* Listen on port socket for incoming data connections */
619  rv = listen( port_socket, 1);
620  if (rv != 0) {
621    eno = EBUSY;
622    goto cleanup;
623  }
624
625  /* Send RETR or STOR command with filename */
626  reply = rtems_ftpfs_send_command( e->ctrl_socket, file_command, filename);
627  if (reply != RTEMS_FTPFS_REPLY_1) {
628    eno = EIO;
629    goto cleanup;
630  }
631
632  /*
633   * Wait for connect on data connection.
634   *
635   * FIXME: This should become a select instead with a timeout.
636   */
637  size = sizeof( sa);
638  e->data_socket = accept(
639    port_socket,
640    (struct sockaddr *) &sa,
641    &size
642  );
643  if (e->data_socket < 0) {
644    eno = EIO;
645    goto cleanup;
646  }
647
648  /* FIXME: Check, that server data address is really from our server  */
649
650cleanup:
651
652  /* Close port socket if necessary */
653  if (port_socket >= 0) {
654    close( port_socket);
655  }
656
657  return eno;
658}
659
660typedef enum {
661  RTEMS_FTPFS_PASV_START = 0,
662  RTEMS_FTPFS_PASV_JUNK,
663  RTEMS_FTPFS_PASV_DATA,
664  RTEMS_FTPFS_PASV_DONE
665} rtems_ftpfs_pasv_state;
666
667typedef struct {
668  rtems_ftpfs_pasv_state state;
669  uint8_t data [6];
670  size_t index;
671} rtems_ftpfs_pasv_entry;
672
673static void rtems_ftpfs_pasv_parser(
674  const char* buf,
675  size_t len,
676  void *arg
677)
678{
679  rtems_ftpfs_pasv_entry *e = (rtems_ftpfs_pasv_entry *) arg;
680  size_t i = 0;
681
682  for (i = 0; i < len; ++i) {
683    char c = buf [i];
684
685    switch (e->state) {
686      case RTEMS_FTPFS_PASV_START:
687        if (!isdigit( c)) {
688          e->state = RTEMS_FTPFS_PASV_JUNK;
689          e->index = 0;
690        }
691        break;
692      case RTEMS_FTPFS_PASV_JUNK:
693        if (isdigit( c)) {
694          e->state = RTEMS_FTPFS_PASV_DATA;
695          e->data [e->index] = (uint8_t) (c - '0');
696        }
697        break;
698      case RTEMS_FTPFS_PASV_DATA:
699        if (isdigit( c)) {
700          e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0');
701        } else if (c == ',') {
702          ++e->index;
703          if (e->index < sizeof( e->data)) {
704            e->data [e->index] = 0;
705          } else {
706            e->state = RTEMS_FTPFS_PASV_DONE;
707          }
708        } else {
709          e->state = RTEMS_FTPFS_PASV_DONE;
710        }
711        break;
712      default:
713        return;
714    }
715  }
716}
717
718static int rtems_ftpfs_open_data_connection_passive(
719  rtems_ftpfs_entry *e,
720  uint32_t client_address,
721  const char *file_command,
722  const char *filename
723)
724{
725  int rv = 0;
726  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
727  struct sockaddr_in sa;
728  socklen_t size = 0;
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;
746  }
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;
761  }
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;
771  }
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;
777  }
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 rv = 0;
790  int eno = 0;
791  bool ok = false;
792  rtems_ftpfs_entry *e = NULL;
793  const char *user = NULL;
794  const char *password = NULL;
795  const char *hostname = NULL;
796  const char *filename = NULL;
797  const char *file_command = (iop->flags & LIBIO_FLAGS_WRITE) != 0
798    ? "STOR "
799    : "RETR ";
800  uint32_t client_address = 0;
801  char *location = strdup( (const char *) iop->file_info);
802
803  /* Check allocation */
804  if (location == NULL) {
805    return ENOMEM;
806  }
807
808  /* Check for either read-only or write-only flags */
809  if (
810    (iop->flags & LIBIO_FLAGS_WRITE) != 0
811      && (iop->flags & LIBIO_FLAGS_READ) != 0
812  ) {
813    eno = ENOTSUP;
814    goto cleanup;
815  }
816
817  /* Split location into parts */
818  ok = rtems_ftpfs_split_names(
819      location,
820      &user,
821      &password,
822      &hostname,
823      &filename
824  );
825  if (!ok) {
826    eno = ENOENT;
827    goto cleanup;
828  }
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) {
839    eno = ENOMEM;
840    goto cleanup;
841  }
842
843  /* Initialize connection entry */
844  e->ctrl_socket = -1;
845  e->data_socket = -1;
846  e->eof = false;
847
848  /* Open control connection */
849  eno = rtems_ftpfs_open_ctrl_connection(
850    e,
851    user,
852    password,
853    hostname,
854    &client_address
855  );
856  if (eno != 0) {
857    goto cleanup;
858  }
859
860  /* Open passive data connection */
861  eno = rtems_ftpfs_open_data_connection_passive(
862    e,
863    client_address,
864    file_command,
865    filename
866  );
867  if (eno == ENOTSUP) {
868    /* Open active data connection */
869    eno = rtems_ftpfs_open_data_connection_active(
870      e,
871      client_address,
872      file_command,
873      filename
874    );
875  }
876
877cleanup:
878
879  /* Free location parts buffer */
880  free( location);
881
882  if (eno == 0) {
883    /* Save connection state */
884    iop->data1 = e;
885  } else {
886    /* Free all resources if an error occured */
887    rtems_ftpfs_terminate( e, iop);
888  }
889
890  return eno;
891}
892
893static ssize_t rtems_ftpfs_read(
894  rtems_libio_t *iop,
895  void *buffer,
896  size_t count
897)
898{
899  rtems_ftpfs_entry *e = (rtems_ftpfs_entry *) iop->data1;
900  char *in = (char *) buffer;
901  size_t todo = count;
902
903  if (e->eof) {
904    return 0;
905  }
906
907  while (todo > 0) {
908    ssize_t rv = recv( e->data_socket, in, todo, 0);
909
910    if (rv <= 0) {
911      if (rv == 0) {
912        rtems_ftpfs_reply reply =
913          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
914
915        if (reply == RTEMS_FTPFS_REPLY_2) {
916          e->eof = true;
917          break;
918        }
919      }
920
921      rtems_set_errno_and_return_minus_one( EIO);
922    }
923
924    in += rv;
925    todo -= (size_t) rv;
926  }
927
928  return (ssize_t) (count - todo);
929}
930
931static ssize_t rtems_ftpfs_write(
932  rtems_libio_t *iop,
933  const void *buffer,
934  size_t count
935)
936{
937  rtems_ftpfs_entry *e = (rtems_ftpfs_entry *) iop->data1;
938  const char *out = (const char *) buffer;
939  size_t todo = count;
940
941  if (e->eof) {
942    return 0;
943  }
944
945  while (todo > 0) {
946    ssize_t rv = send( e->data_socket, out, todo, 0);
947
948    if (rv <= 0) {
949      if (rv == 0) {
950        rtems_ftpfs_reply reply =
951          rtems_ftpfs_get_reply( e->ctrl_socket, NULL, NULL);
952
953        if (reply == RTEMS_FTPFS_REPLY_2) {
954          e->eof = true;
955          break;
956        }
957      }
958
959      rtems_set_errno_and_return_minus_one( EIO);
960    }
961
962    out += rv;
963    todo -= (size_t) rv;
964  }
965
966  return (ssize_t) (count - todo);
967}
968
969static int rtems_ftpfs_close( rtems_libio_t *iop)
970{
971  rtems_ftpfs_entry *e = (rtems_ftpfs_entry *) iop->data1;
972
973  rtems_ftpfs_terminate( e, iop);
974
975  return 0;
976}
977
978/* Dummy version to let fopen( *,"w") work properly */
979static int rtems_ftpfs_ftruncate( rtems_libio_t *iop, off_t count)
980{
981  return 0;
982}
983
984static int rtems_ftpfs_eval_path(
985  const char *pathname,
986  int flags,
987  rtems_filesystem_location_info_t *pathloc
988)
989{
990  /*
991   * The caller of this routine has striped off the mount prefix from the path.
992   * We need to store this path here or otherwise we would have to do this job
993   * again.  It is not possible to allocate resources here since there is no
994   * way to free them later in every case.  The path is used in
995   * rtems_ftpfs_open() via iop->file_info.
996   *
997   * FIXME: Avoid to discard the const qualifier.
998   */
999  pathloc->node_access = (void *) pathname;
1000
1001  return 0;
1002}
1003
1004static int rtems_ftpfs_eval_for_make(
1005  const char *pathname,
1006  rtems_filesystem_location_info_t *pathloc,
1007  const char **name
1008)
1009{
1010  rtems_set_errno_and_return_minus_one( EIO);
1011}
1012
1013static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1014  rtems_filesystem_location_info_t *pathloc
1015)
1016{
1017  return RTEMS_FILESYSTEM_MEMORY_FILE;
1018}
1019
1020static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
1021
1022static int rtems_ftpfs_mount_me(
1023  rtems_filesystem_mount_table_entry_t *e
1024)
1025{
1026  /* Set handler and oparations table */
1027  e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
1028  e->mt_fs_root.ops = &rtems_ftpfs_ops;
1029
1030  /* We have no FTP file system specific data to maintain */
1031  e->fs_info = NULL;
1032
1033  /* We maintain no real file system nodes, so there is no real root */
1034  e->mt_fs_root.node_access = NULL;
1035
1036  /* Just use the limits from IMFS */
1037  e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
1038
1039  return 0;
1040}
1041
1042/*
1043 * The stat() support is intended only for the cp shell command.  Each request
1044 * will return that we have a regular file with read, write and execute
1045 * permissions for every one.  The node index uses a global counter to support
1046 * a remote to remote copy.  Is not a very sophisticated method.
1047 */
1048static int rtems_ftpfs_fstat(
1049  rtems_filesystem_location_info_t *loc,
1050  struct stat *st
1051)
1052{
1053  static unsigned ino = 0;
1054
1055  memset( st, 0, sizeof( *st));
1056
1057  /* FIXME */
1058  st->st_ino = ++ino;
1059
1060  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1061
1062  return 0;
1063}
1064
1065const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1066  .evalpath_h = rtems_ftpfs_eval_path,
1067  .evalformake_h = rtems_ftpfs_eval_for_make,
1068  .link_h = NULL,
1069  .unlink_h = NULL,
1070  .node_type_h = rtems_ftpfs_node_type,
1071  .mknod_h = NULL,
1072  .chown_h = NULL,
1073  .freenod_h = NULL,
1074  .mount_h = NULL,
1075  .fsmount_me_h = rtems_ftpfs_mount_me,
1076  .unmount_h = NULL,
1077  .fsunmount_me_h = NULL,
1078  .utime_h = NULL,
1079  .eval_link_h = NULL,
1080  .symlink_h = NULL,
1081  .readlink_h = NULL
1082};
1083
1084static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1085  .open_h = rtems_ftpfs_open,
1086  .close_h = rtems_ftpfs_close,
1087  .read_h = rtems_ftpfs_read,
1088  .write_h = rtems_ftpfs_write,
1089  .ioctl_h = NULL,
1090  .lseek_h = NULL,
1091  .fstat_h = rtems_ftpfs_fstat,
1092  .fchmod_h = NULL,
1093  .ftruncate_h = rtems_ftpfs_ftruncate,
1094  .fpathconf_h = NULL,
1095  .fsync_h = NULL,
1096  .fdatasync_h = NULL,
1097  .fcntl_h = NULL,
1098  .rmnod_h = NULL
1099};
Note: See TracBrowser for help on using the repository browser.