source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ b25b88e7

4.104.11
Last change on this file since b25b88e7 was b25b88e7, checked in by Ralf Corsepius <ralf.corsepius@…>, on Mar 28, 2010 at 5:50:29 AM

Add HAVE_CONFIG_H support to let files receive configure defines.

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