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

4.11
Last change on this file since ad5e070 was ad5e070, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 21, 2012 at 2:47:46 PM

ftpfs: Open control connection during path eval

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