source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 00d10d55

4.104.115
Last change on this file since 00d10d55 was 00d10d55, checked in by Ralf Corsepius <ralf.corsepius@…>, on 09/29/09 at 07:18:28

2009-09-29 Ralf Corsépius <ralf.corsepius@…>

  • libnetworking/lib/tftpDriver.c: Remove vim: meta-tags.
  • Property mode set to 100644
File size: 27.0 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#include <stdio.h>
17#include <stdlib.h>
18#include <errno.h>
19#include <malloc.h>
20#include <string.h>
21#include <unistd.h>
22#include <fcntl.h>
23#include <rtems.h>
24#include <rtems/libio.h>
25#include <rtems/libio_.h>
26#include <rtems/seterr.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#ifdef RTEMS_TFTP_DRIVER_DEBUG
34int rtems_tftp_driver_debug = 1;
35#endif
36
37/*
38 * Range of UDP ports to try
39 */
40#define UDP_PORT_BASE        3180
41
42/*
43 * Pathname prefix
44 */
45#define TFTP_PATHNAME_PREFIX "/TFTP/"
46
47/*
48 * Root node_access value
49 * By using the address of a local static variable
50 * we ensure a unique value for this identifier.
51 */
52#define ROOT_NODE_ACCESS    (&tftp_mutex)
53
54/*
55 * Default limits
56 */
57#define PACKET_FIRST_TIMEOUT_MILLISECONDS  400L
58#define PACKET_TIMEOUT_MILLISECONDS        6000L
59#define OPEN_RETRY_LIMIT                   10
60#define IO_RETRY_LIMIT                     10
61
62/*
63 * TFTP opcodes
64 */
65#define TFTP_OPCODE_RRQ     1
66#define TFTP_OPCODE_WRQ     2
67#define TFTP_OPCODE_DATA    3
68#define TFTP_OPCODE_ACK     4
69#define TFTP_OPCODE_ERROR   5
70
71/*
72 * Largest data transfer
73 */
74#define TFTP_BUFSIZE        512
75
76/*
77 * Packets transferred between machines
78 */
79union tftpPacket {
80    /*
81     * RRQ/WRQ packet
82     */
83    struct tftpRWRQ {
84        uint16_t      opcode;
85        char                filename_mode[TFTP_BUFSIZE];
86    } tftpRWRQ;
87
88    /*
89     * DATA packet
90     */
91    struct tftpDATA {
92        uint16_t      opcode;
93        uint16_t      blocknum;
94        uint8_t       data[TFTP_BUFSIZE];
95    } tftpDATA;
96
97    /*
98     * ACK packet
99     */
100    struct tftpACK {
101        uint16_t      opcode;
102        uint16_t      blocknum;
103    } tftpACK;
104
105    /*
106     * ERROR packet
107     */
108    struct tftpERROR {
109        uint16_t      opcode;
110        uint16_t      errorCode;
111        char                errorMessage[TFTP_BUFSIZE];
112    } tftpERROR;
113};
114
115/*
116 * State of each TFTP stream
117 */
118struct tftpStream {
119    /*
120     * Buffer for storing most recently-received packet
121     */
122    union tftpPacket    pkbuf;
123
124    /*
125     * Last block number transferred
126     */
127    uint16_t      blocknum;
128
129    /*
130     * Data transfer socket
131     */
132    int                 socket;
133    struct sockaddr_in  myAddress;
134    struct sockaddr_in  farAddress;
135
136    /*
137     * Indices into buffer
138     */
139    int     nleft;
140    int     nused;
141
142    /*
143     * Flags
144     */
145    int     firstReply;
146    int     eof;
147    int     writing;
148};
149
150/*
151 * Number of streams open at the same time
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 = ROOT_NODE_ACCESS;
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    rtems_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 (void)
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        uint16_t      opcode;
291        uint16_t      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 / 1000L;
320        tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000L) * 1000L;
321    }
322    else {
323        tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000L;
324        tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000L) * 1000L;
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        socklen_t fromlen = sizeof from;
333        len = recvfrom (tp->socket, &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: %d-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 __attribute__((unused)),       /* IN     */
423   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
424   const char                        **name __attribute__((unused))        /* OUT    */
425)
426
427  pathloc->node_access = NULL;
428  rtems_set_errno_and_return_minus_one( EIO );   
429}
430
431/*
432 * Convert a path to canonical form
433 */
434static void
435fixPath (char *path)
436{
437    char *inp, *outp, *base;
438
439    outp = inp = path;
440    base = NULL;
441    for (;;) {
442        if (inp[0] == '.') {
443            if (inp[1] == '\0')
444                break;
445            if (inp[1] == '/') {
446                inp += 2;
447                continue;
448            }
449            if (inp[1] == '.') {
450                if (inp[2] == '\0') {
451                    if ((base != NULL) && (outp > base)) {
452                        outp--;
453                        while ((outp > base) && (outp[-1] != '/'))
454                            outp--;
455                    }
456                    break;
457                }
458                if (inp[2] == '/') {
459                    inp += 3;
460                    if (base == NULL)
461                        continue;
462                    if (outp > base) {
463                        outp--;
464                        while ((outp > base) && (outp[-1] != '/'))
465                            outp--;
466                    }
467                    continue;
468                }
469            }
470        }
471        if (base == NULL)
472            base = inp;
473        while (inp[0] != '/') {
474            if ((*outp++ = *inp++) == '\0')
475                return;
476        }
477        *outp++ = '/';
478        while (inp[0] == '/')
479            inp++;
480    }
481    *outp = '\0';
482    return;
483}
484
485static int rtems_tftp_eval_path( 
486  const char                        *pathname,     /* IN     */
487  int                                pathnamelen,  /* IN     */         
488  int                                flags,        /* IN     */
489  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
490)
491{
492    pathloc->handlers    = &rtems_tftp_handlers;
493
494    /*
495     * Hack to provide the illusion of directories inside the TFTP file system.
496     * Paths ending in a / are assumed to be directories.
497     */
498    if (pathname[strlen(pathname)-1] == '/') {
499        int isRelative = (pathloc->node_access != ROOT_NODE_ACCESS);
500        char *cp;
501       
502        /*
503         * Reject attempts to open() directories
504         */
505        if (flags & RTEMS_LIBIO_PERMS_RDWR)
506            rtems_set_errno_and_return_minus_one( EISDIR );
507        if (isRelative) {
508            cp = malloc (strlen(pathloc->node_access)+strlen(pathname)+1);
509            if (cp == NULL)
510                rtems_set_errno_and_return_minus_one( ENOMEM );
511            strcpy (cp, pathloc->node_access);
512            strcat (cp, pathname);
513        }
514        else {
515            cp = strdup (pathname);
516            if (cp == NULL)
517                rtems_set_errno_and_return_minus_one( ENOMEM );
518        }
519        fixPath (cp);
520        pathloc->node_access = cp;
521        return 0;
522    }
523    if (pathloc->node_access != ROOT_NODE_ACCESS)
524        pathloc->node_access = 0;
525
526    /*
527     * Reject it if it's not read-only or write-only.
528     */
529    flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
530    if ((flags != RTEMS_LIBIO_PERMS_READ) && (flags != RTEMS_LIBIO_PERMS_WRITE) )
531        rtems_set_errno_and_return_minus_one( EINVAL );
532    return 0;
533}
534
535/*
536 * The routine which does most of the work for the IMFS open handler
537 */
538static int rtems_tftp_open_worker(
539    rtems_libio_t *iop,
540    char          *full_path_name,
541    uint32_t       flags,
542    uint32_t       mode __attribute__((unused))
543)
544{
545    struct tftpStream    *tp;
546    int                  retryCount;
547    struct in_addr       farAddress;
548    int                  s;
549    int                  len;
550    char                 *cp1;
551    char                 *cp2;
552    char                 *remoteFilename;
553    rtems_interval       now;
554    rtems_status_code    sc;
555    char                 *hostname;
556
557    /*
558     * Extract the host name component
559     */
560    cp2 = full_path_name;
561    while (*cp2 == '/')
562        cp2++;
563    hostname = cp2;
564    while (*cp2 != '/') {
565        if (*cp2 == '\0')
566            return ENOENT;
567        cp2++;
568    }
569    *cp2++ = '\0';
570
571    /*
572     * Convert hostname to Internet address
573     */
574    if (strcmp (hostname, "BOOTP_HOST") == 0)
575        farAddress = rtems_bsdnet_bootp_server_address;
576    else
577        farAddress.s_addr = inet_addr (hostname);
578    if ((farAddress.s_addr == INADDR_ANY) || (farAddress.s_addr == INADDR_BROADCAST))
579        return ENOENT;
580
581    /*
582     * Extract file pathname component
583     */
584    while (*cp2 == '/')
585        cp2++;
586    if (strcmp (cp2, "BOOTP_FILE") == 0) {
587        cp2 = rtems_bsdnet_bootp_boot_file_name;
588        while (*cp2 == '/')
589            cp2++;
590    }
591    if (*cp2 == '\0')
592        return ENOENT;
593    remoteFilename = cp2;
594    if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
595        return ENOENT;       
596
597    /*
598     * Find a free stream
599     */
600    sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
601    if (sc != RTEMS_SUCCESSFUL)
602        return EBUSY;
603    for (s = 0 ; s < nStreams ; s++) {
604        if (tftpStreams[s] == NULL)
605        break;
606    }
607    if (s == nStreams) {
608        /*
609         * Reallocate stream pointers
610         * Guard against the case where realloc() returns NULL.
611         */
612        struct tftpStream **np;
613
614        np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
615        if (np == NULL) {
616            rtems_semaphore_release (tftp_mutex);
617            return ENOMEM;
618        }
619        tftpStreams = np;
620    }
621    tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
622    rtems_semaphore_release (tftp_mutex);
623    if (tp == NULL)
624        return ENOMEM;
625    iop->data0 = s;
626    iop->data1 = tp;
627
628    /*
629     * Create the socket
630     */
631    if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
632        releaseStream (s);
633        return ENOMEM;
634    }
635
636    /*
637     * Bind the socket to a local address
638     */
639    retryCount = 0;
640    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
641    for (;;) {
642        int try = (now + retryCount) % 10;
643
644        tp->myAddress.sin_family = AF_INET;
645        tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + s);
646        tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
647        if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
648            break;
649        if (++retryCount == 10) {
650            close (tp->socket);
651            releaseStream (s);
652            return EBUSY;
653        }
654    }
655
656    /*
657     * Set the UDP destination to the TFTP server
658     * port on the remote machine.
659     */
660    tp->farAddress.sin_family = AF_INET;
661    tp->farAddress.sin_addr = farAddress;
662    tp->farAddress.sin_port = htons (69);
663
664    /*
665     * Start the transfer
666     */
667    tp->firstReply = 1;
668    retryCount = 0;
669    for (;;) {
670        /*
671         * Create the request
672         */
673        if ((flags & O_ACCMODE) == O_RDONLY) {
674            tp->writing = 0;
675            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
676        }
677        else {
678            tp->writing = 1;
679            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ);
680        }
681        cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
682        cp2 = (char *) remoteFilename;
683        while ((*cp1++ = *cp2++) != '\0')
684            continue;
685        cp2 = "octet";
686        while ((*cp1++ = *cp2++) != '\0')
687            continue;
688        len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
689
690        /*
691         * Send the request
692         */
693        if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
694                    (struct sockaddr *)&tp->farAddress,
695                    sizeof tp->farAddress) < 0) {
696            close (tp->socket);
697            releaseStream (s);
698            return EIO;
699        }
700
701        /*
702         * Get reply
703         */
704        len = getPacket (tp, retryCount);
705        if (len >= (int) sizeof tp->pkbuf.tftpACK) {
706            int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
707            if (!tp->writing
708             && (opcode == TFTP_OPCODE_DATA)
709             && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
710                tp->nused = 0;
711                tp->blocknum = 1;
712                tp->nleft = len - 2 * sizeof (uint16_t  );
713                tp->eof = (tp->nleft < TFTP_BUFSIZE);
714                if (sendAck (tp) != 0) {
715                    close (tp->socket);
716                    releaseStream (s);
717                    return EIO;
718                }
719                break;
720            }
721            if (tp->writing
722             && (opcode == TFTP_OPCODE_ACK)
723             && (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) {
724                tp->nused = 0;
725                tp->blocknum = 1;
726                break;
727            }
728            if (opcode == TFTP_OPCODE_ERROR) {
729                int e = tftpErrno (tp);
730                close (tp->socket);
731                releaseStream (s);
732                return e;
733            }
734        }
735
736        /*
737         * Keep trying
738         */
739        if (++retryCount >= OPEN_RETRY_LIMIT) {
740            close (tp->socket);
741            releaseStream (s);
742            return EIO;
743        }
744    }
745    return 0;
746}
747
748/*
749 * The IMFS open handler
750 */
751static int rtems_tftp_open(
752    rtems_libio_t *iop,
753    const char    *new_name,
754    uint32_t       flags,
755    uint32_t       mode
756)
757{
758    char *full_path_name;
759    char *s1;
760    int err;
761
762    /*
763     * Tack the `current directory' on to relative paths.
764     * We know that the current directory ends in a / character.
765     */
766    if (*new_name == '/') {
767        /*
768         * Skip the TFTP filesystem prefix.
769         */
770        int len = strlen (TFTP_PATHNAME_PREFIX);
771        if (strncmp (new_name, TFTP_PATHNAME_PREFIX, len))
772            return ENOENT;
773        new_name += len;
774        s1 = "";
775    }
776    else {
777        s1 = rtems_filesystem_current.node_access;
778    }
779    full_path_name = malloc (strlen (s1) + strlen (new_name) + 1);
780    if (full_path_name == NULL)
781        return ENOMEM;
782    strcpy (full_path_name, s1);
783    strcat (full_path_name, new_name);
784    fixPath (full_path_name);
785    err = rtems_tftp_open_worker (iop, full_path_name, flags, mode);
786    free (full_path_name);
787    return err;
788}
789
790/*
791 * Read from a TFTP stream
792 */
793static ssize_t rtems_tftp_read(
794    rtems_libio_t *iop,
795    void          *buffer,
796    size_t         count
797)
798{
799    char              *bp;
800    struct tftpStream *tp = iop->data1;
801    int               retryCount;
802    int               nwant;
803
804
805    /*
806     * Read till user request is satisfied or EOF is reached
807     */
808    bp = buffer;
809    nwant = count;
810    while (nwant) {
811        if (tp->nleft) {
812            int ncopy;
813            if (nwant < tp->nleft)
814                ncopy = nwant;
815            else
816                ncopy = tp->nleft;
817            memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy);
818            tp->nused += ncopy;
819            tp->nleft -= ncopy;
820            bp += ncopy;
821            nwant -= ncopy;
822            if (nwant == 0)
823                break;
824        }
825        if (tp->eof)
826            break;
827
828        /*
829         * Wait for the next packet
830         */
831        retryCount = 0;
832        for (;;) {
833            int len = getPacket (tp, retryCount);
834            if (len >= (int)sizeof tp->pkbuf.tftpACK) {
835                int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
836                uint16_t   nextBlock = tp->blocknum + 1;
837                if ((opcode == TFTP_OPCODE_DATA)
838                 && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
839                    tp->nused = 0;
840                    tp->nleft = len - 2 * sizeof (uint16_t  );
841                    tp->eof = (tp->nleft < TFTP_BUFSIZE);
842                    tp->blocknum++;
843                    if (sendAck (tp) != 0)
844                        rtems_set_errno_and_return_minus_one( EIO );
845                    break;
846                }
847                if (opcode == TFTP_OPCODE_ERROR)
848                        rtems_set_errno_and_return_minus_one( tftpErrno (tp) );
849            }
850
851            /*
852             * Keep trying?
853             */
854            if (++retryCount == IO_RETRY_LIMIT)
855                rtems_set_errno_and_return_minus_one( EIO );
856            if (sendAck (tp) != 0)
857                rtems_set_errno_and_return_minus_one( EIO );
858        }
859    }
860    return count - nwant;
861}
862
863/*
864 * Flush a write buffer and wait for acknowledgement
865 */
866static int rtems_tftp_flush ( struct tftpStream *tp )
867{
868    int wlen, rlen;
869    int retryCount = 0;
870
871    wlen = tp->nused + 2 * sizeof (uint16_t  );
872    for (;;) {
873        tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA);
874        tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum);
875#ifdef RTEMS_TFTP_DRIVER_DEBUG
876        if (rtems_tftp_driver_debug)
877            printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused);
878#endif
879        if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0,
880                                        (struct sockaddr *)&tp->farAddress,
881                                        sizeof tp->farAddress) < 0)
882            return EIO;
883        rlen = getPacket (tp, retryCount);
884        /*
885         * Our last packet won't necessarily be acknowledged!
886         */
887        if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data))
888                return 0;
889        if (rlen >= (int)sizeof tp->pkbuf.tftpACK) {
890            int opcode = ntohs (tp->pkbuf.tftpACK.opcode);
891            if ((opcode == TFTP_OPCODE_ACK)
892             && (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) {
893                tp->nused = 0;
894                tp->blocknum++;
895                return 0;
896            }
897            if (opcode == TFTP_OPCODE_ERROR)
898                return tftpErrno (tp);
899        }
900
901        /*
902         * Keep trying?
903         */
904        if (++retryCount == IO_RETRY_LIMIT)
905            return EIO;
906    }
907}
908
909/*
910 * Close a TFTP stream
911 */
912static int rtems_tftp_close(
913    rtems_libio_t *iop
914)
915{
916    struct tftpStream *tp = iop->data1;;
917
918    if (tp->writing)
919        rtems_tftp_flush (tp);
920    if (!tp->eof && !tp->firstReply) {
921        /*
922         * Tell the other end to stop
923         */
924        rtems_interval ticksPerSecond;
925        sendStifle (tp, &tp->farAddress);
926        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
927        rtems_task_wake_after (1 + ticksPerSecond / 10);
928    }
929    close (tp->socket);
930    releaseStream (iop->data0);
931    return RTEMS_SUCCESSFUL;
932}
933
934static ssize_t rtems_tftp_write(
935    rtems_libio_t   *iop,
936    const void      *buffer,
937    size_t           count
938)
939{
940    const char        *bp;
941    struct tftpStream *tp = iop->data1;
942    int               nleft, nfree, ncopy;
943
944    /*
945     * Bail out if an error has occurred
946     */
947    if (!tp->writing)
948        return EIO;
949
950
951    /*
952     * Write till user request is satisfied
953     * Notice that the buffer is flushed as soon as it is filled rather
954     * than waiting for the next write or a close.  This ensures that
955     * the flush in close writes a less than full buffer so the far
956     * end can detect the end-of-file condition.
957     */
958    bp = buffer;
959    nleft = count;
960    while (nleft) {
961        nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
962        if (nleft < nfree)
963            ncopy = nleft;
964        else
965            ncopy = nfree;
966        memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
967        tp->nused += ncopy;
968        nleft -= ncopy;
969        bp += ncopy;
970        if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
971            int e = rtems_tftp_flush (tp);
972            if (e) {
973                tp->writing = 0;
974                rtems_set_errno_and_return_minus_one (e);
975            }
976        }
977    }
978    return count;
979}
980
981/*
982 * Dummy version to let fopen(xxxx,"w") work properly.
983 */
984static int rtems_tftp_ftruncate(
985    rtems_libio_t   *iop __attribute__((unused)),
986    rtems_off64_t    count __attribute__((unused))
987)
988{
989    return 0;
990}
991
992static rtems_filesystem_node_types_t rtems_tftp_node_type(
993     rtems_filesystem_location_info_t        *pathloc                 /* IN */
994)
995{
996    if ((pathloc->node_access == NULL)
997     || (pathloc->node_access == ROOT_NODE_ACCESS))
998        return RTEMS_FILESYSTEM_MEMORY_FILE;
999    return RTEMS_FILESYSTEM_DIRECTORY;
1000}
1001
1002static int rtems_tftp_free_node_info(
1003     rtems_filesystem_location_info_t        *pathloc                 /* IN */
1004)
1005{
1006    if (pathloc->node_access && (pathloc->node_access != ROOT_NODE_ACCESS)) {
1007        free (pathloc->node_access);
1008        pathloc->node_access = NULL;
1009    }
1010    return 0;
1011}
1012
1013
1014rtems_filesystem_operations_table  rtems_tftp_ops = {
1015    rtems_tftp_eval_path,            /* eval_path */
1016    rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
1017    NULL,                            /* link */
1018    NULL,                            /* unlink */
1019    rtems_tftp_node_type,            /* node_type */
1020    NULL,                            /* mknod */
1021    NULL,                            /* chown */
1022    rtems_tftp_free_node_info,       /* freenodinfo */
1023    NULL,                            /* mount */
1024    rtems_tftp_mount_me,             /* initialize */
1025    NULL,                            /* unmount */
1026    NULL,                            /* fsunmount */
1027    NULL,                            /* utime, */
1028    NULL,                            /* evaluate_link */
1029    NULL,                            /* symlink */
1030    NULL,                            /* readlin */
1031};
1032 
1033rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
1034    rtems_tftp_open,   /* open */     
1035    rtems_tftp_close,  /* close */   
1036    rtems_tftp_read,   /* read */     
1037    rtems_tftp_write,  /* write */   
1038    NULL,              /* ioctl */   
1039    NULL,              /* lseek */   
1040    NULL,              /* fstat */   
1041    NULL,              /* fchmod */   
1042    rtems_tftp_ftruncate, /* ftruncate */
1043    NULL,              /* fpathconf */
1044    NULL,              /* fsync */   
1045    NULL,              /* fdatasync */
1046    NULL,              /* fcntl */
1047    NULL               /* rmnod */
1048};
Note: See TracBrowser for help on using the repository browser.