source: rtems/cpukit/libnetworking/lib/ftpfs.c @ 984c4c49

4.115
Last change on this file since 984c4c49 was 984c4c49, checked in by Ralf Corsepius <ralf.corsepius@…>, on 11/06/11 at 12:00:04

2011-11-06 Ralf Corsépius <ralf.corsepius@…>

PR1945/cpukit

  • libfs/src/nfsclient/src/nfs.c, libfs/src/rfs/rtems-rfs-rtems-dev.c, libfs/src/rfs/rtems-rfs-rtems-dir.c, libfs/src/rfs/rtems-rfs-rtems-file.c, libnetworking/lib/ftpfs.c, libnetworking/lib/tftpDriver.c: Replace rtems_off64_t with off_t.
  • Property mode set to 100644
File size: 29.5 KB
RevLine 
[8916bdc7]1/**
2 * @file
[dda0bffc]3 *
[d2ec643]4 * File Transfer Protocol file system (FTP client).
[8916bdc7]5 */
6
7/*
[00db336a]8 * Copyright (c) 2009, 2010, 2011
[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.
30 *
31 * $Id$
[dda0bffc]32 */
33
[b25b88e7]34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
[8916bdc7]38#include <ctype.h>
[dda0bffc]39#include <errno.h>
[8916bdc7]40#include <fcntl.h>
[dda0bffc]41#include <malloc.h>
[8916bdc7]42#include <netdb.h>
43#include <stdarg.h>
44#include <stdio.h>
[dda0bffc]45#include <stdlib.h>
[8916bdc7]46#include <string.h>
[dda0bffc]47#include <unistd.h>
[8916bdc7]48#include <arpa/inet.h>
49#include <netinet/in.h>
[1b508981]50#include <sys/select.h>
[8916bdc7]51#include <sys/socket.h>
52#include <sys/types.h>
53
[dda0bffc]54#include <rtems.h>
[8916bdc7]55#include <rtems/ftpfs.h>
56#include <rtems/imfs.h>
[dda0bffc]57#include <rtems/libio.h>
58#include <rtems/rtems_bsdnet.h>
[8916bdc7]59#include <rtems/seterr.h>
[dda0bffc]60
[8916bdc7]61#ifdef DEBUG
[ebf2bc6]62  #define DEBUG_PRINTF(...) printf(__VA_ARGS__)
[8916bdc7]63#else
[ebf2bc6]64  #define DEBUG_PRINTF(...)
[dda0bffc]65#endif
66
[8916bdc7]67/**
[d2ec643]68 * Connection entry for each open file stream.
[dda0bffc]69 */
[8916bdc7]70typedef struct {
71  /**
[d2ec643]72   * Control connection socket.
[dda0bffc]73   */
74  int ctrl_socket;
[8916bdc7]75
76  /**
[d2ec643]77   * Data transfer socket.
[dda0bffc]78   */
79  int data_socket;
[8916bdc7]80
[8f6ef478]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
[8916bdc7]96  /**
[d2ec643]97   * End of file flag.
[dda0bffc]98   */
[8916bdc7]99  bool eof;
100} rtems_ftpfs_entry;
[dda0bffc]101
[d2ec643]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
[c69b6fe]117static const rtems_filesystem_operations_table rtems_ftpfs_ops;
118
[d2ec643]119static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers;
120
121static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers;
122
[ebf2bc6]123static bool rtems_ftpfs_use_timeout(const struct timeval *to)
[d2ec643]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{
[ebf2bc6]133  if (rtems_ftpfs_use_timeout(to)) {
[d2ec643]134    int rv = 0;
135   
[ebf2bc6]136    rv = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof(*to));
[d2ec643]137    if (rv != 0) {
138      return EIO;
139    }
140
[ebf2bc6]141    rv = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof(*to));
[d2ec643]142    if (rv != 0) {
143      return EIO;
144    }
145  }
146
147  return 0;
148}
[dda0bffc]149
[d2ec643]150static rtems_status_code rtems_ftpfs_do_ioctl(
151  const char *mount_point,
[959e0a0]152  ioctl_command_t req,
[d2ec643]153  ...
154)
[8916bdc7]155{
[d2ec643]156  rtems_status_code sc = RTEMS_SUCCESSFUL;
157  int rv = 0;
158  int fd = 0;
159  va_list ap;
[dda0bffc]160
[d2ec643]161  if (mount_point == NULL) {
162    mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT;
163  }
164
[ebf2bc6]165  fd = open(mount_point, O_RDWR);
[d2ec643]166  if (fd < 0) {
167    return RTEMS_INVALID_NAME;
168  }
169 
[ebf2bc6]170  va_start(ap, req);
171  rv = ioctl(fd, req, va_arg(ap, void *));
172  va_end(ap);
[d2ec643]173  if (rv != 0) {
174    sc = RTEMS_INVALID_NUMBER;
175  }
176
[ebf2bc6]177  rv = close(fd);
[d2ec643]178  if (rv != 0 && sc == RTEMS_SUCCESSFUL) {
179    sc = RTEMS_IO_ERROR;
180  }
181 
182  return sc;
[8916bdc7]183}
[dda0bffc]184
[ebf2bc6]185rtems_status_code rtems_ftpfs_get_verbose(const char *mount_point, bool *verbose)
[8916bdc7]186{
[d2ec643]187  return rtems_ftpfs_do_ioctl(
188    mount_point,
189    RTEMS_FTPFS_IOCTL_GET_VERBOSE,
190    verbose
191  );
192}
[dda0bffc]193
[ebf2bc6]194rtems_status_code rtems_ftpfs_set_verbose(const char *mount_point, bool verbose)
[d2ec643]195{
196  return rtems_ftpfs_do_ioctl(
197    mount_point,
198    RTEMS_FTPFS_IOCTL_SET_VERBOSE,
199    &verbose
200  );
201}
[dda0bffc]202
[d2ec643]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  );
[8916bdc7]225}
[dda0bffc]226
[8916bdc7]227typedef void (*rtems_ftpfs_reply_parser)(
228  const char * /* reply fragment */,
229  size_t /* reply fragment length */,
230  void * /* parser argument */
231);
[dda0bffc]232
[8916bdc7]233typedef enum {
234  RTEMS_FTPFS_REPLY_START,
235  RTEMS_FTPFS_REPLY_SINGLE_LINE,
[8f6ef478]236  RTEMS_FTPFS_REPLY_DONE,
[8916bdc7]237  RTEMS_FTPFS_REPLY_MULTI_LINE,
[8f6ef478]238  RTEMS_FTPFS_REPLY_MULTI_LINE_START
[8916bdc7]239} rtems_ftpfs_reply_state;
[dda0bffc]240
[8916bdc7]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;
[dda0bffc]249
[8916bdc7]250#define RTEMS_FTPFS_REPLY_SIZE 3
[dda0bffc]251
[8f6ef478]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
[8916bdc7]257static rtems_ftpfs_reply rtems_ftpfs_get_reply(
[8f6ef478]258  rtems_ftpfs_entry *e,
[8916bdc7]259  rtems_ftpfs_reply_parser parser,
[d2ec643]260  void *parser_arg,
261  bool verbose
[dda0bffc]262)
263{
[8916bdc7]264  rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START;
[8f6ef478]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;
[dda0bffc]272
[8916bdc7]273    /* Receive reply fragment from socket */
[8f6ef478]274    if (e->reply_current == e->reply_end) {
275      ssize_t rv = 0;
[dda0bffc]276
[8f6ef478]277      e->reply_current = 0;
278      e->reply_end = 0;
[8916bdc7]279
[8f6ef478]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      }
[dda0bffc]292    }
[8916bdc7]293
[8f6ef478]294    buf = &e->reply_buffer [e->reply_current];
295    n = e->reply_end - e->reply_current;
296
[8916bdc7]297    /* Invoke parser if necessary */
298    if (parser != NULL) {
[8f6ef478]299      parser(buf, n, parser_arg);
[dda0bffc]300    }
[8916bdc7]301
302    /* Parse reply fragment */
[8f6ef478]303    for (i = 0; i < n && state != RTEMS_FTPFS_REPLY_DONE; ++i) {
[8916bdc7]304      char c = buf [i];
305
306      switch (state) {
307        case RTEMS_FTPFS_REPLY_START:
[8f6ef478]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            }
[8916bdc7]317          } else {
[8f6ef478]318            return RTEMS_FTPFS_REPLY_ERROR;
[8916bdc7]319          }
320          break;
321        case RTEMS_FTPFS_REPLY_SINGLE_LINE:
322          if (c == '\n') {
[8f6ef478]323            state = RTEMS_FTPFS_REPLY_DONE;
[8916bdc7]324          }
325          break;
326        case RTEMS_FTPFS_REPLY_MULTI_LINE:
327          if (c == '\n') {
[8f6ef478]328            state = RTEMS_FTPFS_REPLY_MULTI_LINE_START;
329            reply_code_index = 0;
[8916bdc7]330          }
331          break;
[8f6ef478]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            }
[8916bdc7]339          } else {
[8f6ef478]340            if (c == ' ') {
341              state = RTEMS_FTPFS_REPLY_SINGLE_LINE;
342            } else {
343              state = RTEMS_FTPFS_REPLY_MULTI_LINE;
344            }
[8916bdc7]345          }
346          break;
347        default:
348          return RTEMS_FTPFS_REPLY_ERROR;
[dda0bffc]349      }
350    }
[8916bdc7]351
[8f6ef478]352    /* Be verbose if necessary */
353    if (verbose) {
354      write(STDERR_FILENO, buf, i);
[dda0bffc]355    }
[8f6ef478]356
357    /* Update reply index */
358    e->reply_current += i;
[dda0bffc]359  }
[8916bdc7]360
[8f6ef478]361  return reply_code [0];
[8916bdc7]362}
363
364static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser(
[8f6ef478]365  rtems_ftpfs_entry *e,
[8916bdc7]366  const char *cmd,
367  const char *arg,
368  rtems_ftpfs_reply_parser parser,
[d2ec643]369  void *parser_arg,
370  bool verbose
[8916bdc7]371)
372{
[00db336a]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 */
[8f6ef478]390    n = send(e->ctrl_socket, buf, len, 0);
[00db336a]391    if (n == (ssize_t) len) {
392      if (verbose) {
393        write(STDERR_FILENO, buf, len);
394      }
[8916bdc7]395
[00db336a]396      /* Reply */
[8f6ef478]397      reply = rtems_ftpfs_get_reply(e, parser, parser_arg, verbose);
[dda0bffc]398    }
[8916bdc7]399
[00db336a]400    free(buf);
[8916bdc7]401  }
402
[00db336a]403  return reply;
[dda0bffc]404}
[8916bdc7]405
406static rtems_ftpfs_reply rtems_ftpfs_send_command(
[8f6ef478]407  rtems_ftpfs_entry *e,
[8916bdc7]408  const char *cmd,
[d2ec643]409  const char *arg,
410  bool verbose
[dda0bffc]411)
[8916bdc7]412{
[d2ec643]413  return rtems_ftpfs_send_command_with_parser(
[8f6ef478]414    e,
[d2ec643]415    cmd,
416    arg,
417    NULL,
418    NULL,
419    verbose
420  );
[dda0bffc]421}
422
[8916bdc7]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;
[dda0bffc]434
[8916bdc7]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
[dda0bffc]441)
442{
[8916bdc7]443  split_state state = STATE_USER_NAME;
[ebf2bc6]444  size_t len = strlen(s);
[8916bdc7]445  size_t i = 0;
[dda0bffc]446
[8916bdc7]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    }
[dda0bffc]510  }
511
[8916bdc7]512done:
[dda0bffc]513
[71a6a16]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
[8916bdc7]521  /* If we have no password use the user name */
522  if (*password == NULL) {
523    *password = *user;
524  }
525
526  return state == STATE_DONE;
[dda0bffc]527}
528
[8916bdc7]529static socklen_t rtems_ftpfs_create_address(
530  struct sockaddr_in *sa,
531  unsigned long address,
532  unsigned short port
[dda0bffc]533)
534{
[ebf2bc6]535  memset(sa, sizeof(*sa), 0);
[8916bdc7]536
537  sa->sin_family = AF_INET;
538  sa->sin_addr.s_addr = address;
539  sa->sin_port = port;
[ebf2bc6]540  sa->sin_len = sizeof(*sa);
[8916bdc7]541
[ebf2bc6]542  return sizeof(*sa);
[8916bdc7]543}
544
[ebf2bc6]545static int rtems_ftpfs_terminate(rtems_libio_t *iop, bool error)
[8916bdc7]546{
[1cb5aa14]547  int eno = 0;
548  int rv = 0;
[9eab99a]549  rtems_ftpfs_entry *e = iop->data1;
[d2ec643]550  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
551  bool verbose = me->verbose;
[ebf2bc6]552  rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR;
[9eab99a]553
[8916bdc7]554  if (e != NULL) {
555    /* Close data connection if necessary */
556    if (e->data_socket >= 0) {
[ebf2bc6]557      rv = close(e->data_socket);
[1b508981]558      if (rv != 0) {
559        eno = EIO;
[1cb5aa14]560      }
561
562      /* For write connections we have to obtain the transfer reply  */
[1b508981]563      if (
564        e->ctrl_socket >= 0
565          && (iop->flags & LIBIO_FLAGS_WRITE) != 0
566          && !error
567      ) {
[8f6ef478]568        reply = rtems_ftpfs_get_reply(e, NULL, NULL, verbose);
[1cb5aa14]569        if (reply != RTEMS_FTPFS_REPLY_2) {
570          eno = EIO;
571        }
572      }
[dda0bffc]573    }
[8916bdc7]574
575    /* Close control connection if necessary */
576    if (e->ctrl_socket >= 0) {
[8f6ef478]577      reply = rtems_ftpfs_send_command(e, "QUIT", NULL, verbose);
[ebf2bc6]578      if (reply != RTEMS_FTPFS_REPLY_2) {
579        eno = EIO;
580      }
581
582      rv = close(e->ctrl_socket);
[1b508981]583      if (rv != 0) {
584        eno = EIO;
[1cb5aa14]585      }
[dda0bffc]586    }
[8916bdc7]587
588    /* Free connection entry */
[ebf2bc6]589    free(e);
[dda0bffc]590  }
[8916bdc7]591
592  /* Invalidate IO entry */
593  iop->data1 = NULL;
[1cb5aa14]594
595  return eno;
[8916bdc7]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,
[d2ec643]603  uint32_t *client_address,
604  bool verbose,
605  const struct timeval *timeout
[8916bdc7]606)
607{
608  int rv = 0;
[d2ec643]609  int eno = 0;
[8916bdc7]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 */
[ebf2bc6]616  e->ctrl_socket = socket(AF_INET, SOCK_STREAM, 0);
[8916bdc7]617  if (e->ctrl_socket < 0) {
618    return ENOMEM;
[dda0bffc]619  }
[8916bdc7]620
621  /* Set up the server address from the hostname */
[6366c2e]622  if (inet_aton(hostname, &address) == 0) {
[8916bdc7]623    /* Try to get the address by name */
[ebf2bc6]624    struct hostent *he = gethostbyname(hostname);
[8916bdc7]625
626    if (he != NULL) {
[ebf2bc6]627      memcpy(&address, he->h_addr, sizeof(address));
[8916bdc7]628    } else {
629      return ENOENT;
[dda0bffc]630    }
631  }
[ebf2bc6]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));
[8916bdc7]634
635  /* Open control connection */
636  rv = connect(
637    e->ctrl_socket,
638    (struct sockaddr *) &sa,
[ebf2bc6]639    sizeof(sa)
[8916bdc7]640  );
641  if (rv != 0) {
642    return ENOENT;
[dda0bffc]643  }
644
[d2ec643]645  /* Set control connection timeout */
[ebf2bc6]646  eno = rtems_ftpfs_set_connection_timeout(e->ctrl_socket, timeout);
[d2ec643]647  if (eno != 0) {
648    return eno;
649  }
650
[8916bdc7]651  /* Get client address */
[ebf2bc6]652  size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
[8916bdc7]653  rv = getsockname(
654    e->ctrl_socket,
655    (struct sockaddr *) &sa,
656    &size
657  );
658  if (rv != 0) {
659    return ENOMEM;
[dda0bffc]660  }
[ebf2bc6]661  *client_address = ntohl(sa.sin_addr.s_addr);
662  DEBUG_PRINTF("client = %s\n", inet_ntoa(sa.sin_addr));
[8916bdc7]663
664  /* Now we should get a welcome message from the server */
[8f6ef478]665  reply = rtems_ftpfs_get_reply(e, NULL, NULL, verbose);
[8916bdc7]666  if (reply != RTEMS_FTPFS_REPLY_2) {
667    return ENOENT;
[dda0bffc]668  }
[8916bdc7]669
670  /* Send USER command */
[8f6ef478]671  reply = rtems_ftpfs_send_command(e, "USER ", user, verbose);
[8916bdc7]672  if (reply == RTEMS_FTPFS_REPLY_3) {
673    /* Send PASS command */
[8f6ef478]674    reply = rtems_ftpfs_send_command(e, "PASS ", password, verbose);
[8916bdc7]675    if (reply != RTEMS_FTPFS_REPLY_2) {
676      return EACCES;
[dda0bffc]677    }
[8916bdc7]678
679    /* TODO: Some server may require an account */
680  } else if (reply != RTEMS_FTPFS_REPLY_2) {
681    return EACCES;
[dda0bffc]682  }
[8916bdc7]683
684  /* Send TYPE command to set binary mode for all data transfers */
[8f6ef478]685  reply = rtems_ftpfs_send_command(e, "TYPE I", NULL, verbose);
[8916bdc7]686  if (reply != RTEMS_FTPFS_REPLY_2) {
687    return EIO;
[dda0bffc]688  }
[8916bdc7]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,
[d2ec643]697  const char *filename,
698  bool verbose,
699  const struct timeval *timeout
[8916bdc7]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 */
[ebf2bc6]712  port_socket = socket(AF_INET, SOCK_STREAM, 0);
[8916bdc7]713  if (port_socket < 0) {
714    eno = ENOMEM;
715    goto cleanup;
[dda0bffc]716  }
[8916bdc7]717
718  /* Bind port socket */
[ebf2bc6]719  rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
[8916bdc7]720  rv = bind(
721    port_socket,
722    (struct sockaddr *) &sa,
[ebf2bc6]723    sizeof(sa)
[8916bdc7]724  );
725  if (rv != 0) {
726    eno = EBUSY;
727    goto cleanup;
[dda0bffc]728  }
[8916bdc7]729
730  /* Get port number for data socket */
[ebf2bc6]731  size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0);
[8916bdc7]732  rv = getsockname(
733    port_socket,
734    (struct sockaddr *) &sa,
735    &size
736  );
737  if (rv != 0) {
738    eno = ENOMEM;
739    goto cleanup;
[dda0bffc]740  }
[ebf2bc6]741  data_port = ntohs(sa.sin_port);
[8916bdc7]742
743  /* Send PORT command to set data connection port for server */
744  snprintf(
745    port_command,
[ebf2bc6]746    sizeof(port_command),
[1b508981]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
[8916bdc7]754  );
[8f6ef478]755  reply = rtems_ftpfs_send_command(e, port_command, NULL, verbose);
[8916bdc7]756  if (reply != RTEMS_FTPFS_REPLY_2) {
757    eno = ENOTSUP;
758    goto cleanup;
[dda0bffc]759  }
[8916bdc7]760
761  /* Listen on port socket for incoming data connections */
[ebf2bc6]762  rv = listen(port_socket, 1);
[8916bdc7]763  if (rv != 0) {
764    eno = EBUSY;
765    goto cleanup;
[dda0bffc]766  }
[8916bdc7]767
768  /* Send RETR or STOR command with filename */
[8f6ef478]769  reply = rtems_ftpfs_send_command(e, file_command, filename, verbose);
[8916bdc7]770  if (reply != RTEMS_FTPFS_REPLY_1) {
771    eno = EIO;
772    goto cleanup;
[dda0bffc]773  }
[8916bdc7]774
[d2ec643]775  /* Wait for connect on data connection if necessary */
[ebf2bc6]776  if (rtems_ftpfs_use_timeout(timeout)) {
[d2ec643]777    struct timeval to = *timeout;
778    fd_set fds;
779
[ebf2bc6]780    FD_ZERO(&fds);
781    FD_SET(port_socket, &fds);
[d2ec643]782
[ebf2bc6]783    rv = select(port_socket + 1, &fds, NULL, NULL, &to);
[d2ec643]784    if (rv <= 0) {
785      eno = EIO;
786      goto cleanup;
787    }
[1b508981]788  }
789
790  /* Accept data connection  */
[ebf2bc6]791  size = sizeof(sa);
[8916bdc7]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;
[dda0bffc]800  }
[8916bdc7]801
802cleanup:
803
804  /* Close port socket if necessary */
805  if (port_socket >= 0) {
[ebf2bc6]806    rv = close(port_socket);
[1b508981]807    if (rv != 0) {
808      eno = EIO;
809    }
[8916bdc7]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{
[9eab99a]834  rtems_ftpfs_pasv_entry *e = arg;
[8916bdc7]835  size_t i = 0;
836
837  for (i = 0; i < len; ++i) {
[391b4dd]838    int c = buf [i];
[8916bdc7]839
840    switch (e->state) {
841      case RTEMS_FTPFS_PASV_START:
[ebf2bc6]842        if (!isdigit(c)) {
[8916bdc7]843          e->state = RTEMS_FTPFS_PASV_JUNK;
844          e->index = 0;
845        }
846        break;
847      case RTEMS_FTPFS_PASV_JUNK:
[ebf2bc6]848        if (isdigit(c)) {
[8916bdc7]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:
[ebf2bc6]854        if (isdigit(c)) {
[8916bdc7]855          e->data [e->index] = (uint8_t) (e->data [e->index] * 10 + c - '0');
856        } else if (c == ',') {
857          ++e->index;
[ebf2bc6]858          if (e->index < sizeof(e->data)) {
[8916bdc7]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;
[dda0bffc]869    }
870  }
[8916bdc7]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,
[d2ec643]877  const char *filename,
878  bool verbose,
879  const struct timeval *timeout
[8916bdc7]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(
[8f6ef478]894    e,
[8916bdc7]895    "PASV",
896    NULL,
897    rtems_ftpfs_pasv_parser,
[d2ec643]898    &pe,
899    verbose
[8916bdc7]900  );
901  if (reply != RTEMS_FTPFS_REPLY_2) {
902    return ENOTSUP;
[dda0bffc]903  }
[959e0a0]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]));
[8916bdc7]906  data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]);
[ebf2bc6]907  rtems_ftpfs_create_address(&sa, htonl(data_address), htons(data_port));
[8916bdc7]908  DEBUG_PRINTF(
909    "server data = %s:%u\n",
[ebf2bc6]910    inet_ntoa(sa.sin_addr),
911    (unsigned) ntohs(sa.sin_port)
[8916bdc7]912  );
913
914  /* Create data socket */
[ebf2bc6]915  e->data_socket = socket(AF_INET, SOCK_STREAM, 0);
[8916bdc7]916  if (e->data_socket < 0) {
917    return ENOMEM;
[dda0bffc]918  }
[8916bdc7]919
920  /* Open data connection */
921  rv = connect(
922    e->data_socket,
923    (struct sockaddr *) &sa,
[ebf2bc6]924    sizeof(sa)
[8916bdc7]925  );
926  if (rv != 0) {
927    return EIO;
[dda0bffc]928  }
[8916bdc7]929
930  /* Send RETR or STOR command with filename */
[8f6ef478]931  reply = rtems_ftpfs_send_command(e, file_command, filename, verbose);
[8916bdc7]932  if (reply != RTEMS_FTPFS_REPLY_1) {
933    return EIO;
[dda0bffc]934  }
[8916bdc7]935
936  return 0;
937}
938
939static int rtems_ftpfs_open(
940  rtems_libio_t *iop,
941  const char *path,
942  uint32_t flags,
943  uint32_t mode
944)
945{
946  int eno = 0;
947  bool ok = false;
948  rtems_ftpfs_entry *e = NULL;
[d2ec643]949  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
950  bool verbose = me->verbose;
951  const struct timeval *timeout = &me->timeout;
[8916bdc7]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;
[fd2b1634]960  char *location = iop->pathinfo.node_access;
[8916bdc7]961
[9eab99a]962  /* Invalidate data handle */
963  iop->data1 = NULL;
964
965  /* Check location, it was allocated during path evaluation */
[8916bdc7]966  if (location == NULL) {
[ebf2bc6]967    rtems_set_errno_and_return_minus_one(ENOMEM);
[dda0bffc]968  }
[8916bdc7]969
970  /* Split location into parts */
971  ok = rtems_ftpfs_split_names(
972      location,
973      &user,
974      &password,
975      &hostname,
976      &filename
977  );
978  if (!ok) {
[ebf2bc6]979    if (strlen(location) == 0) {
[d2ec643]980      /*
981       * This is an access to the root node that will be used for file system
982       * option settings.
983       */
[fd2b1634]984      iop->pathinfo.handlers = &rtems_ftpfs_root_handlers;
[d2ec643]985
986      return 0;
987    } else {
[ebf2bc6]988      rtems_set_errno_and_return_minus_one(ENOENT);
[d2ec643]989    }
[dda0bffc]990  }
[8916bdc7]991  DEBUG_PRINTF(
992    "user = '%s', password = '%s', filename = '%s'\n",
993    user,
994    password,
995    filename
996  );
997
[d2ec643]998  /* Check for either read-only or write-only flags */
999  if (
1000    (iop->flags & LIBIO_FLAGS_WRITE) != 0
1001      && (iop->flags & LIBIO_FLAGS_READ) != 0
1002  ) {
[ebf2bc6]1003    rtems_set_errno_and_return_minus_one(ENOTSUP);
[d2ec643]1004  }
1005
[8916bdc7]1006  /* Allocate connection entry */
[8f6ef478]1007  e = calloc(1, sizeof(*e));
[8916bdc7]1008  if (e == NULL) {
[ebf2bc6]1009    rtems_set_errno_and_return_minus_one(ENOMEM);
[dda0bffc]1010  }
[8916bdc7]1011
1012  /* Initialize connection entry */
1013  e->ctrl_socket = -1;
1014  e->data_socket = -1;
1015
[9eab99a]1016  /* Save connection state */
1017  iop->data1 = e;
1018
[8916bdc7]1019  /* Open control connection */
1020  eno = rtems_ftpfs_open_ctrl_connection(
1021    e,
1022    user,
1023    password,
1024    hostname,
[d2ec643]1025    &client_address,
1026    verbose,
1027    timeout
[8916bdc7]1028  );
[dda0bffc]1029  if (eno != 0) {
[8916bdc7]1030    goto cleanup;
[dda0bffc]1031  }
[8916bdc7]1032
1033  /* Open passive data connection */
1034  eno = rtems_ftpfs_open_data_connection_passive(
1035    e,
1036    client_address,
1037    file_command,
[d2ec643]1038    filename,
1039    verbose,
1040    timeout
[8916bdc7]1041  );
1042  if (eno == ENOTSUP) {
1043    /* Open active data connection */
1044    eno = rtems_ftpfs_open_data_connection_active(
1045      e,
1046      client_address,
1047      file_command,
[d2ec643]1048      filename,
1049      verbose,
1050      timeout
[8916bdc7]1051    );
[dda0bffc]1052  }
[d2ec643]1053  if (eno != 0) {
1054    goto cleanup;
1055  }
1056
1057  /* Set data connection timeout */
[ebf2bc6]1058  eno = rtems_ftpfs_set_connection_timeout(e->data_socket, timeout);
[8916bdc7]1059
1060cleanup:
1061
[1cb5aa14]1062  if (eno == 0) {
1063    return 0;
1064  } else {
[8916bdc7]1065    /* Free all resources if an error occured */
[ebf2bc6]1066    rtems_ftpfs_terminate(iop, true);
[8916bdc7]1067
[ebf2bc6]1068    rtems_set_errno_and_return_minus_one(eno);
[1cb5aa14]1069  }
[dda0bffc]1070}
1071
[8916bdc7]1072static ssize_t rtems_ftpfs_read(
[dda0bffc]1073  rtems_libio_t *iop,
[8916bdc7]1074  void *buffer,
1075  size_t count
[dda0bffc]1076)
1077{
[9eab99a]1078  rtems_ftpfs_entry *e = iop->data1;
[d2ec643]1079  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1080  bool verbose = me->verbose;
[9eab99a]1081  char *in = buffer;
[8916bdc7]1082  size_t todo = count;
[dda0bffc]1083
[8916bdc7]1084  if (e->eof) {
1085    return 0;
1086  }
1087
1088  while (todo > 0) {
[ebf2bc6]1089    ssize_t rv = recv(e->data_socket, in, todo, 0);
[8916bdc7]1090
1091    if (rv <= 0) {
1092      if (rv == 0) {
1093        rtems_ftpfs_reply reply =
[8f6ef478]1094          rtems_ftpfs_get_reply(e, NULL, NULL, verbose);
[8916bdc7]1095
1096        if (reply == RTEMS_FTPFS_REPLY_2) {
1097          e->eof = true;
1098          break;
1099        }
[dda0bffc]1100      }
[8916bdc7]1101
[ebf2bc6]1102      rtems_set_errno_and_return_minus_one(EIO);
[dda0bffc]1103    }
[8916bdc7]1104
1105    in += rv;
1106    todo -= (size_t) rv;
[dda0bffc]1107  }
[8916bdc7]1108
1109  return (ssize_t) (count - todo);
[dda0bffc]1110}
1111
[8916bdc7]1112static ssize_t rtems_ftpfs_write(
[dda0bffc]1113  rtems_libio_t *iop,
[8916bdc7]1114  const void *buffer,
1115  size_t count
[dda0bffc]1116)
1117{
[9eab99a]1118  rtems_ftpfs_entry *e = iop->data1;
1119  const char *out = buffer;
[8916bdc7]1120  size_t todo = count;
[dda0bffc]1121
[8916bdc7]1122  while (todo > 0) {
[ebf2bc6]1123    ssize_t rv = send(e->data_socket, out, todo, 0);
[8916bdc7]1124
1125    if (rv <= 0) {
1126      if (rv == 0) {
[1cb5aa14]1127        break;
1128      } else {
[ebf2bc6]1129        rtems_set_errno_and_return_minus_one(EIO);
[dda0bffc]1130      }
1131    }
[8916bdc7]1132
1133    out += rv;
1134    todo -= (size_t) rv;
[dda0bffc]1135  }
[8916bdc7]1136
1137  return (ssize_t) (count - todo);
[dda0bffc]1138}
1139
[ebf2bc6]1140static int rtems_ftpfs_close(rtems_libio_t *iop)
[dda0bffc]1141{
[ebf2bc6]1142  int eno = rtems_ftpfs_terminate(iop, false);
[8916bdc7]1143
[1cb5aa14]1144  if (eno == 0) {
1145    return 0;
1146  } else {
[ebf2bc6]1147    rtems_set_errno_and_return_minus_one(eno);
[1cb5aa14]1148  }
[8916bdc7]1149}
1150
[ebf2bc6]1151/* Dummy version to let fopen(*,"w") work properly */
[984c4c49]1152static int rtems_ftpfs_ftruncate(rtems_libio_t *iop, off_t count)
[8916bdc7]1153{
1154  return 0;
1155}
1156
1157static int rtems_ftpfs_eval_path(
1158  const char *pathname,
[4665f32]1159  size_t pathnamelen,
[8916bdc7]1160  int flags,
1161  rtems_filesystem_location_info_t *pathloc
1162)
1163{
[dda0bffc]1164  /*
[8916bdc7]1165   * The caller of this routine has striped off the mount prefix from the path.
1166   * We need to store this path here or otherwise we would have to do this job
[fd2b1634]1167   * again.  The path is used in rtems_ftpfs_open() via iop->pathinfo.node_access.
[dda0bffc]1168   */
[ebf2bc6]1169  char *pathname_dup = malloc(pathnamelen + 1);
1170
1171  if (pathname_dup != NULL) {
1172    memcpy(pathname_dup, pathname, pathnamelen);
1173    pathname_dup [pathnamelen] = '\0';
1174  }
1175
1176  pathloc->node_access = pathname_dup;
[dda0bffc]1177
1178  return 0;
1179}
1180
[ebf2bc6]1181static int rtems_ftpfs_free_node(rtems_filesystem_location_info_t *pathloc)
[dda0bffc]1182{
[ebf2bc6]1183  free(pathloc->node_access);
[9eab99a]1184
1185  return 0;
[dda0bffc]1186}
1187
[8916bdc7]1188static rtems_filesystem_node_types_t rtems_ftpfs_node_type(
1189  rtems_filesystem_location_info_t *pathloc
[dda0bffc]1190)
1191{
[8916bdc7]1192  return RTEMS_FILESYSTEM_MEMORY_FILE;
1193}
1194
[29e92b0]1195int rtems_ftpfs_initialize(
1196  rtems_filesystem_mount_table_entry_t *e,
1197  const void                           *d
[8916bdc7]1198)
1199{
[ebf2bc6]1200  rtems_ftpfs_mount_entry *me = malloc(sizeof(rtems_ftpfs_mount_entry));
[d2ec643]1201
1202  /* Mount entry for FTP file system instance */
1203  e->fs_info = me;
1204  if (e->fs_info == NULL) {
[ebf2bc6]1205    rtems_set_errno_and_return_minus_one(ENOMEM);
[d2ec643]1206  }
1207  me->verbose = false;
1208  me->timeout.tv_sec = 0;
1209  me->timeout.tv_usec = 0;
1210
[8916bdc7]1211  /* Set handler and oparations table */
1212  e->mt_fs_root.handlers = &rtems_ftpfs_handlers;
1213  e->mt_fs_root.ops = &rtems_ftpfs_ops;
1214
1215  /* We maintain no real file system nodes, so there is no real root */
1216  e->mt_fs_root.node_access = NULL;
1217
1218  /* Just use the limits from IMFS */
1219  e->pathconf_limits_and_options = IMFS_LIMITS_AND_OPTIONS;
1220
1221  return 0;
[dda0bffc]1222}
1223
[d2ec643]1224static int rtems_ftpfs_unmount_me(
1225  rtems_filesystem_mount_table_entry_t *e
1226)
1227{
[ebf2bc6]1228  free(e->fs_info);
[d2ec643]1229
1230  return 0;
1231}
1232
1233static int rtems_ftpfs_ioctl(
1234  rtems_libio_t *iop,
1235  uint32_t command,
1236  void *arg
1237)
1238{
1239  rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info;
1240  bool *verbose = arg;
1241  struct timeval *timeout = arg;
1242
1243  if (arg == NULL) {
[ebf2bc6]1244    rtems_set_errno_and_return_minus_one(EINVAL);
[d2ec643]1245  }
1246
1247  switch (command) {
1248    case RTEMS_FTPFS_IOCTL_GET_VERBOSE:
1249      *verbose = me->verbose;
1250      break;
1251    case RTEMS_FTPFS_IOCTL_SET_VERBOSE:
1252      me->verbose = *verbose;
1253      break;
1254    case RTEMS_FTPFS_IOCTL_GET_TIMEOUT:
1255      *timeout = me->timeout;
1256      break;
1257    case RTEMS_FTPFS_IOCTL_SET_TIMEOUT:
1258      me->timeout = *timeout;
1259      break;
1260    default:
[b45ba525]1261      rtems_set_errno_and_return_minus_one(EINVAL);
[d2ec643]1262  }
1263
1264  return 0;
1265}
1266
[8916bdc7]1267/*
1268 * The stat() support is intended only for the cp shell command.  Each request
1269 * will return that we have a regular file with read, write and execute
1270 * permissions for every one.  The node index uses a global counter to support
[d2ec643]1271 * a remote to remote copy.  This is not a very sophisticated method.
[8916bdc7]1272 */
1273static int rtems_ftpfs_fstat(
1274  rtems_filesystem_location_info_t *loc,
1275  struct stat *st
[dda0bffc]1276)
1277{
[8916bdc7]1278  static unsigned ino = 0;
1279
[ebf2bc6]1280  memset(st, 0, sizeof(*st));
[8916bdc7]1281
1282  /* FIXME */
1283  st->st_ino = ++ino;
[ebf2bc6]1284  st->st_dev = rtems_filesystem_make_dev_t(0xcc494cd6U, 0x1d970b4dU);
[8916bdc7]1285
1286  st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
1287
1288  return 0;
[dda0bffc]1289}
1290
[c69b6fe]1291static const rtems_filesystem_operations_table rtems_ftpfs_ops = {
[8916bdc7]1292  .evalpath_h = rtems_ftpfs_eval_path,
[de39b51]1293  .evalformake_h = rtems_filesystem_default_evalformake,
1294  .link_h = rtems_filesystem_default_link,
1295  .unlink_h = rtems_filesystem_default_unlink,
[8916bdc7]1296  .node_type_h = rtems_ftpfs_node_type,
[de39b51]1297  .mknod_h = rtems_filesystem_default_mknod,
1298  .chown_h = rtems_filesystem_default_chown,
[9eab99a]1299  .freenod_h = rtems_ftpfs_free_node,
[de39b51]1300  .mount_h = rtems_filesystem_default_mount,
[29e92b0]1301  .fsmount_me_h = rtems_ftpfs_initialize,
[de39b51]1302  .unmount_h = rtems_filesystem_default_unmount,
[d2ec643]1303  .fsunmount_me_h = rtems_ftpfs_unmount_me,
[de39b51]1304  .utime_h = rtems_filesystem_default_utime,
1305  .eval_link_h = rtems_filesystem_default_evaluate_link,
1306  .symlink_h = rtems_filesystem_default_symlink,
1307  .readlink_h = rtems_filesystem_default_readlink
[dda0bffc]1308};
[8916bdc7]1309
1310static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = {
1311  .open_h = rtems_ftpfs_open,
1312  .close_h = rtems_ftpfs_close,
1313  .read_h = rtems_ftpfs_read,
1314  .write_h = rtems_ftpfs_write,
[de39b51]1315  .ioctl_h = rtems_filesystem_default_ioctl,
1316  .lseek_h = rtems_filesystem_default_lseek,
[8916bdc7]1317  .fstat_h = rtems_ftpfs_fstat,
[de39b51]1318  .fchmod_h = rtems_filesystem_default_fchmod,
[8916bdc7]1319  .ftruncate_h = rtems_ftpfs_ftruncate,
[de39b51]1320  .fpathconf_h = rtems_filesystem_default_fpathconf,
1321  .fsync_h = rtems_filesystem_default_fsync,
1322  .fdatasync_h = rtems_filesystem_default_fdatasync,
1323  .fcntl_h = rtems_filesystem_default_fcntl,
1324  .rmnod_h = rtems_filesystem_default_rmnod
[dda0bffc]1325};
[d2ec643]1326
1327static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = {
[de39b51]1328  .open_h = rtems_filesystem_default_open,
1329  .close_h = rtems_filesystem_default_close,
1330  .read_h = rtems_filesystem_default_read,
1331  .write_h = rtems_filesystem_default_write,
[d2ec643]1332  .ioctl_h = rtems_ftpfs_ioctl,
[de39b51]1333  .lseek_h = rtems_filesystem_default_lseek,
1334  .fstat_h = rtems_filesystem_default_fstat,
1335  .fchmod_h = rtems_filesystem_default_fchmod,
1336  .ftruncate_h = rtems_filesystem_default_ftruncate,
1337  .fpathconf_h = rtems_filesystem_default_fpathconf,
1338  .fsync_h = rtems_filesystem_default_fsync,
1339  .fdatasync_h = rtems_filesystem_default_fdatasync,
1340  .fcntl_h = rtems_filesystem_default_fcntl,
1341  .rmnod_h = rtems_filesystem_default_rmnod
[d2ec643]1342};
Note: See TracBrowser for help on using the repository browser.