source: rtems/c/src/exec/libnetworking/lib/tftpDriver.c @ 8cce445

4.104.114.84.95
Last change on this file since 8cce445 was c1a37d3e, checked in by Joel Sherrill <joel.sherrill@…>, on 02/04/99 at 14:54:31

Debugged and now works except for handling of minor number.

  • Property mode set to 100644
File size: 17.2 KB
Line 
1/*
2 * Trivial File Transfer Protocol (RFC 1350)
3 *
4 * Transfer file to/from remote host
5 *
6 * W. Eric Norum
7 * Saskatchewan Accelerator Laboratory
8 * University of Saskatchewan
9 * Saskatoon, Saskatchewan, CANADA
10 * eric@skatter.usask.ca
11 *
12 *  $Id$
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <errno.h>
18#include <malloc.h>
19#include <string.h>
20#include <unistd.h>
21#include <fcntl.h>
22#include <rtems.h>
23#include <rtems/libio.h>
24#include <rtems/rtems_bsdnet.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29
30#ifndef set_errno_and_return_minus_one
31#define set_errno_and_return_minus_one( _error ) \
32  do { errno = (_error); return -1; } while(0)
33#endif
34
35
36/*
37 * Range of UDP ports to try
38 */
39#define UDP_PORT_BASE   3180
40
41/*
42 * Pathname prefix
43 */
44#define TFTP_PATHNAME_PREFIX    "/TFTP/"
45
46/*
47 * Default limits
48 */
49#define PACKET_REPLY_MILLISECONDS       6000
50#define OPEN_RETRY_LIMIT        10
51#define IO_RETRY_LIMIT          10
52
53/*
54 * TFTP opcodes
55 */
56#define TFTP_OPCODE_RRQ         1
57#define TFTP_OPCODE_WRQ         2
58#define TFTP_OPCODE_DATA        3
59#define TFTP_OPCODE_ACK         4
60#define TFTP_OPCODE_ERROR       5
61
62/*
63 * Largest data transfer
64 */
65#define TFTP_BUFSIZE    512
66
67/*
68 * Packets transferred between machines
69 */
70union tftpPacket {
71        /*
72         * RRQ/WRQ packet
73         */
74        struct tftpRWRQ {
75                rtems_unsigned16        opcode;
76                char                    filename_mode[TFTP_BUFSIZE];
77        } tftpRWRQ;
78
79        /*
80         * DATA packet
81         */
82        struct tftpDATA {
83                rtems_unsigned16        opcode;
84                rtems_unsigned16        blocknum;
85                rtems_unsigned8         data[TFTP_BUFSIZE];
86        } tftpDATA;
87
88        /*
89         * ACK packet
90         */
91        struct tftpACK {
92                rtems_unsigned16        opcode;
93                rtems_unsigned16        blocknum;
94        } tftpACK;
95
96        /*
97         * ERROR packet
98         */
99        struct tftpERROR {
100                rtems_unsigned16        opcode;
101                rtems_unsigned16        errorCode;
102                char                    errorMessage[TFTP_BUFSIZE];
103        } tftpERROR;
104};
105
106/*
107 * State of each TFTP stream
108 */
109struct tftpStream {
110        /*
111         * Buffer for storing most recently-received packet
112         */
113        union tftpPacket        pkbuf;
114
115        /*
116         * Last block number received
117         */
118        rtems_unsigned16        blocknum;
119
120        /*
121         * Data transfer socket
122         */
123        int                     socket;
124        struct sockaddr_in      myAddress;
125        struct sockaddr_in      farAddress;
126
127        /*
128         * Indices into buffer
129         */
130        int     nleft;
131        int     nused;
132
133        /*
134         * Flags
135         */
136        int     firstReply;
137        int     eof;
138};
139
140/*
141 * Number of streams open at the same time
142 */
143
144static rtems_id tftp_mutex;
145static int nStreams;
146static struct tftpStream ** volatile tftpStreams;
147
148typedef const char *tftp_node;
149extern rtems_filesystem_operations_table  rtems_tftp_ops;
150extern rtems_filesystem_file_handlers_r   rtems_tftp_handlers;
151
152/*
153 *  Direct copy from the IMFS.  Look at this.
154 */
155
156rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
157   5,   /* link_max */
158   6,   /* max_canon */
159   7,   /* max_input */
160   255, /* name_max */
161   255, /* path_max */
162   2,   /* pipe_buf */
163   1,   /* posix_async_io */
164   2,   /* posix_chown_restrictions */
165   3,   /* posix_no_trunc */
166   4,   /* posix_prio_io */
167   5,   /* posix_sync_io */
168   6    /* posix_vdisable */
169};
170
171int rtems_tftp_mount_me(
172  rtems_filesystem_mount_table_entry_t *temp_mt_entry
173)
174{
175  rtems_status_code  sc;
176
177  temp_mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers;
178  temp_mt_entry->mt_fs_root.ops      = &rtems_tftp_ops;
179
180  /*
181   *   We have no tftp filesystem specific data to maintain.  This
182   *   filesystem may only be mounted ONCE.
183   *
184   *   And we maintain no real filesystem nodes, so there is no real root.
185   */
186
187  temp_mt_entry->fs_info                = NULL;
188  temp_mt_entry->mt_fs_root.node_access = NULL;
189
190  /*
191   *  These need to be looked at for full POSIX semantics.
192   */
193
194  temp_mt_entry->pathconf_limits_and_options = rtems_tftp_limits_and_options;
195
196
197  /*
198   *  Now allocate a semaphore for mutual exclusion.
199   *
200   *  NOTE:  This could be in an fsinfo for this filesystem type.
201   */
202 
203  sc = rtems_semaphore_create (
204    rtems_build_name('T', 'F', 'T', 'P'),
205    1,
206    RTEMS_FIFO |
207    RTEMS_BINARY_SEMAPHORE |
208    RTEMS_NO_INHERIT_PRIORITY |
209    RTEMS_NO_PRIORITY_CEILING |
210    RTEMS_LOCAL,
211    0,
212    &tftp_mutex
213  );
214
215  if (sc != RTEMS_SUCCESSFUL)
216    set_errno_and_return_minus_one( ENOMEM );
217
218  return 0;
219}
220
221/*
222 * Initialize the TFTP driver
223 */
224
225int rtems_bsdnet_initialize_tftp_filesystem ()
226{
227 int                                   status;
228 rtems_filesystem_mount_table_entry_t *entry;
229
230 status = mkdir( TFTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
231 if ( status == -1 )
232   return status;
233
234  status = mount(
235     &entry,
236     &rtems_tftp_ops,
237     "RO",
238     NULL,
239     TFTP_PATHNAME_PREFIX
240  );
241
242  if ( status )
243    perror( "TFTP mount failed" );
244
245  return status;
246}
247
248/*
249 * Set error message
250 * This RTEMS/UNIX error mapping needs to be fixed!
251 */
252static void
253tftpSetErrno (struct tftpStream *tp)
254{
255        unsigned int tftpError;
256        static const int errorMap[] = {
257                0,
258                ENOENT,
259                EPERM,
260                ENOSPC,
261                EINVAL,
262                ENXIO,
263                EEXIST,
264                ESRCH,
265                0,
266        };
267
268        tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
269        if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
270                errno = errorMap[tftpError];
271        else
272                errno = 1000 + tftpError;
273}
274
275/*
276 * Send a message to make the other end shut up
277 */
278static void
279sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
280{
281        int len;
282
283        /*
284         * Create the error packet (Unknown transfer ID).
285         */
286        tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR);
287        tp->pkbuf.tftpERROR.errorCode = htons (5);
288        len = sizeof tp->pkbuf.tftpERROR.opcode +
289                                sizeof tp->pkbuf.tftpERROR.errorCode + 1;
290        len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
291
292        /*
293         * Send it
294         */
295        sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
296                                        (struct sockaddr *)to, sizeof *to);
297}
298
299/*
300 * Wait for a data packet
301 */
302static int
303getPacket (struct tftpStream *tp)
304{
305        int len;
306        struct timeval tv;
307
308        tv.tv_sec = 6;
309        tv.tv_usec = 0;
310        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
311        for (;;) {
312                union {
313                        struct sockaddr s;
314                        struct sockaddr_in i;
315                } from;
316                int fromlen = sizeof from;
317                len = recvfrom (tp->socket, (char *)&tp->pkbuf,
318                                                        sizeof tp->pkbuf, 0,
319                                                        &from.s, &fromlen);
320                if (len < 0)
321                        break;
322                if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
323                        if (tp->firstReply) {
324                                tp->firstReply = 0;
325                                tp->farAddress.sin_port = from.i.sin_port;
326                        }
327                        if (tp->farAddress.sin_port == from.i.sin_port)
328                                break;
329                }
330
331                /*
332                 * Packet is from someone with whom we are
333                 * not interested.  Tell them to go away.
334                 */
335                sendStifle (tp, &from.i);
336        }
337        tv.tv_sec = 0;
338        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
339        return len;
340}
341
342/*
343 * Send an acknowledgement
344 */
345static int
346sendAck (struct tftpStream *tp)
347{
348        /*
349         * Create the acknowledgement
350         */
351        tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
352        tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
353
354        /*
355         * Send it
356         */
357        if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
358                                        (struct sockaddr *)&tp->farAddress,
359                                        sizeof tp->farAddress) < 0)
360                return errno;
361        return 0;
362}
363
364/*
365 * Release a stream and clear the pointer to it
366 */
367static void
368releaseStream (int s)
369{
370        rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
371        free (tftpStreams[s]);
372        tftpStreams[s] = NULL;
373        rtems_semaphore_release (tftp_mutex);
374}
375
376int rtems_tftp_evaluate_for_make(
377   const char                         *path,       /* IN     */
378   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
379   const char                        **name        /* OUT    */
380)
381
382  set_errno_and_return_minus_one( EIO );   
383}
384
385/*
386 * XXX - Fix return values.
387 */
388
389int rtems_tftp_eval_path( 
390  const char                        *pathname,     /* IN     */
391  int                                flags,        /* IN     */
392  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
393)
394{
395
396  /*
397   * Read-only for now
398   */
399   
400  if ( (flags & O_WRONLY) == O_WRONLY )
401    set_errno_and_return_minus_one( ENOENT );
402
403  /*
404   * The File system is mounted at TFTP_PATHNAME_PREFIX
405   * the caller of this routine has striped off this part of the
406   * name. Save the remainder of the name for use by the open routine.
407   */
408
409  pathloc->node_access = (void * ) pathname;
410  pathloc->handlers    = &rtems_tftp_handlers;
411
412  return 0;
413}
414
415
416int rtems_tftp_open(
417  rtems_libio_t *iop,
418  const char    *new_name,
419  unsigned32     flag,
420  unsigned32     mode
421)
422{
423  struct tftpStream  *tp;
424  int                 retryCount;
425  rtems_unsigned32    farAddress;
426  int                 s;
427  int                 len;
428  char               *cp1;
429  char               *cp2;
430  char               *remoteFilename;
431  rtems_interval      now;
432  rtems_status_code   sc;
433
434  /*
435   * This came from the evaluate path.
436   */
437
438  cp2 = iop->file_info; 
439  if (*cp2 == '/') {
440    farAddress = rtems_bsdnet_bootp_server_address.s_addr;
441  }
442  else {
443    char *hostname;
444
445    cp1 = cp2;
446    while (*cp2 != '/') {
447      if (*cp2 == '\0')
448        set_errno_and_return_minus_one( ENOENT );
449       cp2++;
450    }
451
452    len = cp2 - cp1;
453    hostname = malloc (len + 1);
454    if (hostname == NULL)
455      set_errno_and_return_minus_one( ENOMEM );
456
457    strncpy (hostname, cp1, len);
458    hostname[len] = '\0';
459    farAddress = inet_addr (hostname);
460    free (hostname);
461  }
462
463  if ((farAddress == 0) || (farAddress == ~0))
464    set_errno_and_return_minus_one( ENOENT );
465
466  if (*++cp2 == '\0')
467    set_errno_and_return_minus_one( ENOENT );
468
469  remoteFilename = cp2;
470  if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
471    set_errno_and_return_minus_one( ENOENT );
472
473  /*
474   * Find a free stream
475   */
476
477  sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
478  if (sc != RTEMS_SUCCESSFUL)
479    set_errno_and_return_minus_one( EBUSY );
480
481  for (s = 0 ; s < nStreams ; s++) {
482    if (tftpStreams[s] == NULL)
483    break;
484  }
485
486  if (s == nStreams) {
487    /*
488     * Reallocate stream pointers
489     * Guard against the case where realloc() returns NULL.
490     */
491    struct tftpStream **np;
492
493    np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
494    if (np == NULL) {
495      rtems_semaphore_release (tftp_mutex);
496      set_errno_and_return_minus_one( ENOMEM );
497    }
498    tftpStreams = np;
499  }
500
501  tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
502  rtems_semaphore_release (tftp_mutex);
503  if (tp == NULL)
504    set_errno_and_return_minus_one( ENOMEM );
505  iop->data0 = s;
506  iop->data1 = tp;
507
508  /*
509   * Create the socket
510   */
511
512  if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
513    releaseStream (s);
514    set_errno_and_return_minus_one( ENOMEM );
515  }
516
517  /*
518   * Bind the socket to a local address
519   */
520
521  retryCount = 0;
522  rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
523  for (;;) {
524    int try = (now + retryCount) % 10;
525
526    tp->myAddress.sin_family = AF_INET;
527    /*
528     *  XXX Eric .. how do we get the minor information???
529     * tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + minor);
530     */
531    tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try);
532    tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
533    if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
534      break;
535    if (++retryCount == 10) {
536      close (tp->socket);
537      /*
538       *  XXX Eric .. how do we get the minor information to release this???
539        releaseStream (minor);
540       */
541      set_errno_and_return_minus_one( EBUSY );
542    }
543  }
544
545  /*
546   * Set the UDP destination to the TFTP server
547   * port on the remote machine.
548   */
549  tp->farAddress.sin_family = AF_INET;
550  tp->farAddress.sin_addr.s_addr = farAddress;
551  tp->farAddress.sin_port = htons (69);
552
553  /*
554   * Start the transfer
555   */
556  tp->firstReply = 1;
557  for (;;) {
558    /*
559     * Create the request
560     */
561    tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
562      /*
563       *  XXX Eric .. is this cast taking the const off right?
564       */
565
566    cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
567    cp2 = (char *) remoteFilename;
568    while ((*cp1++ = *cp2++) != '\0')
569      continue;
570    cp2 = "octet";
571    while ((*cp1++ = *cp2++) != '\0')
572      continue;
573    len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
574
575    /*
576     * Send the request
577     */
578    if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
579          (struct sockaddr *)&tp->farAddress,
580          sizeof tp->farAddress) < 0) {
581      close (tp->socket);
582      /*
583       *  XXX Eric .. how do we get the minor information to release this???
584        releaseStream (minor);
585       */
586      set_errno_and_return_minus_one( EIO );
587    }
588
589    /*
590     * Get reply
591     */
592    len = getPacket (tp);
593    if (len >= (int) sizeof tp->pkbuf.tftpACK) {
594      int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
595      if ((opcode == TFTP_OPCODE_DATA)
596       && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
597        tp->nused = 0;
598        tp->blocknum = 1;
599        tp->nleft = len - 2 * sizeof (rtems_unsigned16);
600        tp->eof = (tp->nleft < TFTP_BUFSIZE);
601        if (sendAck (tp) != 0) {
602          close (tp->socket);
603      /*
604       *  XXX Eric .. how do we get the minor information to release this???
605          releaseStream (minor);
606       */
607
608          set_errno_and_return_minus_one( EIO );
609        }
610        break;
611      }
612      if (opcode == TFTP_OPCODE_ERROR) {
613        tftpSetErrno (tp);
614        close (tp->socket);
615      /*
616       *  XXX Eric .. how do we get the minor information to release this???
617       *  releaseStream (minor);
618       */
619        set_errno_and_return_minus_one( EIO );
620      }
621    }
622
623    /*
624     * Keep trying
625     */
626    if (++retryCount >= OPEN_RETRY_LIMIT) {
627      close (tp->socket);
628      /*
629       *  XXX Eric .. how do we get the minor information to release this???
630        releaseStream (minor);
631       */
632      set_errno_and_return_minus_one( EIO );
633    }
634  }
635
636  return 0;
637}
638
639/*
640 * Read from a TFTP stream
641 */
642
643int rtems_tftp_read(
644  rtems_libio_t *iop,
645  void          *buffer,
646  unsigned32     count
647)
648{
649  char              *bp;
650  struct tftpStream *tp;
651  int                retryCount;
652  int                nwant;
653
654  tp = iop->data1;
655
656  /*
657   * Read till user request is satisfied or EOF is reached
658   */
659
660  bp = buffer;
661  nwant = count;
662  while (nwant) {
663    if (tp->nleft) {
664      int count;
665      if (nwant < tp->nleft)
666        count = nwant;
667      else
668        count = tp->nleft;
669      memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
670      tp->nused += count;
671      tp->nleft -= count;
672      bp += count;
673      nwant -= count;
674      if (nwant == 0)
675        break;
676    }
677    if (tp->eof)
678      break;
679
680    /*
681     * Wait for the next packet
682     */
683    retryCount = 0;
684    for (;;) {
685      int len = getPacket (tp);
686      if (len >= (int)sizeof tp->pkbuf.tftpACK) {
687        int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
688        rtems_unsigned16 nextBlock = tp->blocknum + 1;
689        if ((opcode == TFTP_OPCODE_DATA)
690         && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
691          tp->nused = 0;
692          tp->nleft = len - 2 * sizeof (rtems_unsigned16);
693          tp->eof = (tp->nleft < TFTP_BUFSIZE);
694          tp->blocknum++;
695          if (sendAck (tp) != 0)
696            set_errno_and_return_minus_one( EIO );
697          break;
698        }
699        if (opcode == TFTP_OPCODE_ERROR) {
700          tftpSetErrno (tp);
701          return RTEMS_INTERNAL_ERROR;
702        }
703      }
704
705      /*
706       * Keep trying?
707       */
708      if (++retryCount == IO_RETRY_LIMIT)
709        set_errno_and_return_minus_one( EIO );
710      if (sendAck (tp) != 0)
711        set_errno_and_return_minus_one( EIO );
712    }
713  }
714
715 /*
716  * XXX - Eric is this right?
717  *
718  */
719  return count - nwant;
720}
721
722/*
723 * Close a TFTP stream
724 */
725int rtems_tftp_close(
726  rtems_libio_t *iop
727)
728{
729  struct tftpStream *tp = iop->data1;;
730
731  if (!tp->eof && !tp->firstReply) {
732    /*
733     * Tell the other end to stop
734     */
735    rtems_interval ticksPerSecond;
736    sendStifle (tp, &tp->farAddress);
737    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
738    rtems_task_wake_after (1 + ticksPerSecond / 10);
739  }
740  close (tp->socket);
741  releaseStream (iop->data0);
742  return RTEMS_SUCCESSFUL;
743}
744
745int rtems_tftp_write(
746  rtems_libio_t *iop,
747  const void    *buffer,
748  unsigned32     count
749)
750{
751  return RTEMS_NOT_CONFIGURED;
752}
753
754rtems_device_driver rtems_tftp_control(
755  rtems_device_major_number major,
756  rtems_device_minor_number minor,
757  void *pargp
758)
759{
760  return RTEMS_NOT_CONFIGURED;
761}
762
763rtems_filesystem_node_types_t rtems_tftp_node_type(
764   rtems_filesystem_location_info_t    *pathloc         /* IN */
765)
766{
767  return RTEMS_FILESYSTEM_MEMORY_FILE;
768}
769
770
771rtems_filesystem_operations_table  rtems_tftp_ops = {
772  rtems_tftp_eval_path,            /* eval_path */
773  rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
774  NULL,                            /* link */
775  NULL,                            /* unlink */
776  rtems_tftp_node_type,            /* node_type */
777  NULL,                            /* mknod */
778  NULL,                            /* rmnod */
779  NULL,                            /* chown */
780  NULL,                            /* freenodinfo */
781  NULL,                            /* mount */
782  rtems_tftp_mount_me,             /* initialize */
783  NULL,                            /* unmount */
784  NULL,                            /* fsunmount */
785  NULL,                            /* utime, */
786  NULL,                            /* evaluate_link */
787  NULL,                            /* symlink */
788  NULL,                            /* readlin */
789};
790
791rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
792  rtems_tftp_open,
793  rtems_tftp_close,
794  rtems_tftp_read,
795  rtems_tftp_write,
796  NULL,
797  NULL,
798  NULL,
799  NULL,
800  NULL,
801  NULL,
802  NULL,
803  NULL,
804};
Note: See TracBrowser for help on using the repository browser.