source: rtems/c/src/lib/libnetworking/lib/tftpDriver.c @ 09ea257c

4.104.114.84.95
Last change on this file since 09ea257c was 09ea257c, checked in by Joel Sherrill <joel.sherrill@…>, on 07/12/99 at 15:52:35

Patch from Eric Norum <eric@…>:

I get the following warning when compiling the latest snapshot. I had
a quick look at the source -- it certainly looks to me like this is a
real bug.

../../../../src/rtems-19990709/c/src/lib/libc/mount.c:97: warning:
`options' might be used uninitialized in this function

Also, I changed the TFTP test program and TFTP driver to reflect the
changes in the way paths are passed to the TFTP driver. The TFTP driver
now needs a proper `dotted-decimal' hostname as the second component of
the path name.

  • Property mode set to 100644
File size: 16.7 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 = 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  char               *hostname;
434
435  /*
436   * This came from the evaluate path.
437   */
438
439  cp2 = iop->file_info; 
440
441  cp1 = cp2;
442  while (*cp2 != '/') {
443    if (*cp2 == '\0')
444      return ENOENT;
445    cp2++;
446  }
447
448  len = cp2 - cp1;
449  hostname = malloc (len + 1);
450  if (hostname == NULL)
451    return ENOMEM;
452
453  strncpy (hostname, cp1, len);
454  hostname[len] = '\0';
455  farAddress = inet_addr (hostname);
456  free (hostname);
457
458  if ((farAddress == 0) || (farAddress == ~0))
459    return ENOENT;
460
461  if (*++cp2 == '\0')
462    return ENOENT;
463
464  remoteFilename = cp2;
465  if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
466    return ENOENT;   
467
468  /*
469   * Find a free stream
470   */
471
472  sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
473  if (sc != RTEMS_SUCCESSFUL)
474    return EBUSY;
475
476  for (s = 0 ; s < nStreams ; s++) {
477    if (tftpStreams[s] == NULL)
478    break;
479  }
480
481  if (s == nStreams) {
482    /*
483     * Reallocate stream pointers
484     * Guard against the case where realloc() returns NULL.
485     */
486    struct tftpStream **np;
487
488    np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
489    if (np == NULL) {
490      rtems_semaphore_release (tftp_mutex);
491      return ENOMEM;
492    }
493    tftpStreams = np;
494  }
495
496  tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
497  rtems_semaphore_release (tftp_mutex);
498  if (tp == NULL)
499    return ENOMEM;
500  iop->data0 = s;
501  iop->data1 = tp;
502
503  /*
504   * Create the socket
505   */
506
507  if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
508    releaseStream (s);
509    return ENOMEM;
510  }
511
512  /*
513   * Bind the socket to a local address
514   */
515
516  retryCount = 0;
517  rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
518  for (;;) {
519    int try = (now + retryCount) % 10;
520
521    tp->myAddress.sin_family = AF_INET;
522    /*
523     *  XXX Eric .. how do we get the minor information???
524     * tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + minor);
525     */
526    tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try);
527    tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
528    if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
529      break;
530    if (++retryCount == 10) {
531      close (tp->socket);
532      /*
533       *  XXX Eric .. how do we get the minor information to release this???
534        releaseStream (minor);
535       */
536      return EBUSY;
537    }
538  }
539
540  /*
541   * Set the UDP destination to the TFTP server
542   * port on the remote machine.
543   */
544  tp->farAddress.sin_family = AF_INET;
545  tp->farAddress.sin_addr.s_addr = farAddress;
546  tp->farAddress.sin_port = htons (69);
547
548  /*
549   * Start the transfer
550   */
551  tp->firstReply = 1;
552  for (;;) {
553    /*
554     * Create the request
555     */
556    tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
557      /*
558       *  XXX Eric .. is this cast taking the const off right?
559       */
560
561    cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
562    cp2 = (char *) remoteFilename;
563    while ((*cp1++ = *cp2++) != '\0')
564      continue;
565    cp2 = "octet";
566    while ((*cp1++ = *cp2++) != '\0')
567      continue;
568    len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
569
570    /*
571     * Send the request
572     */
573    if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
574          (struct sockaddr *)&tp->farAddress,
575          sizeof tp->farAddress) < 0) {
576      close (tp->socket);
577      /*
578       *  XXX Eric .. how do we get the minor information to release this???
579        releaseStream (minor);
580       */
581      return EIO;
582    }
583
584    /*
585     * Get reply
586     */
587    len = getPacket (tp);
588    if (len >= (int) sizeof tp->pkbuf.tftpACK) {
589      int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
590      if ((opcode == TFTP_OPCODE_DATA)
591       && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
592        tp->nused = 0;
593        tp->blocknum = 1;
594        tp->nleft = len - 2 * sizeof (rtems_unsigned16);
595        tp->eof = (tp->nleft < TFTP_BUFSIZE);
596        if (sendAck (tp) != 0) {
597          close (tp->socket);
598      /*
599       *  XXX Eric .. how do we get the minor information to release this???
600          releaseStream (minor);
601       */
602
603          return EIO;
604        }
605        break;
606      }
607      if (opcode == TFTP_OPCODE_ERROR) {
608        tftpSetErrno (tp);
609        close (tp->socket);
610      /*
611       *  XXX Eric .. how do we get the minor information to release this???
612       *  releaseStream (minor);
613       */
614        return EIO;
615      }
616    }
617
618    /*
619     * Keep trying
620     */
621    if (++retryCount >= OPEN_RETRY_LIMIT) {
622      close (tp->socket);
623      /*
624       *  XXX Eric .. how do we get the minor information to release this???
625        releaseStream (minor);
626       */
627      return EIO;
628    }
629  }
630
631  return 0;
632}
633
634/*
635 * Read from a TFTP stream
636 */
637
638int rtems_tftp_read(
639  rtems_libio_t *iop,
640  void          *buffer,
641  unsigned32     count
642)
643{
644  char              *bp;
645  struct tftpStream *tp;
646  int                retryCount;
647  int                nwant;
648
649  tp = iop->data1;
650
651  /*
652   * Read till user request is satisfied or EOF is reached
653   */
654
655  bp = buffer;
656  nwant = count;
657  while (nwant) {
658    if (tp->nleft) {
659      int count;
660      if (nwant < tp->nleft)
661        count = nwant;
662      else
663        count = tp->nleft;
664      memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
665      tp->nused += count;
666      tp->nleft -= count;
667      bp += count;
668      nwant -= count;
669      if (nwant == 0)
670        break;
671    }
672    if (tp->eof)
673      break;
674
675    /*
676     * Wait for the next packet
677     */
678    retryCount = 0;
679    for (;;) {
680      int len = getPacket (tp);
681      if (len >= (int)sizeof tp->pkbuf.tftpACK) {
682        int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
683        rtems_unsigned16 nextBlock = tp->blocknum + 1;
684        if ((opcode == TFTP_OPCODE_DATA)
685         && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
686          tp->nused = 0;
687          tp->nleft = len - 2 * sizeof (rtems_unsigned16);
688          tp->eof = (tp->nleft < TFTP_BUFSIZE);
689          tp->blocknum++;
690          if (sendAck (tp) != 0)
691            set_errno_and_return_minus_one( EIO );
692          break;
693        }
694        if (opcode == TFTP_OPCODE_ERROR) {
695          tftpSetErrno (tp);
696          return RTEMS_INTERNAL_ERROR;
697        }
698      }
699
700      /*
701       * Keep trying?
702       */
703      if (++retryCount == IO_RETRY_LIMIT)
704        set_errno_and_return_minus_one( EIO );
705      if (sendAck (tp) != 0)
706        set_errno_and_return_minus_one( EIO );
707    }
708  }
709
710 /*
711  * XXX - Eric is this right?
712  *
713  */
714  return count - nwant;
715}
716
717/*
718 * Close a TFTP stream
719 */
720int rtems_tftp_close(
721  rtems_libio_t *iop
722)
723{
724  struct tftpStream *tp = iop->data1;;
725
726  if (!tp->eof && !tp->firstReply) {
727    /*
728     * Tell the other end to stop
729     */
730    rtems_interval ticksPerSecond;
731    sendStifle (tp, &tp->farAddress);
732    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
733    rtems_task_wake_after (1 + ticksPerSecond / 10);
734  }
735  close (tp->socket);
736  releaseStream (iop->data0);
737  return RTEMS_SUCCESSFUL;
738}
739
740int rtems_tftp_write(
741  rtems_libio_t *iop,
742  const void    *buffer,
743  unsigned32     count
744)
745{
746  return RTEMS_NOT_CONFIGURED;
747}
748
749rtems_device_driver rtems_tftp_control(
750  rtems_device_major_number major,
751  rtems_device_minor_number minor,
752  void *pargp
753)
754{
755  return RTEMS_NOT_CONFIGURED;
756}
757
758rtems_filesystem_node_types_t rtems_tftp_node_type(
759   rtems_filesystem_location_info_t    *pathloc         /* IN */
760)
761{
762  return RTEMS_FILESYSTEM_MEMORY_FILE;
763}
764
765
766rtems_filesystem_operations_table  rtems_tftp_ops = {
767  rtems_tftp_eval_path,            /* eval_path */
768  rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
769  NULL,                            /* link */
770  NULL,                            /* unlink */
771  rtems_tftp_node_type,            /* node_type */
772  NULL,                            /* mknod */
773  NULL,                            /* rmnod */
774  NULL,                            /* chown */
775  NULL,                            /* freenodinfo */
776  NULL,                            /* mount */
777  rtems_tftp_mount_me,             /* initialize */
778  NULL,                            /* unmount */
779  NULL,                            /* fsunmount */
780  NULL,                            /* utime, */
781  NULL,                            /* evaluate_link */
782  NULL,                            /* symlink */
783  NULL,                            /* readlin */
784};
785
786rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
787  rtems_tftp_open,
788  rtems_tftp_close,
789  rtems_tftp_read,
790  rtems_tftp_write,
791  NULL,
792  NULL,
793  NULL,
794  NULL,
795  NULL,
796  NULL,
797  NULL,
798  NULL,
799};
Note: See TracBrowser for help on using the repository browser.