source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 984c4c49

4.11
Last change on this file since 984c4c49 was 984c4c49, checked in by Ralf Corsepius <ralf.corsepius@…>, on Nov 6, 2011 at 12:00:04 PM

2011-11-06 Ralf Corsépius <ralf.corsepius@…>

PR1945/cpukit

  • libfs/src/nfsclient/src/nfs.c, libfs/src/rfs/rtems-rfs-rtems-dev.c, libfs/src/rfs/rtems-rfs-rtems-dir.c, libfs/src/rfs/rtems-rfs-rtems-file.c, libnetworking/lib/ftpfs.c, libnetworking/lib/tftpDriver.c: Replace rtems_off64_t with off_t.
  • Property mode set to 100644
File size: 29.5 KB
Line 
1/**
2 * @file
3 *
4 * File Transfer Protocol file system (FTP client).
5 */
6
7/*
8 * Copyright (c) 2009, 2010, 2011
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  uint32_t flags,
943  uint32_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  /* Check location, it was allocated during path evaluation */
966  if (location == NULL) {
967    rtems_set_errno_and_return_minus_one(ENOMEM);
968  }
969
970  /* Split location into parts */
971  ok = rtems_ftpfs_split_names(
972      location,
973      &user,
974      &password,
975      &hostname,
976      &filename
977  );
978  if (!ok) {
979    if (strlen(location) == 0) {
980      /*
981       * This is an access to the root node that will be used for file system
982       * option settings.
983       */
984      iop->pathinfo.handlers = &rtems_ftpfs_root_handlers;
985
986      return 0;
987    } else {
988      rtems_set_errno_and_return_minus_one(ENOENT);
989    }
990  }
991  DEBUG_PRINTF(
992    "user = '%s', password = '%s', filename = '%s'\n",
993    user,
994    password,
995    filename
996  );
997
998  /* Check for either read-only or write-only flags */
999  if (
1000    (iop->flags & LIBIO_FLAGS_WRITE) != 0
1001      && (iop->flags & LIBIO_FLAGS_READ) != 0
1002  ) {
1003    rtems_set_errno_and_return_minus_one(ENOTSUP);
1004  }
1005
1006  /* Allocate connection entry */
1007  e = calloc(1, sizeof(*e));
1008  if (e == NULL) {
1009    rtems_set_errno_and_return_minus_one(ENOMEM);
1010  }
1011
1012  /* Initialize connection entry */
1013  e->ctrl_socket = -1;
1014  e->data_socket = -1;
1015
1016  /* Save connection state */
1017  iop->data1 = e;
1018
1019  /* Open control connection */
1020  eno = rtems_ftpfs_open_ctrl_connection(
1021    e,
1022    user,
1023    password,
1024    hostname,
1025    &client_address,
1026    verbose,
1027    timeout
1028  );
1029  if (eno != 0) {
1030    goto cleanup;
1031  }
1032
1033  /* Open passive data connection */
1034  eno = rtems_ftpfs_open_data_connection_passive(
1035    e,
1036    client_address,
1037    file_command,
1038    filename,
1039    verbose,
1040    timeout
1041  );
1042  if (eno == ENOTSUP) {
1043    /* Open active data connection */
1044    eno = rtems_ftpfs_open_data_connection_active(
1045      e,
1046      client_address,
1047      file_command,
1048      filename,
1049      verbose,
1050      timeout
1051    );
1052  }
1053  if (eno != 0) {
1054    goto cleanup;
1055  }
1056
1057  /* Set data connection timeout */
1058  eno = rtems_ftpfs_set_connection_timeout(e->data_socket, timeout);
1059
1060cleanup:
1061
1062  if (eno == 0) {
1063    return 0;
1064  } else {
1065    /* Free all resources if an error occured */
1066    rtems_ftpfs_terminate(iop, true);
1067
1068    rtems_set_errno_and_return_minus_one(eno);
1069  }
1070}
1071
1072static ssize_t rtems_ftpfs_read(
1073  rtems_libio_t *iop,
1074  void *buffer,
1075  size_t count
1076)
1077{
1078  rtems_ftpfs_entry *e = iop->data1;
1079  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1080  bool verbose = me->verbose;
1081  char *in = buffer;
1082  size_t todo = count;
1083
1084  if (e->eof) {
1085    return 0;
1086  }
1087
1088  while (todo > 0) {
1089    ssize_t rv = recv(e->data_socket, in, todo, 0);
1090
1091    if (rv <= 0) {
1092      if (rv == 0) {
1093        rtems_ftpfs_reply reply =
1094          rtems_ftpfs_get_reply(e, NULL, NULL, verbose);
1095
1096        if (reply == RTEMS_FTPFS_REPLY_2) {
1097          e->eof = true;
1098          break;
1099        }
1100      }
1101
1102      rtems_set_errno_and_return_minus_one(EIO);
1103    }
1104
1105    in += rv;
1106    todo -= (size_t) rv;
1107  }
1108
1109  return (ssize_t) (count - todo);
1110}
1111
1112static ssize_t rtems_ftpfs_write(
1113  rtems_libio_t *iop,
1114  const void *buffer,
1115  size_t count
1116)
1117{
1118  rtems_ftpfs_entry *e = iop->data1;
1119  const char *out = buffer;
1120  size_t todo = count;
1121
1122  while (todo > 0) {
1123    ssize_t rv = send(e->data_socket, out, todo, 0);
1124
1125    if (rv <= 0) {
1126      if (rv == 0) {
1127        break;
1128      } else {
1129        rtems_set_errno_and_return_minus_one(EIO);
1130      }
1131    }
1132
1133    out += rv;
1134    todo -= (size_t) rv;
1135  }
1136
1137  return (ssize_t) (count - todo);
1138}
1139
1140static int rtems_ftpfs_close(rtems_libio_t *iop)
1141{
1142  int eno = rtems_ftpfs_terminate(iop, false);
1143
1144  if (eno == 0) {
1145    return 0;
1146  } else {
1147    rtems_set_errno_and_return_minus_one(eno);
1148  }
1149}
1150
1151/* Dummy version to let fopen(*,"w") work properly */
1152static int rtems_ftpfs_ftruncate(rtems_libio_t *iop, off_t count)
1153{
1154  return 0;
1155}
1156
1157static int rtems_ftpfs_eval_path(
1158  const char *pathname,
1159  size_t pathnamelen,
1160  int flags,
1161  rtems_filesystem_location_info_t *pathloc
1162)
1163{
1164  /*
1165   * The caller of this routine has striped off the mount prefix from the path.
1166   * We need to store this path here or otherwise we would have to do this job
1167   * again.  The path is used in rtems_ftpfs_open() via iop->pathinfo.node_access.
1168   */
1169  char *pathname_dup = malloc(pathnamelen + 1);
1170
1171  if (pathname_dup != NULL) {
1172    memcpy(pathname_dup, pathname, pathnamelen);
1173    pathname_dup [pathnamelen] = '\0';
1174  }
1175
1176  pathloc->node_access = pathname_dup;
1177
1178  return 0;
1179}
1180
1181static int rtems_ftpfs_free_node(rtems_filesystem_location_info_t *pathloc)
1182{
1183  free(pathloc->node_access);
1184
1185  return 0;
1186}
1187
1188static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1189  rtems_filesystem_location_info_t *pathloc
1190)
1191{
1192  return RTEMS_FILESYSTEM_MEMORY_FILE;
1193}
1194
1195int rtems_ftpfs_initialize(
1196  rtems_filesystem_mount_table_entry_t *e,
1197  const void                           *d
1198)
1199{
1200  rtems_ftpfs_mount_entry *me = malloc(sizeof(rtems_ftpfs_mount_entry));
1201
1202  /* Mount entry for FTP file system instance */
1203  e->fs_info = me;
1204  if (e->fs_info == NULL) {
1205    rtems_set_errno_and_return_minus_one(ENOMEM);
1206  }
1207  me->verbose = false;
1208  me->timeout.tv_sec = 0;
1209  me->timeout.tv_usec = 0;
1210
1211  /* Set handler and oparations table */
1212  e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
1213  e->mt_fs_root.ops = &rtems_ftpfs_ops;
1214
1215  /* We maintain no real file system nodes, so there is no real root */
1216  e->mt_fs_root.node_access = NULL;
1217
1218  /* Just use the limits from IMFS */
1219  e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
1220
1221  return 0;
1222}
1223
1224static int rtems_ftpfs_unmount_me(
1225  rtems_filesystem_mount_table_entry_t *e
1226)
1227{
1228  free(e->fs_info);
1229
1230  return 0;
1231}
1232
1233static int rtems_ftpfs_ioctl(
1234  rtems_libio_t *iop,
1235  uint32_t command,
1236  void *arg
1237)
1238{
1239  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1240  bool *verbose = arg;
1241  struct timeval *timeout = arg;
1242
1243  if (arg == NULL) {
1244    rtems_set_errno_and_return_minus_one(EINVAL);
1245  }
1246
1247  switch (command) {
1248    case RTEMS_FTPFS_IOCTL_GET_VERBOSE:
1249      *verbose = me->verbose;
1250      break;
1251    case RTEMS_FTPFS_IOCTL_SET_VERBOSE:
1252      me->verbose = *verbose;
1253      break;
1254    case RTEMS_FTPFS_IOCTL_GET_TIMEOUT:
1255      *timeout = me->timeout;
1256      break;
1257    case RTEMS_FTPFS_IOCTL_SET_TIMEOUT:
1258      me->timeout = *timeout;
1259      break;
1260    default:
1261      rtems_set_errno_and_return_minus_one(EINVAL);
1262  }
1263
1264  return 0;
1265}
1266
1267/*
1268 * The stat() support is intended only for the cp shell command.  Each request
1269 * will return that we have a regular file with read, write and execute
1270 * permissions for every one.  The node index uses a global counter to support
1271 * a remote to remote copy.  This is not a very sophisticated method.
1272 */
1273static int rtems_ftpfs_fstat(
1274  rtems_filesystem_location_info_t *loc,
1275  struct stat *st
1276)
1277{
1278  static unsigned ino = 0;
1279
1280  memset(st, 0, sizeof(*st));
1281
1282  /* FIXME */
1283  st->st_ino = ++ino;
1284  st->st_dev = rtems_filesystem_make_dev_t(0xcc494cd6U, 0x1d970b4dU);
1285
1286  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1287
1288  return 0;
1289}
1290
1291static const rtems_filesystem_operations_table rtems_ftpfs_ops = {
1292  .evalpath_h = rtems_ftpfs_eval_path,
1293  .evalformake_h = rtems_filesystem_default_evalformake,
1294  .link_h = rtems_filesystem_default_link,
1295  .unlink_h = rtems_filesystem_default_unlink,
1296  .node_type_h = rtems_ftpfs_node_type,
1297  .mknod_h = rtems_filesystem_default_mknod,
1298  .chown_h = rtems_filesystem_default_chown,
1299  .freenod_h = rtems_ftpfs_free_node,
1300  .mount_h = rtems_filesystem_default_mount,
1301  .fsmount_me_h = rtems_ftpfs_initialize,
1302  .unmount_h = rtems_filesystem_default_unmount,
1303  .fsunmount_me_h = rtems_ftpfs_unmount_me,
1304  .utime_h = rtems_filesystem_default_utime,
1305  .eval_link_h = rtems_filesystem_default_evaluate_link,
1306  .symlink_h = rtems_filesystem_default_symlink,
1307  .readlink_h = rtems_filesystem_default_readlink
1308};
1309
1310static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1311  .open_h = rtems_ftpfs_open,
1312  .close_h = rtems_ftpfs_close,
1313  .read_h = rtems_ftpfs_read,
1314  .write_h = rtems_ftpfs_write,
1315  .ioctl_h = rtems_filesystem_default_ioctl,
1316  .lseek_h = rtems_filesystem_default_lseek,
1317  .fstat_h = rtems_ftpfs_fstat,
1318  .fchmod_h = rtems_filesystem_default_fchmod,
1319  .ftruncate_h = rtems_ftpfs_ftruncate,
1320  .fpathconf_h = rtems_filesystem_default_fpathconf,
1321  .fsync_h = rtems_filesystem_default_fsync,
1322  .fdatasync_h = rtems_filesystem_default_fdatasync,
1323  .fcntl_h = rtems_filesystem_default_fcntl,
1324  .rmnod_h = rtems_filesystem_default_rmnod
1325};
1326
1327static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = {
1328  .open_h = rtems_filesystem_default_open,
1329  .close_h = rtems_filesystem_default_close,
1330  .read_h = rtems_filesystem_default_read,
1331  .write_h = rtems_filesystem_default_write,
1332  .ioctl_h = rtems_ftpfs_ioctl,
1333  .lseek_h = rtems_filesystem_default_lseek,
1334  .fstat_h = rtems_filesystem_default_fstat,
1335  .fchmod_h = rtems_filesystem_default_fchmod,
1336  .ftruncate_h = rtems_filesystem_default_ftruncate,
1337  .fpathconf_h = rtems_filesystem_default_fpathconf,
1338  .fsync_h = rtems_filesystem_default_fsync,
1339  .fdatasync_h = rtems_filesystem_default_fdatasync,
1340  .fcntl_h = rtems_filesystem_default_fcntl,
1341  .rmnod_h = rtems_filesystem_default_rmnod
1342};
Note: See TracBrowser for help on using the repository browser.