source: rtems/cpukit/libnetworking/lib/ftpfs.c @ ae55da72

4.115
Last change on this file since ae55da72 was 9b4422a2, checked in by Joel Sherrill <joel.sherrill@…>, on 05/03/12 at 15:09:24

Remove All CVS Id Strings Possible Using a Script

Script does what is expected and tries to do it as
smartly as possible.

+ remove occurrences of two blank comment lines

next to each other after Id string line removed.

+ remove entire comment blocks which only exited to

contain CVS Ids

+ If the processing left a blank line at the top of

a file, it was removed.

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