source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 29e92b0

4.104.115
Last change on this file since 29e92b0 was 29e92b0, checked in by Chris Johns <chrisj@…>, on 05/31/10 at 13:56:37

2010-05-31 Chris Johns <chrisj@…>

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