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

4.104.114.84.95
Last change on this file since 9da12bd was 9da12bd, checked in by Joel Sherrill <joel.sherrill@…>, on 02/05/03 at 21:25:55

2003-02-05 Thomas Doerfler <Thomas.Doerfler@…>

PR 341/networking

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