source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 0a95800a

4.11
Last change on this file since 0a95800a was 0a95800a, checked in by Sebastian Huber <sebastian.huber@…>, on May 15, 2012 at 8:27:46 AM

Filesystem: Change pathconf_limits_and_options

The pathconf_limits_and_options field of
rtems_filesystem_mount_table_entry_t is now a const pointer to reduce
the read-write memory demands of file system instances.

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