source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 183af89

4.115
Last change on this file since 183af89 was 4116fce6, checked in by Sebastian Huber <sebastian.huber@…>, on 02/24/12 at 16:39:27

Filesystem: New defaults fsync_h and fdatasync_h

New defaults rtems_filesystem_default_fsync_or_fdatasync() and
rtems_filesystem_default_fsync_or_fdatasync_success() for fsync_h and
fdatasync_h. The rtems_filesystem_default_fsync_or_fdatasync() sets now
errno to EINVAL according to POSIX.

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