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

4.104.114.84.95
Last change on this file since f20a20b was 1ef8e3d4, checked in by Joel Sherrill <joel.sherrill@…>, on 09/27/01 at 13:31:56

2001-09-27 Eric Norum <eric.norum@…>

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