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

4.104.115
Last change on this file since eea48e2f was 4665f32, checked in by Ralf Corsepius <ralf.corsepius@…>, on 05/27/10 at 16:36:02

2010-05-27 Ralf Corsépius <ralf.corsepius@…>

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