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

4.104.11
Last change on this file since 9eab99a was 9eab99a, checked in by Joel Sherrill <joel.sherrill@…>, on Mar 30, 2009 at 5:05:23 PM

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
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_libio_t *iop)
442{
443  rtems_ftpfs_entry *e = iop->data1;
444
445  if (e != NULL) {
446    /* Close data connection if necessary */
447    if (e->data_socket >= 0) {
448      close( e->data_socket);
449    }
450
451    /* Close control connection if necessary */
452    if (e->ctrl_socket >= 0) {
453      close( e->ctrl_socket);
454    }
455
456    /* Free connection entry */
457    free( e);
458  }
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;
482  }
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;
496    }
497  }
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;
509  }
510
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;
520  }
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;
528  }
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;
537    }
538
539    /* TODO: Some server may require an account */
540  } else if (reply != RTEMS_FTPFS_REPLY_2) {
541    return EACCES;
542  }
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;
548  }
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;
574  }
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;
586  }
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;
598  }
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;
617  }
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;
624  }
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;
631  }
632
633  /*
634   * Wait for connect on data connection.
635   *
636   * FIXME: This should become a select instead with a timeout.
637   */
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;
647  }
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{
680  rtems_ftpfs_pasv_entry *e = arg;
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;
715    }
716  }
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;
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 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;
800  char *location = iop->file_info;
801
802  /* Invalidate data handle */
803  iop->data1 = NULL;
804
805  /* Check location, it was allocated during path evaluation */
806  if (location == NULL) {
807    return ENOMEM;
808  }
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  ) {
815    return ENOTSUP;
816  }
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) {
827    return ENOENT;
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    return ENOMEM;
840  }
841
842  /* Initialize connection entry */
843  e->ctrl_socket = -1;
844  e->data_socket = -1;
845  e->eof = false;
846
847  /* Save connection state */
848  iop->data1 = e;
849
850  /* Open control connection */
851  eno = rtems_ftpfs_open_ctrl_connection(
852    e,
853    user,
854    password,
855    hostname,
856    &client_address
857  );
858  if (eno != 0) {
859    goto cleanup;
860  }
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    );
877  }
878
879cleanup:
880
881  if (eno != 0) {
882    /* Free all resources if an error occured */
883    rtems_ftpfs_terminate( iop);
884  }
885
886  return eno;
887}
888
889static ssize_t rtems_ftpfs_read(
890  rtems_libio_t *iop,
891  void *buffer,
892  size_t count
893)
894{
895  rtems_ftpfs_entry *e = iop->data1;
896  char *in = buffer;
897  size_t todo = count;
898
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        }
915      }
916
917      rtems_set_errno_and_return_minus_one( EIO);
918    }
919
920    in += rv;
921    todo -= (size_t) rv;
922  }
923
924  return (ssize_t) (count - todo);
925}
926
927static ssize_t rtems_ftpfs_write(
928  rtems_libio_t *iop,
929  const void *buffer,
930  size_t count
931)
932{
933  rtems_ftpfs_entry *e = iop->data1;
934  const char *out = buffer;
935  size_t todo = count;
936
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        }
953      }
954
955      rtems_set_errno_and_return_minus_one( EIO);
956    }
957
958    out += rv;
959    todo -= (size_t) rv;
960  }
961
962  return (ssize_t) (count - todo);
963}
964
965static int rtems_ftpfs_close( rtems_libio_t *iop)
966{
967  rtems_ftpfs_terminate( iop);
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{
984  /*
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
987   * again.  The path is used in rtems_ftpfs_open() via iop->file_info.
988   */
989  pathloc->node_access = strdup( pathname);
990
991  return 0;
992}
993
994static int rtems_ftpfs_free_node( rtems_filesystem_location_info_t *pathloc)
995{
996  free( pathloc->node_access);
997
998  return 0;
999}
1000
1001static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1002  rtems_filesystem_location_info_t *pathloc
1003)
1004{
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;
1028}
1029
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
1039)
1040{
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;
1051}
1052
1053const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1054  .evalpath_h = rtems_ftpfs_eval_path,
1055  .evalformake_h = NULL,
1056  .link_h = NULL,
1057  .unlink_h = NULL,
1058  .node_type_h = rtems_ftpfs_node_type,
1059  .mknod_h = NULL,
1060  .chown_h = NULL,
1061  .freenod_h = rtems_ftpfs_free_node,
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
1070};
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
1087};
Note: See TracBrowser for help on using the repository browser.