source: rtems/c/src/exec/libnetworking/lib/tftpDriver.c @ 989b938

4.104.114.84.95
Last change on this file since 989b938 was 86d9ca2, checked in by Joel Sherrill <joel.sherrill@…>, on 12/07/01 at 12:59:12

2001-12-07 Eric Norum <eric.norum@…>

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