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

4.115
Last change on this file since 3b7c123 was 3b7c123, checked in by Sebastian Huber <sebastian.huber@…>, on 03/13/12 at 10:33:51

Filesystem: Reference counting for locations

o A new data structure rtems_filesystem_global_location_t was

introduced to be used for

o the mount point location in the mount table entry,
o the file system root location in the mount table entry,
o the root directory location in the user environment, and
o the current directory location in the user environment.

During the path evaluation global start locations are obtained to
ensure that the current file system instance will be not unmounted in
the meantime.

o The user environment uses now reference counting and is protected

from concurrent access.

o The path evaluation process was completely rewritten and simplified.

The IMFS, RFS, NFS, and DOSFS use now a generic path evaluation
method. Recursive calls in the path evaluation have been replaced
with iteration to avoid stack overflows. Only the evaluation of
symbolic links is recursive. No dynamic memory allocations and
intermediate buffers are used in the high level path evaluation. No
global locks are held during the file system instance specific path
evaluation process.

o Recursive symbolic link evaluation is now limited by

RTEMS_FILESYSTEM_SYMLOOP_MAX. Applications can retrieve this value
via sysconf().

o The device file system (devFS) uses now no global variables and

allocation from the workspace. Node names are allocated from the
heap.

o The upper layer lseek() performs now some parameter checks.
o The upper layer ftruncate() performs now some parameter checks.
o unmask() is now restricted to the RWX flags and protected from

concurrent access.

o The fchmod_h and rmnod_h file system node handlers are now a file

system operation.

o The unlink_h operation has been removed. All nodes are now destroyed

with the rmnod_h operation.

o New lock_h, unlock_h, clonenod_h, and are_nodes_equal_h file system

operations.

o The path evaluation and file system operations are now protected by

per file system instance lock and unlock operations.

o Fix and test file descriptor duplicate in fcntl().
o New test fstests/fsnofs01.

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