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

4.104.114.84.95
Last change on this file since d4651e4 was d4651e4, checked in by Joel Sherrill <joel.sherrill@…>, on 01/04/02 at 18:25:27

2002-02-04 Ralf Corsepius <corsepiu@…>

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