source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 809feccc

4.104.114.84.95
Last change on this file since 809feccc was 809feccc, checked in by Joel Sherrill <joel.sherrill@…>, on 01/25/01 at 22:59:32

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

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