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

4.104.114.84.95
Last change on this file since dfe5a4e3 was dfe5a4e3, checked in by Joel Sherrill <joel.sherrill@…>, on 10/18/01 at 18:48:55

2001-10-18 Eric Norum <eric.norum@…>

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