source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 9b4422a2

4.115
Last change on this file since 9b4422a2 was 9b4422a2, checked in by Joel Sherrill <joel.sherrill@…>, on 05/03/12 at 15:09:24

Remove All CVS Id Strings Possible Using a Script

Script does what is expected and tries to do it as
smartly as possible.

+ remove occurrences of two blank comment lines

next to each other after Id string line removed.

+ remove entire comment blocks which only exited to

contain CVS Ids

+ If the processing left a blank line at the top of

a file, it was removed.

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