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

4.11
Last change on this file since be6f505 was be6f505, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 21, 2012 at 11:20:14 AM

ftpfs: Format

  • 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 embedded brains GmbH.
9 *
10 *  embedded brains GmbH
11 *  Obere Lagerstr. 30
12 *  82178 Puchheim
13 *  Germany
14 *  <rtems@embedded-brains.de>
15 *
16 * (c) Copyright 2002
17 * Thomas Doerfler
18 * IMD Ingenieurbuero fuer Microcomputertechnik
19 * Herbststr. 8
20 * 82178 Puchheim, Germany
21 * <Thomas.Doerfler@imd-systems.de>
22 *
23 * This code has been created after closly inspecting "tftpdriver.c" from Eric
24 * Norum.
25 *
26 * The license and distribution terms for this file may be
27 * found in the file LICENSE in this distribution or at
28 * http://www.rtems.com/license/LICENSE.
29 */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include <ctype.h>
36#include <errno.h>
37#include <fcntl.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/select.h>
48#include <sys/socket.h>
49#include <sys/types.h>
50
51#include <rtems.h>
52#include <rtems/ftpfs.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 * Connection entry for each open file stream.
65 */
66typedef struct {
67  /**
68   * Control connection socket.
69   */
70  int ctrl_socket;
71
72  /**
73   * Data transfer socket.
74   */
75  int data_socket;
76
77  /**
78   * Current index into the reply buffer.
79   */
80  size_t reply_current;
81
82  /**
83   * End index of the reply buffer.
84   */
85  size_t reply_end;
86
87  /**
88   * Buffer for relpy data.
89   */
90  char reply_buffer [128];
91
92  /**
93   * End of file flag.
94   */
95  bool eof;
96} rtems_ftpfs_entry;
97
98/**
99 * Mount entry for each file system instance.
100 */
101typedef struct {
102  /**
103   * Verbose mode enabled or disabled.
104   */
105  bool verbose;
106
107  /**
108   * Timeout value
109   */
110  struct timeval timeout;
111} rtems_ftpfs_mount_entry;
112
113static const rtems_filesystem_operations_table rtems_ftpfs_ops;
114
115static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
116
117static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers;
118
119static bool rtems_ftpfs_use_timeout(const struct timeval *to)
120{
121  return to->tv_sec != 0 || to->tv_usec != 0;
122}
123
124static int rtems_ftpfs_set_connection_timeout(
125  int socket,
126  const struct timeval *to
127)
128{
129  if (rtems_ftpfs_use_timeout(to)) {
130    int rv = 0;
131
132    rv = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof(*to));
133    if (rv != 0) {
134      return EIO;
135    }
136
137    rv = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof(*to));
138    if (rv != 0) {
139      return EIO;
140    }
141  }
142
143  return 0;
144}
145
146static rtems_status_code rtems_ftpfs_do_ioctl(
147  const char *mount_point,
148  ioctl_command_t req,
149  ...
150)
151{
152  rtems_status_code sc = RTEMS_SUCCESSFUL;
153  int rv = 0;
154  int fd = 0;
155  va_list ap;
156
157  if (mount_point == NULL) {
158    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
159  }
160
161  fd = open(mount_point, O_RDWR);
162  if (fd < 0) {
163    return RTEMS_INVALID_NAME;
164  }
165
166  va_start(ap, req);
167  rv = ioctl(fd, req, va_arg(ap, void *));
168  va_end(ap);
169  if (rv != 0) {
170    sc = RTEMS_INVALID_NUMBER;
171  }
172
173  rv = close(fd);
174  if (rv != 0 && sc == RTEMS_SUCCESSFUL) {
175    sc = RTEMS_IO_ERROR;
176  }
177
178  return sc;
179}
180
181rtems_status_code rtems_ftpfs_get_verbose(const char *mount_point, bool *verbose)
182{
183  return rtems_ftpfs_do_ioctl(
184    mount_point,
185    RTEMS_FTPFS_IOCTL_GET_VERBOSE,
186    verbose
187  );
188}
189
190rtems_status_code rtems_ftpfs_set_verbose(const char *mount_point, bool verbose)
191{
192  return rtems_ftpfs_do_ioctl(
193    mount_point,
194    RTEMS_FTPFS_IOCTL_SET_VERBOSE,
195    &verbose
196  );
197}
198
199rtems_status_code rtems_ftpfs_get_timeout(
200  const char *mount_point,
201  struct timeval *timeout
202)
203{
204  return rtems_ftpfs_do_ioctl(
205    mount_point,
206    RTEMS_FTPFS_IOCTL_GET_TIMEOUT,
207    timeout
208  );
209}
210
211rtems_status_code rtems_ftpfs_set_timeout(
212  const char *mount_point,
213  const struct timeval *timeout
214)
215{
216  return rtems_ftpfs_do_ioctl(
217    mount_point,
218    RTEMS_FTPFS_IOCTL_SET_TIMEOUT,
219    timeout
220  );
221}
222
223typedef void (*rtems_ftpfs_reply_parser)(
224  const char * /* reply fragment */,
225  size_t /* reply fragment length */,
226  void * /* parser argument */
227);
228
229typedef enum {
230  RTEMS_FTPFS_REPLY_START,
231  RTEMS_FTPFS_REPLY_SINGLE_LINE,
232  RTEMS_FTPFS_REPLY_DONE,
233  RTEMS_FTPFS_REPLY_MULTI_LINE,
234  RTEMS_FTPFS_REPLY_MULTI_LINE_START
235} rtems_ftpfs_reply_state;
236
237typedef enum {
238  RTEMS_FTPFS_REPLY_ERROR = 0,
239  RTEMS_FTPFS_REPLY_1 = '1',
240  RTEMS_FTPFS_REPLY_2 = '2',
241  RTEMS_FTPFS_REPLY_3 = '3',
242  RTEMS_FTPFS_REPLY_4 = '4',
243  RTEMS_FTPFS_REPLY_5 = '5'
244} rtems_ftpfs_reply;
245
246#define RTEMS_FTPFS_REPLY_SIZE 3
247
248static bool rtems_ftpfs_is_reply_code_valid(unsigned char *reply)
249{
250  return isdigit(reply [0]) && isdigit(reply [1]) && isdigit(reply [2]);
251}
252
253static rtems_ftpfs_reply rtems_ftpfs_get_reply(
254  rtems_ftpfs_entry *e,
255  rtems_ftpfs_reply_parser parser,
256  void *parser_arg,
257  bool verbose
258)
259{
260  rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START;
261  unsigned char reply_code [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' };
262  size_t reply_code_index = 0;
263
264  while (state != RTEMS_FTPFS_REPLY_DONE) {
265    char *buf = NULL;
266    size_t i = 0;
267    size_t n = 0;
268
269    /* Receive reply fragment from socket */
270    if (e->reply_current == e->reply_end) {
271      ssize_t rv = 0;
272
273      e->reply_current = 0;
274      e->reply_end = 0;
275
276      rv = recv(
277        e->ctrl_socket,
278        &e->reply_buffer [0],
279        sizeof(e->reply_buffer),
280        0
281      );
282
283      if (rv > 0) {
284        e->reply_end = (size_t) rv;
285      } else {
286        return RTEMS_FTPFS_REPLY_ERROR;
287      }
288    }
289
290    buf = &e->reply_buffer [e->reply_current];
291    n = e->reply_end - e->reply_current;
292
293    /* Invoke parser if necessary */
294    if (parser != NULL) {
295      parser(buf, n, parser_arg);
296    }
297
298    /* Parse reply fragment */
299    for (i = 0; i < n && state != RTEMS_FTPFS_REPLY_DONE; ++i) {
300      char c = buf [i];
301
302      switch (state) {
303        case RTEMS_FTPFS_REPLY_START:
304          if (reply_code_index < RTEMS_FTPFS_REPLY_SIZE) {
305            reply_code [reply_code_index] = c;
306            ++reply_code_index;
307          } else if (rtems_ftpfs_is_reply_code_valid(reply_code)) {
308            if (c == '-') {
309              state = RTEMS_FTPFS_REPLY_MULTI_LINE;
310            } else {
311              state = RTEMS_FTPFS_REPLY_SINGLE_LINE;
312            }
313          } else {
314            return RTEMS_FTPFS_REPLY_ERROR;
315          }
316          break;
317        case RTEMS_FTPFS_REPLY_SINGLE_LINE:
318          if (c == '\n') {
319            state = RTEMS_FTPFS_REPLY_DONE;
320          }
321          break;
322        case RTEMS_FTPFS_REPLY_MULTI_LINE:
323          if (c == '\n') {
324            state = RTEMS_FTPFS_REPLY_MULTI_LINE_START;
325            reply_code_index = 0;
326          }
327          break;
328        case RTEMS_FTPFS_REPLY_MULTI_LINE_START:
329          if (reply_code_index < RTEMS_FTPFS_REPLY_SIZE) {
330            if (reply_code [reply_code_index] == c) {
331              ++reply_code_index;
332            } else {
333              state = RTEMS_FTPFS_REPLY_MULTI_LINE;
334            }
335          } else {
336            if (c == ' ') {
337              state = RTEMS_FTPFS_REPLY_SINGLE_LINE;
338            } else {
339              state = RTEMS_FTPFS_REPLY_MULTI_LINE;
340            }
341          }
342          break;
343        default:
344          return RTEMS_FTPFS_REPLY_ERROR;
345      }
346    }
347
348    /* Be verbose if necessary */
349    if (verbose) {
350      write(STDERR_FILENO, buf, i);
351    }
352
353    /* Update reply index */
354    e->reply_current += i;
355  }
356
357  return reply_code [0];
358}
359
360static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser(
361  rtems_ftpfs_entry *e,
362  const char *cmd,
363  const char *arg,
364  rtems_ftpfs_reply_parser parser,
365  void *parser_arg,
366  bool verbose
367)
368{
369  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
370  size_t cmd_len = strlen(cmd);
371  size_t arg_len = arg != NULL ? strlen(arg) : 0;
372  size_t len = cmd_len + arg_len + 2;
373  char *buf = malloc(len);
374
375  if (buf != NULL) {
376    ssize_t n = 0;
377    char *buf_arg = buf + cmd_len;
378    char *buf_eol = buf_arg + arg_len;
379
380    memcpy(buf, cmd, cmd_len);
381    memcpy(buf_arg, arg, arg_len);
382    buf_eol [0] = '\r';
383    buf_eol [1] = '\n';
384
385    /* Send */
386    n = send(e->ctrl_socket, buf, len, 0);
387    if (n == (ssize_t) len) {
388      if (verbose) {
389        write(STDERR_FILENO, buf, len);
390      }
391
392      /* Reply */
393      reply = rtems_ftpfs_get_reply(e, parser, parser_arg, verbose);
394    }
395
396    free(buf);
397  }
398
399  return reply;
400}
401
402static rtems_ftpfs_reply rtems_ftpfs_send_command(
403  rtems_ftpfs_entry *e,
404  const char *cmd,
405  const char *arg,
406  bool verbose
407)
408{
409  return rtems_ftpfs_send_command_with_parser(
410    e,
411    cmd,
412    arg,
413    NULL,
414    NULL,
415    verbose
416  );
417}
418
419typedef enum {
420  STATE_USER_NAME,
421  STATE_START_PASSWORD,
422  STATE_START_HOST_NAME,
423  STATE_START_HOST_NAME_OR_PATH,
424  STATE_START_PATH,
425  STATE_PASSWORD,
426  STATE_HOST_NAME,
427  STATE_DONE,
428  STATE_INVALID
429} split_state;
430
431static bool rtems_ftpfs_split_names (
432  char *s,
433  const char **user,
434  const char **password,
435  const char **hostname,
436  const char **path
437)
438{
439  split_state state = STATE_USER_NAME;
440  size_t len = strlen(s);
441  size_t i = 0;
442
443  *user = s;
444  *password = NULL;
445  *hostname = NULL;
446  *path = NULL;
447
448  for (i = 0; i < len; ++i) {
449    char c = s [i];
450
451    switch (state) {
452      case STATE_USER_NAME:
453        if (c == ':') {
454          state = STATE_START_PASSWORD;
455          s [i] = '\0';
456        } else if (c == '@') {
457          state = STATE_START_HOST_NAME;
458          s [i] = '\0';
459        } else if (c == '/') {
460          state = STATE_START_HOST_NAME_OR_PATH;
461          s [i] = '\0';
462        }
463        break;
464      case STATE_START_PASSWORD:
465        state = STATE_PASSWORD;
466        *password = &s [i];
467        --i;
468        break;
469      case STATE_START_HOST_NAME:
470        state = STATE_HOST_NAME;
471        *hostname = &s [i];
472        --i;
473        break;
474      case STATE_START_HOST_NAME_OR_PATH:
475        if (c == '@') {
476          state = STATE_START_HOST_NAME;
477        } else {
478          state = STATE_DONE;
479          *path = &s [i];
480          goto done;
481        }
482        break;
483      case STATE_START_PATH:
484        state = STATE_DONE;
485        *path = &s [i];
486        goto done;
487      case STATE_PASSWORD:
488        if (c == '@') {
489          state = STATE_START_HOST_NAME;
490          s [i] = '\0';
491        } else if (c == '/') {
492          state = STATE_START_HOST_NAME_OR_PATH;
493          s [i] = '\0';
494        }
495        break;
496      case STATE_HOST_NAME:
497        if (c == '/') {
498          state = STATE_START_PATH;
499          s [i] = '\0';
500        }
501        break;
502      default:
503        state = STATE_INVALID;
504        goto done;
505    }
506  }
507
508done:
509
510  /* This is a special case with no username and password */
511  if (*hostname == NULL) {
512    *hostname = &s [0];
513    *user = "anonymous";
514    *password = *user;
515  }
516
517  /* If we have no password use the user name */
518  if (*password == NULL) {
519    *password = *user;
520  }
521
522  return state == STATE_DONE;
523}
524
525static socklen_t rtems_ftpfs_create_address(
526  struct sockaddr_in *sa,
527  unsigned long address,
528  unsigned short port
529)
530{
531  memset(sa, sizeof(*sa), 0);
532
533  sa->sin_family = AF_INET;
534  sa->sin_addr.s_addr = address;
535  sa->sin_port = port;
536  sa->sin_len = sizeof(*sa);
537
538  return sizeof(*sa);
539}
540
541static int rtems_ftpfs_terminate(rtems_libio_t *iop, bool error)
542{
543  int eno = 0;
544  int rv = 0;
545  rtems_ftpfs_entry *e = iop->data1;
546  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
547  bool verbose = me->verbose;
548  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
549
550  if (e != NULL) {
551    /* Close data connection if necessary */
552    if (e->data_socket >= 0) {
553      rv = close(e->data_socket);
554      if (rv != 0) {
555        eno = EIO;
556      }
557
558      /* For write connections we have to obtain the transfer reply  */
559      if (
560        e->ctrl_socket >= 0
561          && (iop->flags & LIBIO_FLAGS_WRITE) != 0
562          && !error
563      ) {
564        reply = rtems_ftpfs_get_reply(e, NULL, NULL, verbose);
565        if (reply != RTEMS_FTPFS_REPLY_2) {
566          eno = EIO;
567        }
568      }
569    }
570
571    /* Close control connection if necessary */
572    if (e->ctrl_socket >= 0) {
573      reply = rtems_ftpfs_send_command(e, "QUIT", NULL, verbose);
574      if (reply != RTEMS_FTPFS_REPLY_2) {
575        eno = EIO;
576      }
577
578      rv = close(e->ctrl_socket);
579      if (rv != 0) {
580        eno = EIO;
581      }
582    }
583
584    /* Free connection entry */
585    free(e);
586  }
587
588  /* Invalidate IO entry */
589  iop->data1 = NULL;
590
591  return eno;
592}
593
594static int rtems_ftpfs_open_ctrl_connection(
595  rtems_ftpfs_entry *e,
596  const char *user,
597  const char *password,
598  const char *hostname,
599  uint32_t *client_address,
600  bool verbose,
601  const struct timeval *timeout
602)
603{
604  int rv = 0;
605  int eno = 0;
606  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
607  struct in_addr address = { .s_addr = 0 };
608  struct sockaddr_in sa;
609  socklen_t size = 0;
610
611  /* Create the socket for the control connection */
612  e->ctrl_socket = socket(AF_INET, SOCK_STREAM, 0);
613  if (e->ctrl_socket < 0) {
614    return ENOMEM;
615  }
616
617  /* Set up the server address from the hostname */
618  if (inet_aton(hostname, &address) == 0) {
619    /* Try to get the address by name */
620    struct hostent *he = gethostbyname(hostname);
621
622    if (he != NULL) {
623      memcpy(&address, he->h_addr, sizeof(address));
624    } else {
625      return ENOENT;
626    }
627  }
628  rtems_ftpfs_create_address(&sa, address.s_addr, htons(RTEMS_FTPFS_CTRL_PORT));
629  DEBUG_PRINTF("server = %s\n", inet_ntoa(sa.sin_addr));
630
631  /* Open control connection */
632  rv = connect(
633    e->ctrl_socket,
634    (struct sockaddr *) &sa,
635    sizeof(sa)
636  );
637  if (rv != 0) {
638    return ENOENT;
639  }
640
641  /* Set control connection timeout */
642  eno = rtems_ftpfs_set_connection_timeout(e->ctrl_socket, timeout);
643  if (eno != 0) {
644    return eno;
645  }
646
647  /* Get client address */
648  size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
649  rv = getsockname(
650    e->ctrl_socket,
651    (struct sockaddr *) &sa,
652    &size
653  );
654  if (rv != 0) {
655    return ENOMEM;
656  }
657  *client_address = ntohl(sa.sin_addr.s_addr);
658  DEBUG_PRINTF("client = %s\n", inet_ntoa(sa.sin_addr));
659
660  /* Now we should get a welcome message from the server */
661  reply = rtems_ftpfs_get_reply(e, NULL, NULL, verbose);
662  if (reply != RTEMS_FTPFS_REPLY_2) {
663    return ENOENT;
664  }
665
666  /* Send USER command */
667  reply = rtems_ftpfs_send_command(e, "USER ", user, verbose);
668  if (reply == RTEMS_FTPFS_REPLY_3) {
669    /* Send PASS command */
670    reply = rtems_ftpfs_send_command(e, "PASS ", password, verbose);
671    if (reply != RTEMS_FTPFS_REPLY_2) {
672      return EACCES;
673    }
674
675    /* TODO: Some server may require an account */
676  } else if (reply != RTEMS_FTPFS_REPLY_2) {
677    return EACCES;
678  }
679
680  /* Send TYPE command to set binary mode for all data transfers */
681  reply = rtems_ftpfs_send_command(e, "TYPE I", NULL, verbose);
682  if (reply != RTEMS_FTPFS_REPLY_2) {
683    return EIO;
684  }
685
686  return 0;
687}
688
689static int rtems_ftpfs_open_data_connection_active(
690  rtems_ftpfs_entry *e,
691  uint32_t client_address,
692  const char *file_command,
693  const char *filename,
694  bool verbose,
695  const struct timeval *timeout
696)
697{
698  int rv = 0;
699  int eno = 0;
700  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
701  struct sockaddr_in sa;
702  socklen_t size = 0;
703  int port_socket = -1;
704  char port_command [] = "PORT 000,000,000,000,000,000";
705  uint16_t data_port = 0;
706
707  /* Create port socket to establish a data data connection */
708  port_socket = socket(AF_INET, SOCK_STREAM, 0);
709  if (port_socket < 0) {
710    eno = ENOMEM;
711    goto cleanup;
712  }
713
714  /* Bind port socket */
715  rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
716  rv = bind(
717    port_socket,
718    (struct sockaddr *) &sa,
719    sizeof(sa)
720  );
721  if (rv != 0) {
722    eno = EBUSY;
723    goto cleanup;
724  }
725
726  /* Get port number for data socket */
727  size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
728  rv = getsockname(
729    port_socket,
730    (struct sockaddr *) &sa,
731    &size
732  );
733  if (rv != 0) {
734    eno = ENOMEM;
735    goto cleanup;
736  }
737  data_port = ntohs(sa.sin_port);
738
739  /* Send PORT command to set data connection port for server */
740  snprintf(
741    port_command,
742    sizeof(port_command),
743    "PORT %lu,%lu,%lu,%lu,%lu,%lu",
744    (client_address >> 24) & 0xffUL,
745    (client_address >> 16) & 0xffUL,
746    (client_address >> 8) & 0xffUL,
747    (client_address >> 0) & 0xffUL,
748    (data_port >> 8) & 0xffUL,
749    (data_port >> 0) & 0xffUL
750  );
751  reply = rtems_ftpfs_send_command(e, port_command, NULL, verbose);
752  if (reply != RTEMS_FTPFS_REPLY_2) {
753    eno = ENOTSUP;
754    goto cleanup;
755  }
756
757  /* Listen on port socket for incoming data connections */
758  rv = listen(port_socket, 1);
759  if (rv != 0) {
760    eno = EBUSY;
761    goto cleanup;
762  }
763
764  /* Send RETR or STOR command with filename */
765  reply = rtems_ftpfs_send_command(e, file_command, filename, verbose);
766  if (reply != RTEMS_FTPFS_REPLY_1) {
767    eno = EIO;
768    goto cleanup;
769  }
770
771  /* Wait for connect on data connection if necessary */
772  if (rtems_ftpfs_use_timeout(timeout)) {
773    struct timeval to = *timeout;
774    fd_set fds;
775
776    FD_ZERO(&fds);
777    FD_SET(port_socket, &fds);
778
779    rv = select(port_socket + 1, &fds, NULL, NULL, &to);
780    if (rv <= 0) {
781      eno = EIO;
782      goto cleanup;
783    }
784  }
785
786  /* Accept data connection  */
787  size = sizeof(sa);
788  e->data_socket = accept(
789    port_socket,
790    (struct sockaddr *) &sa,
791    &size
792  );
793  if (e->data_socket < 0) {
794    eno = EIO;
795    goto cleanup;
796  }
797
798cleanup:
799
800  /* Close port socket if necessary */
801  if (port_socket >= 0) {
802    rv = close(port_socket);
803    if (rv != 0) {
804      eno = EIO;
805    }
806  }
807
808  return eno;
809}
810
811typedef enum {
812  RTEMS_FTPFS_PASV_START = 0,
813  RTEMS_FTPFS_PASV_JUNK,
814  RTEMS_FTPFS_PASV_DATA,
815  RTEMS_FTPFS_PASV_DONE
816} rtems_ftpfs_pasv_state;
817
818typedef struct {
819  rtems_ftpfs_pasv_state state;
820  size_t index;
821  uint8_t data [6];
822} rtems_ftpfs_pasv_entry;
823
824static void rtems_ftpfs_pasv_parser(
825  const char* buf,
826  size_t len,
827  void *arg
828)
829{
830  rtems_ftpfs_pasv_entry *pe = arg;
831  size_t i = 0;
832
833  for (i = 0; i < len; ++i) {
834    int c = buf [i];
835
836    switch (pe->state) {
837      case RTEMS_FTPFS_PASV_START:
838        if (!isdigit(c)) {
839          pe->state = RTEMS_FTPFS_PASV_JUNK;
840          pe->index = 0;
841        }
842        break;
843      case RTEMS_FTPFS_PASV_JUNK:
844        if (isdigit(c)) {
845          pe->state = RTEMS_FTPFS_PASV_DATA;
846          pe->data [pe->index] = (uint8_t) (c - '0');
847        }
848        break;
849      case RTEMS_FTPFS_PASV_DATA:
850        if (isdigit(c)) {
851          pe->data [pe->index] =
852            (uint8_t) (pe->data [pe->index] * 10 + c - '0');
853        } else if (c == ',') {
854          ++pe->index;
855          if (pe->index < sizeof(pe->data)) {
856            pe->data [pe->index] = 0;
857          } else {
858            pe->state = RTEMS_FTPFS_PASV_DONE;
859          }
860        } else {
861          pe->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  rtems_ftpfs_pasv_entry pe;
885
886  memset(&pe, 0, sizeof(pe));
887
888  /* Send PASV command */
889  reply = rtems_ftpfs_send_command_with_parser(
890    e,
891    "PASV",
892    NULL,
893    rtems_ftpfs_pasv_parser,
894    &pe,
895    verbose
896  );
897  if (reply != RTEMS_FTPFS_REPLY_2) {
898    return ENOTSUP;
899  }
900  data_address = ((uint32_t)(pe.data [0]) << 24)
901    + ((uint32_t)(pe.data [1]) << 16)
902    + ((uint32_t)(pe.data [2]) << 8)
903    + ((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->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  return 0;
1203}
1204
1205static void rtems_ftpfs_unmount_me(
1206  rtems_filesystem_mount_table_entry_t *e
1207)
1208{
1209  free(e->fs_info);
1210}
1211
1212static int rtems_ftpfs_ioctl(
1213  rtems_libio_t *iop,
1214  uint32_t command,
1215  void *arg
1216)
1217{
1218  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1219  bool *verbose = arg;
1220  struct timeval *timeout = arg;
1221
1222  if (arg == NULL) {
1223    rtems_set_errno_and_return_minus_one(EINVAL);
1224  }
1225
1226  switch (command) {
1227    case RTEMS_FTPFS_IOCTL_GET_VERBOSE:
1228      *verbose = me->verbose;
1229      break;
1230    case RTEMS_FTPFS_IOCTL_SET_VERBOSE:
1231      me->verbose = *verbose;
1232      break;
1233    case RTEMS_FTPFS_IOCTL_GET_TIMEOUT:
1234      *timeout = me->timeout;
1235      break;
1236    case RTEMS_FTPFS_IOCTL_SET_TIMEOUT:
1237      me->timeout = *timeout;
1238      break;
1239    default:
1240      rtems_set_errno_and_return_minus_one(EINVAL);
1241  }
1242
1243  return 0;
1244}
1245
1246/*
1247 * The stat() support is intended only for the cp shell command.  Each request
1248 * will return that we have a regular file with read, write and execute
1249 * permissions for every one.  The node index uses a global counter to support
1250 * a remote to remote copy.  This is not a very sophisticated method.
1251 */
1252static int rtems_ftpfs_fstat(
1253  const rtems_filesystem_location_info_t *loc,
1254  struct stat *st
1255)
1256{
1257  static unsigned ino = 0;
1258
1259  /* FIXME */
1260  st->st_ino = ++ino;
1261  st->st_dev = rtems_filesystem_make_dev_t(0xcc494cd6U, 0x1d970b4dU);
1262
1263  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1264
1265  return 0;
1266}
1267
1268static const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1269  .lock_h = rtems_filesystem_default_lock,
1270  .unlock_h = rtems_filesystem_default_unlock,
1271  .eval_path_h = rtems_ftpfs_eval_path,
1272  .link_h = rtems_filesystem_default_link,
1273  .are_nodes_equal_h = rtems_filesystem_default_are_nodes_equal,
1274  .node_type_h = rtems_ftpfs_node_type,
1275  .mknod_h = rtems_filesystem_default_mknod,
1276  .rmnod_h = rtems_filesystem_default_rmnod,
1277  .fchmod_h = rtems_filesystem_default_fchmod,
1278  .chown_h = rtems_filesystem_default_chown,
1279  .clonenod_h = rtems_filesystem_default_clonenode,
1280  .freenod_h = rtems_ftpfs_free_node,
1281  .mount_h = rtems_filesystem_default_mount,
1282  .fsmount_me_h = rtems_ftpfs_initialize,
1283  .unmount_h = rtems_filesystem_default_unmount,
1284  .fsunmount_me_h = rtems_ftpfs_unmount_me,
1285  .utime_h = rtems_filesystem_default_utime,
1286  .symlink_h = rtems_filesystem_default_symlink,
1287  .readlink_h = rtems_filesystem_default_readlink,
1288  .rename_h = rtems_filesystem_default_rename,
1289  .statvfs_h = rtems_filesystem_default_statvfs
1290};
1291
1292static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1293  .open_h = rtems_ftpfs_open,
1294  .close_h = rtems_ftpfs_close,
1295  .read_h = rtems_ftpfs_read,
1296  .write_h = rtems_ftpfs_write,
1297  .ioctl_h = rtems_filesystem_default_ioctl,
1298  .lseek_h = rtems_filesystem_default_lseek,
1299  .fstat_h = rtems_ftpfs_fstat,
1300  .ftruncate_h = rtems_ftpfs_ftruncate,
1301  .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
1302  .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
1303  .fcntl_h = rtems_filesystem_default_fcntl
1304};
1305
1306static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = {
1307  .open_h = rtems_filesystem_default_open,
1308  .close_h = rtems_filesystem_default_close,
1309  .read_h = rtems_filesystem_default_read,
1310  .write_h = rtems_filesystem_default_write,
1311  .ioctl_h = rtems_ftpfs_ioctl,
1312  .lseek_h = rtems_filesystem_default_lseek,
1313  .fstat_h = rtems_filesystem_default_fstat,
1314  .ftruncate_h = rtems_filesystem_default_ftruncate,
1315  .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
1316  .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
1317  .fcntl_h = rtems_filesystem_default_fcntl
1318};
Note: See TracBrowser for help on using the repository browser.