source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 0da0dea

4.104.114.84.95
Last change on this file since 0da0dea was 0da0dea, checked in by Joel Sherrill <joel.sherrill@…>, on Oct 19, 2000 at 3:33:03 PM

2000-10-19 Antti P Miettinen <anmietti@…>

  • lib/tftpDriver.c: add comments to handlers struct function pointers.
  • rtems/rtems_glue.c: move pointer arithmetic to be _after_ pointer has been checked against NULL.
  • Property mode set to 100644
File size: 16.3 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     RTEMS_FILESYSTEM_READ_ONLY,
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 = PACKET_REPLY_MILLISECONDS / 1000;
309        tv.tv_usec = (PACKET_REPLY_MILLISECONDS % 1000) * 1000;
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        tv.tv_usec = 0;
339        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
340        return len;
341}
342
343/*
344 * Send an acknowledgement
345 */
346static int
347sendAck (struct tftpStream *tp)
348{
349        /*
350         * Create the acknowledgement
351         */
352        tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
353        tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
354
355        /*
356         * Send it
357         */
358        if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
359                                        (struct sockaddr *)&tp->farAddress,
360                                        sizeof tp->farAddress) < 0)
361                return errno;
362        return 0;
363}
364
365/*
366 * Release a stream and clear the pointer to it
367 */
368static void
369releaseStream (int s)
370{
371        rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
372        free (tftpStreams[s]);
373        tftpStreams[s] = NULL;
374        rtems_semaphore_release (tftp_mutex);
375}
376
377int rtems_tftp_evaluate_for_make(
378   const char                         *path,       /* IN     */
379   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
380   const char                        **name        /* OUT    */
381)
382{ 
383  set_errno_and_return_minus_one( EIO );   
384}
385
386/*
387 * XXX - Fix return values.
388 */
389
390int rtems_tftp_eval_path( 
391  const char                        *pathname,     /* IN     */
392  int                                flags,        /* IN     */
393  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
394)
395{
396
397  /*
398   * Read-only for now
399   */
400   
401  if ( (flags & O_WRONLY) == O_WRONLY )
402    set_errno_and_return_minus_one( ENOENT );
403
404  /*
405   * The File system is mounted at TFTP_PATHNAME_PREFIX
406   * the caller of this routine has striped off this part of the
407   * name. Save the remainder of the name for use by the open routine.
408   */
409
410  pathloc->node_access = (void * ) pathname;
411  pathloc->handlers    = &rtems_tftp_handlers;
412
413  return 0;
414}
415
416
417int rtems_tftp_open(
418  rtems_libio_t *iop,
419  const char    *new_name,
420  unsigned32     flag,
421  unsigned32     mode
422)
423{
424  struct tftpStream  *tp;
425  int                 retryCount;
426  rtems_unsigned32    farAddress;
427  int                 s;
428  int                 len;
429  char               *cp1;
430  char               *cp2;
431  char               *remoteFilename;
432  rtems_interval      now;
433  rtems_status_code   sc;
434  char               *hostname;
435
436  /*
437   * This came from the evaluate path.
438   */
439
440  cp2 = iop->file_info; 
441
442  cp1 = cp2;
443  while (*cp2 != '/') {
444    if (*cp2 == '\0')
445      return ENOENT;
446    cp2++;
447  }
448
449  len = cp2 - cp1;
450  hostname = malloc (len + 1);
451  if (hostname == NULL)
452    return ENOMEM; 
453
454  strncpy (hostname, cp1, len);
455  hostname[len] = '\0';
456  farAddress = inet_addr (hostname);
457  free (hostname);
458
459  if ((farAddress == 0) || (farAddress == ~0))
460    return ENOENT;
461
462  if (*++cp2 == '\0')
463    return ENOENT;
464
465  remoteFilename = cp2;
466  if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
467    return ENOENT;   
468
469  /*
470   * Find a free stream
471   */
472
473  sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
474  if (sc != RTEMS_SUCCESSFUL)
475    return EBUSY;
476
477  for (s = 0 ; s < nStreams ; s++) {
478    if (tftpStreams[s] == NULL)
479    break;
480  }
481
482  if (s == nStreams) {
483    /*
484     * Reallocate stream pointers
485     * Guard against the case where realloc() returns NULL.
486     */
487    struct tftpStream **np;
488
489    np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
490    if (np == NULL) {
491      rtems_semaphore_release (tftp_mutex);
492      return ENOMEM;
493    }
494    tftpStreams = np;
495  }
496
497  tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
498  rtems_semaphore_release (tftp_mutex);
499  if (tp == NULL)
500    return ENOMEM;
501  iop->data0 = s;
502  iop->data1 = tp;
503
504  /*
505   * Create the socket
506   */
507
508  if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
509    releaseStream (s);
510    return ENOMEM;
511  }
512
513  /*
514   * Bind the socket to a local address
515   */
516
517  retryCount = 0;
518  rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
519  for (;;) {
520    int try = (now + retryCount) % 10;
521
522    tp->myAddress.sin_family = AF_INET;
523    tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + s);
524    tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
525    if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
526      break;
527    if (++retryCount == 10) {
528      close (tp->socket);
529      releaseStream (s);
530      return EBUSY;
531    }
532  }
533
534  /*
535   * Set the UDP destination to the TFTP server
536   * port on the remote machine.
537   */
538  tp->farAddress.sin_family = AF_INET;
539  tp->farAddress.sin_addr.s_addr = farAddress;
540  tp->farAddress.sin_port = htons (69);
541
542  /*
543   * Start the transfer
544   */
545  tp->firstReply = 1;
546  for (;;) {
547    /*
548     * Create the request
549     */
550    tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
551    cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
552    cp2 = (char *) remoteFilename;
553    while ((*cp1++ = *cp2++) != '\0')
554      continue;
555    cp2 = "octet";
556    while ((*cp1++ = *cp2++) != '\0')
557      continue;
558    len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
559
560    /*
561     * Send the request
562     */
563    if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0, 
564          (struct sockaddr *)&tp->farAddress,
565          sizeof tp->farAddress) < 0) {
566      close (tp->socket);
567      releaseStream (s);
568      return EIO;
569    }
570
571    /*
572     * Get reply
573     */
574    len = getPacket (tp);
575    if (len >= (int) sizeof tp->pkbuf.tftpACK) {
576      int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
577      if ((opcode == TFTP_OPCODE_DATA)
578       && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
579        tp->nused = 0;
580        tp->blocknum = 1;
581        tp->nleft = len - 2 * sizeof (rtems_unsigned16);
582        tp->eof = (tp->nleft < TFTP_BUFSIZE);
583        if (sendAck (tp) != 0) {
584          close (tp->socket);
585          releaseStream (s);
586          return EIO;
587        }
588        break;
589      }
590      if (opcode == TFTP_OPCODE_ERROR) {
591        tftpSetErrno (tp);
592        close (tp->socket);
593        releaseStream (s);
594        return EIO;
595      }
596    }
597
598    /*
599     * Keep trying
600     */
601    if (++retryCount >= OPEN_RETRY_LIMIT) {
602      close (tp->socket);
603      releaseStream (s);
604      return EIO;
605    }
606  }
607
608  return 0;
609}
610
611/*
612 * Read from a TFTP stream
613 */
614
615int rtems_tftp_read(
616  rtems_libio_t *iop,
617  void          *buffer,
618  unsigned32     count
619)
620{
621  char              *bp;
622  struct tftpStream *tp;
623  int                retryCount;
624  int                nwant;
625
626  tp = iop->data1;
627
628  /*
629   * Read till user request is satisfied or EOF is reached
630   */
631
632  bp = buffer;
633  nwant = count;
634  while (nwant) {
635    if (tp->nleft) {
636      int count;
637      if (nwant < tp->nleft)
638        count = nwant;
639      else
640        count = tp->nleft;
641      memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
642      tp->nused += count;
643      tp->nleft -= count;
644      bp += count;
645      nwant -= count;
646      if (nwant == 0)
647        break;
648    }
649    if (tp->eof)
650      break;
651
652    /*
653     * Wait for the next packet
654     */
655    retryCount = 0;
656    for (;;) {
657      int len = getPacket (tp);
658      if (len >= (int)sizeof tp->pkbuf.tftpACK) {
659        int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
660        rtems_unsigned16 nextBlock = tp->blocknum + 1;
661        if ((opcode == TFTP_OPCODE_DATA)
662         && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
663          tp->nused = 0;
664          tp->nleft = len - 2 * sizeof (rtems_unsigned16);
665          tp->eof = (tp->nleft < TFTP_BUFSIZE);
666          tp->blocknum++;
667          if (sendAck (tp) != 0)
668            set_errno_and_return_minus_one( EIO );
669          break;
670        }
671        if (opcode == TFTP_OPCODE_ERROR) {
672          tftpSetErrno (tp);
673          return RTEMS_INTERNAL_ERROR;
674        }
675      }
676
677      /*
678       * Keep trying?
679       */
680      if (++retryCount == IO_RETRY_LIMIT)
681        set_errno_and_return_minus_one( EIO );
682      if (sendAck (tp) != 0)
683        set_errno_and_return_minus_one( EIO );
684    }
685  }
686
687 /*
688  * XXX - Eric is this right?
689  *
690  */
691  return count - nwant;
692}
693
694/*
695 * Close a TFTP stream
696 */
697int rtems_tftp_close(
698  rtems_libio_t *iop
699)
700{
701  struct tftpStream *tp = iop->data1;;
702
703  if (!tp->eof && !tp->firstReply) {
704    /*
705     * Tell the other end to stop
706     */
707    rtems_interval ticksPerSecond;
708    sendStifle (tp, &tp->farAddress);
709    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
710    rtems_task_wake_after (1 + ticksPerSecond / 10);
711  }
712  close (tp->socket);
713  releaseStream (iop->data0);
714  return RTEMS_SUCCESSFUL;
715}
716
717int rtems_tftp_write(
718  rtems_libio_t *iop,
719  const void    *buffer,
720  unsigned32     count
721)
722{
723  return RTEMS_NOT_CONFIGURED;
724}
725
726rtems_device_driver rtems_tftp_control(
727  rtems_device_major_number major,
728  rtems_device_minor_number minor,
729  void *pargp
730)
731{
732  return RTEMS_NOT_CONFIGURED;
733}
734
735rtems_filesystem_node_types_t rtems_tftp_node_type(
736   rtems_filesystem_location_info_t    *pathloc         /* IN */
737)
738{
739  return RTEMS_FILESYSTEM_MEMORY_FILE;
740}
741
742
743rtems_filesystem_operations_table  rtems_tftp_ops = {
744  rtems_tftp_eval_path,            /* eval_path */
745  rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
746  NULL,                            /* link */
747  NULL,                            /* unlink */
748  rtems_tftp_node_type,            /* node_type */
749  NULL,                            /* mknod */
750  NULL,                            /* chown */
751  NULL,                            /* freenodinfo */
752  NULL,                            /* mount */
753  rtems_tftp_mount_me,             /* initialize */
754  NULL,                            /* unmount */
755  NULL,                            /* fsunmount */
756  NULL,                            /* utime, */
757  NULL,                            /* evaluate_link */
758  NULL,                            /* symlink */
759  NULL,                            /* readlin */
760};
761
762rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
763  rtems_tftp_open,   /* open */     
764  rtems_tftp_close,  /* close */   
765  rtems_tftp_read,   /* read */     
766  rtems_tftp_write,  /* write */   
767  NULL,              /* ioctl */   
768  NULL,              /* lseek */   
769  NULL,              /* fstat */   
770  NULL,              /* fchmod */   
771  NULL,              /* ftruncate */
772  NULL,              /* fpathconf */
773  NULL,              /* fsync */   
774  NULL,              /* fdatasync */
775  NULL,              /* fcntl */
776  NULL               /* rmnod */
777};
Note: See TracBrowser for help on using the repository browser.