source: rtems/c/src/libnetworking/lib/tftpDriver.c @ cf42e735

4.104.114.84.95
Last change on this file since cf42e735 was cf42e735, checked in by Joel Sherrill <joel.sherrill@…>, on 01/16/02 at 22:50:04

2001-01-16 Eric Norum <eric.norum@…>

  • lib/tftpDriver.clib/tftpDriver.c: Fix TFTP block number checking.
  • Property mode set to 100644
File size: 26.9 KB
Line 
1/*
2 * vim: set expandtab tabstop=4 shiftwidth=4 ai :
3 *
4 * Trivial File Transfer Protocol (RFC 1350)
5 *
6 * Transfer file to/from remote host
7 *
8 * W. Eric Norum
9 * Saskatchewan Accelerator Laboratory
10 * University of Saskatchewan
11 * Saskatoon, Saskatchewan, CANADA
12 * eric@skatter.usask.ca
13 *
14 *  $Id$
15 *
16 */
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <errno.h>
21#include <malloc.h>
22#include <string.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <rtems.h>
26#include <rtems/libio.h>
27#include <rtems/libio_.h>
28#include <rtems/seterr.h>
29#include <rtems/rtems_bsdnet.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <netinet/in.h>
33#include <arpa/inet.h>
34
35#ifdef RTEMS_TFTP_DRIVER_DEBUG
36int rtems_tftp_driver_debug = 1;
37#endif
38
39/*
40 * Range of UDP ports to try
41 */
42#define UDP_PORT_BASE        3180
43
44/*
45 * Pathname prefix
46 */
47#define TFTP_PATHNAME_PREFIX "/TFTP/"
48
49/*
50 * Root node_access value
51 * By using the address of a local static variable
52 * we ensure a unique value for this identifier.
53 */
54#define ROOT_NODE_ACCESS    (&tftp_mutex)
55
56/*
57 * Default limits
58 */
59#define PACKET_FIRST_TIMEOUT_MILLISECONDS  400
60#define PACKET_TIMEOUT_MILLISECONDS        6000
61#define OPEN_RETRY_LIMIT                   10
62#define IO_RETRY_LIMIT                     10
63
64/*
65 * TFTP opcodes
66 */
67#define TFTP_OPCODE_RRQ     1
68#define TFTP_OPCODE_WRQ     2
69#define TFTP_OPCODE_DATA    3
70#define TFTP_OPCODE_ACK     4
71#define TFTP_OPCODE_ERROR   5
72
73/*
74 * Largest data transfer
75 */
76#define TFTP_BUFSIZE        512
77
78/*
79 * Packets transferred between machines
80 */
81union tftpPacket {
82    /*
83     * RRQ/WRQ packet
84     */
85    struct tftpRWRQ {
86        rtems_unsigned16    opcode;
87        char                filename_mode[TFTP_BUFSIZE];
88    } tftpRWRQ;
89
90    /*
91     * DATA packet
92     */
93    struct tftpDATA {
94        rtems_unsigned16    opcode;
95        rtems_unsigned16    blocknum;
96        rtems_unsigned8     data[TFTP_BUFSIZE];
97    } tftpDATA;
98
99    /*
100     * ACK packet
101     */
102    struct tftpACK {
103        rtems_unsigned16    opcode;
104        rtems_unsigned16    blocknum;
105    } tftpACK;
106
107    /*
108     * ERROR packet
109     */
110    struct tftpERROR {
111        rtems_unsigned16    opcode;
112        rtems_unsigned16    errorCode;
113        char                errorMessage[TFTP_BUFSIZE];
114    } tftpERROR;
115};
116
117/*
118 * State of each TFTP stream
119 */
120struct tftpStream {
121    /*
122     * Buffer for storing most recently-received packet
123     */
124    union tftpPacket    pkbuf;
125
126    /*
127     * Last block number transferred
128     */
129    rtems_unsigned16    blocknum;
130
131    /*
132     * Data transfer socket
133     */
134    int                 socket;
135    struct sockaddr_in  myAddress;
136    struct sockaddr_in  farAddress;
137
138    /*
139     * Indices into buffer
140     */
141    int     nleft;
142    int     nused;
143
144    /*
145     * Flags
146     */
147    int     firstReply;
148    int     eof;
149    int     writing;
150};
151
152/*
153 * Number of streams open at the same time
154 */
155static rtems_id tftp_mutex;
156static int nStreams;
157static struct tftpStream ** volatile tftpStreams;
158
159typedef const char *tftp_node;
160extern rtems_filesystem_operations_table  rtems_tftp_ops;
161extern rtems_filesystem_file_handlers_r   rtems_tftp_handlers;
162
163/*
164 *  Direct copy from the IMFS.  Look at this.
165 */
166
167rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
168   5,   /* link_max */
169   6,   /* max_canon */
170   7,   /* max_input */
171   255, /* name_max */
172   255, /* path_max */
173   2,   /* pipe_buf */
174   1,   /* posix_async_io */
175   2,   /* posix_chown_restrictions */
176   3,   /* posix_no_trunc */
177   4,   /* posix_prio_io */
178   5,   /* posix_sync_io */
179   6    /* posix_vdisable */
180};
181
182static int rtems_tftp_mount_me(
183  rtems_filesystem_mount_table_entry_t *temp_mt_entry
184)
185{
186  rtems_status_code  sc;
187
188  temp_mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers;
189  temp_mt_entry->mt_fs_root.ops      = &rtems_tftp_ops;
190
191  /*
192   *   We have no tftp filesystem specific data to maintain.  This
193   *   filesystem may only be mounted ONCE.
194   *
195   *   And we maintain no real filesystem nodes, so there is no real root.
196   */
197
198  temp_mt_entry->fs_info                = NULL;
199  temp_mt_entry->mt_fs_root.node_access = ROOT_NODE_ACCESS;
200
201  /*
202   *  These need to be looked at for full POSIX semantics.
203   */
204
205  temp_mt_entry->pathconf_limits_and_options = rtems_tftp_limits_and_options;
206
207
208  /*
209   *  Now allocate a semaphore for mutual exclusion.
210   *
211   *  NOTE:  This could be in an fsinfo for this filesystem type.
212   */
213 
214  sc = rtems_semaphore_create (
215    rtems_build_name('T', 'F', 'T', 'P'),
216    1,
217    RTEMS_FIFO |
218    RTEMS_BINARY_SEMAPHORE |
219    RTEMS_NO_INHERIT_PRIORITY |
220    RTEMS_NO_PRIORITY_CEILING |
221    RTEMS_LOCAL,
222    0,
223    &tftp_mutex
224  );
225
226  if (sc != RTEMS_SUCCESSFUL)
227    rtems_set_errno_and_return_minus_one( ENOMEM );
228
229  return 0;
230}
231
232/*
233 * Initialize the TFTP driver
234 */
235
236int rtems_bsdnet_initialize_tftp_filesystem ()
237{
238    int                                   status;
239    rtems_filesystem_mount_table_entry_t *entry;
240
241    status = mkdir( TFTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
242    if ( status == -1 )
243        return status;
244
245    status = mount(
246            &entry,
247            &rtems_tftp_ops,
248            RTEMS_FILESYSTEM_READ_WRITE,
249            NULL,
250            TFTP_PATHNAME_PREFIX
251    );
252
253    if ( status )
254        perror( "TFTP mount failed" );
255
256    return status;
257}
258
259/*
260 * Map error message
261 */
262static int
263tftpErrno (struct tftpStream *tp)
264{
265    unsigned int tftpError;
266    static const int errorMap[] = {
267        EINVAL,
268        ENOENT,
269        EPERM,
270        ENOSPC,
271        EINVAL,
272        ENXIO,
273        EEXIST,
274        ESRCH,
275    };
276
277    tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
278    if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
279        return errorMap[tftpError];
280    else
281        return 1000 + tftpError;
282}
283
284/*
285 * Send a message to make the other end shut up
286 */
287static void
288sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
289{
290    int len;
291    struct {
292        rtems_unsigned16    opcode;
293        rtems_unsigned16    errorCode;
294        char                errorMessage[12];
295    } msg;
296
297    /*
298     * Create the error packet (Unknown transfer ID).
299     */
300    msg.opcode = htons (TFTP_OPCODE_ERROR);
301    msg.errorCode = htons (5);
302    len = sizeof msg.opcode + sizeof msg.errorCode + 1;
303    len += sprintf (msg.errorMessage, "GO AWAY");
304
305    /*
306     * Send it
307     */
308    sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to);
309}
310
311/*
312 * Wait for a data packet
313 */
314static int
315getPacket (struct tftpStream *tp, int retryCount)
316{
317    int len;
318    struct timeval tv;
319
320    if (retryCount == 0) {
321        tv.tv_sec = PACKET_FIRST_TIMEOUT_MILLISECONDS / 1000;
322        tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000) * 1000;
323    }
324    else {
325        tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000;
326        tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000) * 1000;
327    }
328    setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
329    for (;;) {
330        union {
331            struct sockaddr s;
332            struct sockaddr_in i;
333        } from;
334        int fromlen = sizeof from;
335        len = recvfrom (tp->socket, (char *)&tp->pkbuf,
336                                                    sizeof tp->pkbuf, 0,
337                                                    &from.s, &fromlen);
338        if (len < 0)
339            break;
340        if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
341            if (tp->firstReply) {
342                tp->firstReply = 0;
343                tp->farAddress.sin_port = from.i.sin_port;
344            }
345            if (tp->farAddress.sin_port == from.i.sin_port)
346                break;
347        }
348
349        /*
350         * Packet is from someone with whom we are
351         * not interested.  Tell them to go away.
352         */
353        sendStifle (tp, &from.i);
354    }
355    tv.tv_sec = 0;
356    tv.tv_usec = 0;
357    setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
358#ifdef RTEMS_TFTP_DRIVER_DEBUG
359    if (rtems_tftp_driver_debug) {
360        if (len >= (int) sizeof tp->pkbuf.tftpACK) {
361            int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
362            switch (opcode) {
363            default:
364                printf ("TFTP: OPCODE %d\n", opcode);
365                break;
366
367            case TFTP_OPCODE_DATA:
368                printf ("TFTP: RECV %d\n", ntohs (tp->pkbuf.tftpDATA.blocknum));
369                break;
370
371            case TFTP_OPCODE_ACK:
372                printf ("TFTP: GOT ACK %d\n", ntohs (tp->pkbuf.tftpACK.blocknum));
373                break;
374            }
375        }
376        else {
377            printf ("TFTP: %d-byte packet\n", len);
378        }
379    }
380#endif
381    return len;
382}
383
384/*
385 * Send an acknowledgement
386 */
387static int
388sendAck (struct tftpStream *tp)
389{
390#ifdef RTEMS_TFTP_DRIVER_DEBUG
391    if (rtems_tftp_driver_debug)
392        printf ("TFTP: ACK %d\n", tp->blocknum);
393#endif
394
395    /*
396     * Create the acknowledgement
397     */
398    tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
399    tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
400
401    /*
402     * Send it
403     */
404    if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
405                                    (struct sockaddr *)&tp->farAddress,
406                                    sizeof tp->farAddress) < 0)
407        return errno;
408    return 0;
409}
410
411/*
412 * Release a stream and clear the pointer to it
413 */
414static void
415releaseStream (int s)
416{
417    rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
418    free (tftpStreams[s]);
419    tftpStreams[s] = NULL;
420    rtems_semaphore_release (tftp_mutex);
421}
422
423static int rtems_tftp_evaluate_for_make(
424   const char                         *path,       /* IN     */
425   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
426   const char                        **name        /* OUT    */
427)
428
429  pathloc->node_access = NULL;
430  rtems_set_errno_and_return_minus_one( EIO );   
431}
432
433/*
434 * Convert a path to canonical form
435 */
436static void
437fixPath (char *path)
438{
439    char *inp, *outp, *base;
440
441    outp = inp = path;
442    base = NULL;
443    for (;;) {
444        if (inp[0] == '.') {
445            if (inp[1] == '\0')
446                break;
447            if (inp[1] == '/') {
448                inp += 2;
449                continue;
450            }
451            if (inp[1] == '.') {
452                if (inp[2] == '\0') {
453                    if ((base != NULL) && (outp > base)) {
454                        outp--;
455                        while ((outp > base) && (outp[-1] != '/'))
456                            outp--;
457                    }
458                    break;
459                }
460                if (inp[2] == '/') {
461                    inp += 3;
462                    if (base == NULL)
463                        continue;
464                    if (outp > base) {
465                        outp--;
466                        while ((outp > base) && (outp[-1] != '/'))
467                            outp--;
468                    }
469                    continue;
470                }
471            }
472        }
473        if (base == NULL)
474            base = inp;
475        while (inp[0] != '/') {
476            if ((*outp++ = *inp++) == '\0')
477                return;
478        }
479        *outp++ = '/';
480        while (inp[0] == '/')
481            inp++;
482    }
483    *outp = '\0';
484    return;
485}
486
487static int rtems_tftp_eval_path( 
488  const char                        *pathname,     /* IN     */
489  int                                flags,        /* IN     */
490  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
491)
492{
493    pathloc->handlers    = &rtems_tftp_handlers;
494
495    /*
496     * Hack to provide the illusion of directories inside the TFTP file system.
497     * Paths ending in a / are assumed to be directories.
498     */
499    if (pathname[strlen(pathname)-1] == '/') {
500        int isRelative = (pathloc->node_access != ROOT_NODE_ACCESS);
501        char *cp;
502       
503        /*
504         * Reject attempts to open() directories
505         */
506        if (flags & RTEMS_LIBIO_PERMS_RDWR)
507            rtems_set_errno_and_return_minus_one( EISDIR );
508        if (isRelative) {
509            cp = malloc (strlen(pathloc->node_access)+strlen(pathname)+1);
510            if (cp == NULL)
511                rtems_set_errno_and_return_minus_one( ENOMEM );
512            strcpy (cp, pathloc->node_access);
513            strcat (cp, pathname);
514        }
515        else {
516            cp = strdup (pathname);
517            if (cp == NULL)
518                rtems_set_errno_and_return_minus_one( ENOMEM );
519        }
520        fixPath (cp);
521        pathloc->node_access = cp;
522        return 0;
523    }
524    if (pathloc->node_access != ROOT_NODE_ACCESS)
525        pathloc->node_access = 0;
526
527    /*
528     * Reject it if it's not read-only or write-only.
529     */
530    flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
531    if ((flags != RTEMS_LIBIO_PERMS_READ) && (flags != RTEMS_LIBIO_PERMS_WRITE) )
532        rtems_set_errno_and_return_minus_one( EINVAL );
533    return 0;
534}
535
536/*
537 * The routine which does most of the work for the IMFS open handler
538 */
539static int rtems_tftp_open_worker(
540    rtems_libio_t *iop,
541    char          *full_path_name,
542    unsigned32     flags,
543    unsigned32     mode
544)
545{
546    struct tftpStream    *tp;
547    int                  retryCount;
548    struct in_addr       farAddress;
549    int                  s;
550    int                  len;
551    char                 *cp1;
552    char                 *cp2;
553    char                 *remoteFilename;
554    rtems_interval       now;
555    rtems_status_code    sc;
556    char                 *hostname;
557
558    /*
559     * Extract the host name component
560     */
561    cp2 = full_path_name;
562    while (*cp2 == '/')
563        cp2++;
564    hostname = cp2;
565    while (*cp2 != '/') {
566        if (*cp2 == '\0')
567            return ENOENT;
568        cp2++;
569    }
570    *cp2++ = '\0';
571
572    /*
573     * Convert hostname to Internet address
574     */
575    if (strcmp (hostname, "BOOTP_HOST") == 0)
576        farAddress = rtems_bsdnet_bootp_server_address;
577    else
578        farAddress.s_addr = inet_addr (hostname);
579    if ((farAddress.s_addr == 0) || (farAddress.s_addr == ~0))
580        return ENOENT;
581
582    /*
583     * Extract file pathname component
584     */
585    while (*cp2 == '/')
586        cp2++;
587    if (strcmp (cp2, "BOOTP_FILE") == 0) {
588        cp2 = rtems_bsdnet_bootp_boot_file_name;
589        while (*cp2 == '/')
590            cp2++;
591    }
592    if (*cp2 == '\0')
593        return ENOENT;
594    remoteFilename = cp2;
595    if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
596        return ENOENT;       
597
598    /*
599     * Find a free stream
600     */
601    sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
602    if (sc != RTEMS_SUCCESSFUL)
603        return EBUSY;
604    for (s = 0 ; s < nStreams ; s++) {
605        if (tftpStreams[s] == NULL)
606        break;
607    }
608    if (s == nStreams) {
609        /*
610         * Reallocate stream pointers
611         * Guard against the case where realloc() returns NULL.
612         */
613        struct tftpStream **np;
614
615        np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
616        if (np == NULL) {
617            rtems_semaphore_release (tftp_mutex);
618            return ENOMEM;
619        }
620        tftpStreams = np;
621    }
622    tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
623    rtems_semaphore_release (tftp_mutex);
624    if (tp == NULL)
625        return ENOMEM;
626    iop->data0 = s;
627    iop->data1 = tp;
628
629    /*
630     * Create the socket
631     */
632    if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
633        releaseStream (s);
634        return ENOMEM;
635    }
636
637    /*
638     * Bind the socket to a local address
639     */
640    retryCount = 0;
641    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
642    for (;;) {
643        int try = (now + retryCount) % 10;
644
645        tp->myAddress.sin_family = AF_INET;
646        tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + s);
647        tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
648        if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
649            break;
650        if (++retryCount == 10) {
651            close (tp->socket);
652            releaseStream (s);
653            return EBUSY;
654        }
655    }
656
657    /*
658     * Set the UDP destination to the TFTP server
659     * port on the remote machine.
660     */
661    tp->farAddress.sin_family = AF_INET;
662    tp->farAddress.sin_addr = farAddress;
663    tp->farAddress.sin_port = htons (69);
664
665    /*
666     * Start the transfer
667     */
668    tp->firstReply = 1;
669    retryCount = 0;
670    for (;;) {
671        /*
672         * Create the request
673         */
674        if ((flags & O_ACCMODE) == O_RDONLY) {
675            tp->writing = 0;
676            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
677        }
678        else {
679            tp->writing = 1;
680            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ);
681        }
682        cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
683        cp2 = (char *) remoteFilename;
684        while ((*cp1++ = *cp2++) != '\0')
685            continue;
686        cp2 = "octet";
687        while ((*cp1++ = *cp2++) != '\0')
688            continue;
689        len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
690
691        /*
692         * Send the request
693         */
694        if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
695                    (struct sockaddr *)&tp->farAddress,
696                    sizeof tp->farAddress) < 0) {
697            close (tp->socket);
698            releaseStream (s);
699            return EIO;
700        }
701
702        /*
703         * Get reply
704         */
705        len = getPacket (tp, retryCount);
706        if (len >= (int) sizeof tp->pkbuf.tftpACK) {
707            int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
708            if (!tp->writing
709             && (opcode == TFTP_OPCODE_DATA)
710             && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
711                tp->nused = 0;
712                tp->blocknum = 1;
713                tp->nleft = len - 2 * sizeof (rtems_unsigned16);
714                tp->eof = (tp->nleft < TFTP_BUFSIZE);
715                if (sendAck (tp) != 0) {
716                    close (tp->socket);
717                    releaseStream (s);
718                    return EIO;
719                }
720                break;
721            }
722            if (tp->writing
723             && (opcode == TFTP_OPCODE_ACK)
724             && (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) {
725                tp->nused = 0;
726                tp->blocknum = 1;
727                break;
728            }
729            if (opcode == TFTP_OPCODE_ERROR) {
730                int e = tftpErrno (tp);
731                close (tp->socket);
732                releaseStream (s);
733                return e;
734            }
735        }
736
737        /*
738         * Keep trying
739         */
740        if (++retryCount >= OPEN_RETRY_LIMIT) {
741            close (tp->socket);
742            releaseStream (s);
743            return EIO;
744        }
745    }
746    return 0;
747}
748
749/*
750 * The IMFS open handler
751 */
752static int rtems_tftp_open(
753    rtems_libio_t *iop,
754    const char    *new_name,
755    unsigned32     flags,
756    unsigned32     mode
757)
758{
759    char *full_path_name;
760    char *s1;
761    int err;
762
763    /*
764     * Tack the `current directory' on to relative paths.
765     * We know that the current directory ends in a / character.
766     */
767    if (*new_name == '/') {
768        /*
769         * Skip the TFTP filesystem prefix.
770         */
771        int len = strlen (TFTP_PATHNAME_PREFIX);
772        if (strncmp (new_name, TFTP_PATHNAME_PREFIX, len))
773            return ENOENT;
774        new_name += len;
775        s1 = "";
776    }
777    else {
778        s1 = rtems_filesystem_current.node_access;
779    }
780    full_path_name = malloc (strlen (s1) + strlen (new_name) + 1);
781    if (full_path_name == NULL)
782        return ENOMEM;
783    strcpy (full_path_name, s1);
784    strcat (full_path_name, new_name);
785    fixPath (full_path_name);
786    err = rtems_tftp_open_worker (iop, full_path_name, flags, mode);
787    free (full_path_name);
788    return err;
789}
790
791/*
792 * Read from a TFTP stream
793 */
794static int rtems_tftp_read(
795    rtems_libio_t *iop,
796    void          *buffer,
797    unsigned32    count
798)
799{
800    char              *bp;
801    struct tftpStream *tp = iop->data1;
802    int               retryCount;
803    int               nwant;
804
805
806    /*
807     * Read till user request is satisfied or EOF is reached
808     */
809    bp = buffer;
810    nwant = count;
811    while (nwant) {
812        if (tp->nleft) {
813            int ncopy;
814            if (nwant < tp->nleft)
815                ncopy = nwant;
816            else
817                ncopy = tp->nleft;
818            memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy);
819            tp->nused += ncopy;
820            tp->nleft -= ncopy;
821            bp += ncopy;
822            nwant -= ncopy;
823            if (nwant == 0)
824                break;
825        }
826        if (tp->eof)
827            break;
828
829        /*
830         * Wait for the next packet
831         */
832        retryCount = 0;
833        for (;;) {
834            int len = getPacket (tp, retryCount);
835            if (len >= (int)sizeof tp->pkbuf.tftpACK) {
836                int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
837                rtems_unsigned16 nextBlock = tp->blocknum + 1;
838                if ((opcode == TFTP_OPCODE_DATA)
839                 && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
840                    tp->nused = 0;
841                    tp->nleft = len - 2 * sizeof (rtems_unsigned16);
842                    tp->eof = (tp->nleft < TFTP_BUFSIZE);
843                    tp->blocknum++;
844                    if (sendAck (tp) != 0)
845                        rtems_set_errno_and_return_minus_one( EIO );
846                    break;
847                }
848                if (opcode == TFTP_OPCODE_ERROR)
849                        rtems_set_errno_and_return_minus_one( tftpErrno (tp) );
850            }
851
852            /*
853             * Keep trying?
854             */
855            if (++retryCount == IO_RETRY_LIMIT)
856                rtems_set_errno_and_return_minus_one( EIO );
857            if (sendAck (tp) != 0)
858                rtems_set_errno_and_return_minus_one( EIO );
859        }
860    }
861    return count - nwant;
862}
863
864/*
865 * Flush a write buffer and wait for acknowledgement
866 */
867static int rtems_tftp_flush ( struct tftpStream *tp )
868{
869    int wlen, rlen;
870    int retryCount = 0;
871
872    wlen = tp->nused + 2 * sizeof (rtems_unsigned16);
873    for (;;) {
874        tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA);
875        tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum);
876#ifdef RTEMS_TFTP_DRIVER_DEBUG
877        if (rtems_tftp_driver_debug)
878            printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused);
879#endif
880        if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0,
881                                        (struct sockaddr *)&tp->farAddress,
882                                        sizeof tp->farAddress) < 0)
883            return EIO;
884        rlen = getPacket (tp, retryCount);
885        /*
886         * Our last packet won't necessarily be acknowledged!
887         */
888        if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data))
889                return 0;
890        if (rlen >= (int)sizeof tp->pkbuf.tftpACK) {
891            int opcode = ntohs (tp->pkbuf.tftpACK.opcode);
892            if ((opcode == TFTP_OPCODE_ACK)
893             && (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) {
894                tp->nused = 0;
895                tp->blocknum++;
896                return 0;
897            }
898            if (opcode == TFTP_OPCODE_ERROR)
899                return tftpErrno (tp);
900        }
901
902        /*
903         * Keep trying?
904         */
905        if (++retryCount == IO_RETRY_LIMIT)
906            return EIO;
907    }
908}
909
910/*
911 * Close a TFTP stream
912 */
913static int rtems_tftp_close(
914    rtems_libio_t *iop
915)
916{
917    struct tftpStream *tp = iop->data1;;
918
919    if (tp->writing)
920        rtems_tftp_flush (tp);
921    if (!tp->eof && !tp->firstReply) {
922        /*
923         * Tell the other end to stop
924         */
925        rtems_interval ticksPerSecond;
926        sendStifle (tp, &tp->farAddress);
927        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
928        rtems_task_wake_after (1 + ticksPerSecond / 10);
929    }
930    close (tp->socket);
931    releaseStream (iop->data0);
932    return RTEMS_SUCCESSFUL;
933}
934
935static int rtems_tftp_write(
936    rtems_libio_t   *iop,
937    const void      *buffer,
938    unsigned32      count
939)
940{
941    const char        *bp;
942    struct tftpStream *tp = iop->data1;
943    int               nleft, nfree, ncopy;
944
945    /*
946     * Bail out if an error has occurred
947     */
948    if (!tp->writing)
949        return EIO;
950
951
952    /*
953     * Write till user request is satisfied
954     * Notice that the buffer is flushed as soon as it is filled rather
955     * than waiting for the next write or a close.  This ensures that
956     * the flush in close writes a less than full buffer so the far
957     * end can detect the end-of-file condition.
958     */
959    bp = buffer;
960    nleft = count;
961    while (nleft) {
962        nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
963        if (nleft < nfree)
964            ncopy = nleft;
965        else
966            ncopy = nfree;
967        memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
968        tp->nused += ncopy;
969        nleft -= ncopy;
970        bp += ncopy;
971        if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
972            int e = rtems_tftp_flush (tp);
973            if (e) {
974                tp->writing = 0;
975                rtems_set_errno_and_return_minus_one (e);
976            }
977        }
978    }
979    return count;
980}
981
982/*
983 * Dummy version to let fopen(xxxx,"w") work properly.
984 */
985static int rtems_tftp_ftruncate(
986    rtems_libio_t   *iop,
987    off_t           count
988)
989{
990    return 0;
991}
992
993static rtems_filesystem_node_types_t rtems_tftp_node_type(
994     rtems_filesystem_location_info_t        *pathloc                 /* IN */
995)
996{
997    if ((pathloc->node_access == NULL)
998     || (pathloc->node_access == ROOT_NODE_ACCESS))
999        return RTEMS_FILESYSTEM_MEMORY_FILE;
1000    return RTEMS_FILESYSTEM_DIRECTORY;
1001}
1002
1003static int rtems_tftp_free_node_info(
1004     rtems_filesystem_location_info_t        *pathloc                 /* IN */
1005)
1006{
1007    if (pathloc->node_access && (pathloc->node_access != ROOT_NODE_ACCESS)) {
1008        free (pathloc->node_access);
1009        pathloc->node_access = NULL;
1010    }
1011    return 0;
1012}
1013
1014
1015rtems_filesystem_operations_table  rtems_tftp_ops = {
1016    rtems_tftp_eval_path,            /* eval_path */
1017    rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
1018    NULL,                            /* link */
1019    NULL,                            /* unlink */
1020    rtems_tftp_node_type,            /* node_type */
1021    NULL,                            /* mknod */
1022    NULL,                            /* chown */
1023    rtems_tftp_free_node_info,       /* freenodinfo */
1024    NULL,                            /* mount */
1025    rtems_tftp_mount_me,             /* initialize */
1026    NULL,                            /* unmount */
1027    NULL,                            /* fsunmount */
1028    NULL,                            /* utime, */
1029    NULL,                            /* evaluate_link */
1030    NULL,                            /* symlink */
1031    NULL,                            /* readlin */
1032};
1033 
1034rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
1035    rtems_tftp_open,   /* open */     
1036    rtems_tftp_close,  /* close */   
1037    rtems_tftp_read,   /* read */     
1038    rtems_tftp_write,  /* write */   
1039    NULL,              /* ioctl */   
1040    NULL,              /* lseek */   
1041    NULL,              /* fstat */   
1042    NULL,              /* fchmod */   
1043    rtems_tftp_ftruncate, /* ftruncate */
1044    NULL,              /* fpathconf */
1045    NULL,              /* fsync */   
1046    NULL,              /* fdatasync */
1047    NULL,              /* fcntl */
1048    NULL               /* rmnod */
1049};
Note: See TracBrowser for help on using the repository browser.