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

4.104.115
Last change on this file since b25b88e7 was b25b88e7, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/28/10 at 05:50:29

Add HAVE_CONFIG_H support to let files receive configure defines.

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