source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 3d3a18e6

4.10
Last change on this file since 3d3a18e6 was 3d3a18e6, checked in by Sebastian Huber <sebastian.huber@…>, on Jul 1, 2010 at 2:39:39 PM

2010-06-10 Sebastian Huber <sebastian.huber@…>

  • libcsupport/src/unmount.c: Removed obsolete declarations. Fixed invalid memory free.

2010-06-10 Sebastian Huber <sebastian.huber@…>

  • libnetworking/rtems/ftpfs.h, libnetworking/lib/ftpfs.c: Removed rtems_ftpfs_mount().

2010-06-10 Sebastian Huber <sebastian.huber@…>

  • libcsupport/src/mount-mktgt.c: New file.
  • libcsupport/Makefile.am: Reflect change above.
  • libcsupport/include/rtems/libio.h: Declare mount_and_make_target_path().

2010-06-09 Sebastian Huber <sebastian.huber@…>

  • libnetworking/rtems/ftpfs.h, libnetworking/lib/ftpfs.c: Added rtems_ftpfs_mount() again. Documentation.

2010-06-09 Sebastian Huber <sebastian.huber@…>

  • libcsupport/include/rtems/libio.h, sapi/include/confdefs.h: Added and use defines for file system types.

2010-06-09 Sebastian Huber <sebastian.huber@…>

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