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

4.104.11
Last change on this file since bcf8ee2 was bcf8ee2, checked in by Ralf Corsepius <ralf.corsepius@…>, on Feb 5, 2009 at 5:45:55 AM

2009-02-05 Sebastian Huber <sebastian.huber@…>

  • libnetworking/lib/ftpfs.c: Enable write and mount filesystem with read-write access.
  • 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 <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  bool 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 (void) 
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_WRITE, 
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  ssize_t tmp_size;
236  int eno = 0;
237  bool 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  bool is_write = false;
539  bool 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     
637      hent = gethostbyname(hostname);
638      if (hent != NULL) {
639        memcpy(&fsp->farCtrlAddress.sin_addr, 
640              hent->h_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%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%" PRIu16 "\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  size_t         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 = 0;
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  size_t         count
1020)
1021{
1022  int eno = 0;
1023  struct ftpStream *fsp;
1024  size_t want_cnt;
1025  ssize_t wr_cnt;
1026  int msg_tmp = 0;
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.