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

4.104.114.84.95
Last change on this file since 11458809 was 7192476f, checked in by Ralf Corsepius <ralf.corsepius@…>, on 12/08/06 at 07:18:27

Use size_t instead of uint32_t for read/write count-args.

  • Property mode set to 100644
File size: 27.6 KB
Line 
1/*
2 * File Transfer Protocol client
3 *
4 * Transfer file to/from remote host
5 *
6 * This driver can be added to the RTEMS file system with a call to
7 * "rtems_bsdnet_initialize_ftp_filesystem () ".
8 * From then on, you can open, read and close files on a remote FTP server
9 * using the following syntax:
10 * To open a file "myfile.txt" in the directory "mydir" (relative to home
11 * directory) on a server named "myserver" using the user id
12 * "myuserid" and the password "my_very_secret_password" you must
13 * specify the following path:
14 *
15 * /FTP/myuserid:my_very_secret_password/@myserver/mydirectory/myfile.txt
16 *
17 * If the server is the default server specified in BOOTP, it can be ommitted:
18 *
19 * /FTP/myuserid:my_very_secret_password/mydirectory/myfile.txt
20 *
21 * WARNING: write accesses have not yet been tested.
22 *
23 *
24 * (c) Copyright 2002
25 * Thomas Doerfler
26 * IMD Ingenieurbuero fuer Microcomputertechnik
27 * Herbststr. 8
28 * 82178 Puchheim, Germany
29 * <Thomas.Doerfler@imd-systems.de>
30 *
31 * This code has been created after closly inspecting
32 * "tftpdriver.c" from Eric Norum.
33 *
34 *  $Id$
35 */
36
37#include <stdio.h>
38#include <errno.h>
39#include <malloc.h>
40#include <string.h>
41#include <stdlib.h>
42#include <fcntl.h>
43#include <unistd.h>
44#include <inttypes.h>
45#include <ctype.h>
46#include <rtems.h>
47#include <rtems/libio.h>
48#include <rtems/rtems_bsdnet.h>
49#include <sys/types.h>
50#include <sys/socket.h>
51#include <netinet/in.h>
52#include <arpa/inet.h>
53#include <netdb.h>
54#include <rtems/ftpfs.h>
55
56
57#ifndef set_errno_and_return_minus_one
58#define set_errno_and_return_minus_one( _error ) \
59  do { errno = (_error); return -1; } while(0)
60#endif
61
62/* #define DEBUG_OUT */
63
64/*
65 * Well-known port for FTP
66 */
67#define FTP_PORT_NUM    21
68
69/*
70 * Pathname prefix
71 */
72#define FTP_PATHNAME_PREFIX     "/FTP/"
73/*
74 * reply codes
75 */
76#define FTP_REPLY_CONNECT 220  /* Connection established       */
77#define FTP_REPLY_PASSREQ 331  /* user ok, password required   */
78#define FTP_REPLY_LOGIN   230  /* login finished               */
79#define FTP_REPLY_SUCCESS 200  /* xxx successful               */
80#define FTP_REPLY_OPENCONN 150 /* opening connection for tfer  */
81#define FTP_REPLY_TFERCMPL 226 /* transfer complete            */
82
83extern rtems_filesystem_operations_table  rtems_ftp_ops; 
84extern rtems_filesystem_file_handlers_r rtems_ftp_handlers;
85
86/*
87 * FTP command strings
88 */
89#define FTP_USER_CMD   "USER "
90#define FTP_PASS_CMD   "PASS "
91#define FTP_BINARY_CMD "TYPE I"
92#define FTP_PORT_CMD   "PORT "
93#define FTP_STOR_CMD   "STOR "
94#define FTP_RETR_CMD   "RETR "
95#define FTP_QUIT_CMD   "QUIT"
96
97/*
98 * State of each FTP stream
99 */
100struct ftpStream {
101  /*
102   * Control connection socket
103   */
104  int ctrl_socket;
105  struct sockaddr_in    myCtrlAddress;
106  struct sockaddr_in    farCtrlAddress;
107  /*
108   * Data transfer socket
109   */
110  int port_socket;
111  int data_socket;
112  struct sockaddr_in    myDataAddress;
113  struct sockaddr_in    farDataAddress;
114  /*
115   * other stuff to remember
116   */
117  boolean eof_reached;
118};
119
120/*
121 * Number of streams open at the same time
122 */
123static rtems_id ftp_mutex;
124static int nStreams;
125static struct ftpStream ** volatile ftpStreams;
126
127extern rtems_filesystem_operations_table  rtems_tftp_ops;
128extern rtems_filesystem_file_handlers_r   rtems_tftp_handlers;
129
130/*
131 *  Direct copy from the IMFS/TFTP.  Look at this.
132 */
133
134rtems_filesystem_limits_and_options_t rtems_ftp_limits_and_options = {
135   5,   /* link_max */
136   6,   /* max_canon */
137   7,   /* max_input */
138   255, /* name_max */
139   255, /* path_max */
140   2,   /* pipe_buf */
141   1,   /* posix_async_io */
142   2,   /* posix_chown_restrictions */
143   3,   /* posix_no_trunc */
144   4,   /* posix_prio_io */
145   5,   /* posix_sync_io */
146   6    /* posix_vdisable */
147};
148
149int rtems_ftp_mount_me(
150  rtems_filesystem_mount_table_entry_t *temp_mt_entry
151)
152{
153  rtems_status_code  sc;
154
155  temp_mt_entry->mt_fs_root.handlers = &rtems_ftp_handlers;
156  temp_mt_entry->mt_fs_root.ops      = &rtems_ftp_ops;
157
158  /*
159   *   We have no ftp filesystem specific data to maintain.  This
160   *   filesystem may only be mounted ONCE.
161   *
162   *   And we maintain no real filesystem nodes, so there is no real root.
163   */
164
165  temp_mt_entry->fs_info                = NULL;
166  temp_mt_entry->mt_fs_root.node_access = NULL;
167
168  /*
169   *  These need to be looked at for full POSIX semantics.
170   */
171
172  temp_mt_entry->pathconf_limits_and_options = rtems_ftp_limits_and_options;
173
174
175  /*
176   *  Now allocate a semaphore for mutual exclusion.
177   *
178   *  NOTE:  This could be in an fsinfo for this filesystem type.
179   */
180 
181  sc = rtems_semaphore_create (rtems_build_name('F','T','P',' '),
182                               1,
183                               RTEMS_FIFO |
184                               RTEMS_BINARY_SEMAPHORE |
185                               RTEMS_NO_INHERIT_PRIORITY |
186                               RTEMS_NO_PRIORITY_CEILING |
187                               RTEMS_LOCAL,
188                               0,
189                               &ftp_mutex);
190
191  if (sc != RTEMS_SUCCESSFUL)
192    set_errno_and_return_minus_one( ENOMEM );
193
194  return 0;
195}
196
197/*
198 * Initialize the FTP driver
199 */
200
201int rtems_bsdnet_initialize_ftp_filesystem ()
202{
203 int                                   status;
204 rtems_filesystem_mount_table_entry_t *entry;
205
206 status = mkdir( FTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
207 if ( status == -1 )
208   return status;
209
210  status = mount(
211     &entry,
212     &rtems_ftp_ops,
213     RTEMS_FILESYSTEM_READ_ONLY,
214     NULL,
215     FTP_PATHNAME_PREFIX
216  );
217
218  if ( status )
219    perror( "FTP mount failed" );
220
221  return status;
222}
223
224/*
225 * read and return message code from ftp control connection
226 */
227int rtems_ftp_get_message
228(
229  const struct ftpStream *fsp,  /* ptr to ftp control structure */
230  int *msg_code                 /* ptr to return message code   */
231)
232{
233  char rd_buffer[4];
234  size_t rd_size;
235  size_t tmp_size;
236  int eno = 0;
237  rtems_boolean finished = FALSE;
238  do {
239    /*
240     * fetch (at least) 4 characters from control connection
241     * FIXME: how about a timeout?
242     */   
243    rd_size = 0;
244    while ((eno == 0) &&
245           (rd_size < sizeof(rd_buffer))) {
246      tmp_size = read(fsp->ctrl_socket,
247                      (&rd_buffer)+rd_size,
248                      sizeof(rd_buffer)-rd_size);
249      if (tmp_size < 0) {
250        eno = EIO;
251      }
252      else {
253#ifdef DEBUG_OUT
254        write(1,(&rd_buffer)+rd_size,tmp_size);
255#endif
256        rd_size += tmp_size;
257      }
258    }
259    /*
260     * check for 3 digits and space, otherwise not finished
261     */   
262    if ((eno == 0) &&
263        (isdigit((unsigned int)rd_buffer[0])) &&
264        (isdigit((unsigned int)rd_buffer[1])) &&
265        (isdigit((unsigned int)rd_buffer[2])) &&
266        (rd_buffer[3] == ' ')) {
267      finished = TRUE;
268      rd_buffer[3] = '\0';
269      *msg_code = atol(rd_buffer);
270    }
271    /*
272     * skip rest until end-of-line
273     */
274    do {
275      tmp_size = read(fsp->ctrl_socket,
276                      &rd_buffer,
277                      1);
278      if (tmp_size < 0) {
279        eno = EIO;
280      }
281#ifdef DEBUG_OUT
282      else {
283        write(1,(&rd_buffer),tmp_size);
284      }
285#endif
286    } while ((eno == 0) &&
287             (rd_buffer[0] != '\n'));
288  } while ((eno == 0) && !finished);
289  return eno;
290}
291
292/*
293 * split a pseudo file name into host, user, password, filename
294 * NOTE: this function will allocate space for these strings,
295 * the calling function should free the space, when no longer needed
296 * exception: when we return any error, we will also cleanup
297 * the strings
298 * valid forms:
299 * /FTP/user:pass/filepath
300 * /FTP/user:pass@hostname/filepath
301
302 * /FTP/user:pass/filepath
303 * /FTP/user:pass/@hostname/filepath
304 * NOTE: /FTP is already stripped from the name
305 */
306int rtems_ftp_split_names
307( const char *pathname,         /* total path name (including prefix)     */
308  char **usernamep,             /* ptr to ptr to user name                */
309  char **passwordp,             /* ptr to ptr to password                 */
310  char **hostnamep,             /* ptr to ptr to host name                */
311  char **filenamep)             /* ptr to ptr to hostremaining file name  */
312{
313  const char  *chunk_start;
314  const char  *chunk_end;
315  size_t chunk_len;
316  int rc = 0;
317
318  /*
319   * ensure, that result pointers are NULL...
320   */
321  *usernamep = NULL;
322  *passwordp = NULL;
323  *hostnamep = NULL;
324  *filenamep = NULL;
325
326#if 1
327  chunk_start = pathname;
328#else /* no longer needed with IMFS */
329  /*
330   * check, that total path is long enough, skip prefix
331   */
332  if (rc == 0) {
333    if (strlen (pathname) <= strlen (FTP_PATHNAME_PREFIX)) {
334      rc = ENOENT;
335    }
336    else {
337      chunk_start = pathname + strlen (FTP_PATHNAME_PREFIX);
338    }
339  }
340#endif
341  /*
342   * fetch user name: terminated with ":"
343   */
344  if (rc == 0) {
345    chunk_end = strchr(chunk_start,':');
346    if ((chunk_end == NULL) ||         /* No ':' found or                  */
347        (chunk_end == chunk_start)) {  /* ':' is first character-> no name */
348      rc = ENOENT;
349    }
350    else {
351      chunk_len = chunk_end-chunk_start;
352      *usernamep = malloc(chunk_len+1);
353      if (*usernamep == NULL) {
354        rc = ENOMEM;
355      }
356      else {
357        memcpy(*usernamep,chunk_start,chunk_len);
358        (*usernamep)[chunk_len] = '\0';
359      }
360    }
361  }
362  /*
363   * fetch password: terminated with "/" or "@"
364   */
365  if (rc == 0) {
366    chunk_start = chunk_end + 1; /* skip ":" after user name */
367    chunk_end = strchr(chunk_start,'/');
368    if ((chunk_end == NULL) ||         /* No '/' found or                  */
369        (chunk_end == chunk_start)) {  /* '/' is first character-> no pwd  */
370      rc = ENOENT;
371    }
372    else {
373      /*
374       * we have found a proper '/'
375       * this is the end of the password
376       */
377      chunk_len = chunk_end-chunk_start;
378      *passwordp = malloc(chunk_len+1);
379      if (*passwordp == NULL) {
380        rc = ENOMEM;
381      }
382      else {
383        memcpy(*passwordp,chunk_start,chunk_len);
384        (*passwordp)[chunk_len] = '\0';
385      }
386    }
387  }
388  /*
389   * if first char after '/' is '@', then this is the hostname
390   * fetch hostname terminated with "/"
391   * if exists at all. otherwise take default server from bootp
392   */
393  if (rc == 0) {
394    chunk_start = chunk_end+1;
395    if (*chunk_start == '@') {
396      /*
397       * hostname follows
398       */
399      chunk_start = chunk_start + 1; /* skip "@" after password */
400      chunk_end = strchr(chunk_start,'/');
401      if ((chunk_end == NULL) ||         /* No '/' found or                  */
402          (chunk_end == chunk_start)) {  /* '/' is first character-> no host */
403        rc = ENOENT;
404      }
405      else {
406        /*
407         * we have found a proper '/'
408         */
409        chunk_len = chunk_end-chunk_start;
410        *hostnamep = malloc(chunk_len+1);
411        if (*hostnamep == NULL) {
412          rc = ENOMEM;
413        }
414        else {
415          memcpy(*hostnamep,chunk_start,chunk_len);
416          (*hostnamep)[chunk_len] = '\0';
417        }
418      }
419    }
420    else { /* chunk_start != '@' */
421      /*
422       * no host name given, keep string empty
423       */
424      *hostnamep = malloc(1);
425      if (*hostnamep == NULL) {
426        rc = ENOMEM;
427      }
428      else {
429        (*hostnamep)[0] = '\0';
430      }
431    }     
432  }
433  /*
434   * fetch filename. This is all the rest...
435   */
436  if (rc == 0) {
437    chunk_start = chunk_end+1;
438    if (*chunk_start == '\0') {  /* nothing left for filename */
439      rc = ENOENT;
440    }
441    else {
442      chunk_len = strlen(chunk_start);
443      *filenamep = malloc(chunk_len+1);
444      if (*filenamep == NULL) {
445        rc = ENOMEM;
446      }
447      else {
448        memcpy(*filenamep,chunk_start,chunk_len);
449        (*filenamep)[chunk_len] = '\0';
450      }
451    }
452  }
453 
454  /*
455   * cleanup anything, if error occured
456   */
457  if (rc != 0) {
458    if (*hostnamep != NULL) {
459      free(*hostnamep);
460      *hostnamep = NULL;
461    }
462    if (*usernamep != NULL) {
463      free(*usernamep);
464      *usernamep = NULL;
465    }
466    if (*passwordp != NULL) {
467      free(*passwordp);
468      *passwordp = NULL;
469    }
470    if (*filenamep != NULL) {
471      free(*filenamep);
472      *filenamep = NULL;
473    }
474  }
475  return rc;
476}
477                                       
478int rtems_ftp_evaluate_for_make(
479   const char                         *path,       /* IN     */
480   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
481   const char                        **name        /* OUT    */
482)
483
484  set_errno_and_return_minus_one( EIO );   
485}
486
487/*
488 * XXX - Fix return values.
489 */
490
491int rtems_ftp_eval_path( 
492  const char                        *pathname,     /* IN     */
493  int                                flags,        /* IN     */
494  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
495)
496{
497
498  /*
499   * Read-only for now
500   */
501   
502  if ( ((flags & O_RDONLY) != O_RDONLY ) &&
503       ((flags & O_WRONLY) != O_WRONLY )) {
504    set_errno_and_return_minus_one( ENOENT );
505  }
506  /*
507   * The File system is mounted at FTP_PATHNAME_PREFIX
508   * the caller of this routine has striped off this part of the
509   * name. Save the remainder of the name for use by the open routine.
510   */
511
512  pathloc->node_access = (void * ) pathname;
513  pathloc->handlers    = &rtems_ftp_handlers;
514
515  return 0;
516}
517
518/*
519 * Open a FTP stream
520 */
521int rtems_ftp_open(
522  rtems_libio_t *iop,
523  const char    *new_name,
524  uint32_t       flag,
525  uint32_t       mode
526)
527{
528  int s = 0;
529  char *filename  = NULL;
530  char *uname     = NULL;
531  char *upass     = NULL;
532  char *hostname  = NULL;
533  char port_buffer[sizeof(FTP_PORT_CMD)+6*4+1+1];
534  uint32_t   my_ip;
535  uint16_t   my_port;
536  int eno = 0;
537  rtems_status_code rc;
538  rtems_boolean is_write = FALSE;
539  rtems_boolean sema_obtained = FALSE;
540  struct ftpStream *fsp = NULL;
541  int msg_tmp = 0;
542  socklen_t sockaddr_size;
543  /*
544   * check for R/O or W/O flags
545   */
546  if (eno == 0) {
547    if ((0 != (iop->flags & LIBIO_FLAGS_WRITE)) &&
548        (0 != (iop->flags & LIBIO_FLAGS_READ))) {
549      eno = ENOTSUP;
550    }
551    else {
552      is_write = (0 != (iop->flags & LIBIO_FLAGS_WRITE));
553    }
554  }
555  /*
556   * split pathname into parts
557   */
558  if (eno == 0) {
559    eno = rtems_ftp_split_names(iop->file_info,
560                                &uname,
561                                &upass,
562                                &hostname,
563                                &filename);
564  }
565 
566  /*
567   * Find a free stream
568   */
569  if (eno == 0) {
570    rc = rtems_semaphore_obtain (ftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
571    if (rc == RTEMS_SUCCESSFUL) {
572      sema_obtained = TRUE;
573    }
574    else {
575      eno = EBUSY;
576    }
577  }
578  if (eno == 0) {
579    for (s = 0 ; s < nStreams ; s++) {
580      if (ftpStreams[s] == NULL)
581        break;
582    }
583    if (s == nStreams) {
584      /*
585       * Reallocate stream pointers
586       * Guard against the case where realloc() returns NULL.
587       */
588      struct ftpStream **np;
589     
590      np = realloc (ftpStreams, ++nStreams * sizeof *ftpStreams);
591      if (np == NULL) {
592        eno = ENOMEM;
593      }
594      else {
595        ftpStreams = np;
596      }
597    }
598  }
599  if (eno == 0) {
600    fsp = ftpStreams[s] = malloc (sizeof (struct ftpStream));
601    rtems_semaphore_release (ftp_mutex);
602    sema_obtained = FALSE;
603    if (fsp == NULL) {
604      eno = ENOMEM;
605    }
606    else {
607      iop->data0 = s;
608      iop->data1 = fsp;
609      fsp->ctrl_socket = -1; /* mark, that sockets not yet created */
610      fsp->port_socket = -1;
611      fsp->data_socket = -1;
612      fsp->eof_reached = FALSE;
613    }
614  }
615  if (eno == 0) { 
616  /*
617   * Create the socket for control connection
618   */
619    if ((fsp->ctrl_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
620      eno = ENOMEM;
621    }
622  }
623
624  if (eno == 0) {
625    /*
626     * Set the destination to the FTP server
627     * port on the remote machine.
628     */
629    memset(&(fsp->farCtrlAddress),sizeof(fsp->farCtrlAddress),0);
630    fsp->farCtrlAddress.sin_family = AF_INET;
631    if (*hostname == '\0') {
632      fsp->farCtrlAddress.sin_addr.s_addr = rtems_bsdnet_bootp_server_address.s_addr;
633    }
634    else if (1 != inet_aton(hostname,&(fsp->farCtrlAddress.sin_addr))) {
635      struct hostent *hent;
636      struct hostent *gethostbyname(const char *name);
637     
638      hent = gethostbyname(hostname);
639      if (hent != NULL) {
640        memcpy(&fsp->farCtrlAddress.sin_addr,
641              hent->h_addr,
642              sizeof(fsp->farCtrlAddress.sin_addr));
643      }
644      else {
645        eno = ENOENT;
646      }
647    }
648    if (eno == 0) {
649      fsp->farCtrlAddress.sin_port = htons (FTP_PORT_NUM); 
650      fsp->farCtrlAddress.sin_len  = sizeof(fsp->farCtrlAddress);   
651      if (0 > connect(fsp->ctrl_socket,
652                      (struct sockaddr *)&(fsp->farCtrlAddress),
653                      sizeof(fsp->farCtrlAddress))) {
654        eno = ENOENT;
655      }
656    }
657    if (eno == 0) {
658      /*
659       * fetch IP address of interface used
660       */
661      memset(&(fsp->myCtrlAddress),sizeof(fsp->myCtrlAddress),0);
662      fsp->myCtrlAddress.sin_family = AF_INET;
663      fsp->myCtrlAddress.sin_addr.s_addr = INADDR_ANY;
664      fsp->myCtrlAddress.sin_port   = 0;
665      fsp->myCtrlAddress.sin_len  = sizeof(fsp->myDataAddress);
666      sockaddr_size = sizeof(fsp->myCtrlAddress);
667      if (0 > getsockname(fsp->ctrl_socket,
668                          (struct sockaddr *)&(fsp->myCtrlAddress),
669                          &sockaddr_size)) {
670        eno = ENOMEM;
671      }
672    }
673  }
674  if (eno == 0) {
675    /*
676     * now we should get a connect message from the FTP server
677     */
678    eno = rtems_ftp_get_message(fsp,&msg_tmp);
679    if ((eno == 0) &&
680        (msg_tmp != FTP_REPLY_CONNECT)) {
681      eno = ENOENT;
682    }
683  }
684  if (eno == 0) {
685    /*
686     * send user ID to server
687     * NOTE: the following lines will be executed in order
688     * and will be aborted whenever an error occures... (see your ANSI C book)
689     */
690    if ((0 > send(fsp->ctrl_socket,FTP_USER_CMD,strlen(FTP_USER_CMD),0)) ||
691        (0 > send(fsp->ctrl_socket,uname,       strlen(uname),       0)) ||
692        (0 > send(fsp->ctrl_socket,"\n",        1,                   0))) {
693      eno = EIO;
694    }
695  }
696  if (eno == 0) {
697    /*
698     * now we should get a request for the password or a login...
699     */
700    eno = rtems_ftp_get_message(fsp,&msg_tmp);
701    if (eno == 0) {
702      if (msg_tmp == FTP_REPLY_PASSREQ) {
703        /*
704         * send password to server
705         */
706#ifdef DEBUG_OUT
707        write(1,FTP_PASS_CMD,strlen(FTP_PASS_CMD));
708        write(1,upass,       strlen(upass)       );
709        write(1,"\n",        1                   );
710#endif   
711        if ((0 > send(fsp->ctrl_socket,FTP_PASS_CMD,strlen(FTP_PASS_CMD),0)) ||
712            (0 > send(fsp->ctrl_socket,upass,       strlen(upass),       0)) ||
713            (0 > send(fsp->ctrl_socket,"\n",        1,                   0))) {
714          eno = EIO;
715        }
716        /*
717         * at least now a login reply should come up...
718         * this is checked some lines downwards the code
719         */
720        if (eno == 0) {
721          eno = rtems_ftp_get_message(fsp,&msg_tmp);
722        }
723      }
724    }
725  }
726  if (eno == 0) {
727    /*
728     * check for a login reply. this should be present now...
729     */
730    if (msg_tmp != FTP_REPLY_LOGIN) {
731      eno = EACCES; /* pseudo for wrong user/pass */
732    }
733  }
734  if (eno == 0) {
735    /*
736     * set binary mode for all transfers
737     */
738#ifdef DEBUG_OUT
739    write(1,FTP_BINARY_CMD,strlen(FTP_BINARY_CMD));
740    write(1,"\n",        1                   );
741#endif   
742    if ((0 > send(fsp->ctrl_socket,FTP_BINARY_CMD,strlen(FTP_BINARY_CMD),0)) ||
743        (0 > send(fsp->ctrl_socket,"\n",          1,                     0))) {
744      eno = EIO;
745    }
746    else {
747      eno = rtems_ftp_get_message(fsp,&msg_tmp);
748    }
749  }
750  if (eno == 0) {
751    /*
752     * check for a "BINARY TYPE command successful" reply
753     */
754    if (msg_tmp != FTP_REPLY_SUCCESS) {
755      eno = EIO;
756    }
757  }
758  if (eno == 0) {
759    /*
760     * create and bind socket for data connection
761     */
762    if ((fsp->port_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
763      eno = ENOMEM;
764    }
765    else {
766      memset(&(fsp->myDataAddress),sizeof(fsp->myDataAddress),0);
767      fsp->myDataAddress.sin_family = AF_INET;
768      fsp->myDataAddress.sin_addr.s_addr = INADDR_ANY;
769      fsp->myDataAddress.sin_port   = 0; /* unique port will be assigned */
770      fsp->myDataAddress.sin_len  = sizeof(fsp->myDataAddress);
771      if (0 > bind(fsp->port_socket,
772                   (struct sockaddr *)&(fsp->myDataAddress),
773                   sizeof(fsp->myDataAddress))) {
774        eno = EBUSY;
775      }
776      else {
777        /*
778         * fetch port number of data socket
779         */
780        memset(&(fsp->myDataAddress),sizeof(fsp->myDataAddress),0);
781        fsp->myDataAddress.sin_family = AF_INET;
782        fsp->myDataAddress.sin_addr.s_addr = INADDR_ANY;
783        fsp->myDataAddress.sin_port   = 0;
784        fsp->myDataAddress.sin_len  = sizeof(fsp->myDataAddress);
785        sockaddr_size = sizeof(fsp->myDataAddress);
786        if (0 > getsockname(fsp->port_socket,
787                            (struct sockaddr *)&(fsp->myDataAddress),
788                            &sockaddr_size)) {
789          eno = ENOMEM;
790        }
791      }
792    }
793  }
794  if (eno == 0) {
795    /*
796     * propagate data connection port to server
797     */
798    my_ip   = ntohl(fsp->myCtrlAddress.sin_addr.s_addr);
799    my_port = ntohs(fsp->myDataAddress.sin_port);
800    sprintf(port_buffer,"%s%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%" PRIu16 "\n",
801            FTP_PORT_CMD,
802            (my_ip >> 24) & 0x00ff,
803            (my_ip >> 16) & 0x00ff,
804            (my_ip >>  8) & 0x00ff,
805            (my_ip >>  0) & 0x00ff,
806            (my_port>> 8) & 0x00ff,
807            (my_port>> 0) & 0x00ff);
808#ifdef DEBUG_OUT
809    write(1,port_buffer,strlen(port_buffer));
810#endif
811    if (0 > send(fsp->ctrl_socket,port_buffer,strlen(port_buffer),0)) {
812      eno = EIO;
813    }   
814    else {
815      eno = rtems_ftp_get_message(fsp,&msg_tmp);
816    }
817  }
818  if (eno == 0) {
819    /*
820     * check for a "PORT command successful" reply
821     */
822    if (msg_tmp != FTP_REPLY_SUCCESS) {
823      eno = EBUSY;
824    }
825  }
826  /*
827   * prepare port socket to listen for incoming connections
828   */
829  if (eno == 0) {
830    if (0 > listen(fsp->port_socket,1)) {
831      eno = EBUSY;
832    }
833  }
834  if (eno == 0) {
835    /*
836     * send retrive/store command with filename
837     */
838    if (is_write) {
839#ifdef DEBUG_OUT
840    write(1,FTP_STOR_CMD,strlen(FTP_STOR_CMD));
841    write(1,filename    ,strlen(filename)    );
842    write(1,"\n",1);
843#endif
844      if ((0 > send(fsp->ctrl_socket,FTP_STOR_CMD,strlen(FTP_STOR_CMD),0)) ||
845          (0 > send(fsp->ctrl_socket,filename,    strlen(filename),    0)) ||
846          (0 > send(fsp->ctrl_socket,"\n",        1,                   0))) {
847        eno = EIO;
848      }
849    }
850    else {
851#ifdef DEBUG_OUT
852    write(1,FTP_RETR_CMD,strlen(FTP_RETR_CMD));
853    write(1,filename    ,strlen(filename)    );
854    write(1,"\n",1);
855#endif
856      if ((0 > send(fsp->ctrl_socket,FTP_RETR_CMD,strlen(FTP_RETR_CMD),0)) ||
857          (0 > send(fsp->ctrl_socket,filename,    strlen(filename),    0)) ||
858          (0 > send(fsp->ctrl_socket,"\n",        1,                   0))) {
859        eno = EIO;
860      }
861    }     
862  }
863#if 1
864  if (eno == 0) {
865    eno = rtems_ftp_get_message(fsp,&msg_tmp);
866  }
867  if (eno == 0) {
868    /*
869     * check for a "OPENING binary connection" reply
870     */
871    if (msg_tmp != FTP_REPLY_OPENCONN) {
872      eno = EACCES;
873    }
874  }
875#endif
876  /*
877   * wait for connect on data connection
878   * FIXME: this should become a select instead with a timeout
879   */
880  if (eno == 0) {
881    sockaddr_size = sizeof(fsp->farDataAddress);
882    fsp->data_socket = accept(fsp->port_socket,
883                              (struct sockaddr *)&(fsp->farDataAddress),
884                              &sockaddr_size);
885    if (0 > fsp->data_socket) {
886      eno = EIO;
887    }
888  }
889  /*
890   * FIXME: check, that fardataAddr is really from our ftp server
891   */
892 
893  /*
894   * clean up temp strings...
895   */
896  if (uname != NULL) {
897    free(uname);
898    uname = NULL;
899  }
900  if (upass != NULL) {
901    free(upass);
902    upass = NULL;
903  }
904  if (hostname != NULL) {
905    free(hostname);
906    hostname = NULL;
907  }
908  if (filename != NULL) {
909    free(filename);
910    filename = NULL;
911  }
912  /*
913   * close part socket, no longer needed
914   */
915  if (fsp->port_socket != -1) {
916    close(fsp->port_socket);
917    fsp->port_socket = -1;
918  }
919  /*
920   * if error, clean up everything
921   */
922  if (eno != 0) {
923    if (fsp != NULL) {
924      /*
925       * check and close ctrl/data connection
926       */
927      if (fsp->data_socket != -1) {
928        close(fsp->data_socket);
929        fsp->data_socket = -1;
930      }
931      if (fsp->ctrl_socket != -1) {
932        close(fsp->ctrl_socket);
933        fsp->ctrl_socket = -1;
934      }
935      /*
936       * free ftpStream structure
937       */
938      ftpStreams[s] = NULL;
939      free(fsp);
940      fsp = NULL;
941    }
942  }
943  /*
944   * return sema, if still occupied
945   */
946  if (sema_obtained) {
947    rtems_semaphore_release (ftp_mutex);
948    sema_obtained = FALSE;
949  }
950#if 0
951  if (eno != 0) {
952    set_errno_and_return_minus_one(eno);
953  }
954  return 0;
955#else
956  return eno;
957#endif
958}
959
960/*
961 * Read from a FTP stream
962 */
963ssize_t rtems_ftp_read(
964  rtems_libio_t *iop,
965  void          *buffer,
966  size_t         count
967)
968{
969  int eno = 0;
970  struct ftpStream *fsp;
971  size_t want_cnt;
972  ssize_t rd_cnt;
973  int msg_tmp = 0;
974
975  fsp = iop->data1;
976  want_cnt = count;
977  /*
978   * check, that data connection present
979   */
980  if (eno == 0) {
981    if ((fsp == NULL) ||
982        (fsp->data_socket < 0)) {
983      eno = EBADF;
984    }
985  } 
986   
987  /*
988   * perform read from data socket
989   * read multiple junks, if smaller than wanted
990   */
991  while ((eno == 0) &&
992         (want_cnt > 0) &&
993         !(fsp->eof_reached) ) {
994    rd_cnt = read(fsp->data_socket,buffer,want_cnt);
995    if (rd_cnt > 0) {
996      buffer += rd_cnt;
997      want_cnt -= rd_cnt;
998    }
999    else {
1000      eno = rtems_ftp_get_message(fsp,&msg_tmp);
1001      fsp->eof_reached = TRUE;
1002      if ((eno == 0) &&
1003          (msg_tmp != FTP_REPLY_TFERCMPL)) {
1004        eno = EIO;
1005      }
1006      if (rd_cnt < 0) {
1007        eno = EIO;
1008      }
1009    }
1010  }
1011  if (eno != 0) {
1012    set_errno_and_return_minus_one(eno);
1013  }
1014  return count - want_cnt;
1015}
1016
1017ssize_t rtems_ftp_write(
1018  rtems_libio_t *iop,
1019  const void    *buffer,
1020  size_t         count
1021)
1022{
1023  int eno = EIO;
1024  struct ftpStream *fsp;
1025  size_t want_cnt;
1026  ssize_t wr_cnt;
1027  int msg_tmp = 0;
1028
1029  fsp = iop->data1;
1030  want_cnt = count;
1031  /*
1032   * check, that data connection present
1033   */
1034  if (eno == 0) {
1035    if ((fsp == NULL) ||
1036        (fsp->data_socket < 0)) {
1037      eno = EBADF;
1038    }
1039  } 
1040   
1041  /*
1042   * perform write to data socket
1043   */
1044  if (eno == 0) {
1045    wr_cnt = write(fsp->data_socket,buffer,want_cnt);
1046    if (wr_cnt > 0) {
1047      buffer += wr_cnt;
1048      want_cnt -= wr_cnt;
1049    }
1050    else {
1051      eno = rtems_ftp_get_message(fsp,&msg_tmp);
1052      if ((eno == 0) &&
1053          (msg_tmp != FTP_REPLY_TFERCMPL)) {
1054        eno = EIO;
1055      }
1056      if (wr_cnt < 0) {
1057        eno = EIO;
1058      }
1059    }
1060  }
1061  if (eno != 0) {
1062    set_errno_and_return_minus_one(eno);
1063  }
1064  return count - want_cnt;
1065}
1066
1067/*
1068 * Close a FTP stream
1069 */
1070int rtems_ftp_close(
1071  rtems_libio_t *iop
1072)
1073{
1074  int s = iop->data0;
1075  struct ftpStream *fsp = iop->data1;
1076
1077  /*
1078   * close ctrl/data connection, if needed
1079   */
1080  if (fsp->data_socket >= 0) {
1081    close(fsp->data_socket);
1082    fsp->data_socket = -1;
1083  }
1084  if (fsp->ctrl_socket >= 0) {
1085    close(fsp->ctrl_socket);
1086    fsp->ctrl_socket = -1;
1087  }
1088  /*
1089   * free any used space...
1090   */
1091  rtems_semaphore_obtain (ftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
1092  free (ftpStreams[s]);
1093  ftpStreams[s] = NULL;
1094  rtems_semaphore_release (ftp_mutex);
1095
1096  return 0;
1097}
1098
1099rtems_device_driver rtems_ftp_control(
1100  rtems_device_major_number major,
1101  rtems_device_minor_number minor,
1102  void *pargp
1103)
1104{
1105  return RTEMS_NOT_CONFIGURED;
1106}
1107
1108/*
1109 * Dummy version to let fopen(xxxx,"w") work properly.
1110 */
1111static int rtems_ftp_ftruncate(
1112    rtems_libio_t   *iop,
1113    off_t           count
1114)
1115{
1116    return 0;
1117}
1118
1119rtems_filesystem_node_types_t rtems_ftp_node_type(
1120     rtems_filesystem_location_info_t        *pathloc                 /* IN */
1121)
1122{
1123    return RTEMS_FILESYSTEM_MEMORY_FILE;
1124}
1125
1126rtems_filesystem_operations_table  rtems_ftp_ops = {
1127    rtems_ftp_eval_path,             /* eval_path */
1128    rtems_ftp_evaluate_for_make,     /* evaluate_for_make */
1129    NULL,                            /* link */
1130    NULL,                            /* unlink */
1131    rtems_ftp_node_type,             /* node_type */
1132    NULL,                            /* mknod */
1133    NULL,                            /* chown */
1134    NULL,                            /* freenodinfo */
1135    NULL,                            /* mount */
1136    rtems_ftp_mount_me,              /* initialize */
1137    NULL,                            /* unmount */
1138    NULL,                            /* fsunmount */
1139    NULL,                            /* utime, */
1140    NULL,                            /* evaluate_link */
1141    NULL,                            /* symlink */
1142    NULL,                            /* readlin */
1143};
1144 
1145rtems_filesystem_file_handlers_r rtems_ftp_handlers = {
1146    rtems_ftp_open,      /* open */     
1147    rtems_ftp_close,     /* close */   
1148    rtems_ftp_read,      /* read */     
1149    rtems_ftp_write,     /* write */   
1150    NULL,                /* ioctl */   
1151    NULL,                /* lseek */   
1152    NULL,                /* fstat */   
1153    NULL,                /* fchmod */   
1154    rtems_ftp_ftruncate, /* ftruncate */
1155    NULL,                /* fpathconf */
1156    NULL,                /* fsync */   
1157    NULL,                /* fdatasync */
1158    NULL,                /* fcntl */
1159    NULL                 /* rmnod */
1160};
Note: See TracBrowser for help on using the repository browser.